J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2001-2006 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | /* Access APIs for Win2K and above */ |
| 27 | #ifndef _WIN32_WINNT |
| 28 | #define _WIN32_WINNT 0x0500 |
| 29 | #endif |
| 30 | |
| 31 | #include <assert.h> |
| 32 | #include <stdio.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <ctype.h> |
| 35 | #include <direct.h> |
| 36 | #include <windows.h> |
| 37 | #include <io.h> |
| 38 | |
| 39 | #include "jvm.h" |
| 40 | #include "jni.h" |
| 41 | #include "jni_util.h" |
| 42 | #include "io_util.h" |
| 43 | #include "jlong.h" |
| 44 | #include "io_util_md.h" |
| 45 | #include "dirent_md.h" |
| 46 | #include "java_io_FileSystem.h" |
| 47 | |
| 48 | #define MAX_PATH_LENGTH 1024 |
| 49 | |
| 50 | static struct { |
| 51 | jfieldID path; |
| 52 | } ids; |
| 53 | |
| 54 | JNIEXPORT void JNICALL |
| 55 | Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls) |
| 56 | { |
| 57 | jclass fileClass = (*env)->FindClass(env, "java/io/File"); |
| 58 | if (!fileClass) return; |
| 59 | ids.path = |
| 60 | (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;"); |
| 61 | } |
| 62 | |
| 63 | /* -- Path operations -- */ |
| 64 | |
| 65 | extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len); |
| 66 | extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len); |
| 67 | |
| 68 | JNIEXPORT jstring JNICALL |
| 69 | Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this, |
| 70 | jstring pathname) |
| 71 | { |
| 72 | jstring rv = NULL; |
| 73 | WCHAR canonicalPath[MAX_PATH_LENGTH]; |
| 74 | |
| 75 | WITH_UNICODE_STRING(env, pathname, path) { |
| 76 | /*we estimate the max length of memory needed as |
| 77 | "currentDir. length + pathname.length" |
| 78 | */ |
| 79 | int len = wcslen(path); |
| 80 | len += currentDirLength(path, len); |
| 81 | if (len > MAX_PATH_LENGTH - 1) { |
| 82 | WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR)); |
| 83 | if (cp != NULL) { |
| 84 | if (wcanonicalize(path, cp, len) >= 0) { |
| 85 | rv = (*env)->NewString(env, cp, wcslen(cp)); |
| 86 | } |
| 87 | free(cp); |
| 88 | } |
| 89 | } else |
| 90 | if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) { |
| 91 | rv = (*env)->NewString(env, canonicalPath, wcslen(canonicalPath)); |
| 92 | } |
| 93 | } END_UNICODE_STRING(env, path); |
| 94 | if (rv == NULL) { |
| 95 | JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); |
| 96 | } |
| 97 | return rv; |
| 98 | } |
| 99 | |
| 100 | |
| 101 | JNIEXPORT jstring JNICALL |
| 102 | Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this, |
| 103 | jstring canonicalPrefixString, |
| 104 | jstring pathWithCanonicalPrefixString) |
| 105 | { |
| 106 | jstring rv = NULL; |
| 107 | WCHAR canonicalPath[MAX_PATH_LENGTH]; |
| 108 | WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) { |
| 109 | WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) { |
| 110 | int len = wcslen(canonicalPrefix) + MAX_PATH; |
| 111 | if (len > MAX_PATH_LENGTH) { |
| 112 | WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR)); |
| 113 | if (cp != NULL) { |
| 114 | if (wcanonicalizeWithPrefix(canonicalPrefix, |
| 115 | pathWithCanonicalPrefix, |
| 116 | cp, len) >= 0) { |
| 117 | rv = (*env)->NewString(env, cp, wcslen(cp)); |
| 118 | } |
| 119 | free(cp); |
| 120 | } |
| 121 | } else |
| 122 | if (wcanonicalizeWithPrefix(canonicalPrefix, |
| 123 | pathWithCanonicalPrefix, |
| 124 | canonicalPath, MAX_PATH_LENGTH) >= 0) { |
| 125 | rv = (*env)->NewString(env, canonicalPath, wcslen(canonicalPath)); |
| 126 | } |
| 127 | } END_UNICODE_STRING(env, pathWithCanonicalPrefix); |
| 128 | } END_UNICODE_STRING(env, canonicalPrefix); |
| 129 | if (rv == NULL) { |
| 130 | JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); |
| 131 | } |
| 132 | return rv; |
| 133 | } |
| 134 | |
| 135 | /* -- Attribute accessors -- */ |
| 136 | |
| 137 | /* Check whether or not the file name in "path" is a Windows reserved |
| 138 | device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the |
| 139 | returned result from GetFullPathName, which should be in thr form of |
| 140 | "\\.\[ReservedDeviceName]" if the path represents a reserved device |
| 141 | name. |
| 142 | Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer |
| 143 | important anyway) is a device name, so we don't check it here. |
| 144 | GetFileAttributesEx will catch it later by returning 0 on NT/XP/ |
| 145 | 200X. |
| 146 | |
| 147 | Note2: Theoretically the implementation could just lookup the table |
| 148 | below linearly if the first 4 characters of the fullpath returned |
| 149 | from GetFullPathName are "\\.\". The current implementation should |
| 150 | achieve the same result. If Microsoft add more names into their |
| 151 | reserved device name repository in the future, which probably will |
| 152 | never happen, we will need to revisit the lookup implementation. |
| 153 | |
| 154 | static WCHAR* ReservedDEviceNames[] = { |
| 155 | L"CON", L"PRN", L"AUX", L"NUL", |
| 156 | L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", |
| 157 | L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9", |
| 158 | L"CLOCK$" |
| 159 | }; |
| 160 | */ |
| 161 | |
| 162 | static BOOL isReservedDeviceNameW(WCHAR* path) { |
| 163 | #define BUFSIZE 9 |
| 164 | WCHAR buf[BUFSIZE]; |
| 165 | WCHAR *lpf = NULL; |
| 166 | DWORD retLen = GetFullPathNameW(path, |
| 167 | BUFSIZE, |
| 168 | buf, |
| 169 | &lpf); |
| 170 | if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) && |
| 171 | buf[0] == L'\\' && buf[1] == L'\\' && |
| 172 | buf[2] == L'.' && buf[3] == L'\\') { |
| 173 | WCHAR* dname = _wcsupr(buf + 4); |
| 174 | if (wcscmp(dname, L"CON") == 0 || |
| 175 | wcscmp(dname, L"PRN") == 0 || |
| 176 | wcscmp(dname, L"AUX") == 0 || |
| 177 | wcscmp(dname, L"NUL") == 0) |
| 178 | return TRUE; |
| 179 | if ((wcsncmp(dname, L"COM", 3) == 0 || |
| 180 | wcsncmp(dname, L"LPT", 3) == 0) && |
| 181 | dname[3] - L'0' > 0 && |
| 182 | dname[3] - L'0' <= 9) |
| 183 | return TRUE; |
| 184 | } |
| 185 | return FALSE; |
| 186 | } |
| 187 | |
| 188 | JNIEXPORT jint JNICALL |
| 189 | Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this, |
| 190 | jobject file) |
| 191 | { |
| 192 | |
| 193 | jint rv = 0; |
| 194 | jint pathlen; |
| 195 | |
| 196 | /* both pagefile.sys and hiberfil.sys have length 12 */ |
| 197 | #define SPECIALFILE_NAMELEN 12 |
| 198 | |
| 199 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 200 | WIN32_FILE_ATTRIBUTE_DATA wfad; |
| 201 | if (pathbuf == NULL) |
| 202 | return rv; |
| 203 | if (!isReservedDeviceNameW(pathbuf)) { |
| 204 | if (GetFileAttributesExW(pathbuf, GetFileExInfoStandard, &wfad)) { |
| 205 | rv = (java_io_FileSystem_BA_EXISTS |
| 206 | | ((wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
| 207 | ? java_io_FileSystem_BA_DIRECTORY |
| 208 | : java_io_FileSystem_BA_REGULAR) |
| 209 | | ((wfad.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) |
| 210 | ? java_io_FileSystem_BA_HIDDEN : 0)); |
| 211 | } else { /* pagefile.sys is a special case */ |
| 212 | if (GetLastError() == ERROR_SHARING_VIOLATION) { |
| 213 | rv = java_io_FileSystem_BA_EXISTS; |
| 214 | if ((pathlen = wcslen(pathbuf)) >= SPECIALFILE_NAMELEN && |
| 215 | (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN, |
| 216 | L"pagefile.sys") == 0) || |
| 217 | (_wcsicmp(pathbuf + pathlen - SPECIALFILE_NAMELEN, |
| 218 | L"hiberfil.sys") == 0)) |
| 219 | rv |= java_io_FileSystem_BA_REGULAR; |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | free(pathbuf); |
| 224 | return rv; |
| 225 | } |
| 226 | |
| 227 | |
| 228 | JNIEXPORT jboolean |
| 229 | JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this, |
| 230 | jobject file, jint access) |
| 231 | { |
| 232 | jboolean rv = JNI_FALSE; |
| 233 | int mode; |
| 234 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 235 | if (pathbuf == NULL) |
| 236 | return JNI_FALSE; |
| 237 | switch (access) { |
| 238 | case java_io_FileSystem_ACCESS_READ: |
| 239 | case java_io_FileSystem_ACCESS_EXECUTE: |
| 240 | mode = 4; |
| 241 | break; |
| 242 | case java_io_FileSystem_ACCESS_WRITE: |
| 243 | mode = 2; |
| 244 | break; |
| 245 | default: assert(0); |
| 246 | } |
| 247 | if (_waccess(pathbuf, mode) == 0) { |
| 248 | rv = JNI_TRUE; |
| 249 | } |
| 250 | free(pathbuf); |
| 251 | return rv; |
| 252 | } |
| 253 | |
| 254 | JNIEXPORT jboolean JNICALL |
| 255 | Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this, |
| 256 | jobject file, |
| 257 | jint access, |
| 258 | jboolean enable, |
| 259 | jboolean owneronly) |
| 260 | { |
| 261 | jboolean rv = JNI_FALSE; |
| 262 | WCHAR *pathbuf; |
| 263 | DWORD a; |
| 264 | if (access == java_io_FileSystem_ACCESS_READ || |
| 265 | access == java_io_FileSystem_ACCESS_EXECUTE) { |
| 266 | return enable; |
| 267 | } |
| 268 | pathbuf = fileToNTPath(env, file, ids.path); |
| 269 | if (pathbuf == NULL) |
| 270 | return JNI_FALSE; |
| 271 | a = GetFileAttributesW(pathbuf); |
| 272 | if (a != INVALID_FILE_ATTRIBUTES) { |
| 273 | if (enable) |
| 274 | a = a & ~FILE_ATTRIBUTE_READONLY; |
| 275 | else |
| 276 | a = a | FILE_ATTRIBUTE_READONLY; |
| 277 | if (SetFileAttributesW(pathbuf, a)) |
| 278 | rv = JNI_TRUE; |
| 279 | } |
| 280 | free(pathbuf); |
| 281 | return rv; |
| 282 | } |
| 283 | |
| 284 | JNIEXPORT jlong JNICALL |
| 285 | Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this, |
| 286 | jobject file) |
| 287 | { |
| 288 | jlong rv = 0; |
| 289 | LARGE_INTEGER modTime; |
| 290 | FILETIME t; |
| 291 | HANDLE h; |
| 292 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 293 | if (pathbuf == NULL) |
| 294 | return rv; |
| 295 | h = CreateFileW(pathbuf, |
| 296 | /* Device query access */ |
| 297 | 0, |
| 298 | /* Share it */ |
| 299 | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 300 | /* No security attributes */ |
| 301 | NULL, |
| 302 | /* Open existing or fail */ |
| 303 | OPEN_EXISTING, |
| 304 | /* Backup semantics for directories */ |
| 305 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, |
| 306 | /* No template file */ |
| 307 | NULL); |
| 308 | if (h != INVALID_HANDLE_VALUE) { |
| 309 | GetFileTime(h, NULL, NULL, &t); |
| 310 | CloseHandle(h); |
| 311 | modTime.LowPart = (DWORD) t.dwLowDateTime; |
| 312 | modTime.HighPart = (LONG) t.dwHighDateTime; |
| 313 | rv = modTime.QuadPart / 10000; |
| 314 | rv -= 11644473600000; |
| 315 | } |
| 316 | free(pathbuf); |
| 317 | return rv; |
| 318 | } |
| 319 | |
| 320 | JNIEXPORT jlong JNICALL |
| 321 | Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file) |
| 322 | { |
| 323 | jlong rv = 0; |
| 324 | WIN32_FILE_ATTRIBUTE_DATA wfad; |
| 325 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 326 | if (pathbuf == NULL) |
| 327 | return rv; |
| 328 | if (GetFileAttributesExW(pathbuf, |
| 329 | GetFileExInfoStandard, |
| 330 | &wfad)) { |
| 331 | rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow; |
| 332 | } else { |
| 333 | if (GetLastError() == ERROR_SHARING_VIOLATION) { |
| 334 | /* The error is "share violation", which means the file/dir |
| 335 | must exists. Try _wstati64, we know this at least works |
| 336 | for pagefile.sys and hiberfil.sys. |
| 337 | */ |
| 338 | struct _stati64 sb; |
| 339 | if (_wstati64(pathbuf, &sb) == 0) { |
| 340 | rv = sb.st_size; |
| 341 | } |
| 342 | } |
| 343 | } |
| 344 | free(pathbuf); |
| 345 | return rv; |
| 346 | } |
| 347 | |
| 348 | /* -- File operations -- */ |
| 349 | |
| 350 | JNIEXPORT jboolean JNICALL |
| 351 | Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls, |
| 352 | jstring path) |
| 353 | { |
| 354 | HANDLE h = NULL; |
| 355 | WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE); |
| 356 | if (pathbuf == NULL) |
| 357 | return JNI_FALSE; |
| 358 | h = CreateFileW( |
| 359 | pathbuf, /* Wide char path name */ |
| 360 | GENERIC_READ | GENERIC_WRITE, /* Read and write permission */ |
| 361 | FILE_SHARE_READ | FILE_SHARE_WRITE, /* File sharing flags */ |
| 362 | NULL, /* Security attributes */ |
| 363 | CREATE_NEW, /* creation disposition */ |
| 364 | FILE_ATTRIBUTE_NORMAL, /* flags and attributes */ |
| 365 | NULL); |
| 366 | |
| 367 | if (h == INVALID_HANDLE_VALUE) { |
| 368 | DWORD error = GetLastError(); |
| 369 | if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) { |
| 370 | |
| 371 | // If a directory by the named path already exists, |
| 372 | // return false (behavior of solaris and linux) instead of |
| 373 | // throwing an exception |
| 374 | DWORD fattr = GetFileAttributesW(pathbuf); |
| 375 | if ((fattr == INVALID_FILE_ATTRIBUTES) || |
| 376 | (fattr & ~FILE_ATTRIBUTE_DIRECTORY)) { |
| 377 | SetLastError(error); |
| 378 | JNU_ThrowIOExceptionWithLastError(env, "Could not open file"); |
| 379 | } |
| 380 | } |
| 381 | free(pathbuf); |
| 382 | return JNI_FALSE; |
| 383 | } |
| 384 | free(pathbuf); |
| 385 | CloseHandle(h); |
| 386 | return JNI_TRUE; |
| 387 | } |
| 388 | |
| 389 | static int |
| 390 | removeFileOrDirectory(const jchar *path) |
| 391 | { |
| 392 | /* Returns 0 on success */ |
| 393 | DWORD a; |
| 394 | |
| 395 | SetFileAttributesW(path, 0); |
| 396 | a = GetFileAttributesW(path); |
| 397 | if (a == ((DWORD)-1)) { |
| 398 | return 1; |
| 399 | } else if (a & FILE_ATTRIBUTE_DIRECTORY) { |
| 400 | return !RemoveDirectoryW(path); |
| 401 | } else { |
| 402 | return !DeleteFileW(path); |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | JNIEXPORT jboolean JNICALL |
| 407 | Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file) |
| 408 | { |
| 409 | jboolean rv = JNI_FALSE; |
| 410 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 411 | if (pathbuf == NULL) { |
| 412 | return JNI_FALSE; |
| 413 | } |
| 414 | if (removeFileOrDirectory(pathbuf) == 0) { |
| 415 | rv = JNI_TRUE; |
| 416 | } |
| 417 | free(pathbuf); |
| 418 | return rv; |
| 419 | } |
| 420 | |
| 421 | JNIEXPORT jobjectArray JNICALL |
| 422 | Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file) |
| 423 | { |
| 424 | WCHAR *search_path; |
| 425 | HANDLE handle; |
| 426 | WIN32_FIND_DATAW find_data; |
| 427 | int len, maxlen; |
| 428 | jobjectArray rv, old; |
| 429 | DWORD fattr; |
| 430 | jstring name; |
| 431 | |
| 432 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 433 | if (pathbuf == NULL) |
| 434 | return NULL; |
| 435 | search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6); |
| 436 | if (search_path == 0) { |
| 437 | free (pathbuf); |
| 438 | errno = ENOMEM; |
| 439 | return NULL; |
| 440 | } |
| 441 | wcscpy(search_path, pathbuf); |
| 442 | free(pathbuf); |
| 443 | fattr = GetFileAttributesW(search_path); |
| 444 | if (fattr == INVALID_FILE_ATTRIBUTES) { |
| 445 | free(search_path); |
| 446 | return NULL; |
| 447 | } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) { |
| 448 | free(search_path); |
| 449 | return NULL; |
| 450 | } |
| 451 | |
| 452 | /* Remove trailing space chars from directory name */ |
| 453 | len = wcslen(search_path); |
| 454 | while (search_path[len-1] == ' ') { |
| 455 | len--; |
| 456 | } |
| 457 | search_path[len] = 0; |
| 458 | |
| 459 | /* Append "*", or possibly "\\*", to path */ |
| 460 | if ((search_path[0] == L'\\' && search_path[1] == L'\0') || |
| 461 | (search_path[1] == L':' |
| 462 | && (search_path[2] == L'\0' |
| 463 | || (search_path[2] == L'\\' && search_path[3] == L'\0')))) { |
| 464 | /* No '\\' needed for cases like "\" or "Z:" or "Z:\" */ |
| 465 | wcscat(search_path, L"*"); |
| 466 | } else { |
| 467 | wcscat(search_path, L"\\*"); |
| 468 | } |
| 469 | |
| 470 | /* Open handle to the first file */ |
| 471 | handle = FindFirstFileW(search_path, &find_data); |
| 472 | free(search_path); |
| 473 | if (handle == INVALID_HANDLE_VALUE) { |
| 474 | if (GetLastError() != ERROR_FILE_NOT_FOUND) { |
| 475 | // error |
| 476 | return NULL; |
| 477 | } else { |
| 478 | // No files found - return an empty array |
| 479 | rv = (*env)->NewObjectArray(env, 0, JNU_ClassString(env), NULL); |
| 480 | return rv; |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | /* Allocate an initial String array */ |
| 485 | len = 0; |
| 486 | maxlen = 16; |
| 487 | rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL); |
| 488 | if (rv == NULL) // Couldn't allocate an array |
| 489 | return NULL; |
| 490 | /* Scan the directory */ |
| 491 | do { |
| 492 | if (!wcscmp(find_data.cFileName, L".") |
| 493 | || !wcscmp(find_data.cFileName, L"..")) |
| 494 | continue; |
| 495 | name = (*env)->NewString(env, find_data.cFileName, |
| 496 | wcslen(find_data.cFileName)); |
| 497 | if (name == NULL) |
| 498 | return NULL; // error; |
| 499 | if (len == maxlen) { |
| 500 | old = rv; |
| 501 | rv = (*env)->NewObjectArray(env, maxlen <<= 1, |
| 502 | JNU_ClassString(env), NULL); |
| 503 | if ( rv == NULL |
| 504 | || JNU_CopyObjectArray(env, rv, old, len) < 0) |
| 505 | return NULL; // error |
| 506 | (*env)->DeleteLocalRef(env, old); |
| 507 | } |
| 508 | (*env)->SetObjectArrayElement(env, rv, len++, name); |
| 509 | (*env)->DeleteLocalRef(env, name); |
| 510 | |
| 511 | } while (FindNextFileW(handle, &find_data)); |
| 512 | |
| 513 | if (GetLastError() != ERROR_NO_MORE_FILES) |
| 514 | return NULL; // error |
| 515 | FindClose(handle); |
| 516 | |
| 517 | /* Copy the final results into an appropriately-sized array */ |
| 518 | old = rv; |
| 519 | rv = (*env)->NewObjectArray(env, len, JNU_ClassString(env), NULL); |
| 520 | if (rv == NULL) |
| 521 | return NULL; /* error */ |
| 522 | if (JNU_CopyObjectArray(env, rv, old, len) < 0) |
| 523 | return NULL; /* error */ |
| 524 | return rv; |
| 525 | } |
| 526 | |
| 527 | |
| 528 | JNIEXPORT jboolean JNICALL |
| 529 | Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this, |
| 530 | jobject file) |
| 531 | { |
| 532 | BOOL h = FALSE; |
| 533 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 534 | if (pathbuf == NULL) { |
| 535 | /* Exception is pending */ |
| 536 | return JNI_FALSE; |
| 537 | } |
| 538 | h = CreateDirectoryW(pathbuf, NULL); |
| 539 | free(pathbuf); |
| 540 | |
| 541 | if (h == 0) { |
| 542 | return JNI_FALSE; |
| 543 | } |
| 544 | |
| 545 | return JNI_TRUE; |
| 546 | } |
| 547 | |
| 548 | |
| 549 | JNIEXPORT jboolean JNICALL |
| 550 | Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from, |
| 551 | jobject to) |
| 552 | { |
| 553 | |
| 554 | jboolean rv = JNI_FALSE; |
| 555 | WCHAR *frompath = fileToNTPath(env, from, ids.path); |
| 556 | WCHAR *topath = fileToNTPath(env, to, ids.path); |
| 557 | if (frompath == NULL || topath == NULL) |
| 558 | return JNI_FALSE; |
| 559 | if (_wrename(frompath, topath) == 0) { |
| 560 | rv = JNI_TRUE; |
| 561 | } |
| 562 | free(frompath); |
| 563 | free(topath); |
| 564 | return rv; |
| 565 | } |
| 566 | |
| 567 | |
| 568 | JNIEXPORT jboolean JNICALL |
| 569 | Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this, |
| 570 | jobject file, jlong time) |
| 571 | { |
| 572 | jboolean rv = JNI_FALSE; |
| 573 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 574 | HANDLE h; |
| 575 | if (pathbuf == NULL) |
| 576 | return JNI_FALSE; |
| 577 | h = CreateFileW(pathbuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, |
| 578 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0); |
| 579 | if (h != INVALID_HANDLE_VALUE) { |
| 580 | LARGE_INTEGER modTime; |
| 581 | FILETIME t; |
| 582 | modTime.QuadPart = (time + 11644473600000L) * 10000L; |
| 583 | t.dwLowDateTime = (DWORD)modTime.LowPart; |
| 584 | t.dwHighDateTime = (DWORD)modTime.HighPart; |
| 585 | if (SetFileTime(h, NULL, NULL, &t)) { |
| 586 | rv = JNI_TRUE; |
| 587 | } |
| 588 | CloseHandle(h); |
| 589 | } |
| 590 | free(pathbuf); |
| 591 | |
| 592 | return rv; |
| 593 | } |
| 594 | |
| 595 | |
| 596 | JNIEXPORT jboolean JNICALL |
| 597 | Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this, |
| 598 | jobject file) |
| 599 | { |
| 600 | jboolean rv = JNI_FALSE; |
| 601 | DWORD a; |
| 602 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 603 | if (pathbuf == NULL) |
| 604 | return JNI_FALSE; |
| 605 | a = GetFileAttributesW(pathbuf); |
| 606 | if (a != INVALID_FILE_ATTRIBUTES) { |
| 607 | if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY)) |
| 608 | rv = JNI_TRUE; |
| 609 | } |
| 610 | free(pathbuf); |
| 611 | return rv; |
| 612 | } |
| 613 | |
| 614 | /* -- Filesystem interface -- */ |
| 615 | |
| 616 | |
| 617 | JNIEXPORT jobject JNICALL |
| 618 | Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this, |
| 619 | jint drive) |
| 620 | { |
| 621 | jstring ret = NULL; |
| 622 | jchar *p = _wgetdcwd(drive, NULL, MAX_PATH); |
| 623 | jchar *pf = p; |
| 624 | if (p == NULL) return NULL; |
| 625 | if (iswalpha(*p) && (p[1] == L':')) p += 2; |
| 626 | ret = (*env)->NewString(env, p, wcslen(p)); |
| 627 | free (pf); |
| 628 | return ret; |
| 629 | } |
| 630 | |
| 631 | typedef BOOL (WINAPI* GetVolumePathNameProc) (LPCWSTR, LPWSTR, DWORD); |
| 632 | |
| 633 | JNIEXPORT jlong JNICALL |
| 634 | Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this, |
| 635 | jobject file, jint t) |
| 636 | { |
| 637 | WCHAR volname[MAX_PATH_LENGTH + 1]; |
| 638 | jlong rv = 0L; |
| 639 | WCHAR *pathbuf = fileToNTPath(env, file, ids.path); |
| 640 | |
| 641 | HMODULE h = LoadLibrary("kernel32"); |
| 642 | GetVolumePathNameProc getVolumePathNameW = NULL; |
| 643 | if (h) { |
| 644 | getVolumePathNameW |
| 645 | = (GetVolumePathNameProc)GetProcAddress(h, "GetVolumePathNameW"); |
| 646 | } |
| 647 | |
| 648 | if (getVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) { |
| 649 | ULARGE_INTEGER totalSpace, freeSpace, usableSpace; |
| 650 | if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) { |
| 651 | switch(t) { |
| 652 | case java_io_FileSystem_SPACE_TOTAL: |
| 653 | rv = long_to_jlong(totalSpace.QuadPart); |
| 654 | break; |
| 655 | case java_io_FileSystem_SPACE_FREE: |
| 656 | rv = long_to_jlong(freeSpace.QuadPart); |
| 657 | break; |
| 658 | case java_io_FileSystem_SPACE_USABLE: |
| 659 | rv = long_to_jlong(usableSpace.QuadPart); |
| 660 | break; |
| 661 | default: |
| 662 | assert(0); |
| 663 | } |
| 664 | } |
| 665 | } |
| 666 | |
| 667 | if (h) { |
| 668 | FreeLibrary(h); |
| 669 | } |
| 670 | free(pathbuf); |
| 671 | return rv; |
| 672 | } |