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