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