1 /*
2 * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /* Access APIs for WinXP and above */
27 #ifndef _WIN32_WINNT
28 #define _WIN32_WINNT 0x0501
29 #endif
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <direct.h>
36 #include <windows.h>
37 #include <io.h>
38 #include <limits.h>
39 #include <wchar.h>
40
41 #include "jni.h"
42 #include "io_util.h"
43 #include "jlong.h"
44 #include "io_util_md.h"
45 #include "dirent_md.h"
46 #include "java_io_FileSystem.h"
47
48 #define MAX_PATH_LENGTH 1024
49
50 static struct {
51 jfieldID path;
52 } ids;
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
60 JNIEXPORT void JNICALL
Java_java_io_WinNTFileSystem_initIDs(JNIEnv * env,jclass cls)61 Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls)
62 {
63 HMODULE handle;
64 jclass fileClass;
65
66 fileClass = (*env)->FindClass(env, "java/io/File");
67 CHECK_NULL(fileClass);
68 ids.path = (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;");
69 CHECK_NULL(ids.path);
70
71 // GetFinalPathNameByHandle requires Windows Vista or newer
72 if (GetModuleHandleExW((GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
73 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT),
74 (LPCWSTR)&CreateFileW, &handle) != 0)
75 {
76 GetFinalPathNameByHandle_func = (GetFinalPathNameByHandleProc)
77 GetProcAddress(handle, "GetFinalPathNameByHandleW");
78 }
79 }
80
81 /* -- Path operations -- */
82
83 extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len);
84 extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len);
85
86 /**
87 * Retrieves the fully resolved (final) path for the given path or NULL
88 * if the function fails.
89 */
getFinalPath(JNIEnv * env,const WCHAR * path)90 static WCHAR* getFinalPath(JNIEnv *env, const WCHAR *path)
91 {
92 HANDLE h;
93 WCHAR *result;
94 DWORD error;
95
96 /* Need Windows Vista or newer to get the final path */
97 if (GetFinalPathNameByHandle_func == NULL)
98 return NULL;
99
100 h = CreateFileW(path,
101 FILE_READ_ATTRIBUTES,
102 FILE_SHARE_DELETE |
103 FILE_SHARE_READ | FILE_SHARE_WRITE,
104 NULL,
105 OPEN_EXISTING,
106 FILE_FLAG_BACKUP_SEMANTICS,
107 NULL);
108 if (h == INVALID_HANDLE_VALUE)
109 return NULL;
110
111 /**
112 * Allocate a buffer for the resolved path. For a long path we may need
113 * to allocate a larger buffer.
114 */
115 result = (WCHAR*)malloc(MAX_PATH * sizeof(WCHAR));
116 if (result != NULL) {
117 DWORD len = (*GetFinalPathNameByHandle_func)(h, result, MAX_PATH, 0);
118 if (len >= MAX_PATH) {
119 /* retry with a buffer of the right size */
120 WCHAR* newResult = (WCHAR*)realloc(result, (len+1) * sizeof(WCHAR));
121 if (newResult != NULL) {
122 result = newResult;
123 len = (*GetFinalPathNameByHandle_func)(h, result, len, 0);
124 } else {
125 len = 0;
126 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
127 }
128 }
129
130 if (len > 0) {
131 /**
132 * Strip prefix (should be \\?\ or \\?\UNC)
133 */
134 if (result[0] == L'\\' && result[1] == L'\\' &&
135 result[2] == L'?' && result[3] == L'\\')
136 {
137 int isUnc = (result[4] == L'U' &&
138 result[5] == L'N' &&
139 result[6] == L'C');
140 int prefixLen = (isUnc) ? 7 : 4;
141 int prefixToKeep = (isUnc) ? 1 : 0;
142 // the amount to copy includes terminator
143 int amountToCopy = len - prefixLen + 1;
144 wmemmove(result + prefixToKeep, result + prefixLen, amountToCopy);
145 }
146 }
147
148 /* unable to get final path */
149 if (len == 0 && result != NULL) {
150 free(result);
151 result = NULL;
152 }
153 } else {
154 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
155 }
156
157 error = GetLastError();
158 if (CloseHandle(h))
159 SetLastError(error);
160 return result;
161 }
162
163 /**
164 * Retrieves file information for the specified file. If the file is
165 * symbolic link then the information on fully resolved target is
166 * returned.
167 */
getFileInformation(const WCHAR * path,BY_HANDLE_FILE_INFORMATION * finfo)168 static BOOL getFileInformation(const WCHAR *path,
169 BY_HANDLE_FILE_INFORMATION *finfo)
170 {
171 BOOL result;
172 DWORD error;
173 HANDLE h = CreateFileW(path,
174 FILE_READ_ATTRIBUTES,
175 FILE_SHARE_DELETE |
176 FILE_SHARE_READ | FILE_SHARE_WRITE,
177 NULL,
178 OPEN_EXISTING,
179 FILE_FLAG_BACKUP_SEMANTICS,
180 NULL);
181 if (h == INVALID_HANDLE_VALUE)
182 return FALSE;
183 result = GetFileInformationByHandle(h, finfo);
184 error = GetLastError();
185 if (CloseHandle(h))
186 SetLastError(error);
187 return result;
188 }
189
190 /**
191 * If the given attributes are the attributes of a reparse point, then
192 * read and return the attributes of the special cases.
193 */
getFinalAttributesIfReparsePoint(WCHAR * path,DWORD a)194 DWORD getFinalAttributesIfReparsePoint(WCHAR *path, DWORD a)
195 {
196 if ((a != INVALID_FILE_ATTRIBUTES) &&
197 ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
198 {
199 BY_HANDLE_FILE_INFORMATION finfo;
200 BOOL res = getFileInformation(path, &finfo);
201 a = (res) ? finfo.dwFileAttributes : INVALID_FILE_ATTRIBUTES;
202 }
203 return a;
204 }
205
206 /**
207 * Take special cases into account when retrieving the attributes
208 * of path
209 */
getFinalAttributes(WCHAR * path)210 DWORD getFinalAttributes(WCHAR *path)
211 {
212 DWORD attr = INVALID_FILE_ATTRIBUTES;
213
214 WIN32_FILE_ATTRIBUTE_DATA wfad;
215 WIN32_FIND_DATAW wfd;
216 HANDLE h;
217
218 if (GetFileAttributesExW(path, GetFileExInfoStandard, &wfad)) {
219 attr = getFinalAttributesIfReparsePoint(path, wfad.dwFileAttributes);
220 } else {
221 DWORD lerr = GetLastError();
222 if ((lerr == ERROR_SHARING_VIOLATION || lerr == ERROR_ACCESS_DENIED) &&
223 (h = FindFirstFileW(path, &wfd)) != INVALID_HANDLE_VALUE) {
224 attr = getFinalAttributesIfReparsePoint(path, wfd.dwFileAttributes);
225 FindClose(h);
226 }
227 }
228 return attr;
229 }
230
231 JNIEXPORT jstring JNICALL
Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv * env,jobject this,jstring pathname)232 Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this,
233 jstring pathname)
234 {
235 jstring rv = NULL;
236 WCHAR canonicalPath[MAX_PATH_LENGTH];
237
238 WITH_UNICODE_STRING(env, pathname, path) {
239 /* we estimate the max length of memory needed as
240 "currentDir. length + pathname.length"
241 */
242 int len = (int)wcslen(path);
243 len += currentDirLength(path, len);
244 if (len > MAX_PATH_LENGTH - 1) {
245 WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
246 if (cp != NULL) {
247 if (wcanonicalize(path, cp, len) >= 0) {
248 rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
249 }
250 free(cp);
251 } else {
252 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
253 }
254 } else if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) {
255 rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
256 }
257 } END_UNICODE_STRING(env, path);
258 if (rv == NULL && !(*env)->ExceptionCheck(env)) {
259 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
260 }
261 return rv;
262 }
263
264
265 JNIEXPORT jstring JNICALL
Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv * env,jobject this,jstring canonicalPrefixString,jstring pathWithCanonicalPrefixString)266 Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this,
267 jstring canonicalPrefixString,
268 jstring pathWithCanonicalPrefixString)
269 {
270 jstring rv = NULL;
271 WCHAR canonicalPath[MAX_PATH_LENGTH];
272 WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) {
273 WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) {
274 int len = (int)wcslen(canonicalPrefix) + MAX_PATH;
275 if (len > MAX_PATH_LENGTH) {
276 WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
277 if (cp != NULL) {
278 if (wcanonicalizeWithPrefix(canonicalPrefix,
279 pathWithCanonicalPrefix,
280 cp, len) >= 0) {
281 rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
282 }
283 free(cp);
284 } else {
285 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
286 }
287 } else if (wcanonicalizeWithPrefix(canonicalPrefix,
288 pathWithCanonicalPrefix,
289 canonicalPath, MAX_PATH_LENGTH) >= 0) {
290 rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
291 }
292 } END_UNICODE_STRING(env, pathWithCanonicalPrefix);
293 } END_UNICODE_STRING(env, canonicalPrefix);
294 if (rv == NULL && !(*env)->ExceptionCheck(env)) {
295 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
296 }
297 return rv;
298 }
299
300 /* -- Attribute accessors -- */
301
302 /* Check whether or not the file name in "path" is a Windows reserved
303 device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the
304 returned result from GetFullPathName, which should be in thr form of
305 "\\.\[ReservedDeviceName]" if the path represents a reserved device
306 name.
307 Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer
308 important anyway) is a device name, so we don't check it here.
309 GetFileAttributesEx will catch it later by returning 0 on NT/XP/
310 200X.
311
312 Note2: Theoretically the implementation could just lookup the table
313 below linearly if the first 4 characters of the fullpath returned
314 from GetFullPathName are "\\.\". The current implementation should
315 achieve the same result. If Microsoft add more names into their
316 reserved device name repository in the future, which probably will
317 never happen, we will need to revisit the lookup implementation.
318
319 static WCHAR* ReservedDEviceNames[] = {
320 L"CON", L"PRN", L"AUX", L"NUL",
321 L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",
322 L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9",
323 L"CLOCK$"
324 };
325 */
326
isReservedDeviceNameW(WCHAR * path)327 static BOOL isReservedDeviceNameW(WCHAR* path) {
328 #define BUFSIZE 9
329 WCHAR buf[BUFSIZE];
330 WCHAR *lpf = NULL;
331 DWORD retLen = GetFullPathNameW(path,
332 BUFSIZE,
333 buf,
334 &lpf);
335 if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) &&
336 buf[0] == L'\\' && buf[1] == L'\\' &&
337 buf[2] == L'.' && buf[3] == L'\\') {
338 WCHAR* dname = _wcsupr(buf + 4);
339 if (wcscmp(dname, L"CON") == 0 ||
340 wcscmp(dname, L"PRN") == 0 ||
341 wcscmp(dname, L"AUX") == 0 ||
342 wcscmp(dname, L"NUL") == 0)
343 return TRUE;
344 if ((wcsncmp(dname, L"COM", 3) == 0 ||
345 wcsncmp(dname, L"LPT", 3) == 0) &&
346 dname[3] - L'0' > 0 &&
347 dname[3] - L'0' <= 9)
348 return TRUE;
349 }
350 return FALSE;
351 }
352
353 JNIEXPORT jint JNICALL
Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv * env,jobject this,jobject file)354 Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this,
355 jobject file)
356 {
357 jint rv = 0;
358
359 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
360 if (pathbuf == NULL)
361 return rv;
362 if (!isReservedDeviceNameW(pathbuf)) {
363 DWORD a = getFinalAttributes(pathbuf);
364 if (a != INVALID_FILE_ATTRIBUTES) {
365 rv = (java_io_FileSystem_BA_EXISTS
366 | ((a & FILE_ATTRIBUTE_DIRECTORY)
367 ? java_io_FileSystem_BA_DIRECTORY
368 : java_io_FileSystem_BA_REGULAR)
369 | ((a & FILE_ATTRIBUTE_HIDDEN)
370 ? java_io_FileSystem_BA_HIDDEN : 0));
371 }
372 }
373 free(pathbuf);
374 return rv;
375 }
376
377
378 JNIEXPORT jboolean
Java_java_io_WinNTFileSystem_checkAccess(JNIEnv * env,jobject this,jobject file,jint access)379 JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this,
380 jobject file, jint access)
381 {
382 DWORD attr;
383 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
384 if (pathbuf == NULL)
385 return JNI_FALSE;
386 attr = GetFileAttributesW(pathbuf);
387 attr = getFinalAttributesIfReparsePoint(pathbuf, attr);
388 free(pathbuf);
389 if (attr == INVALID_FILE_ATTRIBUTES)
390 return JNI_FALSE;
391 switch (access) {
392 case java_io_FileSystem_ACCESS_READ:
393 case java_io_FileSystem_ACCESS_EXECUTE:
394 return JNI_TRUE;
395 case java_io_FileSystem_ACCESS_WRITE:
396 /* Read-only attribute ignored on directories */
397 if ((attr & FILE_ATTRIBUTE_DIRECTORY) ||
398 (attr & FILE_ATTRIBUTE_READONLY) == 0)
399 return JNI_TRUE;
400 else
401 return JNI_FALSE;
402 default:
403 assert(0);
404 return JNI_FALSE;
405 }
406 }
407
408 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_setPermission(JNIEnv * env,jobject this,jobject file,jint access,jboolean enable,jboolean owneronly)409 Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this,
410 jobject file,
411 jint access,
412 jboolean enable,
413 jboolean owneronly)
414 {
415 jboolean rv = JNI_FALSE;
416 WCHAR *pathbuf;
417 DWORD a;
418 if (access == java_io_FileSystem_ACCESS_READ ||
419 access == java_io_FileSystem_ACCESS_EXECUTE) {
420 return enable;
421 }
422 pathbuf = fileToNTPath(env, file, ids.path);
423 if (pathbuf == NULL)
424 return JNI_FALSE;
425 a = GetFileAttributesW(pathbuf);
426
427 /* if reparse point, get final target */
428 if ((a != INVALID_FILE_ATTRIBUTES) &&
429 ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
430 {
431 WCHAR *fp = getFinalPath(env, pathbuf);
432 if (fp == NULL) {
433 a = INVALID_FILE_ATTRIBUTES;
434 } else {
435 free(pathbuf);
436 pathbuf = fp;
437 a = GetFileAttributesW(pathbuf);
438 }
439 }
440 if ((a != INVALID_FILE_ATTRIBUTES) &&
441 ((a & FILE_ATTRIBUTE_DIRECTORY) == 0))
442 {
443 if (enable)
444 a = a & ~FILE_ATTRIBUTE_READONLY;
445 else
446 a = a | FILE_ATTRIBUTE_READONLY;
447 if (SetFileAttributesW(pathbuf, a))
448 rv = JNI_TRUE;
449 }
450 free(pathbuf);
451 return rv;
452 }
453
454 JNIEXPORT jlong JNICALL
Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv * env,jobject this,jobject file)455 Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
456 jobject file)
457 {
458 jlong rv = 0;
459 ULARGE_INTEGER modTime;
460 FILETIME t;
461 HANDLE h;
462 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
463 if (pathbuf == NULL)
464 return rv;
465 h = CreateFileW(pathbuf,
466 /* Device query access */
467 0,
468 /* Share it */
469 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
470 /* No security attributes */
471 NULL,
472 /* Open existing or fail */
473 OPEN_EXISTING,
474 /* Backup semantics for directories */
475 FILE_FLAG_BACKUP_SEMANTICS,
476 /* No template file */
477 NULL);
478 if (h != INVALID_HANDLE_VALUE) {
479 if (GetFileTime(h, NULL, NULL, &t)) {
480 modTime.LowPart = (DWORD) t.dwLowDateTime;
481 modTime.HighPart = (LONG) t.dwHighDateTime;
482 rv = modTime.QuadPart / 10000;
483 rv -= 11644473600000;
484 }
485 CloseHandle(h);
486 }
487 free(pathbuf);
488 return rv;
489 }
490
491 JNIEXPORT jlong JNICALL
Java_java_io_WinNTFileSystem_getLength(JNIEnv * env,jobject this,jobject file)492 Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file)
493 {
494 jlong rv = 0;
495 WIN32_FILE_ATTRIBUTE_DATA wfad;
496 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
497 if (pathbuf == NULL)
498 return rv;
499 if (GetFileAttributesExW(pathbuf,
500 GetFileExInfoStandard,
501 &wfad)) {
502 if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
503 rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow;
504 } else {
505 /* file is a reparse point so read attributes of final target */
506 BY_HANDLE_FILE_INFORMATION finfo;
507 if (getFileInformation(pathbuf, &finfo)) {
508 rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) +
509 finfo.nFileSizeLow;
510 }
511 }
512 } else {
513 if (GetLastError() == ERROR_SHARING_VIOLATION) {
514 //
515 // The error is a "share violation", which means the file/dir
516 // must exist. Try FindFirstFile, we know this at least works
517 // for pagefile.sys.
518 //
519
520 WIN32_FIND_DATAW fileData;
521 HANDLE h = FindFirstFileW(pathbuf, &fileData);
522 if (h != INVALID_HANDLE_VALUE) {
523 if ((fileData.dwFileAttributes &
524 FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
525 WCHAR backslash = L'\\';
526 WCHAR *pslash = wcsrchr(pathbuf, backslash);
527 if (pslash == NULL) {
528 pslash = pathbuf;
529 } else {
530 pslash++;
531 }
532 WCHAR *fslash = wcsrchr(fileData.cFileName, backslash);
533 if (fslash == NULL) {
534 fslash = fileData.cFileName;
535 } else {
536 fslash++;
537 }
538 if (wcscmp(pslash, fslash) == 0) {
539 ULARGE_INTEGER length;
540 length.LowPart = fileData.nFileSizeLow;
541 length.HighPart = fileData.nFileSizeHigh;
542 if (length.QuadPart <= _I64_MAX) {
543 rv = (jlong)length.QuadPart;
544 }
545 }
546 }
547 FindClose(h);
548 }
549 }
550 }
551 free(pathbuf);
552 return rv;
553 }
554
555 /* -- File operations -- */
556
557 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv * env,jclass cls,jstring path)558 Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
559 jstring path)
560 {
561 HANDLE h = NULL;
562 WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
563 if (pathbuf == NULL)
564 return JNI_FALSE;
565 if (isReservedDeviceNameW(pathbuf)) {
566 free(pathbuf);
567 return JNI_FALSE;
568 }
569 h = CreateFileW(
570 pathbuf, /* Wide char path name */
571 GENERIC_READ | GENERIC_WRITE, /* Read and write permission */
572 FILE_SHARE_READ | FILE_SHARE_WRITE, /* File sharing flags */
573 NULL, /* Security attributes */
574 CREATE_NEW, /* creation disposition */
575 FILE_ATTRIBUTE_NORMAL |
576 FILE_FLAG_OPEN_REPARSE_POINT, /* flags and attributes */
577 NULL);
578
579 if (h == INVALID_HANDLE_VALUE) {
580 DWORD error = GetLastError();
581 if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) {
582 // return false rather than throwing an exception when there is
583 // an existing file.
584 DWORD a = GetFileAttributesW(pathbuf);
585 if (a == INVALID_FILE_ATTRIBUTES) {
586 SetLastError(error);
587 JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
588 }
589 }
590 free(pathbuf);
591 return JNI_FALSE;
592 }
593 free(pathbuf);
594 CloseHandle(h);
595 return JNI_TRUE;
596 }
597
598 static int
removeFileOrDirectory(const jchar * path)599 removeFileOrDirectory(const jchar *path)
600 {
601 /* Returns 0 on success */
602 DWORD a;
603
604 SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL);
605 a = GetFileAttributesW(path);
606 if (a == INVALID_FILE_ATTRIBUTES) {
607 return 1;
608 } else if (a & FILE_ATTRIBUTE_DIRECTORY) {
609 return !RemoveDirectoryW(path);
610 } else {
611 return !DeleteFileW(path);
612 }
613 }
614
615 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_delete0(JNIEnv * env,jobject this,jobject file)616 Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
617 {
618 jboolean rv = JNI_FALSE;
619 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
620 if (pathbuf == NULL) {
621 return JNI_FALSE;
622 }
623 if (removeFileOrDirectory(pathbuf) == 0) {
624 rv = JNI_TRUE;
625 }
626 free(pathbuf);
627 return rv;
628 }
629
630 JNIEXPORT jobjectArray JNICALL
Java_java_io_WinNTFileSystem_list(JNIEnv * env,jobject this,jobject file)631 Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
632 {
633 WCHAR *search_path;
634 HANDLE handle;
635 WIN32_FIND_DATAW find_data;
636 int len, maxlen;
637 jobjectArray rv, old;
638 DWORD fattr;
639 jstring name;
640 jclass str_class;
641 WCHAR *pathbuf;
642 DWORD err;
643
644 str_class = JNU_ClassString(env);
645 CHECK_NULL_RETURN(str_class, NULL);
646
647 pathbuf = fileToNTPath(env, file, ids.path);
648 if (pathbuf == NULL)
649 return NULL;
650 search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
651 if (search_path == 0) {
652 free (pathbuf);
653 errno = ENOMEM;
654 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
655 return NULL;
656 }
657 wcscpy(search_path, pathbuf);
658 free(pathbuf);
659 fattr = GetFileAttributesW(search_path);
660 if (fattr == INVALID_FILE_ATTRIBUTES) {
661 free(search_path);
662 return NULL;
663 } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
664 free(search_path);
665 return NULL;
666 }
667
668 /* Remove trailing space chars from directory name */
669 len = (int)wcslen(search_path);
670 while (search_path[len-1] == L' ') {
671 len--;
672 }
673 search_path[len] = 0;
674
675 /* Append "*", or possibly "\\*", to path */
676 if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
677 (search_path[1] == L':'
678 && (search_path[2] == L'\0'
679 || (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
680 /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
681 wcscat(search_path, L"*");
682 } else {
683 wcscat(search_path, L"\\*");
684 }
685
686 /* Open handle to the first file */
687 handle = FindFirstFileW(search_path, &find_data);
688 free(search_path);
689 if (handle == INVALID_HANDLE_VALUE) {
690 if (GetLastError() != ERROR_FILE_NOT_FOUND) {
691 // error
692 return NULL;
693 } else {
694 // No files found - return an empty array
695 rv = (*env)->NewObjectArray(env, 0, str_class, NULL);
696 return rv;
697 }
698 }
699
700 /* Allocate an initial String array */
701 len = 0;
702 maxlen = 16;
703 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
704 if (rv == NULL) { // Couldn't allocate an array
705 FindClose(handle);
706 return NULL;
707 }
708 /* Scan the directory */
709 do {
710 if (!wcscmp(find_data.cFileName, L".")
711 || !wcscmp(find_data.cFileName, L".."))
712 continue;
713 name = (*env)->NewString(env, find_data.cFileName,
714 (jsize)wcslen(find_data.cFileName));
715 if (name == NULL) {
716 FindClose(handle);
717 return NULL; // error
718 }
719 if (len == maxlen) {
720 old = rv;
721 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
722 if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0) {
723 FindClose(handle);
724 return NULL; // error
725 }
726 (*env)->DeleteLocalRef(env, old);
727 }
728 (*env)->SetObjectArrayElement(env, rv, len++, name);
729 (*env)->DeleteLocalRef(env, name);
730
731 } while (FindNextFileW(handle, &find_data));
732
733 err = GetLastError();
734 FindClose(handle);
735 if (err != ERROR_NO_MORE_FILES) {
736 return NULL; // error
737 }
738
739 if (len < maxlen) {
740 /* Copy the final results into an appropriately-sized array */
741 old = rv;
742 rv = (*env)->NewObjectArray(env, len, str_class, NULL);
743 if (rv == NULL)
744 return NULL; /* error */
745 if (JNU_CopyObjectArray(env, rv, old, len) < 0)
746 return NULL; /* error */
747 }
748 return rv;
749 }
750
751
752 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_createDirectory(JNIEnv * env,jobject this,jobject file)753 Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
754 jobject file)
755 {
756 BOOL h = FALSE;
757 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
758 if (pathbuf == NULL) {
759 /* Exception is pending */
760 return JNI_FALSE;
761 }
762 h = CreateDirectoryW(pathbuf, NULL);
763 free(pathbuf);
764
765 if (h == 0) {
766 return JNI_FALSE;
767 }
768
769 return JNI_TRUE;
770 }
771
772
773 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_rename0(JNIEnv * env,jobject this,jobject from,jobject to)774 Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
775 jobject to)
776 {
777
778 jboolean rv = JNI_FALSE;
779 WCHAR *frompath = fileToNTPath(env, from, ids.path);
780 WCHAR *topath = fileToNTPath(env, to, ids.path);
781 if (frompath != NULL && topath != NULL && _wrename(frompath, topath) == 0) {
782 rv = JNI_TRUE;
783 }
784 free(frompath);
785 free(topath);
786 return rv;
787 }
788
789
790 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv * env,jobject this,jobject file,jlong time)791 Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
792 jobject file, jlong time)
793 {
794 jboolean rv = JNI_FALSE;
795 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
796 HANDLE h;
797 if (pathbuf == NULL)
798 return JNI_FALSE;
799 h = CreateFileW(pathbuf,
800 FILE_WRITE_ATTRIBUTES,
801 FILE_SHARE_READ | FILE_SHARE_WRITE,
802 NULL,
803 OPEN_EXISTING,
804 FILE_FLAG_BACKUP_SEMANTICS,
805 0);
806 if (h != INVALID_HANDLE_VALUE) {
807 ULARGE_INTEGER modTime;
808 FILETIME t;
809 modTime.QuadPart = (time + 11644473600000L) * 10000L;
810 t.dwLowDateTime = (DWORD)modTime.LowPart;
811 t.dwHighDateTime = (DWORD)modTime.HighPart;
812 if (SetFileTime(h, NULL, NULL, &t)) {
813 rv = JNI_TRUE;
814 }
815 CloseHandle(h);
816 }
817 free(pathbuf);
818
819 return rv;
820 }
821
822
823 JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv * env,jobject this,jobject file)824 Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
825 jobject file)
826 {
827 jboolean rv = JNI_FALSE;
828 DWORD a;
829 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
830 if (pathbuf == NULL)
831 return JNI_FALSE;
832 a = GetFileAttributesW(pathbuf);
833
834 /* if reparse point, get final target */
835 if ((a != INVALID_FILE_ATTRIBUTES) &&
836 ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
837 {
838 WCHAR *fp = getFinalPath(env, pathbuf);
839 if (fp == NULL) {
840 a = INVALID_FILE_ATTRIBUTES;
841 } else {
842 free(pathbuf);
843 pathbuf = fp;
844 a = GetFileAttributesW(pathbuf);
845 }
846 }
847
848 if ((a != INVALID_FILE_ATTRIBUTES) &&
849 ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
850 if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
851 rv = JNI_TRUE;
852 }
853 free(pathbuf);
854 return rv;
855 }
856
857 /* -- Filesystem interface -- */
858
859
860 JNIEXPORT jobject JNICALL
Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv * env,jobject this,jint drive)861 Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
862 jint drive)
863 {
864 jstring ret = NULL;
865 jchar *p = currentDir(drive);
866 jchar *pf = p;
867 if (p == NULL) return NULL;
868 if (iswalpha(*p) && (p[1] == L':')) p += 2;
869 ret = (*env)->NewString(env, p, (jsize)wcslen(p));
870 free (pf);
871 return ret;
872 }
873
874 JNIEXPORT jint JNICALL
Java_java_io_WinNTFileSystem_listRoots0(JNIEnv * env,jclass ignored)875 Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored)
876 {
877 return GetLogicalDrives();
878 }
879
880 JNIEXPORT jlong JNICALL
Java_java_io_WinNTFileSystem_getSpace0(JNIEnv * env,jobject this,jobject file,jint t)881 Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
882 jobject file, jint t)
883 {
884 WCHAR volname[MAX_PATH_LENGTH + 1];
885 jlong rv = 0L;
886 WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
887
888 if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
889 ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
890 if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
891 switch(t) {
892 case java_io_FileSystem_SPACE_TOTAL:
893 rv = long_to_jlong(totalSpace.QuadPart);
894 break;
895 case java_io_FileSystem_SPACE_FREE:
896 rv = long_to_jlong(freeSpace.QuadPart);
897 break;
898 case java_io_FileSystem_SPACE_USABLE:
899 rv = long_to_jlong(usableSpace.QuadPart);
900 break;
901 default:
902 assert(0);
903 }
904 }
905 }
906
907 free(pathbuf);
908 return rv;
909 }
910
911 // pathname is expected to be either null or to contain the root
912 // of the path terminated by a backslash
913 JNIEXPORT jint JNICALL
Java_java_io_WinNTFileSystem_getNameMax0(JNIEnv * env,jobject this,jstring pathname)914 Java_java_io_WinNTFileSystem_getNameMax0(JNIEnv *env, jobject this,
915 jstring pathname)
916 {
917 BOOL res = 0;
918 DWORD maxComponentLength;
919
920 if (pathname == NULL) {
921 res = GetVolumeInformationW(NULL,
922 NULL,
923 0,
924 NULL,
925 &maxComponentLength,
926 NULL,
927 NULL,
928 0);
929 } else {
930 WITH_UNICODE_STRING(env, pathname, path) {
931 res = GetVolumeInformationW(path,
932 NULL,
933 0,
934 NULL,
935 &maxComponentLength,
936 NULL,
937 NULL,
938 0);
939 } END_UNICODE_STRING(env, path);
940 }
941
942 if (res == 0) {
943 JNU_ThrowIOExceptionWithLastError(env,
944 "Could not get maximum component length");
945 }
946
947 return (jint)maxComponentLength;
948 }
949