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