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 /**
  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) {
 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         if (enable)
 439             a =  a & ~FILE_ATTRIBUTE_READONLY;
 440         else
 441             a =  a | FILE_ATTRIBUTE_READONLY;
 442         if (SetFileAttributesW(pathbuf, a))
 443             rv = JNI_TRUE;
 444     }
 445     free(pathbuf);
 446     return rv;
 447 }
 448 
 449 JNIEXPORT jlong JNICALL
 450 Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
 451                                                  jobject file)
 452 {
 453     jlong rv = 0;
 454     LARGE_INTEGER modTime;
 455     FILETIME t;
 456     HANDLE h;
 457     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 458     if (pathbuf == NULL)
 459         return rv;
 460     h = CreateFileW(pathbuf,
 461                     /* Device query access */
 462                     0,
 463                     /* Share it */
 464                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
 465                     /* No security attributes */
 466                     NULL,
 467                     /* Open existing or fail */
 468                     OPEN_EXISTING,
 469                     /* Backup semantics for directories */
 470                     FILE_FLAG_BACKUP_SEMANTICS,
 471                     /* No template file */
 472                     NULL);
 473     if (h != INVALID_HANDLE_VALUE) {
 474         if (GetFileTime(h, NULL, NULL, &t)) {
 475             modTime.LowPart = (DWORD) t.dwLowDateTime;
 476             modTime.HighPart = (LONG) t.dwHighDateTime;
 477             rv = modTime.QuadPart / 10000;
 478             rv -= 11644473600000;
 479         }
 480         CloseHandle(h);
 481     }
 482     free(pathbuf);
 483     return rv;
 484 }
 485 
 486 JNIEXPORT jlong JNICALL
 487 Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file)
 488 {
 489     jlong rv = 0;
 490     WIN32_FILE_ATTRIBUTE_DATA wfad;
 491     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 492     if (pathbuf == NULL)
 493         return rv;
 494     if (GetFileAttributesExW(pathbuf,
 495                              GetFileExInfoStandard,
 496                              &wfad)) {
 497         if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
 498             rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow;
 499         } else {
 500             /* file is a reparse point so read attributes of final target */
 501             BY_HANDLE_FILE_INFORMATION finfo;
 502             if (getFileInformation(pathbuf, &finfo)) {
 503                 rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) +
 504                     finfo.nFileSizeLow;
 505             }
 506         }
 507     } else {
 508         if (GetLastError() == ERROR_SHARING_VIOLATION) {
 509             /* The error is "share violation", which means the file/dir
 510                must exists. Try _wstati64, we know this at least works
 511                for pagefile.sys and hiberfil.sys.
 512             */
 513             struct _stati64 sb;
 514             if (_wstati64(pathbuf, &sb) == 0) {
 515                 rv = sb.st_size;
 516             }
 517         }
 518     }
 519     free(pathbuf);
 520     return rv;
 521 }
 522 
 523 /* -- File operations -- */
 524 
 525 JNIEXPORT jboolean JNICALL
 526 Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
 527                                                    jstring path)
 528 {
 529     HANDLE h = NULL;
 530     WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
 531     if (pathbuf == NULL)
 532         return JNI_FALSE;
 533     h = CreateFileW(
 534         pathbuf,                              /* Wide char path name */
 535         GENERIC_READ | GENERIC_WRITE,         /* Read and write permission */
 536         FILE_SHARE_READ | FILE_SHARE_WRITE,   /* File sharing flags */
 537         NULL,                                 /* Security attributes */
 538         CREATE_NEW,                           /* creation disposition */
 539         FILE_ATTRIBUTE_NORMAL |
 540             FILE_FLAG_OPEN_REPARSE_POINT,     /* flags and attributes */
 541         NULL);
 542 
 543     if (h == INVALID_HANDLE_VALUE) {
 544         DWORD error = GetLastError();
 545         if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) {
 546             // return false rather than throwing an exception when there is
 547             // an existing file.
 548             DWORD a = GetFileAttributesW(pathbuf);
 549             if (a == INVALID_FILE_ATTRIBUTES) {
 550                 SetLastError(error);
 551                 JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
 552             }
 553          }
 554          free(pathbuf);
 555          return JNI_FALSE;
 556         }
 557     free(pathbuf);
 558     CloseHandle(h);
 559     return JNI_TRUE;
 560 }
 561 
 562 static int
 563 removeFileOrDirectory(const jchar *path)
 564 {
 565     /* Returns 0 on success */
 566     DWORD a;
 567 
 568     SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL);
 569     a = GetFileAttributesW(path);
 570     if (a == INVALID_FILE_ATTRIBUTES) {
 571         return 1;
 572     } else if (a & FILE_ATTRIBUTE_DIRECTORY) {
 573         return !RemoveDirectoryW(path);
 574     } else {
 575         return !DeleteFileW(path);
 576     }
 577 }
 578 
 579 JNIEXPORT jboolean JNICALL
 580 Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
 581 {
 582     jboolean rv = JNI_FALSE;
 583     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 584     if (pathbuf == NULL) {
 585         return JNI_FALSE;
 586     }
 587     if (removeFileOrDirectory(pathbuf) == 0) {
 588         rv = JNI_TRUE;
 589     }
 590     free(pathbuf);
 591     return rv;
 592 }
 593 
 594 JNIEXPORT jobjectArray JNICALL
 595 Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
 596 {
 597     WCHAR *search_path;
 598     HANDLE handle;
 599     WIN32_FIND_DATAW find_data;
 600     int len, maxlen;
 601     jobjectArray rv, old;
 602     DWORD fattr;
 603     jstring name;
 604 
 605     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 606     if (pathbuf == NULL)
 607         return NULL;
 608     search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
 609     if (search_path == 0) {
 610         free (pathbuf);
 611         errno = ENOMEM;
 612         return NULL;
 613     }
 614     wcscpy(search_path, pathbuf);
 615     free(pathbuf);
 616     fattr = GetFileAttributesW(search_path);
 617     if (fattr == INVALID_FILE_ATTRIBUTES) {
 618         free(search_path);
 619         return NULL;
 620     } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
 621         free(search_path);
 622         return NULL;
 623     }
 624 
 625     /* Remove trailing space chars from directory name */
 626     len = wcslen(search_path);
 627     while (search_path[len-1] == ' ') {
 628         len--;
 629     }
 630     search_path[len] = 0;
 631 
 632     /* Append "*", or possibly "\\*", to path */
 633     if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
 634         (search_path[1] == L':'
 635         && (search_path[2] == L'\0'
 636         || (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
 637         /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
 638         wcscat(search_path, L"*");
 639     } else {
 640         wcscat(search_path, L"\\*");
 641     }
 642 
 643     /* Open handle to the first file */
 644     handle = FindFirstFileW(search_path, &find_data);
 645     free(search_path);
 646     if (handle == INVALID_HANDLE_VALUE) {
 647         if (GetLastError() != ERROR_FILE_NOT_FOUND) {
 648             // error
 649             return NULL;
 650         } else {
 651             // No files found - return an empty array
 652             rv = (*env)->NewObjectArray(env, 0, JNU_ClassString(env), NULL);
 653             return rv;
 654         }
 655     }
 656 
 657     /* Allocate an initial String array */
 658     len = 0;
 659     maxlen = 16;
 660     rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
 661     if (rv == NULL) // Couldn't allocate an array
 662         return NULL;
 663     /* Scan the directory */
 664     do {
 665         if (!wcscmp(find_data.cFileName, L".")
 666                                 || !wcscmp(find_data.cFileName, L".."))
 667            continue;
 668         name = (*env)->NewString(env, find_data.cFileName,
 669                                  wcslen(find_data.cFileName));
 670         if (name == NULL)
 671             return NULL; // error;
 672         if (len == maxlen) {
 673             old = rv;
 674             rv = (*env)->NewObjectArray(env, maxlen <<= 1,
 675                                             JNU_ClassString(env), NULL);
 676             if ( rv == NULL
 677                          || JNU_CopyObjectArray(env, rv, old, len) < 0)
 678                 return NULL; // error
 679             (*env)->DeleteLocalRef(env, old);
 680         }
 681         (*env)->SetObjectArrayElement(env, rv, len++, name);
 682         (*env)->DeleteLocalRef(env, name);
 683 
 684     } while (FindNextFileW(handle, &find_data));
 685 
 686     if (GetLastError() != ERROR_NO_MORE_FILES)
 687         return NULL; // error
 688     FindClose(handle);
 689 
 690     /* Copy the final results into an appropriately-sized array */
 691     old = rv;
 692     rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL);
 693     if (rv == NULL)
 694         return NULL; /* error */
 695     if (JNU_CopyObjectArray(env, rv, old, len) < 0)
 696         return NULL; /* error */
 697     return rv;
 698 }
 699 
 700 
 701 JNIEXPORT jboolean JNICALL
 702 Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
 703                                              jobject file)
 704 {
 705     BOOL h = FALSE;
 706     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 707     if (pathbuf == NULL) {
 708         /* Exception is pending */
 709         return JNI_FALSE;
 710     }
 711     h = CreateDirectoryW(pathbuf, NULL);
 712     free(pathbuf);
 713 
 714     if (h == 0) {
 715         return JNI_FALSE;
 716     }
 717 
 718     return JNI_TRUE;
 719 }
 720 
 721 
 722 JNIEXPORT jboolean JNICALL
 723 Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
 724                                      jobject to)
 725 {
 726 
 727     jboolean rv = JNI_FALSE;
 728     WCHAR *frompath = fileToNTPath(env, from, ids.path);
 729     WCHAR *topath = fileToNTPath(env, to, ids.path);
 730     if (frompath == NULL || topath == NULL)
 731         return JNI_FALSE;
 732     if (_wrename(frompath, topath) == 0) {
 733         rv = JNI_TRUE;
 734     }
 735     free(frompath);
 736     free(topath);
 737     return rv;
 738 }
 739 
 740 
 741 JNIEXPORT jboolean JNICALL
 742 Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
 743                                                  jobject file, jlong time)
 744 {
 745     jboolean rv = JNI_FALSE;
 746     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 747     HANDLE h;
 748     if (pathbuf == NULL)
 749         return JNI_FALSE;
 750     h = CreateFileW(pathbuf,
 751                     FILE_WRITE_ATTRIBUTES,
 752                     FILE_SHARE_READ | FILE_SHARE_WRITE,
 753                     NULL,
 754                     OPEN_EXISTING,
 755                     FILE_FLAG_BACKUP_SEMANTICS,
 756                     0);
 757     if (h != INVALID_HANDLE_VALUE) {
 758         LARGE_INTEGER modTime;
 759         FILETIME t;
 760         modTime.QuadPart = (time + 11644473600000L) * 10000L;
 761         t.dwLowDateTime = (DWORD)modTime.LowPart;
 762         t.dwHighDateTime = (DWORD)modTime.HighPart;
 763         if (SetFileTime(h, NULL, NULL, &t)) {
 764             rv = JNI_TRUE;
 765         }
 766         CloseHandle(h);
 767     }
 768     free(pathbuf);
 769 
 770     return rv;
 771 }
 772 
 773 
 774 JNIEXPORT jboolean JNICALL
 775 Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
 776                                          jobject file)
 777 {
 778     jboolean rv = JNI_FALSE;
 779     DWORD a;
 780     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 781     if (pathbuf == NULL)
 782         return JNI_FALSE;
 783     a = GetFileAttributesW(pathbuf);
 784 
 785     /* if reparse point, get final target */
 786     if ((a != INVALID_FILE_ATTRIBUTES) &&
 787         ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
 788     {
 789         WCHAR *fp = getFinalPath(pathbuf);
 790         if (fp == NULL) {
 791             a = INVALID_FILE_ATTRIBUTES;
 792         } else {
 793             free(pathbuf);
 794             pathbuf = fp;
 795             a = GetFileAttributesW(pathbuf);
 796         }
 797     }
 798 
 799     if (a != INVALID_FILE_ATTRIBUTES) {
 800         if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
 801         rv = JNI_TRUE;
 802     }
 803     free(pathbuf);
 804     return rv;
 805 }
 806 
 807 /* -- Filesystem interface -- */
 808 
 809 
 810 JNIEXPORT jobject JNICALL
 811 Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
 812                                                jint drive)
 813 {
 814     jstring ret = NULL;
 815     jchar *p = _wgetdcwd(drive, NULL, MAX_PATH);
 816     jchar *pf = p;
 817     if (p == NULL) return NULL;
 818     if (iswalpha(*p) && (p[1] == L':')) p += 2;
 819     ret = (*env)->NewString(env, p, wcslen(p));
 820     free (pf);
 821     return ret;
 822 }
 823 
 824 typedef BOOL (WINAPI* GetVolumePathNameProc) (LPCWSTR, LPWSTR, DWORD);
 825 
 826 JNIEXPORT jlong JNICALL
 827 Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
 828                                        jobject file, jint t)
 829 {
 830     WCHAR volname[MAX_PATH_LENGTH + 1];
 831     jlong rv = 0L;
 832     WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
 833 
 834     HMODULE h = LoadLibrary("kernel32");
 835     GetVolumePathNameProc getVolumePathNameW = NULL;
 836     if (h) {
 837         getVolumePathNameW
 838             = (GetVolumePathNameProc)GetProcAddress(h, "GetVolumePathNameW");
 839     }
 840 
 841     if (getVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
 842         ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
 843         if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
 844             switch(t) {
 845             case java_io_FileSystem_SPACE_TOTAL:
 846                 rv = long_to_jlong(totalSpace.QuadPart);
 847                 break;
 848             case java_io_FileSystem_SPACE_FREE:
 849                 rv = long_to_jlong(freeSpace.QuadPart);
 850                 break;
 851             case java_io_FileSystem_SPACE_USABLE:
 852                 rv = long_to_jlong(usableSpace.QuadPart);
 853                 break;
 854             default:
 855                 assert(0);
 856             }
 857         }
 858     }
 859 
 860     if (h) {
 861         FreeLibrary(h);
 862     }
 863     free(pathbuf);
 864     return rv;
 865 }