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