1 /*
   2  * Copyright 2001-2008 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any 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 JNIEXPORT void JNICALL
  55 Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls)
  56 {
  57     jclass fileClass = (*env)->FindClass(env, "java/io/File");
  58     if (!fileClass) return;
  59     ids.path =
  60              (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;");
  61 }
  62 
  63 /* -- Path operations -- */
  64 
  65 extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len);
  66 extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len);
  67 
  68 JNIEXPORT jstring JNICALL
  69 Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this,
  70                                            jstring pathname)
  71 {
  72     jstring rv = NULL;
  73     WCHAR canonicalPath[MAX_PATH_LENGTH];
  74 
  75     WITH_UNICODE_STRING(env, pathname, path) {
  76         /*we estimate the max length of memory needed as
  77           "currentDir. length + pathname.length"
  78          */
  79         int len = wcslen(path);
  80         len += currentDirLength(path, len);
  81         if (len  > MAX_PATH_LENGTH - 1) {
  82             WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
  83             if (cp != NULL) {
  84                 if (wcanonicalize(path, cp, len) >= 0) {
  85                     rv = (*env)->NewString(env, cp, wcslen(cp));
  86                 }
  87                 free(cp);
  88             }
  89         } else
  90         if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) {
  91             rv = (*env)->NewString(env, canonicalPath, wcslen(canonicalPath));
  92         }
  93     } END_UNICODE_STRING(env, path);
  94     if (rv == NULL) {
  95         JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
  96     }
  97     return rv;
  98 }
  99 
 100 
 101 JNIEXPORT jstring JNICALL
 102 Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this,
 103                                                      jstring canonicalPrefixString,
 104                                                      jstring pathWithCanonicalPrefixString)
 105 {
 106     jstring rv = NULL;
 107     WCHAR canonicalPath[MAX_PATH_LENGTH];
 108     WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) {
 109         WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) {
 110             int len = wcslen(canonicalPrefix) + MAX_PATH;
 111             if (len > MAX_PATH_LENGTH) {
 112                 WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
 113                 if (cp != NULL) {
 114                     if (wcanonicalizeWithPrefix(canonicalPrefix,
 115                                                 pathWithCanonicalPrefix,
 116                                                 cp, len) >= 0) {
 117                       rv = (*env)->NewString(env, cp, wcslen(cp));
 118                     }
 119                     free(cp);
 120                 }
 121             } else
 122             if (wcanonicalizeWithPrefix(canonicalPrefix,
 123                                         pathWithCanonicalPrefix,
 124                                         canonicalPath, MAX_PATH_LENGTH) >= 0) {
 125                 rv = (*env)->NewString(env, canonicalPath, wcslen(canonicalPath));
 126             }
 127         } END_UNICODE_STRING(env, pathWithCanonicalPrefix);
 128     } END_UNICODE_STRING(env, canonicalPrefix);
 129     if (rv == NULL) {
 130         JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
 131     }
 132     return rv;
 133 }
 134 
 135 /* -- Attribute accessors -- */
 136 
 137 /* Check whether or not the file name in "path" is a Windows reserved
 138    device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the
 139    returned result from GetFullPathName, which should be in thr form of
 140    "\\.\[ReservedDeviceName]" if the path represents a reserved device
 141    name.
 142    Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer
 143    important anyway) is a device name, so we don't check it here.
 144    GetFileAttributesEx will catch it later by returning 0 on NT/XP/
 145    200X.
 146 
 147    Note2: Theoretically the implementation could just lookup the table
 148    below linearly if the first 4 characters of the fullpath returned
 149    from GetFullPathName are "\\.\". The current implementation should
 150    achieve the same result. If Microsoft add more names into their
 151    reserved device name repository in the future, which probably will
 152    never happen, we will need to revisit the lookup implementation.
 153 
 154 static WCHAR* ReservedDEviceNames[] = {
 155     L"CON", L"PRN", L"AUX", L"NUL",
 156     L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",
 157     L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9",
 158     L"CLOCK$"
 159 };
 160  */
 161 
 162 static BOOL isReservedDeviceNameW(WCHAR* path) {
 163 #define BUFSIZE 9
 164     WCHAR buf[BUFSIZE];
 165     WCHAR *lpf = NULL;
 166     DWORD retLen = GetFullPathNameW(path,
 167                                    BUFSIZE,
 168                                    buf,
 169                                    &lpf);
 170     if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) &&
 171         buf[0] == L'\\' && buf[1] == L'\\' &&
 172         buf[2] == L'.' && buf[3] == L'\\') {
 173         WCHAR* dname = _wcsupr(buf + 4);
 174         if (wcscmp(dname, L"CON") == 0 ||
 175             wcscmp(dname, L"PRN") == 0 ||
 176             wcscmp(dname, L"AUX") == 0 ||
 177             wcscmp(dname, L"NUL") == 0)
 178             return TRUE;
 179         if ((wcsncmp(dname, L"COM", 3) == 0 ||
 180              wcsncmp(dname, L"LPT", 3) == 0) &&
 181             dname[3] - L'0' > 0 &&
 182             dname[3] - L'0' <= 9)
 183             return TRUE;
 184     }
 185     return FALSE;
 186 }
 187 
 188 JNIEXPORT jint JNICALL
 189 Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this,
 190                                                   jobject file)
 191 {
 192 
 193     jint rv = 0;
 194     jint pathlen;
 195 
 196     /* both pagefile.sys and hiberfil.sys have length 12 */
 197 #define SPECIALFILE_NAMELEN 12
 198 
 199     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 200     WIN32_FILE_ATTRIBUTE_DATA wfad;
 201     if (pathbuf == NULL)
 202         return rv;
 203     if (!isReservedDeviceNameW(pathbuf)) {
 204         if (GetFileAttributesExW(pathbuf, GetFileExInfoStandard, &wfad)) {
 205             rv = (java_io_FileSystem_BA_EXISTS
 206                   | ((wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 207                      ? java_io_FileSystem_BA_DIRECTORY
 208                      : java_io_FileSystem_BA_REGULAR)
 209                   | ((wfad.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
 210                      ? java_io_FileSystem_BA_HIDDEN : 0));
 211         } else { /* pagefile.sys is a special case */
 212             if (GetLastError() == ERROR_SHARING_VIOLATION) {
 213                 rv = java_io_FileSystem_BA_EXISTS;
 214                 if ((pathlen = wcslen(pathbuf)) >= SPECIALFILE_NAMELEN &&
 215                     (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN,
 216                               L"pagefile.sys") == 0) ||
 217                     (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN,
 218                               L"hiberfil.sys") == 0))
 219                   rv |= java_io_FileSystem_BA_REGULAR;
 220             }
 221         }
 222     }
 223     free(pathbuf);
 224     return rv;
 225 }
 226 
 227 
 228 JNIEXPORT jboolean
 229 JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this,
 230                                                  jobject file, jint access)
 231 {
 232     DWORD attr;
 233     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 234     if (pathbuf == NULL)
 235         return JNI_FALSE;
 236     attr = GetFileAttributesW(pathbuf);
 237     free(pathbuf);
 238     if (attr == INVALID_FILE_ATTRIBUTES)
 239         return JNI_FALSE;
 240     switch (access) {
 241     case java_io_FileSystem_ACCESS_READ:
 242     case java_io_FileSystem_ACCESS_EXECUTE:
 243         return JNI_TRUE;
 244     case java_io_FileSystem_ACCESS_WRITE:
 245         /* Read-only attribute ignored on directories */
 246         if ((attr & FILE_ATTRIBUTE_DIRECTORY) ||
 247             (attr & FILE_ATTRIBUTE_READONLY) == 0)
 248             return JNI_TRUE;
 249         else
 250             return JNI_FALSE;
 251     default:
 252         assert(0);
 253         return JNI_FALSE;
 254     }
 255 }
 256 
 257 JNIEXPORT jboolean JNICALL
 258 Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this,
 259                                            jobject file,
 260                                            jint access,
 261                                            jboolean enable,
 262                                            jboolean owneronly)
 263 {
 264     jboolean rv = JNI_FALSE;
 265     WCHAR *pathbuf;
 266     DWORD a;
 267     if (access == java_io_FileSystem_ACCESS_READ ||
 268         access == java_io_FileSystem_ACCESS_EXECUTE) {
 269         return enable;
 270     }
 271     pathbuf = fileToNTPath(env, file, ids.path);
 272     if (pathbuf == NULL)
 273         return JNI_FALSE;
 274     a = GetFileAttributesW(pathbuf);
 275     if (a != INVALID_FILE_ATTRIBUTES) {
 276         if (enable)
 277             a =  a & ~FILE_ATTRIBUTE_READONLY;
 278         else
 279             a =  a | FILE_ATTRIBUTE_READONLY;
 280         if (SetFileAttributesW(pathbuf, a))
 281             rv = JNI_TRUE;
 282     }
 283     free(pathbuf);
 284     return rv;
 285 }
 286 
 287 JNIEXPORT jlong JNICALL
 288 Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
 289                                                  jobject file)
 290 {
 291     jlong rv = 0;
 292     LARGE_INTEGER modTime;
 293     FILETIME t;
 294     HANDLE h;
 295     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 296     if (pathbuf == NULL)
 297         return rv;
 298     h = CreateFileW(pathbuf,
 299                     /* Device query access */
 300                     0,
 301                     /* Share it */
 302                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
 303                     /* No security attributes */
 304                     NULL,
 305                     /* Open existing or fail */
 306                     OPEN_EXISTING,
 307                     /* Backup semantics for directories */
 308                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
 309                     /* No template file */
 310                     NULL);
 311     if (h != INVALID_HANDLE_VALUE) {
 312         if (GetFileTime(h, NULL, NULL, &t)) {
 313             modTime.LowPart = (DWORD) t.dwLowDateTime;
 314             modTime.HighPart = (LONG) t.dwHighDateTime;
 315             rv = modTime.QuadPart / 10000;
 316             rv -= 11644473600000;
 317         }
 318         CloseHandle(h);
 319     }
 320     free(pathbuf);
 321     return rv;
 322 }
 323 
 324 JNIEXPORT jlong JNICALL
 325 Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file)
 326 {
 327     jlong rv = 0;
 328     WIN32_FILE_ATTRIBUTE_DATA wfad;
 329     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 330     if (pathbuf == NULL)
 331         return rv;
 332     if (GetFileAttributesExW(pathbuf,
 333                              GetFileExInfoStandard,
 334                              &wfad)) {
 335         rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow;
 336     } else {
 337         if (GetLastError() == ERROR_SHARING_VIOLATION) {
 338             /* The error is "share violation", which means the file/dir
 339                must exists. Try _wstati64, we know this at least works
 340                for pagefile.sys and hiberfil.sys.
 341             */
 342             struct _stati64 sb;
 343             if (_wstati64(pathbuf, &sb) == 0) {
 344                 rv = sb.st_size;
 345             }
 346         }
 347     }
 348     free(pathbuf);
 349     return rv;
 350 }
 351 
 352 /* -- File operations -- */
 353 
 354 JNIEXPORT jboolean JNICALL
 355 Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
 356                                                    jstring path)
 357 {
 358     HANDLE h = NULL;
 359     WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
 360     if (pathbuf == NULL)
 361         return JNI_FALSE;
 362     h = CreateFileW(
 363         pathbuf,                             /* Wide char path name */
 364         GENERIC_READ | GENERIC_WRITE,  /* Read and write permission */
 365         FILE_SHARE_READ | FILE_SHARE_WRITE,   /* File sharing flags */
 366         NULL,                                /* Security attributes */
 367         CREATE_NEW,                         /* creation disposition */
 368         FILE_ATTRIBUTE_NORMAL,              /* flags and attributes */
 369         NULL);
 370 
 371     if (h == INVALID_HANDLE_VALUE) {
 372         DWORD error = GetLastError();
 373         if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) {
 374 
 375             // If a directory by the named path already exists,
 376             // return false (behavior of solaris and linux) instead of
 377             // throwing an exception
 378             DWORD fattr = GetFileAttributesW(pathbuf);
 379             if ((fattr == INVALID_FILE_ATTRIBUTES) ||
 380                     (fattr & ~FILE_ATTRIBUTE_DIRECTORY)) {
 381                 SetLastError(error);
 382                 JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
 383             }
 384          }
 385          free(pathbuf);
 386          return JNI_FALSE;
 387     }
 388     free(pathbuf);
 389     CloseHandle(h);
 390     return JNI_TRUE;
 391 }
 392 
 393 static int
 394 removeFileOrDirectory(const jchar *path)
 395 {
 396     /* Returns 0 on success */
 397     DWORD a;
 398 
 399     SetFileAttributesW(path, 0);
 400     a = GetFileAttributesW(path);
 401     if (a == ((DWORD)-1)) {
 402         return 1;
 403     } else if (a & FILE_ATTRIBUTE_DIRECTORY) {
 404         return !RemoveDirectoryW(path);
 405     } else {
 406         return !DeleteFileW(path);
 407     }
 408 }
 409 
 410 JNIEXPORT jboolean JNICALL
 411 Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
 412 {
 413     jboolean rv = JNI_FALSE;
 414     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 415     if (pathbuf == NULL) {
 416         return JNI_FALSE;
 417     }
 418     if (removeFileOrDirectory(pathbuf) == 0) {
 419         rv = JNI_TRUE;
 420     }
 421     free(pathbuf);
 422     return rv;
 423 }
 424 
 425 JNIEXPORT jobjectArray JNICALL
 426 Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
 427 {
 428     WCHAR *search_path;
 429     HANDLE handle;
 430     WIN32_FIND_DATAW find_data;
 431     int len, maxlen;
 432     jobjectArray rv, old;
 433     DWORD fattr;
 434     jstring name;
 435 
 436     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 437     if (pathbuf == NULL)
 438         return NULL;
 439     search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
 440     if (search_path == 0) {
 441         free (pathbuf);
 442         errno = ENOMEM;
 443         return NULL;
 444     }
 445     wcscpy(search_path, pathbuf);
 446     free(pathbuf);
 447     fattr = GetFileAttributesW(search_path);
 448     if (fattr == INVALID_FILE_ATTRIBUTES) {
 449         free(search_path);
 450         return NULL;
 451     } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
 452         free(search_path);
 453         return NULL;
 454     }
 455 
 456     /* Remove trailing space chars from directory name */
 457     len = wcslen(search_path);
 458     while (search_path[len-1] == ' ') {
 459         len--;
 460     }
 461     search_path[len] = 0;
 462 
 463     /* Append "*", or possibly "\\*", to path */
 464     if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
 465         (search_path[1] == L':'
 466         && (search_path[2] == L'\0'
 467         || (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
 468         /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
 469         wcscat(search_path, L"*");
 470     } else {
 471         wcscat(search_path, L"\\*");
 472     }
 473 
 474     /* Open handle to the first file */
 475     handle = FindFirstFileW(search_path, &find_data);
 476     free(search_path);
 477     if (handle == INVALID_HANDLE_VALUE) {
 478         if (GetLastError() != ERROR_FILE_NOT_FOUND) {
 479             // error
 480             return NULL;
 481         } else {
 482             // No files found - return an empty array
 483             rv = (*env)->NewObjectArray(env, 0, JNU_ClassString(env), NULL);
 484             return rv;
 485         }
 486     }
 487 
 488     /* Allocate an initial String array */
 489     len = 0;
 490     maxlen = 16;
 491     rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
 492     if (rv == NULL) // Couldn't allocate an array
 493         return NULL;
 494     /* Scan the directory */
 495     do {
 496         if (!wcscmp(find_data.cFileName, L".")
 497                                 || !wcscmp(find_data.cFileName, L".."))
 498            continue;
 499         name = (*env)->NewString(env, find_data.cFileName,
 500                                  wcslen(find_data.cFileName));
 501         if (name == NULL)
 502             return NULL; // error;
 503         if (len == maxlen) {
 504             old = rv;
 505             rv = (*env)->NewObjectArray(env, maxlen <<= 1,
 506                                             JNU_ClassString(env), NULL);
 507             if ( rv == NULL
 508                          || JNU_CopyObjectArray(env, rv, old, len) < 0)
 509                 return NULL; // error
 510             (*env)->DeleteLocalRef(env, old);
 511         }
 512         (*env)->SetObjectArrayElement(env, rv, len++, name);
 513         (*env)->DeleteLocalRef(env, name);
 514 
 515     } while (FindNextFileW(handle, &find_data));
 516 
 517     if (GetLastError() != ERROR_NO_MORE_FILES)
 518         return NULL; // error
 519     FindClose(handle);
 520 
 521     /* Copy the final results into an appropriately-sized array */
 522     old = rv;
 523     rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
 524     if (rv == NULL)
 525         return NULL; /* error */
 526     if (JNU_CopyObjectArray(env, rv, old, len) < 0)
 527         return NULL; /* error */
 528     return rv;
 529 }
 530 
 531 
 532 JNIEXPORT jboolean JNICALL
 533 Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
 534                                              jobject file)
 535 {
 536     BOOL h = FALSE;
 537     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 538     if (pathbuf == NULL) {
 539         /* Exception is pending */
 540         return JNI_FALSE;
 541     }
 542     h = CreateDirectoryW(pathbuf, NULL);
 543     free(pathbuf);
 544 
 545     if (h == 0) {
 546         return JNI_FALSE;
 547     }
 548 
 549     return JNI_TRUE;
 550 }
 551 
 552 
 553 JNIEXPORT jboolean JNICALL
 554 Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
 555                                      jobject to)
 556 {
 557 
 558     jboolean rv = JNI_FALSE;
 559     WCHAR *frompath = fileToNTPath(env, from, ids.path);
 560     WCHAR *topath = fileToNTPath(env, to, ids.path);
 561     if (frompath == NULL || topath == NULL)
 562         return JNI_FALSE;
 563     if (_wrename(frompath, topath) == 0) {
 564         rv = JNI_TRUE;
 565     }
 566     free(frompath);
 567     free(topath);
 568     return rv;
 569 }
 570 
 571 
 572 JNIEXPORT jboolean JNICALL
 573 Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 574                                                  jobject file, jlong time)
 575 {
 576     jboolean rv = JNI_FALSE;
 577     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 578     HANDLE h;
 579     if (pathbuf == NULL)
 580         return JNI_FALSE;
 581     h = CreateFileW(pathbuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
 582                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
 583     if (h != INVALID_HANDLE_VALUE) {
 584         LARGE_INTEGER modTime;
 585         FILETIME t;
 586         modTime.QuadPart = (time + 11644473600000L) * 10000L;
 587         t.dwLowDateTime = (DWORD)modTime.LowPart;
 588         t.dwHighDateTime = (DWORD)modTime.HighPart;
 589         if (SetFileTime(h, NULL, NULL, &t)) {
 590             rv = JNI_TRUE;
 591         }
 592         CloseHandle(h);
 593     }
 594     free(pathbuf);
 595 
 596     return rv;
 597 }
 598 
 599 
 600 JNIEXPORT jboolean JNICALL
 601 Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
 602                                          jobject file)
 603 {
 604     jboolean rv = JNI_FALSE;
 605     DWORD a;
 606     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 607     if (pathbuf == NULL)
 608         return JNI_FALSE;
 609     a = GetFileAttributesW(pathbuf);
 610     if (a != INVALID_FILE_ATTRIBUTES) {
 611         if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
 612         rv = JNI_TRUE;
 613     }
 614     free(pathbuf);
 615     return rv;
 616 }
 617 
 618 /* -- Filesystem interface -- */
 619 
 620 
 621 JNIEXPORT jobject JNICALL
 622 Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
 623                                                jint drive)
 624 {
 625     jstring ret = NULL;
 626     jchar *p = _wgetdcwd(drive, NULL, MAX_PATH);
 627     jchar *pf = p;
 628     if (p == NULL) return NULL;
 629     if (iswalpha(*p) && (p[1] == L':')) p += 2;
 630     ret = (*env)->NewString(env, p, wcslen(p));
 631     free (pf);
 632     return ret;
 633 }
 634 
 635 typedef BOOL (WINAPI* GetVolumePathNameProc) (LPCWSTR, LPWSTR, DWORD);
 636 
 637 JNIEXPORT jlong JNICALL
 638 Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
 639                                        jobject file, jint t)
 640 {
 641     WCHAR volname[MAX_PATH_LENGTH + 1];
 642     jlong rv = 0L;
 643     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 644 
 645     HMODULE h = LoadLibrary("kernel32");
 646     GetVolumePathNameProc getVolumePathNameW = NULL;
 647     if (h) {
 648         getVolumePathNameW
 649             = (GetVolumePathNameProc)GetProcAddress(h, "GetVolumePathNameW");
 650     }
 651 
 652     if (getVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
 653         ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
 654         if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
 655             switch(t) {
 656             case java_io_FileSystem_SPACE_TOTAL:
 657                 rv = long_to_jlong(totalSpace.QuadPart);
 658                 break;
 659             case java_io_FileSystem_SPACE_FREE:
 660                 rv = long_to_jlong(freeSpace.QuadPart);
 661                 break;
 662             case java_io_FileSystem_SPACE_USABLE:
 663                 rv = long_to_jlong(usableSpace.QuadPart);
 664                 break;
 665             default:
 666                 assert(0);
 667             }
 668         }
 669     }
 670 
 671     if (h) {
 672         FreeLibrary(h);
 673     }
 674     free(pathbuf);
 675     return rv;
 676 }