Improve performance of WindowsDirectoryStream

Use `NtQueryDirectoryInformation` instead of `FindFirst/FindNext` to
retrieve the list of entries of a directory.

`NtQueryDirectionInformation` has 2 main benefits over
`FindFist`/`FindNext`:

* Performance is about 40% faster

* Each retrieved entry retrieved contains a 64-bit `FileId` in addition
  to the usual attributes, ensuring that returned `java.nio.Path`
  instances hold onto a `BasicFileAttributes` instance that exposes a
  non-null `java.nio.file.attribute.BasicFileAttributes.fileKey()`.

This change also requires creating a new WindowsFileKey class, similar
to UnixFileKey class, so that
`java.nio.file.attribute.BasicFileAttributes.fileKey()` can return an
Object instance that can be used to compare files for equality.

With this change, the Windows implementation of Files.walkFileTree is
about 40% faster when the FOLLOW_LINKS option is not used, and about
2.5x faster when the FOLLOW_LINKS option is used.

When the FOLLOW_LINKS option is used, most calls to
`Files.isSameFile`, which is expensive as it requires 2 file I/O
operations, are avoided because the Path entries returned by the
new WindowsDirectoryStream implementation now contain a non-null
BasicFileAttributes.fileKey(). The remaining calls to
`Files.isSameFile` are performed when Files.walkFileTree need
to compare the initial directory with other entries.

Change-Id: Id79d89d477a6d5dcf151c63a9d6072c6f7ef43b2

