WinNTFileSystem_md.c revision 10561:e9c0f24789ee
1/* 2 * Copyright (c) 2001, 2013, 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 if (GetLastError() == ERROR_SHARING_VIOLATION && 237 (h = FindFirstFileW(path, &wfd)) != INVALID_HANDLE_VALUE) { 238 attr = getFinalAttributesIfReparsePoint(path, wfd.dwFileAttributes); 239 FindClose(h); 240 } 241 return attr; 242} 243 244JNIEXPORT jstring JNICALL 245Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this, 246 jstring pathname) 247{ 248 jstring rv = NULL; 249 WCHAR canonicalPath[MAX_PATH_LENGTH]; 250 251 WITH_UNICODE_STRING(env, pathname, path) { 252 /* we estimate the max length of memory needed as 253 "currentDir. length + pathname.length" 254 */ 255 int len = (int)wcslen(path); 256 len += currentDirLength(path, len); 257 if (len > MAX_PATH_LENGTH - 1) { 258 WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR)); 259 if (cp != NULL) { 260 if (wcanonicalize(path, cp, len) >= 0) { 261 rv = (*env)->NewString(env, cp, (jsize)wcslen(cp)); 262 } 263 free(cp); 264 } else { 265 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed"); 266 } 267 } else if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) { 268 rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath)); 269 } 270 } END_UNICODE_STRING(env, path); 271 if (rv == NULL && !(*env)->ExceptionCheck(env)) { 272 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); 273 } 274 return rv; 275} 276 277 278JNIEXPORT jstring JNICALL 279Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this, 280 jstring canonicalPrefixString, 281 jstring pathWithCanonicalPrefixString) 282{ 283 jstring rv = NULL; 284 WCHAR canonicalPath[MAX_PATH_LENGTH]; 285 WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) { 286 WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) { 287 int len = (int)wcslen(canonicalPrefix) + MAX_PATH; 288 if (len > MAX_PATH_LENGTH) { 289 WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR)); 290 if (cp != NULL) { 291 if (wcanonicalizeWithPrefix(canonicalPrefix, 292 pathWithCanonicalPrefix, 293 cp, len) >= 0) { 294 rv = (*env)->NewString(env, cp, (jsize)wcslen(cp)); 295 } 296 free(cp); 297 } else { 298 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed"); 299 } 300 } else if (wcanonicalizeWithPrefix(canonicalPrefix, 301 pathWithCanonicalPrefix, 302 canonicalPath, MAX_PATH_LENGTH) >= 0) { 303 rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath)); 304 } 305 } END_UNICODE_STRING(env, pathWithCanonicalPrefix); 306 } END_UNICODE_STRING(env, canonicalPrefix); 307 if (rv == NULL && !(*env)->ExceptionCheck(env)) { 308 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); 309 } 310 return rv; 311} 312 313/* -- Attribute accessors -- */ 314 315/* Check whether or not the file name in "path" is a Windows reserved 316 device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the 317 returned result from GetFullPathName, which should be in thr form of 318 "\\.\[ReservedDeviceName]" if the path represents a reserved device 319 name. 320 Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer 321 important anyway) is a device name, so we don't check it here. 322 GetFileAttributesEx will catch it later by returning 0 on NT/XP/ 323 200X. 324 325 Note2: Theoretically the implementation could just lookup the table 326 below linearly if the first 4 characters of the fullpath returned 327 from GetFullPathName are "\\.\". The current implementation should 328 achieve the same result. If Microsoft add more names into their 329 reserved device name repository in the future, which probably will 330 never happen, we will need to revisit the lookup implementation. 331 332static WCHAR* ReservedDEviceNames[] = { 333 L"CON", L"PRN", L"AUX", L"NUL", 334 L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", 335 L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9", 336 L"CLOCK$" 337}; 338 */ 339 340static BOOL isReservedDeviceNameW(WCHAR* path) { 341#define BUFSIZE 9 342 WCHAR buf[BUFSIZE]; 343 WCHAR *lpf = NULL; 344 DWORD retLen = GetFullPathNameW(path, 345 BUFSIZE, 346 buf, 347 &lpf); 348 if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) && 349 buf[0] == L'\\' && buf[1] == L'\\' && 350 buf[2] == L'.' && buf[3] == L'\\') { 351 WCHAR* dname = _wcsupr(buf + 4); 352 if (wcscmp(dname, L"CON") == 0 || 353 wcscmp(dname, L"PRN") == 0 || 354 wcscmp(dname, L"AUX") == 0 || 355 wcscmp(dname, L"NUL") == 0) 356 return TRUE; 357 if ((wcsncmp(dname, L"COM", 3) == 0 || 358 wcsncmp(dname, L"LPT", 3) == 0) && 359 dname[3] - L'0' > 0 && 360 dname[3] - L'0' <= 9) 361 return TRUE; 362 } 363 return FALSE; 364} 365 366JNIEXPORT jint JNICALL 367Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this, 368 jobject file) 369{ 370 jint rv = 0; 371 372 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 373 if (pathbuf == NULL) 374 return rv; 375 if (!isReservedDeviceNameW(pathbuf)) { 376 DWORD a = getFinalAttributes(pathbuf); 377 if (a != INVALID_FILE_ATTRIBUTES) { 378 rv = (java_io_FileSystem_BA_EXISTS 379 | ((a & FILE_ATTRIBUTE_DIRECTORY) 380 ? java_io_FileSystem_BA_DIRECTORY 381 : java_io_FileSystem_BA_REGULAR) 382 | ((a & FILE_ATTRIBUTE_HIDDEN) 383 ? java_io_FileSystem_BA_HIDDEN : 0)); 384 } 385 } 386 free(pathbuf); 387 return rv; 388} 389 390 391JNIEXPORT jboolean 392JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this, 393 jobject file, jint access) 394{ 395 DWORD attr; 396 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 397 if (pathbuf == NULL) 398 return JNI_FALSE; 399 attr = GetFileAttributesW(pathbuf); 400 attr = getFinalAttributesIfReparsePoint(pathbuf, attr); 401 free(pathbuf); 402 if (attr == INVALID_FILE_ATTRIBUTES) 403 return JNI_FALSE; 404 switch (access) { 405 case java_io_FileSystem_ACCESS_READ: 406 case java_io_FileSystem_ACCESS_EXECUTE: 407 return JNI_TRUE; 408 case java_io_FileSystem_ACCESS_WRITE: 409 /* Read-only attribute ignored on directories */ 410 if ((attr & FILE_ATTRIBUTE_DIRECTORY) || 411 (attr & FILE_ATTRIBUTE_READONLY) == 0) 412 return JNI_TRUE; 413 else 414 return JNI_FALSE; 415 default: 416 assert(0); 417 return JNI_FALSE; 418 } 419} 420 421JNIEXPORT jboolean JNICALL 422Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this, 423 jobject file, 424 jint access, 425 jboolean enable, 426 jboolean owneronly) 427{ 428 jboolean rv = JNI_FALSE; 429 WCHAR *pathbuf; 430 DWORD a; 431 if (access == java_io_FileSystem_ACCESS_READ || 432 access == java_io_FileSystem_ACCESS_EXECUTE) { 433 return enable; 434 } 435 pathbuf = fileToNTPath(env, file, ids.path); 436 if (pathbuf == NULL) 437 return JNI_FALSE; 438 a = GetFileAttributesW(pathbuf); 439 440 /* if reparse point, get final target */ 441 if ((a != INVALID_FILE_ATTRIBUTES) && 442 ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0)) 443 { 444 WCHAR *fp = getFinalPath(env, pathbuf); 445 if (fp == NULL) { 446 a = INVALID_FILE_ATTRIBUTES; 447 } else { 448 free(pathbuf); 449 pathbuf = fp; 450 a = GetFileAttributesW(pathbuf); 451 } 452 } 453 if ((a != INVALID_FILE_ATTRIBUTES) && 454 ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) 455 { 456 if (enable) 457 a = a & ~FILE_ATTRIBUTE_READONLY; 458 else 459 a = a | FILE_ATTRIBUTE_READONLY; 460 if (SetFileAttributesW(pathbuf, a)) 461 rv = JNI_TRUE; 462 } 463 free(pathbuf); 464 return rv; 465} 466 467JNIEXPORT jlong JNICALL 468Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, 469 jobject file) 470{ 471 jlong rv = 0; 472 LARGE_INTEGER modTime; 473 FILETIME t; 474 HANDLE h; 475 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 476 if (pathbuf == NULL) 477 return rv; 478 h = CreateFileW(pathbuf, 479 /* Device query access */ 480 0, 481 /* Share it */ 482 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 483 /* No security attributes */ 484 NULL, 485 /* Open existing or fail */ 486 OPEN_EXISTING, 487 /* Backup semantics for directories */ 488 FILE_FLAG_BACKUP_SEMANTICS, 489 /* No template file */ 490 NULL); 491 if (h != INVALID_HANDLE_VALUE) { 492 if (GetFileTime(h, NULL, NULL, &t)) { 493 modTime.LowPart = (DWORD) t.dwLowDateTime; 494 modTime.HighPart = (LONG) t.dwHighDateTime; 495 rv = modTime.QuadPart / 10000; 496 rv -= 11644473600000; 497 } 498 CloseHandle(h); 499 } 500 free(pathbuf); 501 return rv; 502} 503 504JNIEXPORT jlong JNICALL 505Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file) 506{ 507 jlong rv = 0; 508 WIN32_FILE_ATTRIBUTE_DATA wfad; 509 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 510 if (pathbuf == NULL) 511 return rv; 512 if (GetFileAttributesExW(pathbuf, 513 GetFileExInfoStandard, 514 &wfad)) { 515 if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { 516 rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow; 517 } else { 518 /* file is a reparse point so read attributes of final target */ 519 BY_HANDLE_FILE_INFORMATION finfo; 520 if (getFileInformation(pathbuf, &finfo)) { 521 rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) + 522 finfo.nFileSizeLow; 523 } 524 } 525 } else { 526 if (GetLastError() == ERROR_SHARING_VIOLATION) { 527 /* The error is "share violation", which means the file/dir 528 must exists. Try _wstati64, we know this at least works 529 for pagefile.sys and hiberfil.sys. 530 */ 531 struct _stati64 sb; 532 if (_wstati64(pathbuf, &sb) == 0) { 533 rv = sb.st_size; 534 } 535 } 536 } 537 free(pathbuf); 538 return rv; 539} 540 541/* -- File operations -- */ 542 543JNIEXPORT jboolean JNICALL 544Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls, 545 jstring path) 546{ 547 HANDLE h = NULL; 548 WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE); 549 if (pathbuf == NULL) 550 return JNI_FALSE; 551 if (isReservedDeviceNameW(pathbuf)) { 552 free(pathbuf); 553 return JNI_FALSE; 554 } 555 h = CreateFileW( 556 pathbuf, /* Wide char path name */ 557 GENERIC_READ | GENERIC_WRITE, /* Read and write permission */ 558 FILE_SHARE_READ | FILE_SHARE_WRITE, /* File sharing flags */ 559 NULL, /* Security attributes */ 560 CREATE_NEW, /* creation disposition */ 561 FILE_ATTRIBUTE_NORMAL | 562 FILE_FLAG_OPEN_REPARSE_POINT, /* flags and attributes */ 563 NULL); 564 565 if (h == INVALID_HANDLE_VALUE) { 566 DWORD error = GetLastError(); 567 if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) { 568 // return false rather than throwing an exception when there is 569 // an existing file. 570 DWORD a = GetFileAttributesW(pathbuf); 571 if (a == INVALID_FILE_ATTRIBUTES) { 572 SetLastError(error); 573 JNU_ThrowIOExceptionWithLastError(env, "Could not open file"); 574 } 575 } 576 free(pathbuf); 577 return JNI_FALSE; 578 } 579 free(pathbuf); 580 CloseHandle(h); 581 return JNI_TRUE; 582} 583 584static int 585removeFileOrDirectory(const jchar *path) 586{ 587 /* Returns 0 on success */ 588 DWORD a; 589 590 SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL); 591 a = GetFileAttributesW(path); 592 if (a == INVALID_FILE_ATTRIBUTES) { 593 return 1; 594 } else if (a & FILE_ATTRIBUTE_DIRECTORY) { 595 return !RemoveDirectoryW(path); 596 } else { 597 return !DeleteFileW(path); 598 } 599} 600 601JNIEXPORT jboolean JNICALL 602Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file) 603{ 604 jboolean rv = JNI_FALSE; 605 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 606 if (pathbuf == NULL) { 607 return JNI_FALSE; 608 } 609 if (removeFileOrDirectory(pathbuf) == 0) { 610 rv = JNI_TRUE; 611 } 612 free(pathbuf); 613 return rv; 614} 615 616JNIEXPORT jobjectArray JNICALL 617Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file) 618{ 619 WCHAR *search_path; 620 HANDLE handle; 621 WIN32_FIND_DATAW find_data; 622 int len, maxlen; 623 jobjectArray rv, old; 624 DWORD fattr; 625 jstring name; 626 jclass str_class; 627 WCHAR *pathbuf; 628 629 str_class = JNU_ClassString(env); 630 CHECK_NULL_RETURN(str_class, NULL); 631 632 pathbuf = fileToNTPath(env, file, ids.path); 633 if (pathbuf == NULL) 634 return NULL; 635 search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6); 636 if (search_path == 0) { 637 free (pathbuf); 638 errno = ENOMEM; 639 JNU_ThrowOutOfMemoryError(env, "native memory allocation failed"); 640 return NULL; 641 } 642 wcscpy(search_path, pathbuf); 643 free(pathbuf); 644 fattr = GetFileAttributesW(search_path); 645 if (fattr == INVALID_FILE_ATTRIBUTES) { 646 free(search_path); 647 return NULL; 648 } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) { 649 free(search_path); 650 return NULL; 651 } 652 653 /* Remove trailing space chars from directory name */ 654 len = (int)wcslen(search_path); 655 while (search_path[len-1] == L' ') { 656 len--; 657 } 658 search_path[len] = 0; 659 660 /* Append "*", or possibly "\\*", to path */ 661 if ((search_path[0] == L'\\' && search_path[1] == L'\0') || 662 (search_path[1] == L':' 663 && (search_path[2] == L'\0' 664 || (search_path[2] == L'\\' && search_path[3] == L'\0')))) { 665 /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */ 666 wcscat(search_path, L"*"); 667 } else { 668 wcscat(search_path, L"\\*"); 669 } 670 671 /* Open handle to the first file */ 672 handle = FindFirstFileW(search_path, &find_data); 673 free(search_path); 674 if (handle == INVALID_HANDLE_VALUE) { 675 if (GetLastError() != ERROR_FILE_NOT_FOUND) { 676 // error 677 return NULL; 678 } else { 679 // No files found - return an empty array 680 rv = (*env)->NewObjectArray(env, 0, str_class, NULL); 681 return rv; 682 } 683 } 684 685 /* Allocate an initial String array */ 686 len = 0; 687 maxlen = 16; 688 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL); 689 if (rv == NULL) // Couldn't allocate an array 690 return NULL; 691 /* Scan the directory */ 692 do { 693 if (!wcscmp(find_data.cFileName, L".") 694 || !wcscmp(find_data.cFileName, L"..")) 695 continue; 696 name = (*env)->NewString(env, find_data.cFileName, 697 (jsize)wcslen(find_data.cFileName)); 698 if (name == NULL) 699 return NULL; // error; 700 if (len == maxlen) { 701 old = rv; 702 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL); 703 if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0) 704 return NULL; // error 705 (*env)->DeleteLocalRef(env, old); 706 } 707 (*env)->SetObjectArrayElement(env, rv, len++, name); 708 (*env)->DeleteLocalRef(env, name); 709 710 } while (FindNextFileW(handle, &find_data)); 711 712 if (GetLastError() != ERROR_NO_MORE_FILES) 713 return NULL; // error 714 FindClose(handle); 715 716 if (len < maxlen) { 717 /* Copy the final results into an appropriately-sized array */ 718 old = rv; 719 rv = (*env)->NewObjectArray(env, len, str_class, NULL); 720 if (rv == NULL) 721 return NULL; /* error */ 722 if (JNU_CopyObjectArray(env, rv, old, len) < 0) 723 return NULL; /* error */ 724 } 725 return rv; 726} 727 728 729JNIEXPORT jboolean JNICALL 730Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this, 731 jobject file) 732{ 733 BOOL h = FALSE; 734 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 735 if (pathbuf == NULL) { 736 /* Exception is pending */ 737 return JNI_FALSE; 738 } 739 h = CreateDirectoryW(pathbuf, NULL); 740 free(pathbuf); 741 742 if (h == 0) { 743 return JNI_FALSE; 744 } 745 746 return JNI_TRUE; 747} 748 749 750JNIEXPORT jboolean JNICALL 751Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from, 752 jobject to) 753{ 754 755 jboolean rv = JNI_FALSE; 756 WCHAR *frompath = fileToNTPath(env, from, ids.path); 757 WCHAR *topath = fileToNTPath(env, to, ids.path); 758 if (frompath != NULL && topath != NULL && _wrename(frompath, topath) == 0) { 759 rv = JNI_TRUE; 760 } 761 free(frompath); 762 free(topath); 763 return rv; 764} 765 766 767JNIEXPORT jboolean JNICALL 768Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, 769 jobject file, jlong time) 770{ 771 jboolean rv = JNI_FALSE; 772 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 773 HANDLE h; 774 if (pathbuf == NULL) 775 return JNI_FALSE; 776 h = CreateFileW(pathbuf, 777 FILE_WRITE_ATTRIBUTES, 778 FILE_SHARE_READ | FILE_SHARE_WRITE, 779 NULL, 780 OPEN_EXISTING, 781 FILE_FLAG_BACKUP_SEMANTICS, 782 0); 783 if (h != INVALID_HANDLE_VALUE) { 784 LARGE_INTEGER modTime; 785 FILETIME t; 786 modTime.QuadPart = (time + 11644473600000L) * 10000L; 787 t.dwLowDateTime = (DWORD)modTime.LowPart; 788 t.dwHighDateTime = (DWORD)modTime.HighPart; 789 if (SetFileTime(h, NULL, NULL, &t)) { 790 rv = JNI_TRUE; 791 } 792 CloseHandle(h); 793 } 794 free(pathbuf); 795 796 return rv; 797} 798 799 800JNIEXPORT jboolean JNICALL 801Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this, 802 jobject file) 803{ 804 jboolean rv = JNI_FALSE; 805 DWORD a; 806 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 807 if (pathbuf == NULL) 808 return JNI_FALSE; 809 a = GetFileAttributesW(pathbuf); 810 811 /* if reparse point, get final target */ 812 if ((a != INVALID_FILE_ATTRIBUTES) && 813 ((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0)) 814 { 815 WCHAR *fp = getFinalPath(env, pathbuf); 816 if (fp == NULL) { 817 a = INVALID_FILE_ATTRIBUTES; 818 } else { 819 free(pathbuf); 820 pathbuf = fp; 821 a = GetFileAttributesW(pathbuf); 822 } 823 } 824 825 if ((a != INVALID_FILE_ATTRIBUTES) && 826 ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) { 827 if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY)) 828 rv = JNI_TRUE; 829 } 830 free(pathbuf); 831 return rv; 832} 833 834/* -- Filesystem interface -- */ 835 836 837JNIEXPORT jobject JNICALL 838Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this, 839 jint drive) 840{ 841 jstring ret = NULL; 842 jchar *p = currentDir(drive); 843 jchar *pf = p; 844 if (p == NULL) return NULL; 845 if (iswalpha(*p) && (p[1] == L':')) p += 2; 846 ret = (*env)->NewString(env, p, (jsize)wcslen(p)); 847 free (pf); 848 return ret; 849} 850 851JNIEXPORT jint JNICALL 852Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored) 853{ 854 return GetLogicalDrives(); 855} 856 857JNIEXPORT jlong JNICALL 858Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this, 859 jobject file, jint t) 860{ 861 WCHAR volname[MAX_PATH_LENGTH + 1]; 862 jlong rv = 0L; 863 WCHAR *pathbuf = fileToNTPath(env, file, ids.path); 864 865 if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) { 866 ULARGE_INTEGER totalSpace, freeSpace, usableSpace; 867 if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) { 868 switch(t) { 869 case java_io_FileSystem_SPACE_TOTAL: 870 rv = long_to_jlong(totalSpace.QuadPart); 871 break; 872 case java_io_FileSystem_SPACE_FREE: 873 rv = long_to_jlong(freeSpace.QuadPart); 874 break; 875 case java_io_FileSystem_SPACE_USABLE: 876 rv = long_to_jlong(usableSpace.QuadPart); 877 break; 878 default: 879 assert(0); 880 } 881 } 882 } 883 884 free(pathbuf); 885 return rv; 886} 887