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