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 }