8283335: Add exists and readAttributesIfExists methods to FileSystemProvider

Reviewed-by: alanb
This commit is contained in:
Lance Andersen 2022-07-05 19:45:08 +00:00
parent c45d613faa
commit d48694d0f3
13 changed files with 577 additions and 175 deletions

View file

@ -80,7 +80,6 @@ import java.util.stream.StreamSupport;
import jdk.internal.util.ArraysSupport;
import sun.nio.ch.FileChannelImpl;
import sun.nio.cs.UTF_8;
import sun.nio.fs.AbstractFileSystemProvider;
/**
* This class consists exclusively of static methods that operate on files,
@ -1594,7 +1593,7 @@ public final class Files {
byte[] buffer1 = new byte[BUFFER_SIZE];
byte[] buffer2 = new byte[BUFFER_SIZE];
try (InputStream in1 = Files.newInputStream(path);
InputStream in2 = Files.newInputStream(path2);) {
InputStream in2 = Files.newInputStream(path2)) {
long totalRead = 0;
while (true) {
int nRead1 = in1.readNBytes(buffer1, 0, BUFFER_SIZE);
@ -2310,14 +2309,10 @@ public final class Files {
* method denies read access to the file.
*/
public static boolean isDirectory(Path path, LinkOption... options) {
if (options.length == 0) {
FileSystemProvider provider = provider(path);
if (provider instanceof AbstractFileSystemProvider)
return ((AbstractFileSystemProvider)provider).isDirectory(path);
}
try {
return readAttributes(path, BasicFileAttributes.class, options).isDirectory();
var attrs = provider(path)
.readAttributesIfExists(path, BasicFileAttributes.class, options);
return (attrs != null) && attrs.isDirectory();
} catch (IOException ioe) {
return false;
}
@ -2353,14 +2348,10 @@ public final class Files {
* method denies read access to the file.
*/
public static boolean isRegularFile(Path path, LinkOption... options) {
if (options.length == 0) {
FileSystemProvider provider = provider(path);
if (provider instanceof AbstractFileSystemProvider)
return ((AbstractFileSystemProvider)provider).isRegularFile(path);
}
try {
return readAttributes(path, BasicFileAttributes.class, options).isRegularFile();
var attrs = provider(path)
.readAttributesIfExists(path, BasicFileAttributes.class, options);
return (attrs != null) && attrs.isRegularFile();
} catch (IOException ioe) {
return false;
}
@ -2502,7 +2493,7 @@ public final class Files {
* the path to the file to test
* @param options
* options indicating how symbolic links are handled
* .
*
* @return {@code true} if the file exists; {@code false} if the file does
* not exist or its existence cannot be determined.
*
@ -2515,27 +2506,7 @@ public final class Files {
* @see FileSystemProvider#checkAccess
*/
public static boolean exists(Path path, LinkOption... options) {
if (options.length == 0) {
FileSystemProvider provider = provider(path);
if (provider instanceof AbstractFileSystemProvider)
return ((AbstractFileSystemProvider)provider).exists(path);
}
try {
if (followLinks(options)) {
provider(path).checkAccess(path);
} else {
// attempt to read attributes without following links
readAttributes(path, BasicFileAttributes.class,
LinkOption.NOFOLLOW_LINKS);
}
// file exists
return true;
} catch (IOException x) {
// does not exist or unable to determine if file exists
return false;
}
return provider(path).exists(path, options);
}
/**

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1170,4 +1170,122 @@ public abstract class FileSystemProvider {
public abstract void setAttribute(Path path, String attribute,
Object value, LinkOption... options)
throws IOException;
/**
* Tests whether a file exists. This method works in exactly the
* manner specified by the {@link Files#exists(Path, LinkOption...)} method.
*
* @implSpec
* The default implementation of this method invokes the
* {@link #checkAccess(Path, AccessMode...)} method when symbolic links
* are followed. If the option {@link LinkOption#NOFOLLOW_LINKS NOFOLLOW_LINKS}
* is present then symbolic links are not followed and the method
* {@link #readAttributes(Path, Class, LinkOption...)} is called
* to determine whether a file exists.
*
* @param path
* the path to the file to test
* @param options
* options indicating how symbolic links are handled
*
* @return {@code true} if the file exists; {@code false} if the file does
* not exist or its existence cannot be determined.
*
* @throws SecurityException
* In the case of the default provider, the {@link
* SecurityManager#checkRead(String)} is invoked to check
* read access to the file.
*
* @since 20
*/
public boolean exists(Path path, LinkOption... options) {
try {
if (followLinks(options)) {
this.checkAccess(path);
} else {
// attempt to read attributes without following links
readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
}
// file exists
return true;
} catch (IOException x) {
// does not exist or unable to determine if file exists
return false;
}
}
/**
* Reads a file's attributes as a bulk operation if it exists.
*
* <p> The {@code type} parameter is the type of the attributes required
* and this method returns an instance of that type if supported. All
* implementations support a basic set of file attributes and so invoking
* this method with a {@code type} parameter of {@code
* BasicFileAttributes.class} will not throw {@code
* UnsupportedOperationException}.
*
* <p> The {@code options} array may be used to indicate how symbolic links
* are handled for the case that the file is a symbolic link. By default,
* symbolic links are followed and the file attribute of the final target
* of the link is read. If the option {@link LinkOption#NOFOLLOW_LINKS
* NOFOLLOW_LINKS} is present then symbolic links are not followed.
*
* <p> It is implementation specific if all file attributes are read as an
* atomic operation with respect to other file system operations.
*
* @implSpec
* The default implementation of this method invokes the
* {@link #readAttributes(Path, Class, LinkOption...)} method
* to read the file's attributes.
*
* @param <A>
* The {@code BasicFileAttributes} type
* @param path
* the path to the file
* @param type
* the {@code Class} of the file attributes required
* to read
* @param options
* options indicating how symbolic links are handled
*
* @return the file attributes or null if the file does not exist
*
* @throws UnsupportedOperationException
* if an attributes of the given type are not supported
* @throws IOException
* if an I/O error occurs
* @throws SecurityException
* In the case of the default provider, a security manager is
* installed, its {@link SecurityManager#checkRead(String) checkRead}
* method is invoked to check read access to the file. If this
* method is invoked to read security sensitive attributes then the
* security manager may be invoked to check for additional permissions.
*
* @since 20
*/
public <A extends BasicFileAttributes> A readAttributesIfExists(Path path,
Class<A> type,
LinkOption... options)
throws IOException
{
try {
return readAttributes(path, type, options);
} catch (NoSuchFileException ignore) {
return null;
}
}
private static boolean followLinks(LinkOption... options) {
boolean followLinks = true;
for (LinkOption opt: options) {
if (opt == LinkOption.NOFOLLOW_LINKS) {
followLinks = false;
continue;
}
if (opt == null)
throw new NullPointerException();
throw new AssertionError("Should not get here");
}
return followLinks;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,7 +27,6 @@ package sun.nio.fs;
import java.nio.file.Path;
import java.nio.file.LinkOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.spi.FileSystemProvider;
import java.io.IOException;
import java.util.Map;
@ -110,51 +109,6 @@ public abstract class AbstractFileSystemProvider extends FileSystemProvider {
return implDelete(file, false);
}
/**
* Tests whether a file is a directory.
*
* @return {@code true} if the file is a directory; {@code false} if
* the file does not exist, is not a directory, or it cannot
* be determined if the file is a directory or not.
*/
public boolean isDirectory(Path file) {
try {
return readAttributes(file, BasicFileAttributes.class).isDirectory();
} catch (IOException ioe) {
return false;
}
}
/**
* Tests whether a file is a regular file with opaque content.
*
* @return {@code true} if the file is a regular file; {@code false} if
* the file does not exist, is not a regular file, or it
* cannot be determined if the file is a regular file or not.
*/
public boolean isRegularFile(Path file) {
try {
return readAttributes(file, BasicFileAttributes.class).isRegularFile();
} catch (IOException ioe) {
return false;
}
}
/**
* Checks the existence of a file.
*
* @return {@code true} if the file exists; {@code false} if the file does
* not exist or its existence cannot be determined.
*/
public boolean exists(Path file) {
try {
checkAccess(file);
return true;
} catch (IOException ioe) {
return false;
}
}
/**
* Returns a path name as bytes for a Unix domain socket.
* Different encodings may be used for these names on some platforms.

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -74,6 +74,19 @@ class UnixFileAttributes
return attrs;
}
// get the UnixFileAttributes for a given file. Returns null if the file does not exist.
static UnixFileAttributes getIfExists(UnixPath path) throws UnixException {
UnixFileAttributes attrs = new UnixFileAttributes();
int errno = UnixNativeDispatcher.stat2(path, attrs);
if (errno == 0) {
return attrs;
} else if (errno == UnixConstants.ENOENT) {
return null;
} else {
throw new UnixException(errno);
}
}
// get the UnixFileAttributes for an open file
static UnixFileAttributes get(int fd) throws UnixException {
UnixFileAttributes attrs = new UnixFileAttributes();
@ -251,16 +264,6 @@ class UnixFileAttributes
return UnixAsBasicFileAttributes.wrap(this);
}
// unwrap BasicFileAttributes to get the underlying UnixFileAttributes
// object. Returns null is not wrapped.
static UnixFileAttributes toUnixFileAttributes(BasicFileAttributes attrs) {
if (attrs instanceof UnixFileAttributes)
return (UnixFileAttributes)attrs;
if (attrs instanceof UnixAsBasicFileAttributes) {
return ((UnixAsBasicFileAttributes)attrs).unwrap();
}
return null;
}
// wrap a UnixFileAttributes object as a BasicFileAttributes
private static class UnixAsBasicFileAttributes implements BasicFileAttributes {
@ -274,9 +277,6 @@ class UnixFileAttributes
return new UnixAsBasicFileAttributes(attrs);
}
UnixFileAttributes unwrap() {
return attrs;
}
@Override
public FileTime lastModifiedTime() {

View file

@ -126,7 +126,7 @@ public abstract class UnixFileSystemProvider
return (V) UnixFileAttributeViews.createOwnerView(file, followLinks);
if (type == null)
throw new NullPointerException();
return (V) null;
return null;
}
@Override
@ -148,6 +148,25 @@ public abstract class UnixFileSystemProvider
return (A) getFileAttributeView(file, view, options).readAttributes();
}
@Override
public <A extends BasicFileAttributes> A readAttributesIfExists(Path path,
Class<A> type,
LinkOption... options)
throws IOException
{
if (type == BasicFileAttributes.class && Util.followLinks(options)) {
UnixPath file = UnixPath.toUnixPath(path);
try {
@SuppressWarnings("unchecked")
A attrs = (A) UnixFileAttributes.getIfExists(file);
return attrs;
} catch (UnixException e) {
e.rethrowAsIOException(file);
}
}
return super.readAttributesIfExists(path, type, options);
}
@Override
protected DynamicFileAttributeView getFileAttributeView(Path obj,
String name,
@ -281,10 +300,9 @@ public abstract class UnixFileSystemProvider
} else {
for (AccessMode mode: modes) {
switch (mode) {
case READ : r = true; break;
case WRITE : w = true; break;
case EXECUTE : x = true; break;
default: throw new AssertionError("Should not get here");
case READ -> r = true;
case WRITE -> w = true;
case EXECUTE -> x = true;
}
}
}
@ -321,9 +339,8 @@ public abstract class UnixFileSystemProvider
return true;
if (obj2 == null)
throw new NullPointerException();
if (!(obj2 instanceof UnixPath))
if (!(obj2 instanceof UnixPath file2))
return false;
UnixPath file2 = (UnixPath)obj2;
// check security manager access to both files
file1.checkRead();
@ -516,28 +533,16 @@ public abstract class UnixFileSystemProvider
}
@Override
public final boolean isDirectory(Path obj) {
UnixPath file = UnixPath.toUnixPath(obj);
file.checkRead();
int mode = UnixNativeDispatcher.stat(file);
return ((mode & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR);
}
public boolean exists(Path path, LinkOption... options) {
if (Util.followLinks(options)) {
UnixPath file = UnixPath.toUnixPath(path);
file.checkRead();
return UnixNativeDispatcher.exists(file);
} else {
return super.exists(path, options);
}
@Override
public final boolean isRegularFile(Path obj) {
UnixPath file = UnixPath.toUnixPath(obj);
file.checkRead();
int mode = UnixNativeDispatcher.stat(file);
return ((mode & UnixConstants.S_IFMT) == UnixConstants.S_IFREG);
}
@Override
public final boolean exists(Path obj) {
UnixPath file = UnixPath.toUnixPath(obj);
file.checkRead();
return UnixNativeDispatcher.exists(file);
}
/**
* Returns a {@code FileTypeDetector} for this platform.
*/

View file

@ -318,33 +318,28 @@ class UnixNativeDispatcher {
try (NativeBuffer buffer = copyToNativeBuffer(path)) {
long comp = Blocker.begin();
try {
stat0(buffer.address(), attrs);
int errno = stat0(buffer.address(), attrs);
if (errno != 0) {
throw new UnixException(errno);
}
} finally {
Blocker.end(comp);
}
}
}
private static native void stat0(long pathAddress, UnixFileAttributes attrs)
throws UnixException;
/**
* stat(const char* path, struct stat* buf)
*
* @return st_mode (file type and mode) or 0 if an error occurs.
*/
static int stat(UnixPath path) {
static int stat2(UnixPath path, UnixFileAttributes attrs) {
try (NativeBuffer buffer = copyToNativeBuffer(path)) {
long comp = Blocker.begin();
try {
return stat1(buffer.address());
return stat0(buffer.address(), attrs);
} finally {
Blocker.end(comp);
}
}
}
private static native int stat1(long pathAddress);
private static native int stat0(long pathAddress, UnixFileAttributes attrs);
/**
* lstat(const char* path, struct stat* buf)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -118,10 +118,11 @@ class UnixUriUtils {
if (sb.charAt(sb.length()-1) != '/') {
try {
up.checkRead();
int mode = UnixNativeDispatcher.stat(up);
if ((mode & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR)
UnixFileAttributes attrs = UnixFileAttributes.getIfExists(up);
if (attrs != null
&& ((attrs.mode() & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR))
sb.append('/');
} catch (SecurityException ignore) { }
} catch (UnixException | SecurityException ignore) { }
}
try {

View file

@ -521,7 +521,7 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
#endif
}
JNIEXPORT void JNICALL
JNIEXPORT jint JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this,
jlong pathAddress, jobject attrs)
{
@ -530,24 +530,11 @@ Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this,
const char* path = (const char*)jlong_to_ptr(pathAddress);
RESTARTABLE(stat64(path, &buf), err);
if (err == -1) {
throwUnixException(env, errno);
} else {
if (err == 0) {
prepAttributes(env, &buf, attrs);
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_stat1(JNIEnv* env, jclass this, jlong pathAddress) {
int err;
struct stat64 buf;
const char* path = (const char*)jlong_to_ptr(pathAddress);
RESTARTABLE(stat64(path, &buf), err);
if (err == -1) {
return 0;
} else {
return (jint)buf.st_mode;
return errno;
}
}