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