49 |
49 |
50 static struct { |
50 static struct { |
51 jfieldID path; |
51 jfieldID path; |
52 } ids; |
52 } ids; |
53 |
53 |
|
54 /** |
|
55 * GetFinalPathNameByHandle is available on Windows Vista and newer |
|
56 */ |
|
57 typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD); |
|
58 static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func; |
|
59 |
54 JNIEXPORT void JNICALL |
60 JNIEXPORT void JNICALL |
55 Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls) |
61 Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls) |
56 { |
62 { |
|
63 HANDLE handle; |
57 jclass fileClass = (*env)->FindClass(env, "java/io/File"); |
64 jclass fileClass = (*env)->FindClass(env, "java/io/File"); |
58 if (!fileClass) return; |
65 if (!fileClass) return; |
59 ids.path = |
66 ids.path = |
60 (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;"); |
67 (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;"); |
|
68 handle = LoadLibrary("kernel32"); |
|
69 if (handle != NULL) { |
|
70 GetFinalPathNameByHandle_func = (GetFinalPathNameByHandleProc) |
|
71 GetProcAddress(handle, "GetFinalPathNameByHandleW"); |
|
72 } |
61 } |
73 } |
62 |
74 |
63 /* -- Path operations -- */ |
75 /* -- Path operations -- */ |
64 |
76 |
65 extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len); |
77 extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len); |
66 extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len); |
78 extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len); |
|
79 |
|
80 /** |
|
81 * Retrieves the fully resolved (final) path for the given path or NULL |
|
82 * if the function fails. |
|
83 */ |
|
84 static WCHAR* getFinalPath(const WCHAR *path) |
|
85 { |
|
86 HANDLE h; |
|
87 WCHAR *result; |
|
88 DWORD error; |
|
89 |
|
90 /* Need Windows Vista or newer to get the final path */ |
|
91 if (GetFinalPathNameByHandle_func == NULL) |
|
92 return NULL; |
|
93 |
|
94 h = CreateFileW(path, |
|
95 FILE_READ_ATTRIBUTES, |
|
96 FILE_SHARE_DELETE | |
|
97 FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
98 NULL, |
|
99 OPEN_EXISTING, |
|
100 FILE_FLAG_BACKUP_SEMANTICS, |
|
101 NULL); |
|
102 if (h == INVALID_HANDLE_VALUE) |
|
103 return NULL; |
|
104 |
|
105 /** |
|
106 * Allocate a buffer for the resolved path. For a long path we may need |
|
107 * to allocate a larger buffer. |
|
108 */ |
|
109 result = (WCHAR*)malloc(MAX_PATH * sizeof(WCHAR)); |
|
110 if (result != NULL) { |
|
111 DWORD len = (*GetFinalPathNameByHandle_func)(h, result, MAX_PATH, 0); |
|
112 if (len >= MAX_PATH) { |
|
113 /* retry with a buffer of the right size */ |
|
114 result = (WCHAR*)realloc(result, (len+1) * sizeof(WCHAR)); |
|
115 if (result != NULL) { |
|
116 len = (*GetFinalPathNameByHandle_func)(h, result, len, 0); |
|
117 } else { |
|
118 len = 0; |
|
119 } |
|
120 } |
|
121 if (len > 0) { |
|
122 /** |
|
123 * Strip prefix (should be \\?\ or \\?\UNC) |
|
124 */ |
|
125 if (result[0] == L'\\' && result[1] == L'\\' && |
|
126 result[2] == L'?' && result[3] == L'\\') |
|
127 { |
|
128 int isUnc = (result[4] == L'U' && |
|
129 result[5] == L'N' && |
|
130 result[6] == L'C'); |
|
131 int prefixLen = (isUnc) ? 7 : 4; |
|
132 /* actual result length (includes terminator) */ |
|
133 int resultLen = len - prefixLen + (isUnc ? 1 : 0) + 1; |
|
134 |
|
135 /* copy result without prefix into new buffer */ |
|
136 WCHAR *tmp = (WCHAR*)malloc(resultLen * sizeof(WCHAR)); |
|
137 if (tmp == NULL) { |
|
138 len = 0; |
|
139 } else { |
|
140 WCHAR *p = result; |
|
141 p += prefixLen; |
|
142 if (isUnc) { |
|
143 WCHAR *p2 = tmp; |
|
144 p2[0] = L'\\'; |
|
145 p2++; |
|
146 wcscpy(p2, p); |
|
147 } else { |
|
148 wcscpy(tmp, p); |
|
149 } |
|
150 free(result); |
|
151 result = tmp; |
|
152 } |
|
153 } |
|
154 } |
|
155 |
|
156 /* unable to get final path */ |
|
157 if (len == 0 && result != NULL) { |
|
158 free(result); |
|
159 result = NULL; |
|
160 } |
|
161 } |
|
162 |
|
163 error = GetLastError(); |
|
164 if (CloseHandle(h)) |
|
165 SetLastError(error); |
|
166 return result; |
|
167 } |
|
168 |
|
169 /** |
|
170 * Retrieves file information for the specified file. If the file is |
|
171 * symbolic link then the information on fully resolved target is |
|
172 * returned. |
|
173 */ |
|
174 static BOOL getFileInformation(const WCHAR *path, |
|
175 BY_HANDLE_FILE_INFORMATION *finfo) |
|
176 { |
|
177 BOOL result; |
|
178 DWORD error; |
|
179 HANDLE h = CreateFileW(path, |
|
180 FILE_READ_ATTRIBUTES, |
|
181 FILE_SHARE_DELETE | |
|
182 FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
183 NULL, |
|
184 OPEN_EXISTING, |
|
185 FILE_FLAG_BACKUP_SEMANTICS, |
|
186 NULL); |
|
187 if (h == INVALID_HANDLE_VALUE) |
|
188 return FALSE; |
|
189 result = GetFileInformationByHandle(h, finfo); |
|
190 error = GetLastError(); |
|
191 if (CloseHandle(h)) |
|
192 SetLastError(error); |
|
193 return result; |
|
194 } |
|
195 |
|
196 /** |
|
197 * If the given attributes are the attributes of a reparse point, then |
|
198 * read and return the attributes of the final target. |
|
199 */ |
|
200 DWORD getFinalAttributesIfReparsePoint(WCHAR *path, DWORD a) |
|
201 { |
|
202 if ((a != INVALID_FILE_ATTRIBUTES) && |
|
203 ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0)) |
|
204 { |
|
205 BY_HANDLE_FILE_INFORMATION finfo; |
|
206 BOOL res = getFileInformation(path, &finfo); |
|
207 a = (res) ? finfo.dwFileAttributes : INVALID_FILE_ATTRIBUTES; |
|
208 } |
|
209 return a; |
|
210 } |
67 |
211 |
68 JNIEXPORT jstring JNICALL |
212 JNIEXPORT jstring JNICALL |
69 Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this, |
213 Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this, |
70 jstring pathname) |
214 jstring pathname) |
71 { |
215 { |
200 WIN32_FILE_ATTRIBUTE_DATA wfad; |
344 WIN32_FILE_ATTRIBUTE_DATA wfad; |
201 if (pathbuf == NULL) |
345 if (pathbuf == NULL) |
202 return rv; |
346 return rv; |
203 if (!isReservedDeviceNameW(pathbuf)) { |
347 if (!isReservedDeviceNameW(pathbuf)) { |
204 if (GetFileAttributesExW(pathbuf, GetFileExInfoStandard, &wfad)) { |
348 if (GetFileAttributesExW(pathbuf, GetFileExInfoStandard, &wfad)) { |
205 rv = (java_io_FileSystem_BA_EXISTS |
349 DWORD a = getFinalAttributesIfReparsePoint(pathbuf, wfad.dwFileAttributes); |
206 | ((wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
350 if (a != INVALID_FILE_ATTRIBUTES) { |
207 ? java_io_FileSystem_BA_DIRECTORY |
351 rv = (java_io_FileSystem_BA_EXISTS |
208 : java_io_FileSystem_BA_REGULAR) |
352 | ((a & FILE_ATTRIBUTE_DIRECTORY) |
209 | ((wfad.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) |
353 ? java_io_FileSystem_BA_DIRECTORY |
210 ? java_io_FileSystem_BA_HIDDEN : 0)); |
354 : java_io_FileSystem_BA_REGULAR) |
|
355 | ((a & FILE_ATTRIBUTE_HIDDEN) |
|
356 ? java_io_FileSystem_BA_HIDDEN : 0)); |
|
357 } |
211 } else { /* pagefile.sys is a special case */ |
358 } else { /* pagefile.sys is a special case */ |
212 if (GetLastError() == ERROR_SHARING_VIOLATION) { |
359 if (GetLastError() == ERROR_SHARING_VIOLATION) { |
213 rv = java_io_FileSystem_BA_EXISTS; |
360 rv = java_io_FileSystem_BA_EXISTS; |
214 if ((pathlen = wcslen(pathbuf)) >= SPECIALFILE_NAMELEN && |
361 if ((pathlen = wcslen(pathbuf)) >= SPECIALFILE_NAMELEN && |
215 (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN, |
362 (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN, |
330 if (pathbuf == NULL) |
492 if (pathbuf == NULL) |
331 return rv; |
493 return rv; |
332 if (GetFileAttributesExW(pathbuf, |
494 if (GetFileAttributesExW(pathbuf, |
333 GetFileExInfoStandard, |
495 GetFileExInfoStandard, |
334 &wfad)) { |
496 &wfad)) { |
335 rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow; |
497 if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { |
|
498 rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow; |
|
499 } else { |
|
500 /* file is a reparse point so read attributes of final target */ |
|
501 BY_HANDLE_FILE_INFORMATION finfo; |
|
502 if (getFileInformation(pathbuf, &finfo)) { |
|
503 rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) + |
|
504 finfo.nFileSizeLow; |
|
505 } |
|
506 } |
336 } else { |
507 } else { |
337 if (GetLastError() == ERROR_SHARING_VIOLATION) { |
508 if (GetLastError() == ERROR_SHARING_VIOLATION) { |
338 /* The error is "share violation", which means the file/dir |
509 /* The error is "share violation", which means the file/dir |
339 must exists. Try _wstati64, we know this at least works |
510 must exists. Try _wstati64, we know this at least works |
340 for pagefile.sys and hiberfil.sys. |
511 for pagefile.sys and hiberfil.sys. |
358 HANDLE h = NULL; |
529 HANDLE h = NULL; |
359 WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE); |
530 WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE); |
360 if (pathbuf == NULL) |
531 if (pathbuf == NULL) |
361 return JNI_FALSE; |
532 return JNI_FALSE; |
362 h = CreateFileW( |
533 h = CreateFileW( |
363 pathbuf, /* Wide char path name */ |
534 pathbuf, /* Wide char path name */ |
364 GENERIC_READ | GENERIC_WRITE, /* Read and write permission */ |
535 GENERIC_READ | GENERIC_WRITE, /* Read and write permission */ |
365 FILE_SHARE_READ | FILE_SHARE_WRITE, /* File sharing flags */ |
536 FILE_SHARE_READ | FILE_SHARE_WRITE, /* File sharing flags */ |
366 NULL, /* Security attributes */ |
537 NULL, /* Security attributes */ |
367 CREATE_NEW, /* creation disposition */ |
538 CREATE_NEW, /* creation disposition */ |
368 FILE_ATTRIBUTE_NORMAL, /* flags and attributes */ |
539 FILE_ATTRIBUTE_NORMAL | |
|
540 FILE_FLAG_OPEN_REPARSE_POINT, /* flags and attributes */ |
369 NULL); |
541 NULL); |
370 |
542 |
371 if (h == INVALID_HANDLE_VALUE) { |
543 if (h == INVALID_HANDLE_VALUE) { |
372 DWORD error = GetLastError(); |
544 DWORD error = GetLastError(); |
373 if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) { |
545 if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) { |
374 |
546 // return false rather than throwing an exception when there is |
375 // If a directory by the named path already exists, |
547 // an existing file. |
376 // return false (behavior of solaris and linux) instead of |
548 DWORD a = GetFileAttributesW(pathbuf); |
377 // throwing an exception |
549 if (a == INVALID_FILE_ATTRIBUTES) { |
378 DWORD fattr = GetFileAttributesW(pathbuf); |
|
379 if ((fattr == INVALID_FILE_ATTRIBUTES) || |
|
380 (fattr & ~FILE_ATTRIBUTE_DIRECTORY)) { |
|
381 SetLastError(error); |
550 SetLastError(error); |
382 JNU_ThrowIOExceptionWithLastError(env, "Could not open file"); |
551 JNU_ThrowIOExceptionWithLastError(env, "Could not open file"); |
383 } |
552 } |
384 } |
553 } |
385 free(pathbuf); |
554 free(pathbuf); |
386 return JNI_FALSE; |
555 return JNI_FALSE; |
387 } |
556 } |
388 free(pathbuf); |
557 free(pathbuf); |
389 CloseHandle(h); |
558 CloseHandle(h); |
390 return JNI_TRUE; |
559 return JNI_TRUE; |
391 } |
560 } |
392 |
561 |
576 jboolean rv = JNI_FALSE; |
745 jboolean rv = JNI_FALSE; |
577 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
746 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
578 HANDLE h; |
747 HANDLE h; |
579 if (pathbuf == NULL) |
748 if (pathbuf == NULL) |
580 return JNI_FALSE; |
749 return JNI_FALSE; |
581 h = CreateFileW(pathbuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, |
750 h = CreateFileW(pathbuf, |
582 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0); |
751 FILE_WRITE_ATTRIBUTES, |
|
752 FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
753 NULL, |
|
754 OPEN_EXISTING, |
|
755 FILE_FLAG_BACKUP_SEMANTICS, |
|
756 0); |
583 if (h != INVALID_HANDLE_VALUE) { |
757 if (h != INVALID_HANDLE_VALUE) { |
584 LARGE_INTEGER modTime; |
758 LARGE_INTEGER modTime; |
585 FILETIME t; |
759 FILETIME t; |
586 modTime.QuadPart = (time + 11644473600000L) * 10000L; |
760 modTime.QuadPart = (time + 11644473600000L) * 10000L; |
587 t.dwLowDateTime = (DWORD)modTime.LowPart; |
761 t.dwLowDateTime = (DWORD)modTime.LowPart; |