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