From 36eb8edadf6120a89a0c978c8d0e33b2f8fb86bb Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Thu, 21 May 2026 08:13:48 +0000 Subject: [PATCH] 8383623: (fs) Update Windows file system provider to use GetFileInformationByName where possible Reviewed-by: alanb --- .../classes/sun/nio/fs/WindowsConstants.java | 8 +- .../sun/nio/fs/WindowsFileAttributes.java | 74 ++++++++++- .../sun/nio/fs/WindowsNativeDispatcher.java | 30 ++++- .../libnio/fs/WindowsNativeDispatcher.c | 124 +++++++++++++----- 4 files changed, 202 insertions(+), 34 deletions(-) diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java index 0c09a80e99e..8e713464f19 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java @@ -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 @@ -115,6 +115,12 @@ class WindowsConstants { public static final int ERROR_NOT_A_REPARSE_POINT = 4390; public static final int ERROR_INVALID_REPARSE_DATA = 4392; + // FILE_INFO_BY_NAME_CLASS enum values for GetFileInformationByName() + public static final int FileStatByNameInfo = 0; + public static final int FileStatLxByNameInfo = 1; + public static final int FileCaseSensitiveByNameInfo = 2; + public static final int FileStatBasicByNameInfo = 3; + // notify filters public static final int FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001; public static final int FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002; diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java index 3c94e8bc4a2..76422c9ecc9 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java @@ -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 @@ -107,6 +107,35 @@ class WindowsFileAttributes private static final short OFFSETOF_FIND_DATA_SIZELOW = 32; private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36; + /* + * typedef struct _FILE_STAT_BASIC_INFORMATION { + * LARGE_INTEGER FileId; // offset = 0 + * LARGE_INTEGER CreationTime; // offset = 8 + * LARGE_INTEGER LastAccessTime; // offset = 16 + * LARGE_INTEGER LastWriteTime; // offset = 24 + * LARGE_INTEGER ChangeTime; // offset = 32 + * LARGE_INTEGER AllocationSize; // offset = 40 + * LARGE_INTEGER EndOfFile; // offset = 48 + * ULONG FileAttributes; // offset = 56 + * ULONG ReparseTag; // offset = 60 + * ULONG NumberOfLinks; // offset = 64 + * ULONG DeviceType; // offset = 68 + * ULONG DeviceCharacteristics; // offset = 72 + * ULONG Reserved; // offset = 76 + * LARGE_INTEGER VolumeSerialNumber; // offset = 80 + * FILE_ID_128 FileId128; // offset = 88 + * } FILE_STAT_BASIC_INFORMATION; + */ + private static final short SIZEOF_STAT_BASIC_INFO = 104; + private static final short OFFSETOF_STAT_BASIC_INFO_FILEID = 0; + private static final short OFFSETOF_STAT_BASIC_INFO_CREATETIME = 8; + private static final short OFFSETOF_STAT_BASIC_INFO_LASTACCESSTIME = 16; + private static final short OFFSETOF_STAT_BASIC_INFO_LASTWRITETIME = 24; + private static final short OFFSETOF_STAT_BASIC_INFO_ENDOFFILE = 48; + private static final short OFFSETOF_STAT_BASIC_INFO_ATTRIBUTES = 56; + private static final short OFFSETOF_STAT_BASIC_INFO_REPARSETAG = 60; + private static final short OFFSETOF_STAT_BASIC_INFO_VOLSERIAL = 80; + // used to adjust values between Windows and java epochs private static final long WINDOWS_EPOCH_IN_MICROS = -11644473600000000L; private static final long WINDOWS_EPOCH_IN_100NS = -116444736000000000L; @@ -226,6 +255,32 @@ class WindowsFileAttributes } + /** + * Create a WindowsFileAttributes from a FILE_STAT_BASIC_INFORMATION structure + */ + static WindowsFileAttributes fromStatBasicInfo(long address) { + int fileAttrs = unsafe.getInt(address + OFFSETOF_STAT_BASIC_INFO_ATTRIBUTES); + long creationTime = unsafe.getLong(address + OFFSETOF_STAT_BASIC_INFO_CREATETIME); + long lastAccessTime = unsafe.getLong(address + OFFSETOF_STAT_BASIC_INFO_LASTACCESSTIME); + long lastWriteTime = unsafe.getLong(address + OFFSETOF_STAT_BASIC_INFO_LASTWRITETIME); + long size = unsafe.getLong(address + OFFSETOF_STAT_BASIC_INFO_ENDOFFILE); + int reparseTag = isReparsePoint(fileAttrs) ? + unsafe.getInt(address + OFFSETOF_STAT_BASIC_INFO_REPARSETAG) : 0; + int volSerialNumber = unsafe.getInt(address + OFFSETOF_STAT_BASIC_INFO_VOLSERIAL); + int fileIndexLow = unsafe.getInt(address + OFFSETOF_STAT_BASIC_INFO_FILEID); + int fileIndexHigh = unsafe.getInt(address + OFFSETOF_STAT_BASIC_INFO_FILEID + 4); + + return new WindowsFileAttributes(fileAttrs, + creationTime, + lastAccessTime, + lastWriteTime, + size, + reparseTag, + volSerialNumber, + fileIndexHigh, + fileIndexLow); + } + /** * Allocates a native buffer for a WIN32_FIND_DATA structure */ @@ -332,6 +387,23 @@ class WindowsFileAttributes } } + if (supportsGetFileInformationByName()) { + try (NativeBuffer buffer = NativeBuffers.getNativeBuffer(SIZEOF_STAT_BASIC_INFO)) { + long addr = buffer.address(); + GetFileInformationByName(path.getPathForWin32Calls(), + FileStatBasicByNameInfo, addr, + SIZEOF_STAT_BASIC_INFO); + + // GetFileInformationByName() doesn't follow reparse points so if + // we discover that this is a reparse point and if we're being asked + // to follow links, then drop to the slow path. + int fileAttrs = unsafe.getInt(addr + OFFSETOF_STAT_BASIC_INFO_ATTRIBUTES); + if (!isReparsePoint(fileAttrs) || !followLinks) { + return fromStatBasicInfo(addr); + } + } + } + // file is reparse point so need to open file to get attributes long handle = path.openForReadAttributeAccess(followLinks); try { diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java b/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java index ba1b7b1aa9f..d6f68b72ed3 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java @@ -367,6 +367,24 @@ class WindowsNativeDispatcher { private static native void GetFileAttributesEx0(long lpFileName, long address) throws WindowsException; + /** + * GetFileInformationByName( + * PCWSTR FileName, + * FILE_INFO_BY_NAME_CLASS FileInformationClass, + * PVOID FileInfoBuffer, + * ULONG FileInfoBufferSize + * ) + */ + static void GetFileInformationByName(String path, int infoClass, long address, int size) + throws WindowsException + { + try (NativeBuffer buffer = asNativeBuffer(path)) { + GetFileInformationByName0(buffer.address(), infoClass, address, size); + } + } + private static native void GetFileInformationByName0(long pathAddress, + int infoClass, long infoAddress, int infoSize) throws WindowsException; + /** * SetFileTime( * HANDLE hFile, @@ -1090,15 +1108,23 @@ class WindowsNativeDispatcher { return buffer; } + // -- capabilities -- + private static final int SUPPORTS_GETFILEINFORMATIONBYNAME = 1 << 1; + private static final int capabilities; + + static boolean supportsGetFileInformationByName() { + return (capabilities & SUPPORTS_GETFILEINFORMATIONBYNAME) != 0; + } + // -- native library initialization -- - private static native void initIDs(); + private static native int init(); static { // nio.dll has dependency on net.dll jdk.internal.loader.BootLoader.loadLibrary("net"); jdk.internal.loader.BootLoader.loadLibrary("nio"); - initIDs(); + capabilities = init(); } } diff --git a/src/java.base/windows/native/libnio/fs/WindowsNativeDispatcher.c b/src/java.base/windows/native/libnio/fs/WindowsNativeDispatcher.c index b6e6a9751c0..07452cbef0a 100644 --- a/src/java.base/windows/native/libnio/fs/WindowsNativeDispatcher.c +++ b/src/java.base/windows/native/libnio/fs/WindowsNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, 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 @@ -79,75 +79,122 @@ static void throwWindowsException(JNIEnv* env, DWORD lastError) { } } +#if !defined(NTDDI_WIN10_NI) || !(NTDDI_VERSION >= NTDDI_WIN10_NI) + +typedef struct _FILE_STAT_BASIC_INFORMATION { + LARGE_INTEGER FileId; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + ULONG ReparseTag; + ULONG NumberOfLinks; + ULONG DeviceType; + ULONG DeviceCharacteristics; + ULONG Reserved; + LARGE_INTEGER VolumeSerialNumber; + FILE_ID_128 FileId128; +} FILE_STAT_BASIC_INFORMATION; + +typedef enum _FILE_INFO_BY_NAME_CLASS { + FileStatByNameInfo, + FileStatLxByNameInfo, + FileCaseSensitiveByNameInfo, + FileStatBasicByNameInfo, + MaximumFileInfoByNameClass +} FILE_INFO_BY_NAME_CLASS; + +#endif /* !defined(NTDDI_WIN10_NI) || !(NTDDI_VERSION >= NTDDI_WIN10_NI) */ + +typedef BOOL (WINAPI *PGetFileInformationByName)( + PCWSTR, FILE_INFO_BY_NAME_CLASS, PVOID, ULONG); + +static PGetFileInformationByName pGetFileInformationByName = NULL; + /** * Initializes jfieldIDs and get address of Win32 calls that are located * at runtime. */ -JNIEXPORT void JNICALL -Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this) +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_init(JNIEnv* env, jclass this) { jclass clazz; + jint capabilities = 0; clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstFile"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); findFirst_handle = (*env)->GetFieldID(env, clazz, "handle", "J"); - CHECK_NULL(findFirst_handle); + CHECK_NULL_RETURN(findFirst_handle, 0); findFirst_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); - CHECK_NULL(findFirst_name); + CHECK_NULL_RETURN(findFirst_name, 0); findFirst_attributes = (*env)->GetFieldID(env, clazz, "attributes", "I"); - CHECK_NULL(findFirst_attributes); + CHECK_NULL_RETURN(findFirst_attributes, 0); clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstStream"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); findStream_handle = (*env)->GetFieldID(env, clazz, "handle", "J"); - CHECK_NULL(findStream_handle); + CHECK_NULL_RETURN(findStream_handle, 0); findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); - CHECK_NULL(findStream_name); + CHECK_NULL_RETURN(findStream_name, 0); clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); volumeInfo_fsName = (*env)->GetFieldID(env, clazz, "fileSystemName", "Ljava/lang/String;"); - CHECK_NULL(volumeInfo_fsName); + CHECK_NULL_RETURN(volumeInfo_fsName, 0); volumeInfo_volName = (*env)->GetFieldID(env, clazz, "volumeName", "Ljava/lang/String;"); - CHECK_NULL(volumeInfo_volName); + CHECK_NULL_RETURN(volumeInfo_volName, 0); volumeInfo_volSN = (*env)->GetFieldID(env, clazz, "volumeSerialNumber", "I"); - CHECK_NULL(volumeInfo_volSN); + CHECK_NULL_RETURN(volumeInfo_volSN, 0); volumeInfo_flags = (*env)->GetFieldID(env, clazz, "flags", "I"); - CHECK_NULL(volumeInfo_flags); + CHECK_NULL_RETURN(volumeInfo_flags, 0); clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$DiskFreeSpace"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); diskSpace_bytesAvailable = (*env)->GetFieldID(env, clazz, "freeBytesAvailable", "J"); - CHECK_NULL(diskSpace_bytesAvailable); + CHECK_NULL_RETURN(diskSpace_bytesAvailable, 0); diskSpace_totalBytes = (*env)->GetFieldID(env, clazz, "totalNumberOfBytes", "J"); - CHECK_NULL(diskSpace_totalBytes); + CHECK_NULL_RETURN(diskSpace_totalBytes, 0); diskSpace_totalFree = (*env)->GetFieldID(env, clazz, "totalNumberOfFreeBytes", "J"); - CHECK_NULL(diskSpace_totalFree); + CHECK_NULL_RETURN(diskSpace_totalFree, 0); diskSpace_bytesPerSector = (*env)->GetFieldID(env, clazz, "bytesPerSector", "J"); - CHECK_NULL(diskSpace_bytesPerSector); + CHECK_NULL_RETURN(diskSpace_bytesPerSector, 0); clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$Account"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); account_domain = (*env)->GetFieldID(env, clazz, "domain", "Ljava/lang/String;"); - CHECK_NULL(account_domain); + CHECK_NULL_RETURN(account_domain, 0); account_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); - CHECK_NULL(account_name); + CHECK_NULL_RETURN(account_name, 0); account_use = (*env)->GetFieldID(env, clazz, "use", "I"); - CHECK_NULL(account_use); + CHECK_NULL_RETURN(account_use, 0); clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$AclInformation"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); aclInfo_aceCount = (*env)->GetFieldID(env, clazz, "aceCount", "I"); - CHECK_NULL(aclInfo_aceCount); + CHECK_NULL_RETURN(aclInfo_aceCount, 0); clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$CompletionStatus"); - CHECK_NULL(clazz); + CHECK_NULL_RETURN(clazz, 0); completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I"); - CHECK_NULL(completionStatus_error); + CHECK_NULL_RETURN(completionStatus_error, 0); completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I"); - CHECK_NULL(completionStatus_bytesTransferred); + CHECK_NULL_RETURN(completionStatus_bytesTransferred, 0); completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "J"); - CHECK_NULL(completionStatus_completionKey); + CHECK_NULL_RETURN(completionStatus_completionKey, 0); + + HMODULE hMod = GetModuleHandleW(L"kernel32.dll"); + if (hMod != NULL) { + pGetFileInformationByName = + (PGetFileInformationByName)GetProcAddress(hMod, "GetFileInformationByName"); + if (pGetFileInformationByName != NULL) { + capabilities |= sun_nio_fs_WindowsNativeDispatcher_SUPPORTS_GETFILEINFORMATIONBYNAME; + } + } + + return capabilities; } JNIEXPORT jlong JNICALL @@ -510,6 +557,23 @@ Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributesEx0(JNIEnv* env, jclass throwWindowsException(env, GetLastError()); } +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFileInformationByName0(JNIEnv* env, + jclass this, jlong pathAddress, jint infoClass, jlong infoAddress, jint infoSize) +{ + LPCWSTR lpFileName = jlong_to_ptr(pathAddress); + PVOID pInfo = jlong_to_ptr(infoAddress); + + if (pGetFileInformationByName == NULL) { + JNU_ThrowInternalError(env, "should not reach here"); + return; + } + + if (!pGetFileInformationByName(lpFileName, (FILE_INFO_BY_NAME_CLASS)infoClass, + pInfo, (ULONG)infoSize)) { + throwWindowsException(env, GetLastError()); + } +} JNIEXPORT void JNICALL Java_sun_nio_fs_WindowsNativeDispatcher_SetFileTime0(JNIEnv* env, jclass this,