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