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 
 643     str_class = JNU_ClassString(env);
 644     CHECK_NULL_RETURN(str_class, NULL);
 645 
 646     pathbuf = fileToNTPath(env, file, ids.path);
 647     if (pathbuf == NULL)
 648         return NULL;
 649     search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
 650     if (search_path == 0) {
 651         free (pathbuf);
 652         errno = ENOMEM;
 653         JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
 654         return NULL;
 655     }
 656     wcscpy(search_path, pathbuf);
 657     free(pathbuf);
 658     fattr = GetFileAttributesW(search_path);
 659     if (fattr == INVALID_FILE_ATTRIBUTES) {
 660         free(search_path);
 661         return NULL;
 662     } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
 663         free(search_path);
 664         return NULL;
 665     }
 666 
 667     /* Remove trailing space chars from directory name */
 668     len = (int)wcslen(search_path);
 669     while (search_path[len-1] == L' ') {
 670         len--;
 671     }
 672     search_path[len] = 0;
 673 
 674     /* Append "*", or possibly "\\*", to path */
 675     if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
 676         (search_path[1] == L':'
 677         && (search_path[2] == L'\0'
 678         || (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
 679         /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
 680         wcscat(search_path, L"*");
 681     } else {
 682         wcscat(search_path, L"\\*");
 683     }
 684 
 685     /* Open handle to the first file */
 686     handle = FindFirstFileW(search_path, &find_data);
 687     free(search_path);
 688     if (handle == INVALID_HANDLE_VALUE) {
 689         if (GetLastError() != ERROR_FILE_NOT_FOUND) {
 690             // error
 691             return NULL;
 692         } else {
 693             // No files found - return an empty array
 694             rv = (*env)->NewObjectArray(env, 0, str_class, NULL);
 695             return rv;
 696         }
 697     }
 698 
 699     /* Allocate an initial String array */
 700     len = 0;
 701     maxlen = 16;
 702     rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
 703     if (rv == NULL) // Couldn't allocate an array
 704         return NULL;
 705     /* Scan the directory */
 706     do {
 707         if (!wcscmp(find_data.cFileName, L".")
 708                                 || !wcscmp(find_data.cFileName, L".."))
 709            continue;
 710         name = (*env)->NewString(env, find_data.cFileName,
 711                                  (jsize)wcslen(find_data.cFileName));
 712         if (name == NULL)
 713             return NULL; // error;
 714         if (len == maxlen) {
 715             old = rv;
 716             rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
 717             if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0)
 718                 return NULL; // error
 719             (*env)->DeleteLocalRef(env, old);
 720         }
 721         (*env)->SetObjectArrayElement(env, rv, len++, name);
 722         (*env)->DeleteLocalRef(env, name);
 723 
 724     } while (FindNextFileW(handle, &find_data));
 725 
 726     if (GetLastError() != ERROR_NO_MORE_FILES)
 727         return NULL; // error
 728     FindClose(handle);
 729 
 730     if (len < maxlen) {
 731         /* Copy the final results into an appropriately-sized array */
 732         old = rv;
 733         rv = (*env)->NewObjectArray(env, len, str_class, NULL);
 734         if (rv == NULL)
 735             return NULL; /* error */
 736         if (JNU_CopyObjectArray(env, rv, old, len) < 0)
 737             return NULL; /* error */
 738     }
 739     return rv;
 740 }
 741 
 742 
 743 JNIEXPORT jboolean JNICALL
 744 Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
 745                                              jobject file)
 746 {
 747     BOOL h = FALSE;
 748     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 749     if (pathbuf == NULL) {
 750         /* Exception is pending */
 751         return JNI_FALSE;
 752     }
 753     h = CreateDirectoryW(pathbuf, NULL);
 754     free(pathbuf);
 755 
 756     if (h == 0) {
 757         return JNI_FALSE;
 758     }
 759 
 760     return JNI_TRUE;
 761 }
 762 
 763 
 764 JNIEXPORT jboolean JNICALL
 765 Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
 766                                      jobject to)
 767 {
 768 
 769     jboolean rv = JNI_FALSE;
 770     WCHAR *frompath = fileToNTPath(env, from, ids.path);
 771     WCHAR *topath = fileToNTPath(env, to, ids.path);
 772     if (frompath != NULL && topath != NULL && _wrename(frompath, topath) == 0) {
 773         rv = JNI_TRUE;
 774     }
 775     free(frompath);
 776     free(topath);
 777     return rv;
 778 }
 779 
 780 
 781 JNIEXPORT jboolean JNICALL
 782 Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 783                                                  jobject file, jlong time)
 784 {
 785     jboolean rv = JNI_FALSE;
 786     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 787     HANDLE h;
 788     if (pathbuf == NULL)
 789         return JNI_FALSE;
 790     h = CreateFileW(pathbuf,
 791                     FILE_WRITE_ATTRIBUTES,
 792                     FILE_SHARE_READ | FILE_SHARE_WRITE,
 793                     NULL,
 794                     OPEN_EXISTING,
 795                     FILE_FLAG_BACKUP_SEMANTICS,
 796                     0);
 797     if (h != INVALID_HANDLE_VALUE) {
 798         ULARGE_INTEGER modTime;
 799         FILETIME t;
 800         modTime.QuadPart = (time + 11644473600000L) * 10000L;
 801         t.dwLowDateTime = (DWORD)modTime.LowPart;
 802         t.dwHighDateTime = (DWORD)modTime.HighPart;
 803         if (SetFileTime(h, NULL, NULL, &t)) {
 804             rv = JNI_TRUE;
 805         }
 806         CloseHandle(h);
 807     }
 808     free(pathbuf);
 809 
 810     return rv;
 811 }
 812 
 813 
 814 JNIEXPORT jboolean JNICALL
 815 Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
 816                                          jobject file)
 817 {
 818     jboolean rv = JNI_FALSE;
 819     DWORD a;
 820     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 821     if (pathbuf == NULL)
 822         return JNI_FALSE;
 823     a = GetFileAttributesW(pathbuf);
 824 
 825     /* if reparse point, get final target */
 826     if ((a != INVALID_FILE_ATTRIBUTES) &&
 827         ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
 828     {
 829         WCHAR *fp = getFinalPath(env, pathbuf);
 830         if (fp == NULL) {
 831             a = INVALID_FILE_ATTRIBUTES;
 832         } else {
 833             free(pathbuf);
 834             pathbuf = fp;
 835             a = GetFileAttributesW(pathbuf);
 836         }
 837     }
 838 
 839     if ((a != INVALID_FILE_ATTRIBUTES) &&
 840         ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
 841         if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
 842             rv = JNI_TRUE;
 843     }
 844     free(pathbuf);
 845     return rv;
 846 }
 847 
 848 /* -- Filesystem interface -- */
 849 
 850 
 851 JNIEXPORT jobject JNICALL
 852 Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
 853                                                jint drive)
 854 {
 855     jstring ret = NULL;
 856     jchar *p = currentDir(drive);
 857     jchar *pf = p;
 858     if (p == NULL) return NULL;
 859     if (iswalpha(*p) && (p[1] == L':')) p += 2;
 860     ret = (*env)->NewString(env, p, (jsize)wcslen(p));
 861     free (pf);
 862     return ret;
 863 }
 864 
 865 JNIEXPORT jint JNICALL
 866 Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored)
 867 {
 868     return GetLogicalDrives();
 869 }
 870 
 871 JNIEXPORT jlong JNICALL
 872 Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
 873                                        jobject file, jint t)
 874 {
 875     WCHAR volname[MAX_PATH_LENGTH + 1];
 876     jlong rv = 0L;
 877     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 878 
 879     if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
 880         ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
 881         if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
 882             switch(t) {
 883             case java_io_FileSystem_SPACE_TOTAL:
 884                 rv = long_to_jlong(totalSpace.QuadPart);
 885                 break;
 886             case java_io_FileSystem_SPACE_FREE:
 887                 rv = long_to_jlong(freeSpace.QuadPart);
 888                 break;
 889             case java_io_FileSystem_SPACE_USABLE:
 890                 rv = long_to_jlong(usableSpace.QuadPart);
 891                 break;
 892             default:
 893                 assert(0);
 894             }
 895         }
 896     }
 897 
 898     free(pathbuf);
 899     return rv;
 900 }
 901 
 902 // pathname is expected to be either null or to contain the root
 903 // of the path terminated by a backslash
 904 JNIEXPORT jint JNICALL
 905 Java_java_io_WinNTFileSystem_getNameMax0(JNIEnv *env, jobject this,
 906                                          jstring pathname)
 907 {
 908     BOOL res = 0;
 909     DWORD maxComponentLength;
 910 
 911     if (pathname == NULL) {
 912             res = GetVolumeInformationW(NULL,
 913                                         NULL,
 914                                         0,
 915                                         NULL,
 916                                         &maxComponentLength,
 917                                         NULL,
 918                                         NULL,
 919                                         0);
 920     } else {
 921         WITH_UNICODE_STRING(env, pathname, path) {
 922             res = GetVolumeInformationW(path,
 923                                         NULL,
 924                                         0,
 925                                         NULL,
 926                                         &maxComponentLength,
 927                                         NULL,
 928                                         NULL,
 929                                         0);
 930         } END_UNICODE_STRING(env, path);
 931     }
 932 
 933     if (res == 0) {
 934         JNU_ThrowIOExceptionWithLastError(env,
 935             "Could not get maximum component length");
 936     }
 937 
 938     return (jint)maxComponentLength;
 939 }