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