8322166: Files.isReadable/isWritable/isExecutable expensive when file does not exist

Reviewed-by: alanb
This commit is contained in:
Brian Burkhalter 2023-12-19 18:27:06 +00:00
parent 0f8e4e0a81
commit 51be857f3c
6 changed files with 98 additions and 50 deletions

View file

@ -80,6 +80,7 @@ import java.util.stream.StreamSupport;
import jdk.internal.util.ArraysSupport; import jdk.internal.util.ArraysSupport;
import sun.nio.ch.FileChannelImpl; import sun.nio.ch.FileChannelImpl;
import sun.nio.cs.UTF_8; import sun.nio.cs.UTF_8;
import sun.nio.fs.AbstractFileSystemProvider;
/** /**
* This class consists exclusively of static methods that operate on files, * This class consists exclusively of static methods that operate on files,
@ -2609,7 +2610,11 @@ public final class Files {
* is invoked to check read access to the file. * is invoked to check read access to the file.
*/ */
public static boolean isReadable(Path path) { public static boolean isReadable(Path path) {
return isAccessible(path, AccessMode.READ); FileSystemProvider provider = provider(path);
if (provider instanceof AbstractFileSystemProvider afsp)
return afsp.isReadable(path);
else
return isAccessible(path, AccessMode.READ);
} }
/** /**
@ -2640,7 +2645,11 @@ public final class Files {
* is invoked to check write access to the file. * is invoked to check write access to the file.
*/ */
public static boolean isWritable(Path path) { public static boolean isWritable(Path path) {
return isAccessible(path, AccessMode.WRITE); FileSystemProvider provider = provider(path);
if (provider instanceof AbstractFileSystemProvider afsp)
return afsp.isWritable(path);
else
return isAccessible(path, AccessMode.WRITE);
} }
/** /**
@ -2675,7 +2684,11 @@ public final class Files {
* checkExec} is invoked to check execute access to the file. * checkExec} is invoked to check execute access to the file.
*/ */
public static boolean isExecutable(Path path) { public static boolean isExecutable(Path path) {
return isAccessible(path, AccessMode.EXECUTE); FileSystemProvider provider = provider(path);
if (provider instanceof AbstractFileSystemProvider afsp)
return afsp.isExecutable(path);
else
return isAccessible(path, AccessMode.EXECUTE);
} }
// -- Recursive operations -- // -- Recursive operations --

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,8 +25,9 @@
package sun.nio.fs; package sun.nio.fs;
import java.nio.file.Path; import java.nio.file.AccessMode;
import java.nio.file.LinkOption; import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.spi.FileSystemProvider; import java.nio.file.spi.FileSystemProvider;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
@ -115,4 +116,40 @@ public abstract class AbstractFileSystemProvider extends FileSystemProvider {
* If path is empty, then an empty byte[] is returned. * If path is empty, then an empty byte[] is returned.
*/ */
public abstract byte[] getSunPathForSocketFile(Path path); public abstract byte[] getSunPathForSocketFile(Path path);
/**
* Tests whether a file is readable.
*/
public boolean isReadable(Path path) {
try {
checkAccess(path, AccessMode.READ);
} catch (IOException e) {
return false;
}
return true;
}
/**
* Tests whether a file is writable.
*/
public boolean isWritable(Path path) {
try {
checkAccess(path, AccessMode.WRITE);
} catch (IOException e) {
return false;
}
return true;
}
/**
* Tests whether a file is executable.
*/
public boolean isExecutable(Path path) {
try {
checkAccess(path, AccessMode.EXECUTE);
} catch (IOException e) {
return false;
}
return true;
}
} }

View file

