21
21
#include < windows.h>
22
22
#include < Shlwapi.h>
23
23
#include < wchar.h>
24
+ #ifndef WINDOWS_MIN
25
+ #include " ntifs_min.h"
26
+ #endif
24
27
25
28
#define ALL_COLORS (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN)
26
29
@@ -31,6 +34,16 @@ void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
31
34
mark_failed_with_code (env, message, GetLastError (), NULL , result);
32
35
}
33
36
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
+
34
47
int map_error_code (int error_code) {
35
48
if (error_code == ERROR_PATH_NOT_FOUND) {
36
49
return FAILURE_NO_SUCH_FILE;
@@ -81,6 +94,7 @@ bool is_path_absolute_unc(wchar_t* path, int path_len) {
81
94
82
95
//
83
96
// Returns a UTF-16 string that is the concatenation of |prefix| and |path|.
97
+ // The string must be deallocated with a call to free().
84
98
//
85
99
wchar_t * add_prefix (wchar_t * path, int path_len, wchar_t * prefix) {
86
100
int prefix_len = wcslen (prefix);
@@ -156,14 +170,13 @@ jlong lastModifiedNanos(FILETIME* time) {
156
170
return ((jlong)time->dwHighDateTime << 32 ) | time->dwLowDateTime ;
157
171
}
158
172
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
+ //
163
176
typedef struct file_stat {
164
- int fileType;
165
- LONG64 lastModified;
166
- LONG64 size;
177
+ LONG fileType;
178
+ LONGLONG lastModified;
179
+ LONGLONG size;
167
180
} file_stat_t ;
168
181
169
182
//
@@ -601,6 +614,140 @@ Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_readdir(JNIEn
601
614
FindClose (dirHandle);
602
615
}
603
616
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
+
604
751
/*
605
752
* Console functions
606
753
*/
0 commit comments