8383623: (fs) Update Windows file system provider to use GetFileInformationByName where possible

Reviewed-by: alanb
This commit is contained in:
Ashay Rane 2026-05-21 08:13:48 +00:00 committed by Alan Bateman
parent 86637704fd
commit 36eb8edadf
4 changed files with 202 additions and 34 deletions

View File

@ -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;

View File

@ -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 {

View File

@ -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();
}
}

View File

@ -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,