8357425: (fs) SecureDirectoryStream setPermissions should use fchmodat

Reviewed-by: alanb
This commit is contained in:
Brian Burkhalter 2025-06-03 15:43:26 +00:00
parent b6f827ef05
commit 4604c86d2f
4 changed files with 125 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2025, 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
@ -347,6 +347,18 @@ class UnixNativeDispatcher {
} }
private static native void fchmod0(int fd, int mode) throws UnixException; private static native void fchmod0(int fd, int mode) throws UnixException;
/**
* fchmodat(int fd, const char *path, mode_t mode, int flag)
*/
static void fchmodat(int fd, UnixPath path, int mode, int flag)
throws UnixException {
try (NativeBuffer buffer = copyToNativeBuffer(path)) {
fchmodat0(fd, buffer.address(), mode, flag);
}
}
private static native void fchmodat0(int fd, long pathAddress, int mode, int flag)
throws UnixException;
/** /**
* futimens(int fildes, const struct timespec times[2]) * futimens(int fildes, const struct timespec times[2])
*/ */
@ -558,6 +570,14 @@ class UnixNativeDispatcher {
return (capabilities & SUPPORTS_XATTR) != 0; return (capabilities & SUPPORTS_XATTR) != 0;
} }
/**
* Supports fchmodat with AT_SYMLINK_NOFOLLOW flag
*/
static boolean fchmodatNoFollowSupported() {
return fchmodatNoFollowSupported0();
}
private static native boolean fchmodatNoFollowSupported0();
private static native int init(); private static native int init();
static { static {
jdk.internal.loader.BootLoader.loadLibrary("nio"); jdk.internal.loader.BootLoader.loadLibrary("nio");

View File

@ -414,15 +414,24 @@ class UnixSecureDirectoryStream
if (!ds.isOpen()) if (!ds.isOpen())
throw new ClosedDirectoryStreamException(); throw new ClosedDirectoryStreamException();
int fd = (file == null) ? dfd : open(); int mode = UnixFileModeAttribute.toUnixMode(perms);
if (file == null)
fchmod(dfd, mode);
else if (followLinks)
fchmodat(dfd, file, mode, 0);
else if (fchmodatNoFollowSupported())
fchmodat(dfd, file, mode, AT_SYMLINK_NOFOLLOW);
else {
int fd = open();
try { try {
fchmod(fd, UnixFileModeAttribute.toUnixMode(perms)); fchmod(fd, mode);
} catch (UnixException x) {
x.rethrowAsIOException(file);
} finally { } finally {
if (file != null && fd >= 0) if (fd >= 0)
UnixNativeDispatcher.close(fd, e-> null); UnixNativeDispatcher.close(fd, e-> null);
} }
}
} catch (UnixException x) {
x.rethrowAsIOException(file);
} finally { } finally {
ds.readLock().unlock(); ds.readLock().unlock();
} }

View File

