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