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