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