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