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