1 /* 2 * Copyright (c) 2001, 2007, 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 #include "jni.h" 27 #include "jni_util.h" 28 #include "jvm.h" 29 #include "io_util.h" 30 #include "io_util_md.h" 31 #include <stdio.h> 32 33 #include <wchar.h> 34 #include <io.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 #include <string.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <limits.h> 41 #include <wincon.h> 42 43 extern jboolean onNT = JNI_FALSE; 44 45 static int MAX_INPUT_EVENTS = 2000; 46 47 void 48 initializeWindowsVersion() { 49 OSVERSIONINFO ver; 50 ver.dwOSVersionInfoSize = sizeof(ver); 51 GetVersionEx(&ver); 52 if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) { 53 onNT = JNI_TRUE; 54 } else { 55 onNT = JNI_FALSE; 56 } 57 } 58 59 /* If this returns NULL then an exception is pending */ 60 WCHAR* 61 fileToNTPath(JNIEnv *env, jobject file, jfieldID id) { 62 jstring path = NULL; 63 if (file != NULL) { 64 path = (*env)->GetObjectField(env, file, id); 65 } 66 return pathToNTPath(env, path, JNI_FALSE); 67 } 68 69 /* We cache the length of current working dir here to avoid 70 calling _wgetcwd() every time we need to resolve a relative 71 path. This piece of code needs to be revisited if chdir 72 makes its way into java runtime. 73 */ 74 75 int 76 currentDirLength(const WCHAR* ps, int pathlen) { 77 WCHAR *dir; 78 if (pathlen > 2 && ps[1] == L':' && ps[2] != L'\\') { 79 //drive-relative 80 WCHAR d = ps[0]; 81 int dirlen = 0; 82 int di = 0; 83 if ((d >= L'a') && (d <= L'z')) di = d - L'a' + 1; 84 else if ((d >= L'A') && (d <= L'Z')) di = d - L'A' + 1; 85 else return 0; /* invalid drive name. */ 86 dir = _wgetdcwd(di, NULL, MAX_PATH); 87 if (dir != NULL){ 88 dirlen = wcslen(dir); 89 free(dir); 90 } 91 return dirlen; 92 } else { 93 static int curDirLenCached = -1; 94 //relative to both drive and directory 95 if (curDirLenCached == -1) { 96 int dirlen = -1; 97 dir = _wgetcwd(NULL, MAX_PATH); 98 if (dir != NULL) { 99 curDirLenCached = wcslen(dir); 100 free(dir); 101 } 102 } 103 return curDirLenCached; 104 } 105 } 106 107 /* 108 The "abpathlen" is the size of the buffer needed by _wfullpath. If the 109 "path" is a relative path, it is "the length of the current dir" + "the 110 length of the path", if it's "absolute" already, it's the same as 111 pathlen which is the length of "path". 112 */ 113 WCHAR* prefixAbpath(const WCHAR* path, int pathlen, int abpathlen) { 114 WCHAR* pathbuf = NULL; 115 WCHAR* abpath = NULL; 116 117 abpathlen += 10; //padding 118 abpath = (WCHAR*)malloc(abpathlen * sizeof(WCHAR)); 119 if (abpath) { 120 /* Collapse instances of "foo\.." and ensure absoluteness before 121 going down to prefixing. 122 */ 123 if (_wfullpath(abpath, path, abpathlen)) { 124 pathbuf = getPrefixed(abpath, abpathlen); 125 } else { 126 /* _wfullpath fails if the pathlength exceeds 32k wchar. 127 Instead of doing more fancy things we simply copy the 128 ps into the return buffer, the subsequent win32 API will 129 probably fail with FileNotFoundException, which is expected 130 */ 131 pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR)); 132 if (pathbuf != 0) { 133 wcscpy(pathbuf, path); 134 } 135 } 136 free(abpath); 137 } 138 return pathbuf; 139 } 140 141 /* If this returns NULL then an exception is pending */ 142 WCHAR* 143 pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) { 144 int pathlen = 0; 145 WCHAR *pathbuf = NULL; 146 int max_path = 248; /* CreateDirectoryW() has the limit of 248 */ 147 148 WITH_UNICODE_STRING(env, path, ps) { 149 pathlen = wcslen(ps); 150 if (pathlen != 0) { 151 if (pathlen > 2 && 152 (ps[0] == L'\\' && ps[1] == L'\\' || //UNC 153 ps[1] == L':' && ps[2] == L'\\')) //absolute 154 { 155 if (pathlen > max_path - 1) { 156 pathbuf = prefixAbpath(ps, pathlen, pathlen); 157 } else { 158 pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR)); 159 if (pathbuf != 0) { 160 wcscpy(pathbuf, ps); 161 } 162 } 163 } else { 164 /* If the path came in as a relative path, need to verify if 165 its absolute form is bigger than max_path or not, if yes 166 need to (1)convert it to absolute and (2)prefix. This is 167 obviously a burden to all relative paths (The current dir/len 168 for "drive & directory" relative path is cached, so we only 169 calculate it once but for "drive-relative path we call 170 _wgetdcwd() and wcslen() everytime), but a hit we have 171 to take if we want to support relative path beyond max_path. 172 There is no way to predict how long the absolute path will be 173 (therefor allocate the sufficient memory block) before calling 174 _wfullpath(), we have to get the length of "current" dir first. 175 */ 176 WCHAR *abpath = NULL; 177 int dirlen = currentDirLength(ps, pathlen); 178 if (dirlen + pathlen + 1 > max_path - 1) { 179 pathbuf = prefixAbpath(ps, pathlen, dirlen + pathlen); 180 } else { 181 pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR)); 182 if (pathbuf != 0) { 183 wcscpy(pathbuf, ps); 184 } 185 } 186 } 187 } 188 } END_UNICODE_STRING(env, ps); 189 190 if (pathlen == 0) { 191 if (throwFNFE == JNI_TRUE) { 192 throwFileNotFoundException(env, path); 193 return NULL; 194 } else { 195 pathbuf = (WCHAR*)malloc(sizeof(WCHAR)); 196 pathbuf[0] = L'\0'; 197 } 198 } 199 if (pathbuf == 0) { 200 JNU_ThrowOutOfMemoryError(env, 0); 201 return NULL; 202 } 203 return pathbuf; 204 } 205 206 jlong 207 winFileHandleOpen(JNIEnv *env, jstring path, int flags) 208 { 209 const DWORD access = 210 (flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : 211 (flags & O_WRONLY) ? GENERIC_WRITE : 212 GENERIC_READ; 213 const DWORD sharing = 214 FILE_SHARE_READ | FILE_SHARE_WRITE; 215 const DWORD disposition = 216 /* Note: O_TRUNC overrides O_CREAT */ 217 (flags & O_TRUNC) ? CREATE_ALWAYS : 218 (flags & O_CREAT) ? OPEN_ALWAYS : 219 OPEN_EXISTING; 220 const DWORD maybeWriteThrough = 221 (flags & (O_SYNC | O_DSYNC)) ? 222 FILE_FLAG_WRITE_THROUGH : 223 FILE_ATTRIBUTE_NORMAL; 224 const DWORD maybeDeleteOnClose = 225 (flags & O_TEMPORARY) ? 226 FILE_FLAG_DELETE_ON_CLOSE : 227 FILE_ATTRIBUTE_NORMAL; 228 const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose; 229 HANDLE h = NULL; 230 231 if (onNT) { 232 WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE); 233 if (pathbuf == NULL) { 234 /* Exception already pending */ 235 return -1; 236 } 237 h = CreateFileW( 238 pathbuf, /* Wide char path name */ 239 access, /* Read and/or write permission */ 240 sharing, /* File sharing flags */ 241 NULL, /* Security attributes */ 242 disposition, /* creation disposition */ 243 flagsAndAttributes, /* flags and attributes */ 244 NULL); 245 free(pathbuf); 246 } else { 247 WITH_PLATFORM_STRING(env, path, _ps) { 248 h = CreateFile(_ps, access, sharing, NULL, disposition, 249 flagsAndAttributes, NULL); 250 } END_PLATFORM_STRING(env, _ps); 251 } 252 if (h == INVALID_HANDLE_VALUE) { 253 int error = GetLastError(); 254 if (error == ERROR_TOO_MANY_OPEN_FILES) { 255 JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException", 256 "Too many open files"); 257 return -1; 258 } 259 throwFileNotFoundException(env, path); 260 return -1; 261 } 262 return (jlong) h; 263 } 264 265 void 266 fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags) 267 { 268 jlong h = winFileHandleOpen(env, path, flags); 269 if (h >= 0) { 270 SET_FD(this, h, fid); 271 } 272 } 273 274 /* These are functions that use a handle fd instead of the 275 old C style int fd as is used in HPI layer */ 276 277 static int 278 handleNonSeekAvailable(jlong, long *); 279 static int 280 handleStdinAvailable(jlong, long *); 281 282 int 283 handleAvailable(jlong fd, jlong *pbytes) { 284 jlong current, end; 285 HANDLE h = (HANDLE)fd; 286 DWORD type = 0; 287 288 type = GetFileType(h); 289 /* Handle is for keyboard or pipe */ 290 if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) { 291 int ret; 292 long lpbytes; 293 HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE); 294 if (stdInHandle == h) { 295 ret = handleStdinAvailable(fd, &lpbytes); /* keyboard */ 296 } else { 297 ret = handleNonSeekAvailable(fd, &lpbytes); /* pipe */ 298 } 299 (*pbytes) = (jlong)(lpbytes); 300 return ret; 301 } 302 /* Handle is for regular file */ 303 if (type == FILE_TYPE_DISK) { 304 long highPos = 0; 305 DWORD sizeLow = 0; 306 DWORD sizeHigh = 0; 307 DWORD lowPos = SetFilePointer(h, 0, &highPos, FILE_CURRENT); 308 if (lowPos == ((DWORD)-1)) { 309 return FALSE; 310 } 311 current = (((jlong)highPos) << 32) | lowPos; 312 end = GetFileSize(h, &sizeHigh); 313 if (sizeLow == ((DWORD)-1)) { 314 return FALSE; 315 } 316 *pbytes = end - current; 317 return TRUE; 318 } 319 return FALSE; 320 } 321 322 static int 323 handleNonSeekAvailable(jlong fd, long *pbytes) { 324 /* This is used for available on non-seekable devices 325 * (like both named and anonymous pipes, such as pipes 326 * connected to an exec'd process). 327 * Standard Input is a special case. 328 * 329 */ 330 HANDLE han; 331 332 if ((han = (HANDLE) fd) == INVALID_HANDLE_VALUE) { 333 return FALSE; 334 } 335 336 if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) { 337 /* PeekNamedPipe fails when at EOF. In that case we 338 * simply make *pbytes = 0 which is consistent with the 339 * behavior we get on Solaris when an fd is at EOF. 340 * The only alternative is to raise and Exception, 341 * which isn't really warranted. 342 */ 343 if (GetLastError() != ERROR_BROKEN_PIPE) { 344 return FALSE; 345 } 346 *pbytes = 0; 347 } 348 return TRUE; 349 } 350 351 static int 352 handleStdinAvailable(jlong fd, long *pbytes) { 353 HANDLE han; 354 DWORD numEventsRead = 0; /* Number of events read from buffer */ 355 DWORD numEvents = 0; /* Number of events in buffer */ 356 DWORD i = 0; /* Loop index */ 357 DWORD curLength = 0; /* Position marker */ 358 DWORD actualLength = 0; /* Number of bytes readable */ 359 BOOL error = FALSE; /* Error holder */ 360 INPUT_RECORD *lpBuffer; /* Pointer to records of input events */ 361 DWORD bufferSize = 0; 362 363 if ((han = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) { 364 return FALSE; 365 } 366 367 /* Construct an array of input records in the console buffer */ 368 error = GetNumberOfConsoleInputEvents(han, &numEvents); 369 if (error == 0) { 370 return handleNonSeekAvailable(fd, pbytes); 371 } 372 373 /* lpBuffer must fit into 64K or else PeekConsoleInput fails */ 374 if (numEvents > MAX_INPUT_EVENTS) { 375 numEvents = MAX_INPUT_EVENTS; 376 } 377 378 bufferSize = numEvents * sizeof(INPUT_RECORD); 379 if (bufferSize == 0) 380 bufferSize = 1; 381 lpBuffer = malloc(bufferSize); 382 if (lpBuffer == NULL) { 383 return FALSE; 384 } 385 386 error = PeekConsoleInput(han, lpBuffer, numEvents, &numEventsRead); 387 if (error == 0) { 388 free(lpBuffer); 389 return FALSE; 390 } 391 392 /* Examine input records for the number of bytes available */ 393 for(i=0; i<numEvents; i++) { 394 if (lpBuffer[i].EventType == KEY_EVENT) { 395 KEY_EVENT_RECORD *keyRecord = (KEY_EVENT_RECORD *) 396 &(lpBuffer[i].Event); 397 if (keyRecord->bKeyDown == TRUE) { 398 CHAR *keyPressed = (CHAR *) &(keyRecord->uChar); 399 curLength++; 400 if (*keyPressed == '\r') 401 actualLength = curLength; 402 } 403 } 404 } 405 if(lpBuffer != NULL) 406 free(lpBuffer); 407 *pbytes = (long) actualLength; 408 return TRUE; 409 } 410 411 /* 412 * This is documented to succeed on read-only files, but Win32's 413 * FlushFileBuffers functions fails with "access denied" in such a 414 * case. So we only signal an error if the error is *not* "access 415 * denied". 416 */ 417 418 JNIEXPORT int 419 handleSync(jlong fd) { 420 /* 421 * From the documentation: 422 * 423 * On Windows NT, the function FlushFileBuffers fails if hFile 424 * is a handle to console output. That is because console 425 * output is not buffered. The function returns FALSE, and 426 * GetLastError returns ERROR_INVALID_HANDLE. 427 * 428 * On the other hand, on Win95, it returns without error. I cannot 429 * assume that 0, 1, and 2 are console, because if someone closes 430 * System.out and then opens a file, they might get file descriptor 431 * 1. An error on *that* version of 1 should be reported, whereas 432 * an error on System.out (which was the original 1) should be 433 * ignored. So I use isatty() to ensure that such an error was due 434 * to this bogosity, and if it was, I ignore the error. 435 */ 436 437 HANDLE handle = (HANDLE)fd; 438 439 if (!FlushFileBuffers(handle)) { 440 if (GetLastError() != ERROR_ACCESS_DENIED) { /* from winerror.h */ 441 return -1; 442 } 443 } 444 return 0; 445 } 446 447 448 int 449 handleSetLength(jlong fd, jlong length) { 450 HANDLE h = (HANDLE)fd; 451 long high = (long)(length >> 32); 452 DWORD ret; 453 454 if (h == (HANDLE)(-1)) return -1; 455 ret = SetFilePointer(h, (long)(length), &high, FILE_BEGIN); 456 if (ret == 0xFFFFFFFF && GetLastError() != NO_ERROR) { 457 return -1; 458 } 459 if (SetEndOfFile(h) == FALSE) return -1; 460 return 0; 461 } 462 463 int 464 handleFileSizeFD(jlong fd, jlong *size) 465 { 466 DWORD sizeLow = 0; 467 DWORD sizeHigh = 0; 468 HANDLE h = (HANDLE)fd; 469 if (h == INVALID_HANDLE_VALUE) { 470 return -1; 471 } 472 sizeLow = GetFileSize(h, &sizeHigh); 473 if (sizeLow == ((DWORD)-1)) { 474 if (GetLastError() != ERROR_SUCCESS) { 475 return -1; 476 } 477 } 478 return (((jlong)sizeHigh) << 32) | sizeLow; 479 } 480 481 JNIEXPORT 482 size_t 483 handleRead(jlong fd, void *buf, jint len) 484 { 485 DWORD read = 0; 486 BOOL result = 0; 487 HANDLE h = (HANDLE)fd; 488 if (h == INVALID_HANDLE_VALUE) { 489 return -1; 490 } 491 result = ReadFile(h, /* File handle to read */ 492 buf, /* address to put data */ 493 len, /* number of bytes to read */ 494 &read, /* number of bytes read */ 495 NULL); /* no overlapped struct */ 496 if (result == 0) { 497 int error = GetLastError(); 498 if (error == ERROR_BROKEN_PIPE) { 499 return 0; /* EOF */ 500 } 501 return -1; 502 } 503 return read; 504 } 505 506 JNIEXPORT 507 size_t 508 handleWrite(jlong fd, const void *buf, jint len) 509 { 510 BOOL result = 0; 511 DWORD written = 0; 512 HANDLE h = (HANDLE)fd; 513 if (h != INVALID_HANDLE_VALUE) { 514 result = WriteFile(h, /* File handle to write */ 515 buf, /* pointers to the buffers */ 516 len, /* number of bytes to write */ 517 &written, /* receives number of bytes written */ 518 NULL); /* no overlapped struct */ 519 } 520 if ((h == INVALID_HANDLE_VALUE) || (result == 0)) { 521 return -1; 522 } 523 return written; 524 } 525 526 jint 527 handleClose(JNIEnv *env, jobject this, jfieldID fid) 528 { 529 FD fd = GET_FD(this, fid); 530 HANDLE h = (HANDLE)fd; 531 532 if (fd == INVALID_HANDLE_VALUE) { 533 return 0; 534 } 535 536 /* Set the fd to -1 before closing it so that the timing window 537 * of other threads using the wrong fd (closed but recycled fd, 538 * that gets re-opened with some other filename) is reduced. 539 * Practically the chance of its occurance is low, however, we are 540 * taking extra precaution over here. 541 */ 542 SET_FD(this, -1, fid); 543 544 if (CloseHandle(h) == 0) { /* Returns zero on failure */ 545 SET_FD(this, fd, fid); // restore fd 546 JNU_ThrowIOExceptionWithLastError(env, "close failed"); 547 } 548 return 0; 549 } 550 551 jlong 552 handleLseek(jlong fd, jlong offset, jint whence) 553 { 554 DWORD lowPos = 0; 555 long highPos = 0; 556 DWORD op = FILE_CURRENT; 557 HANDLE h = (HANDLE)fd; 558 559 if (whence == SEEK_END) { 560 op = FILE_END; 561 } 562 if (whence == SEEK_CUR) { 563 op = FILE_CURRENT; 564 } 565 if (whence == SEEK_SET) { 566 op = FILE_BEGIN; 567 } 568 569 lowPos = (DWORD)offset; 570 highPos = (long)(offset >> 32); 571 lowPos = SetFilePointer(h, lowPos, &highPos, op); 572 if (lowPos == ((DWORD)-1)) { 573 if (GetLastError() != ERROR_SUCCESS) { 574 return -1; 575 } 576 } 577 return (((jlong)highPos) << 32) | lowPos; 578 }