1 /*
   2  * Copyright (c) 2001, 2012, 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 final target.
 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 JNIEXPORT jstring JNICALL
 217 Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this,
 218                                            jstring pathname)
 219 {
 220     jstring rv = NULL;
 221     WCHAR canonicalPath[MAX_PATH_LENGTH];
 222 
 223     WITH_UNICODE_STRING(env, pathname, path) {
 224         /*we estimate the max length of memory needed as
 225           "currentDir. length + pathname.length"
 226          */
 227         int len = (int)wcslen(path);
 228         len += currentDirLength(path, len);
 229         if (len  > MAX_PATH_LENGTH - 1) {
 230             WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
 231             if (cp != NULL) {
 232                 if (wcanonicalize(path, cp, len) >= 0) {
 233                     rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
 234                 }
 235                 free(cp);
 236             }
 237         } else
 238         if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) {
 239             rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
 240         }
 241     } END_UNICODE_STRING(env, path);
 242     if (rv == NULL) {
 243         JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
 244     }
 245     return rv;
 246 }
 247 
 248 
 249 JNIEXPORT jstring JNICALL
 250 Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this,
 251                                                      jstring canonicalPrefixString,
 252                                                      jstring pathWithCanonicalPrefixString)
 253 {
 254     jstring rv = NULL;
 255     WCHAR canonicalPath[MAX_PATH_LENGTH];
 256     WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) {
 257         WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) {
 258             int len = (int)wcslen(canonicalPrefix) + MAX_PATH;
 259             if (len > MAX_PATH_LENGTH) {
 260                 WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
 261                 if (cp != NULL) {
 262                     if (wcanonicalizeWithPrefix(canonicalPrefix,
 263                                                 pathWithCanonicalPrefix,
 264                                                 cp, len) >= 0) {
 265                       rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
 266                     }
 267                     free(cp);
 268                 }
 269             } else
 270             if (wcanonicalizeWithPrefix(canonicalPrefix,
 271                                         pathWithCanonicalPrefix,
 272                                         canonicalPath, MAX_PATH_LENGTH) >= 0) {
 273                 rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
 274             }
 275         } END_UNICODE_STRING(env, pathWithCanonicalPrefix);
 276     } END_UNICODE_STRING(env, canonicalPrefix);
 277     if (rv == NULL) {
 278         JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
 279     }
 280     return rv;
 281 }
 282 
 283 /* -- Attribute accessors -- */
 284 
 285 /* Check whether or not the file name in "path" is a Windows reserved
 286    device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the
 287    returned result from GetFullPathName, which should be in thr form of
 288    "\\.\[ReservedDeviceName]" if the path represents a reserved device
 289    name.
 290    Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer
 291    important anyway) is a device name, so we don't check it here.
 292    GetFileAttributesEx will catch it later by returning 0 on NT/XP/
 293    200X.
 294 
 295    Note2: Theoretically the implementation could just lookup the table
 296    below linearly if the first 4 characters of the fullpath returned
 297    from GetFullPathName are "\\.\". The current implementation should
 298    achieve the same result. If Microsoft add more names into their
 299    reserved device name repository in the future, which probably will
 300    never happen, we will need to revisit the lookup implementation.
 301 
 302 static WCHAR* ReservedDEviceNames[] = {
 303     L"CON", L"PRN", L"AUX", L"NUL",
 304     L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",
 305     L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9",
 306     L"CLOCK$"
 307 };
 308  */
 309 
 310 static BOOL isReservedDeviceNameW(WCHAR* path) {
 311 #define BUFSIZE 9
 312     WCHAR buf[BUFSIZE];
 313     WCHAR *lpf = NULL;
 314     DWORD retLen = GetFullPathNameW(path,
 315                                    BUFSIZE,
 316                                    buf,
 317                                    &lpf);
 318     if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) &&
 319         buf[0] == L'\\' && buf[1] == L'\\' &&
 320         buf[2] == L'.' && buf[3] == L'\\') {
 321         WCHAR* dname = _wcsupr(buf + 4);
 322         if (wcscmp(dname, L"CON") == 0 ||
 323             wcscmp(dname, L"PRN") == 0 ||
 324             wcscmp(dname, L"AUX") == 0 ||
 325             wcscmp(dname, L"NUL") == 0)
 326             return TRUE;
 327         if ((wcsncmp(dname, L"COM", 3) == 0 ||
 328              wcsncmp(dname, L"LPT", 3) == 0) &&
 329             dname[3] - L'0' > 0 &&
 330             dname[3] - L'0' <= 9)
 331             return TRUE;
 332     }
 333     return FALSE;
 334 }
 335 
 336 JNIEXPORT jint JNICALL
 337 Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this,
 338                                                   jobject file)
 339 {
 340 
 341     jint rv = 0;
 342     jint pathlen;
 343 
 344     /* both pagefile.sys and hiberfil.sys have length 12 */
 345 #define SPECIALFILE_NAMELEN 12
 346 
 347     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 348     WIN32_FILE_ATTRIBUTE_DATA wfad;
 349     if (pathbuf == NULL)
 350         return rv;
 351     if (!isReservedDeviceNameW(pathbuf)) {
 352         if (GetFileAttributesExW(pathbuf, GetFileExInfoStandard, &wfad)) {
 353             DWORD a = getFinalAttributesIfReparsePoint(pathbuf, wfad.dwFileAttributes);
 354             if (a != INVALID_FILE_ATTRIBUTES) {
 355                 rv = (java_io_FileSystem_BA_EXISTS
 356                     | ((a & FILE_ATTRIBUTE_DIRECTORY)
 357                         ? java_io_FileSystem_BA_DIRECTORY
 358                         : java_io_FileSystem_BA_REGULAR)
 359                     | ((a & FILE_ATTRIBUTE_HIDDEN)
 360                         ? java_io_FileSystem_BA_HIDDEN : 0));
 361             }
 362         } else { /* pagefile.sys is a special case */
 363             if (GetLastError() == ERROR_SHARING_VIOLATION) {
 364                 rv = java_io_FileSystem_BA_EXISTS;
 365                 if ((pathlen = (jint)wcslen(pathbuf)) >= SPECIALFILE_NAMELEN &&
 366                     (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN,
 367                               L"pagefile.sys") == 0) ||
 368                     (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN,
 369                               L"hiberfil.sys") == 0))
 370                   rv |= java_io_FileSystem_BA_REGULAR;
 371             }
 372         }
 373     }
 374     free(pathbuf);
 375     return rv;
 376 }
 377 
 378 
 379 JNIEXPORT jboolean
 380 JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this,
 381                                                  jobject file, jint access)
 382 {
 383     DWORD attr;
 384     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 385     if (pathbuf == NULL)
 386         return JNI_FALSE;
 387     attr = GetFileAttributesW(pathbuf);
 388     attr = getFinalAttributesIfReparsePoint(pathbuf, attr);
 389     free(pathbuf);
 390     if (attr == INVALID_FILE_ATTRIBUTES)
 391         return JNI_FALSE;
 392     switch (access) {
 393     case java_io_FileSystem_ACCESS_READ:
 394     case java_io_FileSystem_ACCESS_EXECUTE:
 395         return JNI_TRUE;
 396     case java_io_FileSystem_ACCESS_WRITE:
 397         /* Read-only attribute ignored on directories */
 398         if ((attr & FILE_ATTRIBUTE_DIRECTORY) ||
 399             (attr & FILE_ATTRIBUTE_READONLY) == 0)
 400             return JNI_TRUE;
 401         else
 402             return JNI_FALSE;
 403     default:
 404         assert(0);
 405         return JNI_FALSE;
 406     }
 407 }
 408 
 409 JNIEXPORT jboolean JNICALL
 410 Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this,
 411                                            jobject file,
 412                                            jint access,
 413                                            jboolean enable,
 414                                            jboolean owneronly)
 415 {
 416     jboolean rv = JNI_FALSE;
 417     WCHAR *pathbuf;
 418     DWORD a;
 419     if (access == java_io_FileSystem_ACCESS_READ ||
 420         access == java_io_FileSystem_ACCESS_EXECUTE) {
 421         return enable;
 422     }
 423     pathbuf = fileToNTPath(env, file, ids.path);
 424     if (pathbuf == NULL)
 425         return JNI_FALSE;
 426     a = GetFileAttributesW(pathbuf);
 427 
 428     /* if reparse point, get final target */
 429     if ((a != INVALID_FILE_ATTRIBUTES) &&
 430         ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
 431     {
 432         WCHAR *fp = getFinalPath(pathbuf);
 433         if (fp == NULL) {
 434             a = INVALID_FILE_ATTRIBUTES;
 435         } else {
 436             free(pathbuf);
 437             pathbuf = fp;
 438             a = GetFileAttributesW(pathbuf);
 439         }
 440     }
 441     if ((a != INVALID_FILE_ATTRIBUTES) &&
 442         ((a & FILE_ATTRIBUTE_DIRECTORY) == 0))
 443     {
 444         if (enable)
 445             a =  a & ~FILE_ATTRIBUTE_READONLY;
 446         else
 447             a =  a | FILE_ATTRIBUTE_READONLY;
 448         if (SetFileAttributesW(pathbuf, a))
 449             rv = JNI_TRUE;
 450     }
 451     free(pathbuf);
 452     return rv;
 453 }
 454 
 455 JNIEXPORT jlong JNICALL
 456 Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
 457                                                  jobject file)
 458 {
 459     jlong rv = 0;
 460     LARGE_INTEGER modTime;
 461     FILETIME t;
 462     HANDLE h;
 463     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 464     if (pathbuf == NULL)
 465         return rv;
 466     h = CreateFileW(pathbuf,
 467                     /* Device query access */
 468                     0,
 469                     /* Share it */
 470                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
 471                     /* No security attributes */
 472                     NULL,
 473                     /* Open existing or fail */
 474                     OPEN_EXISTING,
 475                     /* Backup semantics for directories */
 476                     FILE_FLAG_BACKUP_SEMANTICS,
 477                     /* No template file */
 478                     NULL);
 479     if (h != INVALID_HANDLE_VALUE) {
 480         if (GetFileTime(h, NULL, NULL, &t)) {
 481             modTime.LowPart = (DWORD) t.dwLowDateTime;
 482             modTime.HighPart = (LONG) t.dwHighDateTime;
 483             rv = modTime.QuadPart / 10000;
 484             rv -= 11644473600000;
 485         }
 486         CloseHandle(h);
 487     }
 488     free(pathbuf);
 489     return rv;
 490 }
 491 
 492 JNIEXPORT jlong JNICALL
 493 Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file)
 494 {
 495     jlong rv = 0;
 496     WIN32_FILE_ATTRIBUTE_DATA wfad;
 497     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 498     if (pathbuf == NULL)
 499         return rv;
 500     if (GetFileAttributesExW(pathbuf,
 501                              GetFileExInfoStandard,
 502                              &wfad)) {
 503         if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
 504             rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow;
 505         } else {
 506             /* file is a reparse point so read attributes of final target */
 507             BY_HANDLE_FILE_INFORMATION finfo;
 508             if (getFileInformation(pathbuf, &finfo)) {
 509                 rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) +
 510                     finfo.nFileSizeLow;
 511             }
 512         }
 513     } else {
 514         if (GetLastError() == ERROR_SHARING_VIOLATION) {
 515             /* The error is "share violation", which means the file/dir
 516                must exists. Try _wstati64, we know this at least works
 517                for pagefile.sys and hiberfil.sys.
 518             */
 519             struct _stati64 sb;
 520             if (_wstati64(pathbuf, &sb) == 0) {
 521                 rv = sb.st_size;
 522             }
 523         }
 524     }
 525     free(pathbuf);
 526     return rv;
 527 }
 528 
 529 /* -- File operations -- */
 530 
 531 JNIEXPORT jboolean JNICALL
 532 Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
 533                                                    jstring path)
 534 {
 535     HANDLE h = NULL;
 536     WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
 537     if (pathbuf == NULL)
 538         return JNI_FALSE;
 539     h = CreateFileW(
 540         pathbuf,                              /* Wide char path name */
 541         GENERIC_READ | GENERIC_WRITE,         /* Read and write permission */
 542         FILE_SHARE_READ | FILE_SHARE_WRITE,   /* File sharing flags */
 543         NULL,                                 /* Security attributes */
 544         CREATE_NEW,                           /* creation disposition */
 545         FILE_ATTRIBUTE_NORMAL |
 546             FILE_FLAG_OPEN_REPARSE_POINT,     /* flags and attributes */
 547         NULL);
 548 
 549     if (h == INVALID_HANDLE_VALUE) {
 550         DWORD error = GetLastError();
 551         if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) {
 552             // return false rather than throwing an exception when there is
 553             // an existing file.
 554             DWORD a = GetFileAttributesW(pathbuf);
 555             if (a == INVALID_FILE_ATTRIBUTES) {
 556                 SetLastError(error);
 557                 JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
 558             }
 559          }
 560          free(pathbuf);
 561          return JNI_FALSE;
 562         }
 563     free(pathbuf);
 564     CloseHandle(h);
 565     return JNI_TRUE;
 566 }
 567 
 568 static int
 569 removeFileOrDirectory(const jchar *path)
 570 {
 571     /* Returns 0 on success */
 572     DWORD a;
 573 
 574     SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL);
 575     a = GetFileAttributesW(path);
 576     if (a == INVALID_FILE_ATTRIBUTES) {
 577         return 1;
 578     } else if (a & FILE_ATTRIBUTE_DIRECTORY) {
 579         return !RemoveDirectoryW(path);
 580     } else {
 581         return !DeleteFileW(path);
 582     }
 583 }
 584 
 585 JNIEXPORT jboolean JNICALL
 586 Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
 587 {
 588     jboolean rv = JNI_FALSE;
 589     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 590     if (pathbuf == NULL) {
 591         return JNI_FALSE;
 592     }
 593     if (removeFileOrDirectory(pathbuf) == 0) {
 594         rv = JNI_TRUE;
 595     }
 596     free(pathbuf);
 597     return rv;
 598 }
 599 
 600 JNIEXPORT jobjectArray JNICALL
 601 Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
 602 {
 603     WCHAR *search_path;
 604     HANDLE handle;
 605     WIN32_FIND_DATAW find_data;
 606     int len, maxlen;
 607     jobjectArray rv, old;
 608     DWORD fattr;
 609     jstring name;
 610 
 611     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 612     if (pathbuf == NULL)
 613         return NULL;
 614     search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
 615     if (search_path == 0) {
 616         free (pathbuf);
 617         errno = ENOMEM;
 618         return NULL;
 619     }
 620     wcscpy(search_path, pathbuf);
 621     free(pathbuf);
 622     fattr = GetFileAttributesW(search_path);
 623     if (fattr == INVALID_FILE_ATTRIBUTES) {
 624         free(search_path);
 625         return NULL;
 626     } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
 627         free(search_path);
 628         return NULL;
 629     }
 630 
 631     /* Remove trailing space chars from directory name */
 632     len = (int)wcslen(search_path);
 633     while (search_path[len-1] == ' ') {
 634         len--;
 635     }
 636     search_path[len] = 0;
 637 
 638     /* Append "*", or possibly "\\*", to path */
 639     if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
 640         (search_path[1] == L':'
 641         && (search_path[2] == L'\0'
 642         || (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
 643         /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
 644         wcscat(search_path, L"*");
 645     } else {
 646         wcscat(search_path, L"\\*");
 647     }
 648 
 649     /* Open handle to the first file */
 650     handle = FindFirstFileW(search_path, &find_data);
 651     free(search_path);
 652     if (handle == INVALID_HANDLE_VALUE) {
 653         if (GetLastError() != ERROR_FILE_NOT_FOUND) {
 654             // error
 655             return NULL;
 656         } else {
 657             // No files found - return an empty array
 658             rv = (*env)->NewObjectArray(env, 0, JNU_ClassString(env), NULL);
 659             return rv;
 660         }
 661     }
 662 
 663     /* Allocate an initial String array */
 664     len = 0;
 665     maxlen = 16;
 666     rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
 667     if (rv == NULL) // Couldn't allocate an array
 668         return NULL;
 669     /* Scan the directory */
 670     do {
 671         if (!wcscmp(find_data.cFileName, L".")
 672                                 || !wcscmp(find_data.cFileName, L".."))
 673            continue;
 674         name = (*env)->NewString(env, find_data.cFileName,
 675                                  (jsize)wcslen(find_data.cFileName));
 676         if (name == NULL)
 677             return NULL; // error;
 678         if (len == maxlen) {
 679             old = rv;
 680             rv = (*env)->NewObjectArray(env, maxlen <<= 1,
 681                                             JNU_ClassString(env), NULL);
 682             if ( rv == NULL
 683                          || JNU_CopyObjectArray(env, rv, old, len) < 0)
 684                 return NULL; // error
 685             (*env)->DeleteLocalRef(env, old);
 686         }
 687         (*env)->SetObjectArrayElement(env, rv, len++, name);
 688         (*env)->DeleteLocalRef(env, name);
 689 
 690     } while (FindNextFileW(handle, &find_data));
 691 
 692     if (GetLastError() != ERROR_NO_MORE_FILES)
 693         return NULL; // error
 694     FindClose(handle);
 695 
 696     /* Copy the final results into an appropriately-sized array */
 697     old = rv;
 698     rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
 699     if (rv == NULL)
 700         return NULL; /* error */
 701     if (JNU_CopyObjectArray(env, rv, old, len) < 0)
 702         return NULL; /* error */
 703     return rv;
 704 }
 705 
 706 
 707 JNIEXPORT jboolean JNICALL
 708 Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
 709                                              jobject file)
 710 {
 711     BOOL h = FALSE;
 712     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 713     if (pathbuf == NULL) {
 714         /* Exception is pending */
 715         return JNI_FALSE;
 716     }
 717     h = CreateDirectoryW(pathbuf, NULL);
 718     free(pathbuf);
 719 
 720     if (h == 0) {
 721         return JNI_FALSE;
 722     }
 723 
 724     return JNI_TRUE;
 725 }
 726 
 727 
 728 JNIEXPORT jboolean JNICALL
 729 Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
 730                                      jobject to)
 731 {
 732 
 733     jboolean rv = JNI_FALSE;
 734     WCHAR *frompath = fileToNTPath(env, from, ids.path);
 735     WCHAR *topath = fileToNTPath(env, to, ids.path);
 736     if (frompath == NULL || topath == NULL)
 737         return JNI_FALSE;
 738     if (_wrename(frompath, topath) == 0) {
 739         rv = JNI_TRUE;
 740     }
 741     free(frompath);
 742     free(topath);
 743     return rv;
 744 }
 745 
 746 
 747 JNIEXPORT jboolean JNICALL
 748 Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 749                                                  jobject file, jlong time)
 750 {
 751     jboolean rv = JNI_FALSE;
 752     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 753     HANDLE h;
 754     if (pathbuf == NULL)
 755         return JNI_FALSE;
 756     h = CreateFileW(pathbuf,
 757                     FILE_WRITE_ATTRIBUTES,
 758                     FILE_SHARE_READ | FILE_SHARE_WRITE,
 759                     NULL,
 760                     OPEN_EXISTING,
 761                     FILE_FLAG_BACKUP_SEMANTICS,
 762                     0);
 763     if (h != INVALID_HANDLE_VALUE) {
 764         LARGE_INTEGER modTime;
 765         FILETIME t;
 766         modTime.QuadPart = (time + 11644473600000L) * 10000L;
 767         t.dwLowDateTime = (DWORD)modTime.LowPart;
 768         t.dwHighDateTime = (DWORD)modTime.HighPart;
 769         if (SetFileTime(h, NULL, NULL, &t)) {
 770             rv = JNI_TRUE;
 771         }
 772         CloseHandle(h);
 773     }
 774     free(pathbuf);
 775 
 776     return rv;
 777 }
 778 
 779 
 780 JNIEXPORT jboolean JNICALL
 781 Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
 782                                          jobject file)
 783 {
 784     jboolean rv = JNI_FALSE;
 785     DWORD a;
 786     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 787     if (pathbuf == NULL)
 788         return JNI_FALSE;
 789     a = GetFileAttributesW(pathbuf);
 790 
 791     /* if reparse point, get final target */
 792     if ((a != INVALID_FILE_ATTRIBUTES) &&
 793         ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
 794     {
 795         WCHAR *fp = getFinalPath(pathbuf);
 796         if (fp == NULL) {
 797             a = INVALID_FILE_ATTRIBUTES;
 798         } else {
 799             free(pathbuf);
 800             pathbuf = fp;
 801             a = GetFileAttributesW(pathbuf);
 802         }
 803     }
 804 
 805     if ((a != INVALID_FILE_ATTRIBUTES) &&
 806         ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
 807         if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
 808             rv = JNI_TRUE;
 809     }
 810     free(pathbuf);
 811     return rv;
 812 }
 813 
 814 /* -- Filesystem interface -- */
 815 
 816 
 817 JNIEXPORT jobject JNICALL
 818 Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
 819                                                jint drive)
 820 {
 821     jstring ret = NULL;
 822     jchar *p = currentDir(drive);
 823     jchar *pf = p;
 824     if (p == NULL) return NULL;
 825     if (iswalpha(*p) && (p[1] == L':')) p += 2;
 826     ret = (*env)->NewString(env, p, (jsize)wcslen(p));
 827     free (pf);
 828     return ret;
 829 }
 830 
 831 JNIEXPORT jint JNICALL
 832 Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored)
 833 {
 834     return GetLogicalDrives();
 835 }
 836 
 837 JNIEXPORT jlong JNICALL
 838 Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
 839                                        jobject file, jint t)
 840 {
 841     WCHAR volname[MAX_PATH_LENGTH + 1];
 842     jlong rv = 0L;
 843     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 844 
 845     if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
 846         ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
 847         if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
 848             switch(t) {
 849             case java_io_FileSystem_SPACE_TOTAL:
 850                 rv = long_to_jlong(totalSpace.QuadPart);
 851                 break;
 852             case java_io_FileSystem_SPACE_FREE:
 853                 rv = long_to_jlong(freeSpace.QuadPart);
 854                 break;
 855             case java_io_FileSystem_SPACE_USABLE:
 856                 rv = long_to_jlong(usableSpace.QuadPart);
 857                 break;
 858             default:
 859                 assert(0);
 860             }
 861         }
 862     }
 863 
 864     free(pathbuf);
 865     return rv;
 866 }