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