@ -893,7 +893,9 @@ abstract class UnixFileSystem
sourceAttrs = UnixFileAttributes.get(source, false); sourceAttrs = UnixFileAttributes.get(source, false);
if (sourceAttrs.isDirectory()) { if (sourceAttrs.isDirectory()) {
// ensure source can be moved // ensure source can be moved
access(source, W_OK); int errno = access(source, W_OK);
if (errno != 0)
new UnixException(errno).rethrowAsIOException(source);
} }
} catch (UnixException x) { } catch (UnixException x) {
x.rethrowAsIOException(source); x.rethrowAsIOException(source);
@ -1024,13 +1026,11 @@ abstract class UnixFileSystem
// ensure source can be copied // ensure source can be copied
if (!sourceAttrs.isSymbolicLink() || flags.followLinks) { if (!sourceAttrs.isSymbolicLink() || flags.followLinks) {
try { // the access(2) system call always follows links so it
// the access(2) system call always follows links so it // is suppressed if the source is an unfollowed link
// is suppressed if the source is an unfollowed link int errno = access(source, R_OK);
access(source, R_OK); if (errno != 0)
} catch (UnixException exc) { new UnixException(errno).rethrowAsIOException(source);
exc.rethrowAsIOException(source);
}
} }
// get attributes of target file (don't follow links) // get attributes of target file (don't follow links)

View file

@ -349,11 +349,35 @@ public abstract class UnixFileSystemProvider
} }
mode |= X_OK; mode |= X_OK;
} }
try { int errno = access(file, mode);
access(file, mode); if (errno != 0)
} catch (UnixException exc) { new UnixException(errno).rethrowAsIOException(file);
exc.rethrowAsIOException(file); }
@Override
public boolean isReadable(Path path) {
UnixPath file = UnixPath.toUnixPath(path);
file.checkRead();
return access(file, R_OK) == 0;
}
@Override
public boolean isWritable(Path path) {
UnixPath file = UnixPath.toUnixPath(path);
file.checkWrite();
return access(file, W_OK) == 0;
}
@Override
public boolean isExecutable(Path path) {
UnixPath file = UnixPath.toUnixPath(path);
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// not cached
sm.checkExec(file.getPathForPermissionCheck());
} }
return access(file, X_OK) == 0;
} }
@Override @Override
@ -561,7 +585,7 @@ public abstract class UnixFileSystemProvider
if (Util.followLinks(options)) { if (Util.followLinks(options)) {
UnixPath file = UnixPath.toUnixPath(path); UnixPath file = UnixPath.toUnixPath(path);
file.checkRead(); file.checkRead();
return UnixNativeDispatcher.exists(file); return access(file, F_OK) == 0;
} else { } else {
return super.exists(path, options); return super.exists(path, options);
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -597,34 +597,17 @@ class UnixNativeDispatcher {
/** /**
* access(const char* path, int amode); * access(const char* path, int amode);
*/ */
static void access(UnixPath path, int amode) throws UnixException { static int access(UnixPath path, int amode) {
try (NativeBuffer buffer = copyToNativeBuffer(path)) { try (NativeBuffer buffer = copyToNativeBuffer(path)) {
long comp = Blocker.begin(); long comp = Blocker.begin();
try { try {
access0(buffer.address(), amode); return access0(buffer.address(), amode);
} finally { } finally {
Blocker.end(comp); Blocker.end(comp);
} }
} }
} }
private static native void access0(long pathAddress, int amode) throws UnixException; private static native int access0(long pathAddress, int amode);
/**
* access(constant char* path, F_OK)
*
* @return true if the file exists, false otherwise
*/
static boolean exists(UnixPath path) {
try (NativeBuffer buffer = copyToNativeBuffer(path)) {
long comp = Blocker.begin();
try {
return exists0(buffer.address());
} finally {
Blocker.end(comp);
}
}
}
private static native boolean exists0(long pathAddress);
/** /**
* struct passwd *getpwuid(uid_t uid); * struct passwd *getpwuid(uid_t uid);

View file

@ -1190,7 +1190,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_realpath0(JNIEnv* env, jclass this,
return result; return result;
} }
JNIEXPORT void JNICALL JNIEXPORT jint JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_access0(JNIEnv* env, jclass this, Java_sun_nio_fs_UnixNativeDispatcher_access0(JNIEnv* env, jclass this,
jlong pathAddress, jint amode) jlong pathAddress, jint amode)
{ {
@ -1198,17 +1198,8 @@ Java_sun_nio_fs_UnixNativeDispatcher_access0(JNIEnv* env, jclass this,
const char* path = (const char*)jlong_to_ptr(pathAddress); const char* path = (const char*)jlong_to_ptr(pathAddress);
RESTARTABLE(access(path, (int)amode), err); RESTARTABLE(access(path, (int)amode), err);
if (err == -1) {
throwUnixException(env, errno);
}
}
JNIEXPORT jboolean JNICALL return (err == -1) ? errno : 0;
Java_sun_nio_fs_UnixNativeDispatcher_exists0(JNIEnv* env, jclass this, jlong pathAddress) {
int err;
const char* path = (const char*)jlong_to_ptr(pathAddress);
RESTARTABLE(access(path, F_OK), err);
return (err == 0) ? JNI_TRUE : JNI_FALSE;
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL