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 }