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