jdk/src/java.base/windows/native/libjava/WinNTFileSystem_md.c
changeset 25859 3317bb8137f4
parent 23565 b28f771b8cbe
child 26226 1d626d5986fe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libjava/WinNTFileSystem_md.c	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,886 @@
+/*
+ * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* Access APIs for WinXP and above */
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <direct.h>
+#include <windows.h>
+#include <io.h>
+
+#include "jni.h"
+#include "io_util.h"
+#include "jlong.h"
+#include "io_util_md.h"
+#include "dirent_md.h"
+#include "java_io_FileSystem.h"
+
+#define MAX_PATH_LENGTH 1024
+
+static struct {
+    jfieldID path;
+} ids;
+
+/**
+ * GetFinalPathNameByHandle is available on Windows Vista and newer
+ */
+typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD);
+static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func;
+
+JNIEXPORT void JNICALL
+Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls)
+{
+    HMODULE handle;
+    jclass fileClass;
+
+    fileClass = (*env)->FindClass(env, "java/io/File");
+    CHECK_NULL(fileClass);
+    ids.path = (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;");
+    CHECK_NULL(ids.path);
+
+    // GetFinalPathNameByHandle requires Windows Vista or newer
+    if (GetModuleHandleExW((GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+                            GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT),
+                           (LPCWSTR)&CreateFileW, &handle) != 0)
+    {
+        GetFinalPathNameByHandle_func = (GetFinalPathNameByHandleProc)
+            GetProcAddress(handle, "GetFinalPathNameByHandleW");
+    }
+}
+
+/* -- Path operations -- */
+
+extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len);
+extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len);
+
+/**
+ * Retrieves the fully resolved (final) path for the given path or NULL
+ * if the function fails.
+ */
+static WCHAR* getFinalPath(JNIEnv *env, const WCHAR *path)
+{
+    HANDLE h;
+    WCHAR *result;
+    DWORD error;
+
+    /* Need Windows Vista or newer to get the final path */
+    if (GetFinalPathNameByHandle_func == NULL)
+        return NULL;
+
+    h = CreateFileW(path,
+                    FILE_READ_ATTRIBUTES,
+                    FILE_SHARE_DELETE |
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    NULL,
+                    OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS,
+                    NULL);
+    if (h == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    /**
+     * Allocate a buffer for the resolved path. For a long path we may need
+     * to allocate a larger buffer.
+     */
+    result = (WCHAR*)malloc(MAX_PATH * sizeof(WCHAR));
+    if (result != NULL) {
+        DWORD len = (*GetFinalPathNameByHandle_func)(h, result, MAX_PATH, 0);
+        if (len >= MAX_PATH) {
+            /* retry with a buffer of the right size */
+            WCHAR* newResult = (WCHAR*)realloc(result, (len+1) * sizeof(WCHAR));
+            if (newResult != NULL) {
+                result = newResult;
+                len = (*GetFinalPathNameByHandle_func)(h, result, len, 0);
+            } else {
+                len = 0;
+                JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+            }
+        }
+
+        if (len > 0) {
+            /**
+             * Strip prefix (should be \\?\ or \\?\UNC)
+             */
+            if (result[0] == L'\\' && result[1] == L'\\' &&
+                result[2] == L'?' && result[3] == L'\\')
+            {
+                int isUnc = (result[4] == L'U' &&
+                             result[5] == L'N' &&
+                             result[6] == L'C');
+                int prefixLen = (isUnc) ? 7 : 4;
+                /* actual result length (includes terminator) */
+                int resultLen = len - prefixLen + (isUnc ? 1 : 0) + 1;
+
+                /* copy result without prefix into new buffer */
+                WCHAR *tmp = (WCHAR*)malloc(resultLen * sizeof(WCHAR));
+                if (tmp == NULL) {
+                    JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+                    len = 0;
+                } else {
+                    WCHAR *p = result;
+                    p += prefixLen;
+                    if (isUnc) {
+                        WCHAR *p2 = tmp;
+                        p2[0] = L'\\';
+                        p2++;
+                        wcscpy(p2, p);
+                    } else {
+                        wcscpy(tmp, p);
+                    }
+                    free(result);
+                    result = tmp;
+                }
+            }
+        }
+
+        /* unable to get final path */
+        if (len == 0 && result != NULL) {
+            free(result);
+            result = NULL;
+        }
+    } else {
+        JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+    }
+
+    error = GetLastError();
+    if (CloseHandle(h))
+        SetLastError(error);
+    return result;
+}
+
+/**
+ * Retrieves file information for the specified file. If the file is
+ * symbolic link then the information on fully resolved target is
+ * returned.
+ */
+static BOOL getFileInformation(const WCHAR *path,
+                               BY_HANDLE_FILE_INFORMATION *finfo)
+{
+    BOOL result;
+    DWORD error;
+    HANDLE h = CreateFileW(path,
+                           FILE_READ_ATTRIBUTES,
+                           FILE_SHARE_DELETE |
+                               FILE_SHARE_READ | FILE_SHARE_WRITE,
+                           NULL,
+                           OPEN_EXISTING,
+                           FILE_FLAG_BACKUP_SEMANTICS,
+                           NULL);
+    if (h == INVALID_HANDLE_VALUE)
+        return FALSE;
+    result = GetFileInformationByHandle(h, finfo);
+    error = GetLastError();
+    if (CloseHandle(h))
+        SetLastError(error);
+    return result;
+}
+
+/**
+ * If the given attributes are the attributes of a reparse point, then
+ * read and return the attributes of the special cases.
+ */
+DWORD getFinalAttributesIfReparsePoint(WCHAR *path, DWORD a)
+{
+    if ((a != INVALID_FILE_ATTRIBUTES) &&
+        ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
+    {
+        BY_HANDLE_FILE_INFORMATION finfo;
+        BOOL res = getFileInformation(path, &finfo);
+        a = (res) ? finfo.dwFileAttributes : INVALID_FILE_ATTRIBUTES;
+    }
+    return a;
+}
+
+/**
+ * Take special cases into account when retrieving the attributes
+ * of path
+ */
+DWORD getFinalAttributes(WCHAR *path)
+{
+    DWORD attr = INVALID_FILE_ATTRIBUTES;
+
+    WIN32_FILE_ATTRIBUTE_DATA wfad;
+    WIN32_FIND_DATAW wfd;
+    HANDLE h;
+
+    if (GetFileAttributesExW(path, GetFileExInfoStandard, &wfad)) {
+        attr = getFinalAttributesIfReparsePoint(path, wfad.dwFileAttributes);
+    } else if (GetLastError() == ERROR_SHARING_VIOLATION &&
+               (h = FindFirstFileW(path, &wfd)) != INVALID_HANDLE_VALUE) {
+        attr = getFinalAttributesIfReparsePoint(path, wfd.dwFileAttributes);
+        FindClose(h);
+    }
+    return attr;
+}
+
+JNIEXPORT jstring JNICALL
+Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this,
+                                           jstring pathname)
+{
+    jstring rv = NULL;
+    WCHAR canonicalPath[MAX_PATH_LENGTH];
+
+    WITH_UNICODE_STRING(env, pathname, path) {
+        /* we estimate the max length of memory needed as
+           "currentDir. length + pathname.length"
+         */
+        int len = (int)wcslen(path);
+        len += currentDirLength(path, len);
+        if (len  > MAX_PATH_LENGTH - 1) {
+            WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
+            if (cp != NULL) {
+                if (wcanonicalize(path, cp, len) >= 0) {
+                    rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
+                }
+                free(cp);
+            } else {
+                JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+            }
+        } else if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) {
+            rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
+        }
+    } END_UNICODE_STRING(env, path);
+    if (rv == NULL && !(*env)->ExceptionCheck(env)) {
+        JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
+    }
+    return rv;
+}
+
+
+JNIEXPORT jstring JNICALL
+Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this,
+                                                     jstring canonicalPrefixString,
+                                                     jstring pathWithCanonicalPrefixString)
+{
+    jstring rv = NULL;
+    WCHAR canonicalPath[MAX_PATH_LENGTH];
+    WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) {
+        WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) {
+            int len = (int)wcslen(canonicalPrefix) + MAX_PATH;
+            if (len > MAX_PATH_LENGTH) {
+                WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
+                if (cp != NULL) {
+                    if (wcanonicalizeWithPrefix(canonicalPrefix,
+                                                pathWithCanonicalPrefix,
+                                                cp, len) >= 0) {
+                      rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
+                    }
+                    free(cp);
+                } else {
+                    JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+                }
+            } else if (wcanonicalizeWithPrefix(canonicalPrefix,
+                                               pathWithCanonicalPrefix,
+                                               canonicalPath, MAX_PATH_LENGTH) >= 0) {
+                rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
+            }
+        } END_UNICODE_STRING(env, pathWithCanonicalPrefix);
+    } END_UNICODE_STRING(env, canonicalPrefix);
+    if (rv == NULL && !(*env)->ExceptionCheck(env)) {
+        JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
+    }
+    return rv;
+}
+
+/* -- Attribute accessors -- */
+
+/* Check whether or not the file name in "path" is a Windows reserved
+   device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the
+   returned result from GetFullPathName, which should be in thr form of
+   "\\.\[ReservedDeviceName]" if the path represents a reserved device
+   name.
+   Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer
+   important anyway) is a device name, so we don't check it here.
+   GetFileAttributesEx will catch it later by returning 0 on NT/XP/
+   200X.
+
+   Note2: Theoretically the implementation could just lookup the table
+   below linearly if the first 4 characters of the fullpath returned
+   from GetFullPathName are "\\.\". The current implementation should
+   achieve the same result. If Microsoft add more names into their
+   reserved device name repository in the future, which probably will
+   never happen, we will need to revisit the lookup implementation.
+
+static WCHAR* ReservedDEviceNames[] = {
+    L"CON", L"PRN", L"AUX", L"NUL",
+    L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",
+    L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9",
+    L"CLOCK$"
+};
+ */
+
+static BOOL isReservedDeviceNameW(WCHAR* path) {
+#define BUFSIZE 9
+    WCHAR buf[BUFSIZE];
+    WCHAR *lpf = NULL;
+    DWORD retLen = GetFullPathNameW(path,
+                                   BUFSIZE,
+                                   buf,
+                                   &lpf);
+    if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) &&
+        buf[0] == L'\\' && buf[1] == L'\\' &&
+        buf[2] == L'.' && buf[3] == L'\\') {
+        WCHAR* dname = _wcsupr(buf + 4);
+        if (wcscmp(dname, L"CON") == 0 ||
+            wcscmp(dname, L"PRN") == 0 ||
+            wcscmp(dname, L"AUX") == 0 ||
+            wcscmp(dname, L"NUL") == 0)
+            return TRUE;
+        if ((wcsncmp(dname, L"COM", 3) == 0 ||
+             wcsncmp(dname, L"LPT", 3) == 0) &&
+            dname[3] - L'0' > 0 &&
+            dname[3] - L'0' <= 9)
+            return TRUE;
+    }
+    return FALSE;
+}
+
+JNIEXPORT jint JNICALL
+Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this,
+                                                  jobject file)
+{
+    jint rv = 0;
+
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return rv;
+    if (!isReservedDeviceNameW(pathbuf)) {
+        DWORD a = getFinalAttributes(pathbuf);
+        if (a != INVALID_FILE_ATTRIBUTES) {
+            rv = (java_io_FileSystem_BA_EXISTS
+                | ((a & FILE_ATTRIBUTE_DIRECTORY)
+                    ? java_io_FileSystem_BA_DIRECTORY
+                    : java_io_FileSystem_BA_REGULAR)
+                | ((a & FILE_ATTRIBUTE_HIDDEN)
+                    ? java_io_FileSystem_BA_HIDDEN : 0));
+        }
+    }
+    free(pathbuf);
+    return rv;
+}
+
+
+JNIEXPORT jboolean
+JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this,
+                                                 jobject file, jint access)
+{
+    DWORD attr;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return JNI_FALSE;
+    attr = GetFileAttributesW(pathbuf);
+    attr = getFinalAttributesIfReparsePoint(pathbuf, attr);
+    free(pathbuf);
+    if (attr == INVALID_FILE_ATTRIBUTES)
+        return JNI_FALSE;
+    switch (access) {
+    case java_io_FileSystem_ACCESS_READ:
+    case java_io_FileSystem_ACCESS_EXECUTE:
+        return JNI_TRUE;
+    case java_io_FileSystem_ACCESS_WRITE:
+        /* Read-only attribute ignored on directories */
+        if ((attr & FILE_ATTRIBUTE_DIRECTORY) ||
+            (attr & FILE_ATTRIBUTE_READONLY) == 0)
+            return JNI_TRUE;
+        else
+            return JNI_FALSE;
+    default:
+        assert(0);
+        return JNI_FALSE;
+    }
+}
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this,
+                                           jobject file,
+                                           jint access,
+                                           jboolean enable,
+                                           jboolean owneronly)
+{
+    jboolean rv = JNI_FALSE;
+    WCHAR *pathbuf;
+    DWORD a;
+    if (access == java_io_FileSystem_ACCESS_READ ||
+        access == java_io_FileSystem_ACCESS_EXECUTE) {
+        return enable;
+    }
+    pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return JNI_FALSE;
+    a = GetFileAttributesW(pathbuf);
+
+    /* if reparse point, get final target */
+    if ((a != INVALID_FILE_ATTRIBUTES) &&
+        ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
+    {
+        WCHAR *fp = getFinalPath(env, pathbuf);
+        if (fp == NULL) {
+            a = INVALID_FILE_ATTRIBUTES;
+        } else {
+            free(pathbuf);
+            pathbuf = fp;
+            a = GetFileAttributesW(pathbuf);
+        }
+    }
+    if ((a != INVALID_FILE_ATTRIBUTES) &&
+        ((a & FILE_ATTRIBUTE_DIRECTORY) == 0))
+    {
+        if (enable)
+            a =  a & ~FILE_ATTRIBUTE_READONLY;
+        else
+            a =  a | FILE_ATTRIBUTE_READONLY;
+        if (SetFileAttributesW(pathbuf, a))
+            rv = JNI_TRUE;
+    }
+    free(pathbuf);
+    return rv;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
+                                                 jobject file)
+{
+    jlong rv = 0;
+    LARGE_INTEGER modTime;
+    FILETIME t;
+    HANDLE h;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return rv;
+    h = CreateFileW(pathbuf,
+                    /* Device query access */
+                    0,
+                    /* Share it */
+                    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    /* No security attributes */
+                    NULL,
+                    /* Open existing or fail */
+                    OPEN_EXISTING,
+                    /* Backup semantics for directories */
+                    FILE_FLAG_BACKUP_SEMANTICS,
+                    /* No template file */
+                    NULL);
+    if (h != INVALID_HANDLE_VALUE) {
+        if (GetFileTime(h, NULL, NULL, &t)) {
+            modTime.LowPart = (DWORD) t.dwLowDateTime;
+            modTime.HighPart = (LONG) t.dwHighDateTime;
+            rv = modTime.QuadPart / 10000;
+            rv -= 11644473600000;
+        }
+        CloseHandle(h);
+    }
+    free(pathbuf);
+    return rv;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file)
+{
+    jlong rv = 0;
+    WIN32_FILE_ATTRIBUTE_DATA wfad;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return rv;
+    if (GetFileAttributesExW(pathbuf,
+                             GetFileExInfoStandard,
+                             &wfad)) {
+        if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+            rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow;
+        } else {
+            /* file is a reparse point so read attributes of final target */
+            BY_HANDLE_FILE_INFORMATION finfo;
+            if (getFileInformation(pathbuf, &finfo)) {
+                rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) +
+                    finfo.nFileSizeLow;
+            }
+        }
+    } else {
+        if (GetLastError() == ERROR_SHARING_VIOLATION) {
+            /* The error is "share violation", which means the file/dir
+               must exists. Try _wstati64, we know this at least works
+               for pagefile.sys and hiberfil.sys.
+            */
+            struct _stati64 sb;
+            if (_wstati64(pathbuf, &sb) == 0) {
+                rv = sb.st_size;
+            }
+        }
+    }
+    free(pathbuf);
+    return rv;
+}
+
+/* -- File operations -- */
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
+                                                   jstring path)
+{
+    HANDLE h = NULL;
+    WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
+    if (pathbuf == NULL)
+        return JNI_FALSE;
+    if (isReservedDeviceNameW(pathbuf)) {
+        free(pathbuf);
+        return JNI_FALSE;
+    }
+    h = CreateFileW(
+        pathbuf,                              /* Wide char path name */
+        GENERIC_READ | GENERIC_WRITE,         /* Read and write permission */
+        FILE_SHARE_READ | FILE_SHARE_WRITE,   /* File sharing flags */
+        NULL,                                 /* Security attributes */
+        CREATE_NEW,                           /* creation disposition */
+        FILE_ATTRIBUTE_NORMAL |
+            FILE_FLAG_OPEN_REPARSE_POINT,     /* flags and attributes */
+        NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        DWORD error = GetLastError();
+        if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) {
+            // return false rather than throwing an exception when there is
+            // an existing file.
+            DWORD a = GetFileAttributesW(pathbuf);
+            if (a == INVALID_FILE_ATTRIBUTES) {
+                SetLastError(error);
+                JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
+            }
+         }
+         free(pathbuf);
+         return JNI_FALSE;
+        }
+    free(pathbuf);
+    CloseHandle(h);
+    return JNI_TRUE;
+}
+
+static int
+removeFileOrDirectory(const jchar *path)
+{
+    /* Returns 0 on success */
+    DWORD a;
+
+    SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL);
+    a = GetFileAttributesW(path);
+    if (a == INVALID_FILE_ATTRIBUTES) {
+        return 1;
+    } else if (a & FILE_ATTRIBUTE_DIRECTORY) {
+        return !RemoveDirectoryW(path);
+    } else {
+        return !DeleteFileW(path);
+    }
+}
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
+{
+    jboolean rv = JNI_FALSE;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL) {
+        return JNI_FALSE;
+    }
+    if (removeFileOrDirectory(pathbuf) == 0) {
+        rv = JNI_TRUE;
+    }
+    free(pathbuf);
+    return rv;
+}
+
+JNIEXPORT jobjectArray JNICALL
+Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
+{
+    WCHAR *search_path;
+    HANDLE handle;
+    WIN32_FIND_DATAW find_data;
+    int len, maxlen;
+    jobjectArray rv, old;
+    DWORD fattr;
+    jstring name;
+    jclass str_class;
+    WCHAR *pathbuf;
+
+    str_class = JNU_ClassString(env);
+    CHECK_NULL_RETURN(str_class, NULL);
+
+    pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return NULL;
+    search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
+    if (search_path == 0) {
+        free (pathbuf);
+        errno = ENOMEM;
+        JNU_ThrowOutOfMemoryError(env, "native memory allocation faiuled");
+        return NULL;
+    }
+    wcscpy(search_path, pathbuf);
+    free(pathbuf);
+    fattr = GetFileAttributesW(search_path);
+    if (fattr == INVALID_FILE_ATTRIBUTES) {
+        free(search_path);
+        return NULL;
+    } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+        free(search_path);
+        return NULL;
+    }
+
+    /* Remove trailing space chars from directory name */
+    len = (int)wcslen(search_path);
+    while (search_path[len-1] == ' ') {
+        len--;
+    }
+    search_path[len] = 0;
+
+    /* Append "*", or possibly "\\*", to path */
+    if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
+        (search_path[1] == L':'
+        && (search_path[2] == L'\0'
+        || (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
+        /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
+        wcscat(search_path, L"*");
+    } else {
+        wcscat(search_path, L"\\*");
+    }
+
+    /* Open handle to the first file */
+    handle = FindFirstFileW(search_path, &find_data);
+    free(search_path);
+    if (handle == INVALID_HANDLE_VALUE) {
+        if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+            // error
+            return NULL;
+        } else {
+            // No files found - return an empty array
+            rv = (*env)->NewObjectArray(env, 0, str_class, NULL);
+            return rv;
+        }
+    }
+
+    /* Allocate an initial String array */
+    len = 0;
+    maxlen = 16;
+    rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
+    if (rv == NULL) // Couldn't allocate an array
+        return NULL;
+    /* Scan the directory */
+    do {
+        if (!wcscmp(find_data.cFileName, L".")
+                                || !wcscmp(find_data.cFileName, L".."))
+           continue;
+        name = (*env)->NewString(env, find_data.cFileName,
+                                 (jsize)wcslen(find_data.cFileName));
+        if (name == NULL)
+            return NULL; // error;
+        if (len == maxlen) {
+            old = rv;
+            rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
+            if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0)
+                return NULL; // error
+            (*env)->DeleteLocalRef(env, old);
+        }
+        (*env)->SetObjectArrayElement(env, rv, len++, name);
+        (*env)->DeleteLocalRef(env, name);
+
+    } while (FindNextFileW(handle, &find_data));
+
+    if (GetLastError() != ERROR_NO_MORE_FILES)
+        return NULL; // error
+    FindClose(handle);
+
+    /* Copy the final results into an appropriately-sized array */
+    old = rv;
+    rv = (*env)->NewObjectArray(env, len, str_class, NULL);
+    if (rv == NULL)
+        return NULL; /* error */
+    if (JNU_CopyObjectArray(env, rv, old, len) < 0)
+        return NULL; /* error */
+    return rv;
+}
+
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
+                                             jobject file)
+{
+    BOOL h = FALSE;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL) {
+        /* Exception is pending */
+        return JNI_FALSE;
+    }
+    h = CreateDirectoryW(pathbuf, NULL);
+    free(pathbuf);
+
+    if (h == 0) {
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
+                                     jobject to)
+{
+
+    jboolean rv = JNI_FALSE;
+    WCHAR *frompath = fileToNTPath(env, from, ids.path);
+    WCHAR *topath = fileToNTPath(env, to, ids.path);
+    if (frompath == NULL || topath == NULL)
+        return JNI_FALSE;
+    if (_wrename(frompath, topath) == 0) {
+        rv = JNI_TRUE;
+    }
+    free(frompath);
+    free(topath);
+    return rv;
+}
+
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
+                                                 jobject file, jlong time)
+{
+    jboolean rv = JNI_FALSE;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    HANDLE h;
+    if (pathbuf == NULL)
+        return JNI_FALSE;
+    h = CreateFileW(pathbuf,
+                    FILE_WRITE_ATTRIBUTES,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    NULL,
+                    OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS,
+                    0);
+    if (h != INVALID_HANDLE_VALUE) {
+        LARGE_INTEGER modTime;
+        FILETIME t;
+        modTime.QuadPart = (time + 11644473600000L) * 10000L;
+        t.dwLowDateTime = (DWORD)modTime.LowPart;
+        t.dwHighDateTime = (DWORD)modTime.HighPart;
+        if (SetFileTime(h, NULL, NULL, &t)) {
+            rv = JNI_TRUE;
+        }
+        CloseHandle(h);
+    }
+    free(pathbuf);
+
+    return rv;
+}
+
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
+                                         jobject file)
+{
+    jboolean rv = JNI_FALSE;
+    DWORD a;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+    if (pathbuf == NULL)
+        return JNI_FALSE;
+    a = GetFileAttributesW(pathbuf);
+
+    /* if reparse point, get final target */
+    if ((a != INVALID_FILE_ATTRIBUTES) &&
+        ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
+    {
+        WCHAR *fp = getFinalPath(env, pathbuf);
+        if (fp == NULL) {
+            a = INVALID_FILE_ATTRIBUTES;
+        } else {
+            free(pathbuf);
+            pathbuf = fp;
+            a = GetFileAttributesW(pathbuf);
+        }
+    }
+
+    if ((a != INVALID_FILE_ATTRIBUTES) &&
+        ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
+        if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
+            rv = JNI_TRUE;
+    }
+    free(pathbuf);
+    return rv;
+}
+
+/* -- Filesystem interface -- */
+
+
+JNIEXPORT jobject JNICALL
+Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
+                                               jint drive)
+{
+    jstring ret = NULL;
+    jchar *p = currentDir(drive);
+    jchar *pf = p;
+    if (p == NULL) return NULL;
+    if (iswalpha(*p) && (p[1] == L':')) p += 2;
+    ret = (*env)->NewString(env, p, (jsize)wcslen(p));
+    free (pf);
+    return ret;
+}
+
+JNIEXPORT jint JNICALL
+Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored)
+{
+    return GetLogicalDrives();
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
+                                       jobject file, jint t)
+{
+    WCHAR volname[MAX_PATH_LENGTH + 1];
+    jlong rv = 0L;
+    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
+
+    if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
+        ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
+        if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
+            switch(t) {
+            case java_io_FileSystem_SPACE_TOTAL:
+                rv = long_to_jlong(totalSpace.QuadPart);
+                break;
+            case java_io_FileSystem_SPACE_FREE:
+                rv = long_to_jlong(freeSpace.QuadPart);
+                break;
+            case java_io_FileSystem_SPACE_USABLE:
+                rv = long_to_jlong(usableSpace.QuadPart);
+                break;
+            default:
+                assert(0);
+            }
+        }
+    }
+
+    free(pathbuf);
+    return rv;
+}