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