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