jdk/src/java.base/windows/native/libjava/io_util_md.c
changeset 25859 3317bb8137f4
parent 23565 b28f771b8cbe
child 27184 2996674bd701
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libjava/io_util_md.c	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "io_util.h"
+#include "io_util_md.h"
+#include <stdio.h>
+#include <windows.h>
+
+#include <wchar.h>
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <wincon.h>
+
+
+static DWORD MAX_INPUT_EVENTS = 2000;
+
+/* If this returns NULL then an exception is pending */
+WCHAR*
+fileToNTPath(JNIEnv *env, jobject file, jfieldID id) {
+    jstring path = NULL;
+    if (file != NULL) {
+        path = (*env)->GetObjectField(env, file, id);
+    }
+    return pathToNTPath(env, path, JNI_FALSE);
+}
+
+/* Returns the working directory for the given drive, or NULL */
+WCHAR*
+currentDir(int di) {
+    UINT dt;
+    WCHAR root[4];
+    // verify drive is valid as _wgetdcwd in the VC++ 2010 runtime
+    // library does not handle invalid drives.
+    root[0] = L'A' + (WCHAR)(di - 1);
+    root[1] = L':';
+    root[2] = L'\\';
+    root[3] = L'\0';
+    dt = GetDriveTypeW(root);
+    if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) {
+        return NULL;
+    } else {
+        return _wgetdcwd(di, NULL, MAX_PATH);
+    }
+}
+
+/* We cache the length of current working dir here to avoid
+   calling _wgetcwd() every time we need to resolve a relative
+   path. This piece of code needs to be revisited if chdir
+   makes its way into java runtime.
+*/
+
+int
+currentDirLength(const WCHAR* ps, int pathlen) {
+    WCHAR *dir;
+    if (pathlen > 2 && ps[1] == L':' && ps[2] != L'\\') {
+        //drive-relative
+        WCHAR d = ps[0];
+        int dirlen = 0;
+        int di = 0;
+        if ((d >= L'a') && (d <= L'z')) di = d - L'a' + 1;
+        else if ((d >= L'A') && (d <= L'Z')) di = d - L'A' + 1;
+        else return 0; /* invalid drive name. */
+        dir = currentDir(di);
+        if (dir != NULL){
+            dirlen = (int)wcslen(dir);
+            free(dir);
+        }
+        return dirlen;
+    } else {
+        static int curDirLenCached = -1;
+        //relative to both drive and directory
+        if (curDirLenCached == -1) {
+            int dirlen = -1;
+            dir = _wgetcwd(NULL, MAX_PATH);
+            if (dir != NULL) {
+                curDirLenCached = (int)wcslen(dir);
+                free(dir);
+            }
+        }
+        return curDirLenCached;
+    }
+}
+
+/*
+  The "abpathlen" is the size of the buffer needed by _wfullpath. If the
+  "path" is a relative path, it is "the length of the current dir" + "the
+  length of the path", if it's "absolute" already, it's the same as
+  pathlen which is the length of "path".
+ */
+WCHAR* prefixAbpath(const WCHAR* path, int pathlen, int abpathlen) {
+    WCHAR* pathbuf = NULL;
+    WCHAR* abpath = NULL;
+
+    abpathlen += 10;  //padding
+    abpath = (WCHAR*)malloc(abpathlen * sizeof(WCHAR));
+    if (abpath) {
+        /* Collapse instances of "foo\.." and ensure absoluteness before
+           going down to prefixing.
+        */
+        if (_wfullpath(abpath, path, abpathlen)) {
+            pathbuf = getPrefixed(abpath, abpathlen);
+        } else {
+            /* _wfullpath fails if the pathlength exceeds 32k wchar.
+               Instead of doing more fancy things we simply copy the
+               ps into the return buffer, the subsequent win32 API will
+               probably fail with FileNotFoundException, which is expected
+            */
+            pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
+            if (pathbuf != 0) {
+                wcscpy(pathbuf, path);
+            }
+        }
+        free(abpath);
+    }
+    return pathbuf;
+}
+
+/* If this returns NULL then an exception is pending */
+WCHAR*
+pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
+    int pathlen = 0;
+    WCHAR *pathbuf = NULL;
+    int max_path = 248; /* CreateDirectoryW() has the limit of 248 */
+
+    WITH_UNICODE_STRING(env, path, ps) {
+        pathlen = (int)wcslen(ps);
+        if (pathlen != 0) {
+            if (pathlen > 2 &&
+                (ps[0] == L'\\' && ps[1] == L'\\' ||   //UNC
+                 ps[1] == L':' && ps[2] == L'\\'))     //absolute
+            {
+                 if (pathlen > max_path - 1) {
+                     pathbuf = prefixAbpath(ps, pathlen, pathlen);
+                 } else {
+                     pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
+                     if (pathbuf != 0) {
+                         wcscpy(pathbuf, ps);
+                     } else {
+                         JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+                         return NULL;
+                     }
+                 }
+            } else {
+                /* If the path came in as a relative path, need to verify if
+                   its absolute form is bigger than max_path or not, if yes
+                   need to (1)convert it to absolute and (2)prefix. This is
+                   obviously a burden to all relative paths (The current dir/len
+                   for "drive & directory" relative path is cached, so we only
+                   calculate it once but for "drive-relative path we call
+                   _wgetdcwd() and wcslen() everytime), but a hit we have
+                   to take if we want to support relative path beyond max_path.
+                   There is no way to predict how long the absolute path will be
+                   (therefor allocate the sufficient memory block) before calling
+                   _wfullpath(), we have to get the length of "current" dir first.
+                */
+                WCHAR *abpath = NULL;
+                int dirlen = currentDirLength(ps, pathlen);
+                if (dirlen + pathlen + 1 > max_path - 1) {
+                    pathbuf = prefixAbpath(ps, pathlen, dirlen + pathlen);
+                } else {
+                    pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
+                    if (pathbuf != 0) {
+                        wcscpy(pathbuf, ps);
+                    } else {
+                        JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
+                        return NULL;
+                    }
+                }
+            }
+        }
+    } END_UNICODE_STRING(env, ps);
+
+    if (pathlen == 0) {
+        if (throwFNFE == JNI_TRUE) {
+            if (!(*env)->ExceptionCheck(env)) {
+                throwFileNotFoundException(env, path);
+            }
+            return NULL;
+        } else {
+            pathbuf = (WCHAR*)malloc(sizeof(WCHAR));
+            if (pathbuf != NULL) {
+                pathbuf[0] = L'\0';
+            } else {
+                JNU_ThrowOutOfMemoryError(env, 0);
+                return NULL;
+            }
+        }
+    }
+    if (pathbuf == 0) {
+        JNU_ThrowOutOfMemoryError(env, 0);
+        return NULL;
+    }
+    return pathbuf;
+}
+
+FD
+winFileHandleOpen(JNIEnv *env, jstring path, int flags)
+{
+    const DWORD access =
+        (flags & O_WRONLY) ?  GENERIC_WRITE :
+        (flags & O_RDWR)   ? (GENERIC_READ | GENERIC_WRITE) :
+        GENERIC_READ;
+    const DWORD sharing =
+        FILE_SHARE_READ | FILE_SHARE_WRITE;
+    const DWORD disposition =
+        /* Note: O_TRUNC overrides O_CREAT */
+        (flags & O_TRUNC) ? CREATE_ALWAYS :
+        (flags & O_CREAT) ? OPEN_ALWAYS   :
+        OPEN_EXISTING;
+    const DWORD  maybeWriteThrough =
+        (flags & (O_SYNC | O_DSYNC)) ?
+        FILE_FLAG_WRITE_THROUGH :
+        FILE_ATTRIBUTE_NORMAL;
+    const DWORD maybeDeleteOnClose =
+        (flags & O_TEMPORARY) ?
+        FILE_FLAG_DELETE_ON_CLOSE :
+        FILE_ATTRIBUTE_NORMAL;
+    const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;
+    HANDLE h = NULL;
+
+    WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);
+    if (pathbuf == NULL) {
+        /* Exception already pending */
+        return -1;
+    }
+    h = CreateFileW(
+        pathbuf,            /* Wide char path name */
+        access,             /* Read and/or write permission */
+        sharing,            /* File sharing flags */
+        NULL,               /* Security attributes */
+        disposition,        /* creation disposition */
+        flagsAndAttributes, /* flags and attributes */
+        NULL);
+    free(pathbuf);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        throwFileNotFoundException(env, path);
+        return -1;
+    }
+    return (jlong) h;
+}
+
+void
+fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
+{
+    FD h = winFileHandleOpen(env, path, flags);
+    if (h >= 0) {
+        SET_FD(this, h, fid);
+    }
+}
+
+/* These are functions that use a handle fd instead of the
+   old C style int fd as is used in HPI layer */
+
+static int
+handleNonSeekAvailable(FD, long *);
+static int
+handleStdinAvailable(FD, long *);
+
+int
+handleAvailable(FD fd, jlong *pbytes) {
+    HANDLE h = (HANDLE)fd;
+    DWORD type = 0;
+
+    type = GetFileType(h);
+    /* Handle is for keyboard or pipe */
+    if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) {
+        int ret;
+        long lpbytes;
+        HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE);
+        if (stdInHandle == h) {
+            ret = handleStdinAvailable(fd, &lpbytes); /* keyboard */
+        } else {
+            ret = handleNonSeekAvailable(fd, &lpbytes); /* pipe */
+        }
+        (*pbytes) = (jlong)(lpbytes);
+        return ret;
+    }
+    /* Handle is for regular file */
+    if (type == FILE_TYPE_DISK) {
+        jlong current, end;
+
+        LARGE_INTEGER filesize;
+        current = handleLseek(fd, 0, SEEK_CUR);
+        if (current < 0) {
+            return FALSE;
+        }
+        if (GetFileSizeEx(h, &filesize) == 0) {
+            return FALSE;
+        }
+        end = long_to_jlong(filesize.QuadPart);
+        *pbytes = end - current;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static int
+handleNonSeekAvailable(FD fd, long *pbytes) {
+    /* This is used for available on non-seekable devices
+     * (like both named and anonymous pipes, such as pipes
+     *  connected to an exec'd process).
+     * Standard Input is a special case.
+     *
+     */
+    HANDLE han;
+
+    if ((han = (HANDLE) fd) == INVALID_HANDLE_VALUE) {
+        return FALSE;
+    }
+
+    if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) {
+        /* PeekNamedPipe fails when at EOF.  In that case we
+         * simply make *pbytes = 0 which is consistent with the
+         * behavior we get on Solaris when an fd is at EOF.
+         * The only alternative is to raise and Exception,
+         * which isn't really warranted.
+         */
+        if (GetLastError() != ERROR_BROKEN_PIPE) {
+            return FALSE;
+        }
+        *pbytes = 0;
+    }
+    return TRUE;
+}
+
+static int
+handleStdinAvailable(FD fd, long *pbytes) {
+    HANDLE han;
+    DWORD numEventsRead = 0;    /* Number of events read from buffer */
+    DWORD numEvents = 0;        /* Number of events in buffer */
+    DWORD i = 0;                /* Loop index */
+    DWORD curLength = 0;        /* Position marker */
+    DWORD actualLength = 0;     /* Number of bytes readable */
+    BOOL error = FALSE;         /* Error holder */
+    INPUT_RECORD *lpBuffer;     /* Pointer to records of input events */
+    DWORD bufferSize = 0;
+
+    if ((han = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
+        return FALSE;
+    }
+
+    /* Construct an array of input records in the console buffer */
+    error = GetNumberOfConsoleInputEvents(han, &numEvents);
+    if (error == 0) {
+        return handleNonSeekAvailable(fd, pbytes);
+    }
+
+    /* lpBuffer must fit into 64K or else PeekConsoleInput fails */
+    if (numEvents > MAX_INPUT_EVENTS) {
+        numEvents = MAX_INPUT_EVENTS;
+    }
+
+    bufferSize = numEvents * sizeof(INPUT_RECORD);
+    if (bufferSize == 0)
+        bufferSize = 1;
+    lpBuffer = malloc(bufferSize);
+    if (lpBuffer == NULL) {
+        return FALSE;
+    }
+
+    error = PeekConsoleInput(han, lpBuffer, numEvents, &numEventsRead);
+    if (error == 0) {
+        free(lpBuffer);
+        return FALSE;
+    }
+
+    /* Examine input records for the number of bytes available */
+    for(i=0; i<numEvents; i++) {
+        if (lpBuffer[i].EventType == KEY_EVENT) {
+            KEY_EVENT_RECORD *keyRecord = (KEY_EVENT_RECORD *)
+                                          &(lpBuffer[i].Event);
+            if (keyRecord->bKeyDown == TRUE) {
+                CHAR *keyPressed = (CHAR *) &(keyRecord->uChar);
+                curLength++;
+                if (*keyPressed == '\r')
+                    actualLength = curLength;
+            }
+        }
+    }
+    if(lpBuffer != NULL)
+        free(lpBuffer);
+    *pbytes = (long) actualLength;
+    return TRUE;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case.  So we only signal an error if the error is *not* "access
+ * denied".
+ */
+
+int
+handleSync(FD fd) {
+    /*
+     * From the documentation:
+     *
+     *     On Windows NT, the function FlushFileBuffers fails if hFile
+     *     is a handle to console output. That is because console
+     *     output is not buffered. The function returns FALSE, and
+     *     GetLastError returns ERROR_INVALID_HANDLE.
+     *
+     * On the other hand, on Win95, it returns without error.  I cannot
+     * assume that 0, 1, and 2 are console, because if someone closes
+     * System.out and then opens a file, they might get file descriptor
+     * 1.  An error on *that* version of 1 should be reported, whereas
+     * an error on System.out (which was the original 1) should be
+     * ignored.  So I use isatty() to ensure that such an error was due
+     * to this bogosity, and if it was, I ignore the error.
+     */
+
+    HANDLE handle = (HANDLE)fd;
+
+    if (!FlushFileBuffers(handle)) {
+        if (GetLastError() != ERROR_ACCESS_DENIED) {    /* from winerror.h */
+            return -1;
+        }
+    }
+    return 0;
+}
+
+
+int
+handleSetLength(FD fd, jlong length) {
+    HANDLE h = (HANDLE)fd;
+    long high = (long)(length >> 32);
+    DWORD ret;
+
+    if (h == (HANDLE)(-1)) return -1;
+    ret = SetFilePointer(h, (long)(length), &high, FILE_BEGIN);
+    if (ret == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
+        return -1;
+    }
+    if (SetEndOfFile(h) == FALSE) return -1;
+    return 0;
+}
+
+JNIEXPORT
+jint
+handleRead(FD fd, void *buf, jint len)
+{
+    DWORD read = 0;
+    BOOL result = 0;
+    HANDLE h = (HANDLE)fd;
+    if (h == INVALID_HANDLE_VALUE) {
+        return -1;
+    }
+    result = ReadFile(h,          /* File handle to read */
+                      buf,        /* address to put data */
+                      len,        /* number of bytes to read */
+                      &read,      /* number of bytes read */
+                      NULL);      /* no overlapped struct */
+    if (result == 0) {
+        int error = GetLastError();
+        if (error == ERROR_BROKEN_PIPE) {
+            return 0; /* EOF */
+        }
+        return -1;
+    }
+    return (jint)read;
+}
+
+static jint writeInternal(FD fd, const void *buf, jint len, jboolean append)
+{
+    BOOL result = 0;
+    DWORD written = 0;
+    HANDLE h = (HANDLE)fd;
+    if (h != INVALID_HANDLE_VALUE) {
+        OVERLAPPED ov;
+        LPOVERLAPPED lpOv;
+        if (append == JNI_TRUE) {
+            ov.Offset = (DWORD)0xFFFFFFFF;
+            ov.OffsetHigh = (DWORD)0xFFFFFFFF;
+            ov.hEvent = NULL;
+            lpOv = &ov;
+        } else {
+            lpOv = NULL;
+        }
+        result = WriteFile(h,                /* File handle to write */
+                           buf,              /* pointers to the buffers */
+                           len,              /* number of bytes to write */
+                           &written,         /* receives number of bytes written */
+                           lpOv);            /* overlapped struct */
+    }
+    if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
+        return -1;
+    }
+    return (jint)written;
+}
+
+jint handleWrite(FD fd, const void *buf, jint len) {
+    return writeInternal(fd, buf, len, JNI_FALSE);
+}
+
+jint handleAppend(FD fd, const void *buf, jint len) {
+    return writeInternal(fd, buf, len, JNI_TRUE);
+}
+
+jint
+handleClose(JNIEnv *env, jobject this, jfieldID fid)
+{
+    FD fd = GET_FD(this, fid);
+    HANDLE h = (HANDLE)fd;
+
+    if (h == INVALID_HANDLE_VALUE) {
+        return 0;
+    }
+
+    /* Set the fd to -1 before closing it so that the timing window
+     * of other threads using the wrong fd (closed but recycled fd,
+     * that gets re-opened with some other filename) is reduced.
+     * Practically the chance of its occurance is low, however, we are
+     * taking extra precaution over here.
+     */
+    SET_FD(this, -1, fid);
+
+    if (CloseHandle(h) == 0) { /* Returns zero on failure */
+        JNU_ThrowIOExceptionWithLastError(env, "close failed");
+    }
+    return 0;
+}
+
+jlong
+handleLseek(FD fd, jlong offset, jint whence)
+{
+    LARGE_INTEGER pos, distance;
+    DWORD lowPos = 0;
+    long highPos = 0;
+    DWORD op = FILE_CURRENT;
+    HANDLE h = (HANDLE)fd;
+
+    if (whence == SEEK_END) {
+        op = FILE_END;
+    }
+    if (whence == SEEK_CUR) {
+        op = FILE_CURRENT;
+    }
+    if (whence == SEEK_SET) {
+        op = FILE_BEGIN;
+    }
+
+    distance.QuadPart = offset;
+    if (SetFilePointerEx(h, distance, &pos, op) == 0) {
+        return -1;
+    }
+    return long_to_jlong(pos.QuadPart);
+}
+
+size_t
+getLastErrorString(char *utf8_jvmErrorMsg, size_t cbErrorMsg)
+{
+    size_t n = 0;
+    if (cbErrorMsg > 0) {
+        BOOLEAN noError = FALSE;
+        WCHAR *utf16_osErrorMsg = (WCHAR *)malloc(cbErrorMsg*sizeof(WCHAR));
+        if (utf16_osErrorMsg == NULL) {
+            // OOM accident
+            strncpy(utf8_jvmErrorMsg, "Out of memory", cbErrorMsg);
+            // truncate if too long
+            utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
+            n = strlen(utf8_jvmErrorMsg);
+        } else {
+            DWORD errval = GetLastError();
+            if (errval != 0) {
+                // WIN32 error
+                n = (size_t)FormatMessageW(
+                    FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+                    NULL,
+                    errval,
+                    0,
+                    utf16_osErrorMsg,
+                    (DWORD)cbErrorMsg,
+                    NULL);
+                if (n > 3) {
+                    // Drop final '.', CR, LF
+                    if (utf16_osErrorMsg[n - 1] == L'\n') --n;
+                    if (utf16_osErrorMsg[n - 1] == L'\r') --n;
+                    if (utf16_osErrorMsg[n - 1] == L'.') --n;
+                    utf16_osErrorMsg[n] = L'\0';
+                }
+            } else if (errno != 0) {
+                // C runtime error that has no corresponding WIN32 error code
+                const WCHAR *rtError = _wcserror(errno);
+                if (rtError != NULL) {
+                    wcsncpy(utf16_osErrorMsg, rtError, cbErrorMsg);
+                    // truncate if too long
+                    utf16_osErrorMsg[cbErrorMsg - 1] = L'\0';
+                    n = wcslen(utf16_osErrorMsg);
+                }
+            } else
+                noError = TRUE; //OS has no error to report
+
+            if (!noError) {
+                if (n > 0) {
+                    n = WideCharToMultiByte(
+                        CP_UTF8,
+                        0,
+                        utf16_osErrorMsg,
+                        n,
+                        utf8_jvmErrorMsg,
+                        cbErrorMsg,
+                        NULL,
+                        NULL);
+
+                    // no way to die
+                    if (n > 0)
+                        utf8_jvmErrorMsg[min(cbErrorMsg - 1, n)] = '\0';
+                }
+
+                if (n <= 0) {
+                    strncpy(utf8_jvmErrorMsg, "Secondary error while OS message extraction", cbErrorMsg);
+                    // truncate if too long
+                    utf8_jvmErrorMsg[cbErrorMsg - 1] = '\0';
+                    n = strlen(utf8_jvmErrorMsg);
+                }
+            }
+            free(utf16_osErrorMsg);
+        }
+    }
+    return n;
+}