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