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