(AKA JBR-3680 Cherry-pick Google's NIO patches to get faster file listing)

(cherry picked from commit 7c2d7541ba)
This commit is contained in:
Renaud Paquay
2020-02-18 16:25:59 -08:00
committed by Maxim Kartashev
parent 9e0ff20be5
commit 9baaee86b0
6 changed files with 526 additions and 54 deletions

View File

@@ -41,16 +41,16 @@ import static sun.nio.fs.WindowsConstants.*;
class WindowsDirectoryStream
implements DirectoryStream<Path>
{
private static final int NATIVE_BUFFER_SIZE = 8192;
private final WindowsPath dir;
private final DirectoryStream.Filter<? super Path> filter;
// handle to directory
private final long handle;
// first entry in the directory
private final String firstName;
// Query directory information data structure
private final QueryDirectoryInformation queryDirectoryInformation;
// buffer for WIN32_FIND_DATA structure that receives information about file
private final NativeBuffer findDataBuffer;
// Buffer used to receive file entries from NtQueryDirectoryInformation calls
private final NativeBuffer queryDirectoryInformationBuffer;
private final Object closeLock = new Object();
@@ -65,21 +65,15 @@ class WindowsDirectoryStream
this.dir = dir;
this.filter = filter;
this.queryDirectoryInformationBuffer = NativeBuffers.getNativeBuffer(NATIVE_BUFFER_SIZE);
try {
// Need to append * or \* to match entries in directory.
// Open the directory for reading and read the first set of entries in the native buffer
String search = dir.getPathForWin32Calls();
char last = search.charAt(search.length() -1);
if (last == ':' || last == '\\') {
search += "*";
} else {
search += "\\*";
}
FirstFile first = FindFirstFile(search);
this.handle = first.handle();
this.firstName = first.name();
this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
this.queryDirectoryInformation = OpenNtQueryDirectoryInformation(search, this.queryDirectoryInformationBuffer);
} catch (WindowsException x) {
// Release the buffer, as this instance is not fully constructed
this.queryDirectoryInformationBuffer.release();
if (x.lastError() == ERROR_DIRECTORY) {
throw new NotDirectoryException(dir.getPathForExceptionMessage());
}
@@ -99,9 +93,9 @@ class WindowsDirectoryStream
return;
isOpen = false;
}
findDataBuffer.release();
queryDirectoryInformationBuffer.release();
try {
FindClose(handle);
CloseNtQueryDirectoryInformation(queryDirectoryInformation);
} catch (WindowsException x) {
x.rethrowAsIOException(dir);
}
@@ -115,20 +109,20 @@ class WindowsDirectoryStream
synchronized (this) {
if (iterator != null)
throw new IllegalStateException("Iterator already obtained");
iterator = new WindowsDirectoryIterator(firstName);
iterator = new WindowsDirectoryIterator();
return iterator;
}
}
private class WindowsDirectoryIterator implements Iterator<Path> {
private boolean atEof;
private String first;
private Path nextEntry;
private String prefix;
private int nextOffset;
WindowsDirectoryIterator(String first) {
WindowsDirectoryIterator() {
atEof = false;
this.first = first;
nextOffset = 0;
if (dir.needsSlashWhenResolving()) {
prefix = dir.toString() + "\\";
} else {
@@ -156,44 +150,35 @@ class WindowsDirectoryStream
// reads next directory entry
private Path readNextEntry() {
// handle first element returned by search
if (first != null) {
nextEntry = isSelfOrParent(first) ? null : acceptEntry(first, null);
first = null;
if (nextEntry != null)
return nextEntry;
}
for (;;) {
String name = null;
String name;
WindowsFileAttributes attrs;
// synchronize on closeLock to prevent close while reading
synchronized (closeLock) {
try {
if (isOpen) {
name = FindNextFile(handle, findDataBuffer.address());
// Fetch next set of entries if we don't have anything available in buffer
if (nextOffset < 0) {
try {
atEof = !NextNtQueryDirectoryInformation(queryDirectoryInformation, queryDirectoryInformationBuffer);
} catch (WindowsException x) {
IOException ioe = x.asIOException(dir);
throw new DirectoryIteratorException(ioe);
}
} catch (WindowsException x) {
IOException ioe = x.asIOException(dir);
throw new DirectoryIteratorException(ioe);
if (atEof) {
return null;
}
nextOffset = 0;
}
// NO_MORE_FILES or stream closed
if (name == null) {
atEof = true;
return null;
}
// ignore link to self and parent directories
if (isSelfOrParent(name))
long fullDirInformationAddress = queryDirectoryInformationBuffer.address() + nextOffset;
int nextEntryOffset = WindowsFileAttributes.getNextOffsetFromFileIdFullDirInformation(fullDirInformationAddress);
nextOffset = nextEntryOffset == 0 ? -1 : nextOffset + nextEntryOffset;
name = WindowsFileAttributes.getFileNameFromFileIdFullDirInformation(fullDirInformationAddress);
if (isSelfOrParent(name)) {
// Skip "." and ".."
continue;
// grab the attributes from the WIN32_FIND_DATA structure
// (needs to be done while holding closeLock because close
// will release the buffer)
attrs = WindowsFileAttributes
.fromFindData(findDataBuffer.address());
}
attrs = WindowsFileAttributes.fromFileIdFullDirInformation(fullDirInformationAddress, queryDirectoryInformation.volSerialNumber());
}
// return entry if accepted by filter

View File

@@ -108,10 +108,42 @@ class WindowsFileAttributes
private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
// 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;
/**
* typedef struct _FILE_ID_FULL_DIR_INFORMATION {
* ULONG NextEntryOffset; // offset = 0
* ULONG FileIndex; // offset = 4
* LARGE_INTEGER CreationTime; // offset = 8
* LARGE_INTEGER LastAccessTime; // offset = 16
* LARGE_INTEGER LastWriteTime; // offset = 24
* LARGE_INTEGER ChangeTime; // offset = 32
* LARGE_INTEGER EndOfFile; // offset = 40
* LARGE_INTEGER AllocationSize; // offset = 48
* ULONG FileAttributes; // offset = 56
* ULONG FileNameLength; // offset = 60
* ULONG EaSize; // offset = 64
* LARGE_INTEGER FileId; // offset = 72
* WCHAR FileName[1]; // offset = 80
* } FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
*/
private static final int OFFSETOF_FULL_DIR_INFO_NEXT_ENTRY_OFFSET = 0;
private static final int OFFSETOF_FULL_DIR_INFO_CREATION_TIME = 8;
private static final int OFFSETOF_FULL_DIR_INFO_LAST_ACCESS_TIME = 16;
private static final int OFFSETOF_FULL_DIR_INFO_LAST_WRITE_TIME = 24;
private static final int OFFSETOF_FULL_DIR_INFO_END_OF_FILE = 40;
private static final int OFFSETOF_FULL_DIR_INFO_FILE_ATTRIBUTES = 56;
private static final int OFFSETOF_FULL_DIR_INFO_FILENAME_LENGTH = 60;
private static final int OFFSETOF_FULL_DIR_INFO_EA_SIZE = 64;
private static final int OFFSETOF_FULL_DIR_INFO_FILE_ID = 72;
private static final int OFFSETOF_FULL_DIR_INFO_FILENAME = 80;
// used to adjust values between Windows and java epoch
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
// indicates if accurate metadata is required (interesting on NTFS only)
private static final boolean ensureAccurateMetadata;
static {
@@ -133,6 +165,9 @@ class WindowsFileAttributes
private final int fileIndexHigh;
private final int fileIndexLow;
// created lazily
private volatile WindowsFileKey key;
/**
* Convert 64-bit value representing the number of 100-nanosecond intervals
* since January 1, 1601 to a FileTime.
@@ -257,6 +292,47 @@ class WindowsFileAttributes
0); // fileIndexLow
}
/**
* Create a WindowsFileAttributes from a FILE_ID_FULL_DIR_INFORMATION structure
*/
static WindowsFileAttributes fromFileIdFullDirInformation(long address, int volSerialNumber) {
int fileAttrs = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILE_ATTRIBUTES);
long creationTime = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_CREATION_TIME);
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_LAST_ACCESS_TIME);
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_LAST_WRITE_TIME);
long size = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_END_OF_FILE);
int reparseTag = isReparsePoint(fileAttrs) ?
unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_EA_SIZE) : 0;
int fileIndexLow = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILE_ID);
int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILE_ID + 4);
return new WindowsFileAttributes(fileAttrs,
creationTime,
lastAccessTime,
lastWriteTime,
size,
reparseTag,
volSerialNumber,
fileIndexHigh, // fileIndexHigh
fileIndexLow); // fileIndexLow
}
static int getNextOffsetFromFileIdFullDirInformation(long address) {
return unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_NEXT_ENTRY_OFFSET);
}
static String getFileNameFromFileIdFullDirInformation(long address) {
// copy the name
int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILENAME_LENGTH);
if ((nameLengthInBytes % 2) != 0) {
throw new AssertionError("FileNameLength is not a multiple of 2");
}
char[] nameAsArray = new char[nameLengthInBytes/2];
unsafe.copyMemory(null, address + OFFSETOF_FULL_DIR_INFO_FILENAME, nameAsArray,
Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
return new String(nameAsArray);
}
/**
* Reads the attributes of an open file
*/
@@ -414,7 +490,17 @@ class WindowsFileAttributes
@Override
public Object fileKey() {
return null;
if (volSerialNumber == 0) {
return null;
}
if (key == null) {
synchronized (this) {
if (key == null) {
key = new WindowsFileKey(volSerialNumber, ((long)fileIndexHigh << 32) + fileIndexLow);
}
}
}
return key;
}
// package private

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2008, 2009, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
/**
* Container for volume/file id to uniquely identify file.
*/
class WindowsFileKey {
private final int volSerialNumber;
private final long fileId;
WindowsFileKey(int volSerialNumber, long fileId) {
this.volSerialNumber = volSerialNumber;
this.fileId = fileId;
}
@Override
public int hashCode() {
return (int)(volSerialNumber ^ (volSerialNumber >>> 16)) +
(int)(fileId ^ (fileId >>> 32));
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof WindowsFileKey))
return false;
WindowsFileKey other = (WindowsFileKey)obj;
return (this.volSerialNumber == other.volSerialNumber) && (this.fileId == other.fileId);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(volId=")
.append(Integer.toHexString(volSerialNumber))
.append(",fileId=")
.append(Long.toHexString(fileId))
.append(')');
return sb.toString();
}
}

