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