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