@ -398,6 +398,16 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
return capabilities; return capabilities;
} }
JNIEXPORT jboolean JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_fchmodatNoFollowSupported0(JNIEnv* env, jclass this) {
#if defined(__linux__)
// Linux recognizes but does not support the AT_SYMLINK_NOFOLLOW flag
return JNI_FALSE;
#else
return JNI_TRUE;
#endif
}
JNIEXPORT jbyteArray JNICALL JNIEXPORT jbyteArray JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_getcwd(JNIEnv* env, jclass this) { Java_sun_nio_fs_UnixNativeDispatcher_getcwd(JNIEnv* env, jclass this) {
jbyteArray result = NULL; jbyteArray result = NULL;
@ -797,6 +807,19 @@ Java_sun_nio_fs_UnixNativeDispatcher_fchmod0(JNIEnv* env, jclass this, jint file
} }
} }
JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_fchmodat0(JNIEnv* env, jclass this,
jint fd, jlong pathAddress, jint mode, jint flag)
{
int err;
const char* path = (const char*)jlong_to_ptr(pathAddress);
RESTARTABLE(fchmodat((int)fd, path, (mode_t)mode, (int)flag), err);
if (err == -1) {
throwUnixException(env, errno);
}
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_chown0(JNIEnv* env, jclass this, Java_sun_nio_fs_UnixNativeDispatcher_chown0(JNIEnv* env, jclass this,
jlong pathAddress, jint uid, jint gid) jlong pathAddress, jint uid, jint gid)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2025, 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
@ -22,10 +22,12 @@
*/ */
/* @test /* @test
* @bug 4313887 6838333 8343020 * @bug 4313887 6838333 8343020 8357425
* @summary Unit test for java.nio.file.SecureDirectoryStream * @summary Unit test for java.nio.file.SecureDirectoryStream
* @requires (os.family == "linux" | os.family == "mac") * @requires (os.family == "linux" | os.family == "mac")
* @library .. * @library .. /test/lib
* @build jdk.test.lib.Platform
* @run main SecureDS
*/ */
import java.nio.file.*; import java.nio.file.*;
@ -37,6 +39,8 @@ import java.nio.channels.*;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import jdk.test.lib.Platform;
public class SecureDS { public class SecureDS {
static boolean supportsSymbolicLinks; static boolean supportsSymbolicLinks;
@ -54,6 +58,7 @@ public class SecureDS {
// run tests // run tests
doBasicTests(dir); doBasicTests(dir);
doMoveTests(dir); doMoveTests(dir);
doSetPermissions(dir);
miscTests(dir); miscTests(dir);
} finally { } finally {
@ -172,6 +177,62 @@ public class SecureDS {
delete(dir2); delete(dir2);
} }
// Exercise setting permisions on the SecureDirectoryStream's view
static void doSetPermissions(Path dir) throws IOException {
Path aDir = createDirectory(dir.resolve("dir"));
Set<PosixFilePermission> noperms = EnumSet.noneOf(PosixFilePermission.class);
Set<PosixFilePermission> permsDir = getPosixFilePermissions(aDir);
try (SecureDirectoryStream<Path> stream =
(SecureDirectoryStream<Path>)newDirectoryStream(aDir);) {
// Test setting permission on directory with no permissions
setPosixFilePermissions(aDir, noperms);
assertTrue(getPosixFilePermissions(aDir).equals(noperms));
PosixFileAttributeView view = stream.getFileAttributeView(PosixFileAttributeView.class);
view.setPermissions(permsDir);
assertTrue(getPosixFilePermissions(aDir).equals(permsDir));
if (supportsSymbolicLinks) {
// Create a file and a link to the file
Path fileEntry = Path.of("file");
Path file = createFile(aDir.resolve(fileEntry));
Set<PosixFilePermission> permsFile = getPosixFilePermissions(file);
Path linkEntry = Path.of("link");
Path link = createSymbolicLink(aDir.resolve(linkEntry), fileEntry);
Set<PosixFilePermission> permsLink = getPosixFilePermissions(link, NOFOLLOW_LINKS);
// Test following link to file
view = stream.getFileAttributeView(link, PosixFileAttributeView.class);
view.setPermissions(noperms);
assertTrue(getPosixFilePermissions(file).equals(noperms));
assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(permsLink));
view.setPermissions(permsFile);
assertTrue(getPosixFilePermissions(file).equals(permsFile));
assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(permsLink));
// Symbolic link permissions do not apply on Linux
if (!Platform.isLinux()) {
// Test not following link to file
view = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS);
view.setPermissions(noperms);
assertTrue(getPosixFilePermissions(file).equals(permsFile));
assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(noperms));
view.setPermissions(permsLink);
assertTrue(getPosixFilePermissions(file).equals(permsFile));
assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(permsLink));
}
delete(link);
delete(file);
}
// clean-up
delete(aDir);
}
}
// Exercise SecureDirectoryStream's move method // Exercise SecureDirectoryStream's move method
static void doMoveTests(Path dir) throws IOException { static void doMoveTests(Path dir) throws IOException {
Path dir1 = createDirectory(dir.resolve("dir1")); Path dir1 = createDirectory(dir.resolve("dir1"));