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