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