2121#include < windows.h>
2222#include < Shlwapi.h>
2323#include < wchar.h>
24+ #ifndef WINDOWS_MIN
25+ #include " ntifs_min.h"
26+ #endif
2427
2528#define ALL_COLORS (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN)
2629
@@ -31,6 +34,16 @@ void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
3134 mark_failed_with_code (env, message, GetLastError (), NULL , result);
3235}
3336
37+ #ifndef WINDOWS_MIN
38+ /*
39+ * Marks the given result as failed, using a NTSTATUS error code
40+ */
41+ void mark_failed_with_ntstatus (JNIEnv *env, const char * message, NTSTATUS status, jobject result) {
42+ ULONG win32ErrorCode = RtlNtStatusToDosError (status);
43+ mark_failed_with_code (env, message, win32ErrorCode, NULL , result);
44+ }
45+ #endif
46+
3447int map_error_code (int error_code) {
3548 if (error_code == ERROR_PATH_NOT_FOUND) {
3649 return FAILURE_NO_SUCH_FILE;
@@ -81,6 +94,7 @@ bool is_path_absolute_unc(wchar_t* path, int path_len) {
8194
8295//
8396// Returns a UTF-16 string that is the concatenation of |prefix| and |path|.
97+ // The string must be deallocated with a call to free().
8498//
8599wchar_t * add_prefix (wchar_t * path, int path_len, wchar_t * prefix) {
86100 int prefix_len = wcslen (prefix);
@@ -156,14 +170,13 @@ jlong lastModifiedNanos(FILETIME* time) {
156170 return ((jlong)time->dwHighDateTime << 32 ) | time->dwLowDateTime ;
157171}
158172
159- jlong lastModifiedNanos (LARGE_INTEGER* time) {
160- return ((jlong)time->HighPart << 32 ) | time->LowPart ;
161- }
162-
173+ //
174+ // Data structure holding information about a single file
175+ //
163176typedef struct file_stat {
164- int fileType;
165- LONG64 lastModified;
166- LONG64 size;
177+ LONG fileType;
178+ LONGLONG lastModified;
179+ LONGLONG size;
167180} file_stat_t ;
168181
169182//
@@ -601,6 +614,140 @@ Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_readdir(JNIEn
601614 FindClose (dirHandle);
602615}
603616
617+ //
618+ // Returns "true" is the various fastReaddirXxx calls are supported on this platform.
619+ //
620+ JNIEXPORT jboolean JNICALL
621+ Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_fastReaddirIsSupported (JNIEnv *env, jclass target) {
622+ #ifdef WINDOWS_MIN
623+ return JNI_FALSE;
624+ #else
625+ return JNI_TRUE;
626+ #endif
627+ }
628+
629+ #ifndef WINDOWS_MIN
630+ typedef struct fast_readdir_handle {
631+ HANDLE handle;
632+ wchar_t * pathStr;
633+ } readdir_fast_handle_t ;
634+ #endif
635+
636+ #ifndef WINDOWS_MIN
637+ NTSTATUS invokeNtQueryDirectoryFile (HANDLE handle, BYTE* buffer, ULONG bufferSize) {
638+ IO_STATUS_BLOCK ioStatusBlock;
639+
640+ return NtQueryDirectoryFile (
641+ handle, // FileHandle
642+ NULL , // Event
643+ NULL , // ApcRoutine
644+ NULL , // ApcContext
645+ &ioStatusBlock, // IoStatusBlock
646+ buffer, // FileInformation
647+ bufferSize, // Length
648+ FileIdFullDirectoryInformation, // FileInformationClass
649+ FALSE , // ReturnSingleEntry
650+ NULL , // FileName
651+ FALSE ); // RestartScan
652+ }
653+ #endif
654+
655+ //
656+ // Returns a DirectByteBuffer pointing to a |fast_readdir_handle| structure on success
657+ // Returns NULL on failure (and sets error message in |result|).
658+ //
659+ JNIEXPORT jlong JNICALL
660+ Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_fastReaddirOpen (JNIEnv *env, jclass target, jstring path, jobject result) {
661+ #ifdef WINDOWS_MIN
662+ mark_failed_with_code (env, " Operation not supported" , ERROR_CALL_NOT_IMPLEMENTED, NULL , result);
663+ return NULL ;
664+ #else
665+ // Open file for directory listing
666+ wchar_t * pathStr = java_to_wchar_path (env, path, result);
667+ if (pathStr == NULL ) {
668+ mark_failed_with_code (env, " Out of native memory" , ERROR_OUTOFMEMORY, NULL , result);
669+ return NULL ;
670+ }
671+ HANDLE handle = CreateFileW (pathStr, FILE_LIST_DIRECTORY,
672+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
673+ NULL , OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL );
674+ if (handle == INVALID_HANDLE_VALUE) {
675+ mark_failed_with_errno (env, " could not open directory" , result);
676+ free (pathStr);
677+ return NULL ;
678+ }
679+ readdir_fast_handle_t * readdirHandle = (readdir_fast_handle_t *)LocalAlloc (LPTR, sizeof (readdir_fast_handle_t ));
680+ if (readdirHandle == NULL ) {
681+ mark_failed_with_code (env, " Out of native memory" , ERROR_OUTOFMEMORY, NULL , result);
682+ CloseHandle (handle);
683+ free (pathStr);
684+ return NULL ;
685+ }
686+ readdirHandle->handle = handle;
687+ readdirHandle->pathStr = pathStr;
688+ return (jlong)readdirHandle;
689+ #endif
690+ }
691+
692+ //
693+ // Releases all native resources associted to the passed in |handle| (a pointer to |fast_readdir_handle_t|).
694+ //
695+ JNIEXPORT void JNICALL
696+ Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_fastReaddirClose (JNIEnv *env, jclass target, jlong handle) {
697+ #ifdef WINDOWS_MIN
698+ // Not supported
699+ #else
700+ readdir_fast_handle_t * readdirHandle = (readdir_fast_handle_t *)handle;
701+ CloseHandle (readdirHandle->handle );
702+ free (readdirHandle->pathStr );
703+ LocalFree (readdirHandle);
704+ #endif
705+ }
706+
707+ //
708+ // Reads the next batch of entries from the directory.
709+ // Returns JNI_TRUE on success and if there are more entries found
710+ // Returns JNI_FALSE and sets an error to |result| if there is an error
711+ // Returns JNI_FALSE if there are no more entries
712+ //
713+ JNIEXPORT jboolean JNICALL
714+ Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_fastReaddirNext (JNIEnv *env, jclass target, jlong handle, jobject buffer, jobject result) {
715+ #ifdef WINDOWS_MIN
716+ mark_failed_with_code (env, " Operation not supported" , ERROR_CALL_NOT_IMPLEMENTED, NULL , result);
717+ return JNI_FALSE;
718+ #else
719+ readdir_fast_handle_t * readdirHandle = (readdir_fast_handle_t *)handle;
720+
721+ BYTE* entryBuffer = (BYTE*)env->GetDirectBufferAddress (buffer);
722+ ULONG entryBufferSize = (ULONG)env->GetDirectBufferCapacity (buffer);
723+
724+ NTSTATUS status = invokeNtQueryDirectoryFile (readdirHandle->handle , entryBuffer, entryBufferSize);
725+ if (!NT_SUCCESS (status)) {
726+ // Normal completion: no more files in directory
727+ if (status == STATUS_NO_MORE_FILES) {
728+ return JNI_FALSE;
729+ }
730+
731+ /*
732+ * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
733+ * asked to enumerate an invalid directory (ie it is a file
734+ * instead of a directory). Verify that is the actual cause
735+ * of the error.
736+ */
737+ if (status == STATUS_INVALID_PARAMETER) {
738+ DWORD attributes = GetFileAttributesW (readdirHandle->pathStr );
739+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) {
740+ status = STATUS_NOT_A_DIRECTORY;
741+ }
742+ }
743+ mark_failed_with_ntstatus (env, " Error reading directory entries" , status, result);
744+ return JNI_FALSE;
745+ }
746+
747+ return JNI_TRUE;
748+ #endif
749+ }
750+
604751/*
605752 * Console functions
606753 */
0 commit comments