View File

@@ -285,6 +285,38 @@ class WindowsNativeDispatcher {
*/
static native void FindClose(long handle) throws WindowsException;
static QueryDirectoryInformation OpenNtQueryDirectoryInformation(String path, NativeBuffer buffer) throws WindowsException {
NativeBuffer pathBuffer = asNativeBuffer(path);
try {
QueryDirectoryInformation data = new QueryDirectoryInformation();
OpenNtQueryDirectoryInformation0(pathBuffer.address(), buffer.address(), buffer.size(), data);
return data;
} finally {
pathBuffer.release();
}
}
static class QueryDirectoryInformation {
private long handle;
private int volSerialNumber;
private QueryDirectoryInformation() { }
public long handle() { return handle; }
public int volSerialNumber() { return volSerialNumber; }
}
private static native void OpenNtQueryDirectoryInformation0(long lpFileName, long buffer, int bufferSize, QueryDirectoryInformation obj)
throws WindowsException;
static boolean NextNtQueryDirectoryInformation(QueryDirectoryInformation data, NativeBuffer buffer) throws WindowsException {
return NextNtQueryDirectoryInformation0(data.handle(), buffer.address(), buffer.size());
}
private static native boolean NextNtQueryDirectoryInformation0(long handle, long buffer, int bufferSize)
throws WindowsException;
static void CloseNtQueryDirectoryInformation(QueryDirectoryInformation data) throws WindowsException {
CloseHandle(data.handle);
}
/**
* GetFileInformationByHandle(
* HANDLE hFile,

View File

@@ -38,6 +38,8 @@
#include "jni_util.h"
#include "jlong.h"
#include "ntifs_min.h"
#include "sun_nio_fs_WindowsNativeDispatcher.h"
/**
@@ -50,6 +52,9 @@ static jfieldID findFirst_attributes;
static jfieldID findStream_handle;
static jfieldID findStream_name;
static jfieldID queryDirectoryInformation_handle;
static jfieldID queryDirectoryInformation_volSerialNumber;
static jfieldID volumeInfo_fsName;
static jfieldID volumeInfo_volName;
static jfieldID volumeInfo_volSN;
@@ -71,6 +76,13 @@ static jfieldID completionStatus_error;
static jfieldID completionStatus_bytesTransferred;
static jfieldID completionStatus_completionKey;
typedef NTSYSCALLAPI NTSTATUS(NTAPI* NtQueryDirectoryFile_Proc) (HANDLE, HANDLE, PIO_APC_ROUTINE,
PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS, BOOLEAN, PUNICODE_STRING, BOOLEAN);
typedef ULONG(NTAPI* RtlNtStatusToDosError_Proc) (NTSTATUS);
static NtQueryDirectoryFile_Proc NtQueryDirectoryFile_func;
static RtlNtStatusToDosError_Proc RtlNtStatusToDosError_func;
static void throwWindowsException(JNIEnv* env, DWORD lastError) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/WindowsException",
"(I)V", lastError);
@@ -87,6 +99,7 @@ JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
{
jclass clazz;
HMODULE h;
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstFile");
CHECK_NULL(clazz);
@@ -104,6 +117,13 @@ Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
CHECK_NULL(findStream_name);
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$QueryDirectoryInformation");
CHECK_NULL(clazz);
queryDirectoryInformation_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
CHECK_NULL(queryDirectoryInformation_handle);
queryDirectoryInformation_volSerialNumber = (*env)->GetFieldID(env, clazz, "volSerialNumber", "I");;
CHECK_NULL(queryDirectoryInformation_volSerialNumber);
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation");
CHECK_NULL(clazz);
volumeInfo_fsName = (*env)->GetFieldID(env, clazz, "fileSystemName", "Ljava/lang/String;");
@@ -148,6 +168,16 @@ Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
CHECK_NULL(completionStatus_bytesTransferred);
completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "J");
CHECK_NULL(completionStatus_completionKey);
// get handle to ntdll
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
L"ntdll.dll", &h) != 0)
{
NtQueryDirectoryFile_func =
(NtQueryDirectoryFile_Proc)GetProcAddress(h, "NtQueryDirectoryFile");
RtlNtStatusToDosError_func =
(RtlNtStatusToDosError_Proc)GetProcAddress(h, "RtlNtStatusToDosError");
}
}
JNIEXPORT jlong JNICALL
@@ -428,6 +458,118 @@ Java_sun_nio_fs_WindowsNativeDispatcher_FindClose(JNIEnv* env, jclass this,
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_OpenNtQueryDirectoryInformation0(JNIEnv* env, jclass this,
jlong address, jlong bufferAddress, jint bufferSize, jobject obj)
{
LPCWSTR lpFileName = jlong_to_ptr(address);
BOOL ok;
BY_HANDLE_FILE_INFORMATION info;
HANDLE handle;
NTSTATUS status;
ULONG win32ErrorCode;
IO_STATUS_BLOCK ioStatusBlock;
if ((NtQueryDirectoryFile_func == NULL) || (RtlNtStatusToDosError_func == NULL)) {
JNU_ThrowInternalError(env, "Should not get here");
return;
}
handle = CreateFileW(lpFileName, FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle == INVALID_HANDLE_VALUE) {
throwWindowsException(env, GetLastError());
return;
}
status = NtQueryDirectoryFile_func(
handle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
jlong_to_ptr(bufferAddress), // FileInformation
bufferSize, // Length
FileIdFullDirectoryInformation, // FileInformationClass
FALSE, // ReturnSingleEntry
NULL, // FileName
FALSE); // RestartScan
if (!NT_SUCCESS(status)) {
/*
* NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
* asked to enumerate an invalid directory (ie it is a file
* instead of a directory). Verify that is the actual cause
* of the error.
*/
if (status == STATUS_INVALID_PARAMETER) {
DWORD attributes = GetFileAttributesW(lpFileName);
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
status = STATUS_NOT_A_DIRECTORY;
}
}
win32ErrorCode = RtlNtStatusToDosError_func(status);
throwWindowsException(env, win32ErrorCode);
CloseHandle(handle);
return;
}
// This call allows retrieving the volume ID of this directory (and all its entries)
ok = GetFileInformationByHandle(handle, &info);
if (!ok) {
throwWindowsException(env, GetLastError());
CloseHandle(handle);
return;
}
(*env)->SetLongField(env, obj, queryDirectoryInformation_handle, ptr_to_jlong(handle));
(*env)->SetIntField(env, obj, queryDirectoryInformation_volSerialNumber, info.dwVolumeSerialNumber);
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_NextNtQueryDirectoryInformation0(JNIEnv* env, jclass this,
jlong handle, jlong address, jint size)
{
HANDLE h = (HANDLE)jlong_to_ptr(handle);
ULONG win32ErrorCode;
IO_STATUS_BLOCK ioStatusBlock;
NTSTATUS status;
if ((NtQueryDirectoryFile_func == NULL) || (RtlNtStatusToDosError_func == NULL)) {
JNU_ThrowInternalError(env, "Should not get here");
return JNI_FALSE;
}
status = NtQueryDirectoryFile_func(
h, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
jlong_to_ptr(address), // FileInformation
size, // Length
FileIdFullDirectoryInformation, // FileInformationClass
FALSE, // ReturnSingleEntry
NULL, // FileName
FALSE); // RestartScan
if (NT_SUCCESS(status)) {
return JNI_TRUE;
}
// Normal completion: no more files in directory
if (status == STATUS_NO_MORE_FILES) {
return JNI_FALSE;
}
win32ErrorCode = RtlNtStatusToDosError_func(status);
throwWindowsException(env, win32ErrorCode);
return JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFileInformationByHandle(JNIEnv* env, jclass this,
jlong handle, jlong address)

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2008, 2018, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _NTIFS_MIN_
#define _NTIFS_MIN_
/*
* Copy necessary structures and definitions out of the Windows DDK
* to enable calling NtQueryDirectoryFile()
*/
typedef LONG NTSTATUS;
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation,
FileBothDirectoryInformation,
FileBasicInformation,
FileStandardInformation,
FileInternalInformation,
FileEaInformation,
FileAccessInformation,
FileNameInformation,
FileRenameInformation,
FileLinkInformation,
FileNamesInformation,
FileDispositionInformation,
FilePositionInformation,
FileFullEaInformation,
FileModeInformation,
FileAlignmentInformation,
FileAllInformation,
FileAllocationInformation,
FileEndOfFileInformation,
FileAlternateNameInformation,
FileStreamInformation,
FilePipeInformation,
FilePipeLocalInformation,
FilePipeRemoteInformation,
FileMailslotQueryInformation,
FileMailslotSetInformation,
FileCompressionInformation,
FileObjectIdInformation,
FileCompletionInformation,
FileMoveClusterInformation,
FileQuotaInformation,
FileReparsePointInformation,
FileNetworkOpenInformation,
FileAttributeTagInformation,
FileTrackingInformation,
FileIdBothDirectoryInformation,
FileIdFullDirectoryInformation,
FileValidDataLengthInformation,
FileShortNameInformation,
FileIoCompletionNotificationInformation,
FileIoStatusBlockRangeInformation,
FileIoPriorityHintInformation,
FileSfioReserveInformation,
FileSfioVolumeInformation,
FileHardLinkInformation,
FileProcessIdsUsingFileInformation,
FileNormalizedNameInformation,
FileNetworkPhysicalNameInformation,
FileIdGlobalTxDirectoryInformation,
FileIsRemoteDeviceInformation,
FileAttributeCacheInformation,
FileNumaNodeInformation,
FileStandardLinkInformation,
FileRemoteProtocolInformation,
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef struct _FILE_ID_FULL_DIR_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
LARGE_INTEGER FileId;
WCHAR FileName[1];
} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
} u;
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef VOID
(NTAPI *PIO_APC_ROUTINE)(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved);
NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryDirectoryFile(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_Out_ PVOID FileInformation,
_In_ ULONG Length,
_In_ FILE_INFORMATION_CLASS FileInformationClass,
_In_ BOOLEAN ReturnSingleEntry,
_In_opt_ PUNICODE_STRING FileName,
_In_ BOOLEAN RestartScan
);
ULONG
NTAPI
RtlNtStatusToDosError(
NTSTATUS Status
);
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
#define STATUS_NOT_A_DIRECTORY ((NTSTATUS)0xC0000103L)
#endif // _NTIFS_MIN_