From e1a3967870c0fc5171e41ebe686eb94c8158dfb2 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 2 Jul 2026 05:35:46 +0000 Subject: [PATCH] 8364322: (fs) fchmodat support for AT_SYMLINK_NOFOLLOW flag too pessimistic on Linux Reviewed-by: alanb --- .../sun/nio/fs/UnixNativeDispatcher.java | 10 ++++---- .../native/libnio/fs/UnixNativeDispatcher.c | 17 +++++++------ .../nio/file/DirectoryStream/SecureDS.java | 24 ++++++++++++++----- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java index 2d72aeb2ee9..ed28e1a1fe7 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -555,9 +555,10 @@ class UnixNativeDispatcher { /** * Capabilities */ - private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls - private static final int SUPPORTS_XATTR = 1 << 3; - private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features + private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls + private static final int SUPPORTS_FCHMODAT_NOFOLLOW = 1 << 2; + private static final int SUPPORTS_XATTR = 1 << 3; + private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features private static final int capabilities; /** @@ -585,9 +586,8 @@ class UnixNativeDispatcher { * Supports fchmodat with AT_SYMLINK_NOFOLLOW flag */ static boolean fchmodatNoFollowSupported() { - return fchmodatNoFollowSupported0(); + return (capabilities & SUPPORTS_FCHMODAT_NOFOLLOW) != 0; } - private static native boolean fchmodatNoFollowSupported0(); private static native int init(); static { diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 4b5cfabebfb..aba16118988 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -388,17 +388,16 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif - 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; + // Linux 6.6+ supports AT_SYMLINK_NOFOLLOW. glibc 2.32+ also provides emulation for older kernels. + if (fchmodat(AT_FDCWD, "", 0, AT_SYMLINK_NOFOLLOW) == 0 || errno != ENOTSUP) { + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; + } #else - return JNI_TRUE; + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; #endif + + return capabilities; } JNIEXPORT jbyteArray JNICALL diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 870a84a8927..f3321a8c04d 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -212,6 +212,13 @@ public class SecureDS { Path link = createSymbolicLink(aDir.resolve(linkEntry), fileEntry); Set permsLink = getPosixFilePermissions(link, NOFOLLOW_LINKS); + // Test setting permissions on a regular file through the no-follow view + view = stream.getFileAttributeView(fileEntry, PosixFileAttributeView.class, NOFOLLOW_LINKS); + view.setPermissions(noperms); + assertEquals(noperms, getPosixFilePermissions(file)); + view.setPermissions(permsFile); + assertEquals(permsFile, getPosixFilePermissions(file)); + // Test following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class); view.setPermissions(noperms); @@ -220,14 +227,19 @@ public class SecureDS { view.setPermissions(permsFile); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - // 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); + + // Test not following link to file + var linkView = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); + if (Platform.isLinux()) { + // Symbolic link permissions do not apply on Linux + assertThrows(IOException.class, () -> linkView.setPermissions(noperms)); + assertEquals(permsFile, getPosixFilePermissions(file)); + assertThrows(IOException.class, () -> linkView.setPermissions(permsLink)); + } else { + linkView.setPermissions(noperms); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(noperms, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - view.setPermissions(permsLink); + linkView.setPermissions(permsLink); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); }