jdk/src/windows/native/java/io/io_util_md.c
changeset 2 90ce3da70b43
child 45 307f564057c1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/native/java/io/io_util_md.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2001-2007 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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 <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>
+
+extern jboolean onNT = JNI_FALSE;
+
+static int MAX_INPUT_EVENTS = 2000;
+
+void
+initializeWindowsVersion() {
+    OSVERSIONINFO ver;
+    ver.dwOSVersionInfoSize = sizeof(ver);
+    GetVersionEx(&ver);
+    if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+        onNT = JNI_TRUE;
+    } else {
+        onNT = JNI_FALSE;
+    }
+}
+
+/* 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);
+}
+
+/* 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 = _wgetdcwd(di, NULL, MAX_PATH);
+        if (dir != NULL){
+            dirlen = 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 = wcslen(dir);
+                free(dir);
+            }
+        }
+        return curDirLenCached;
+    }
+}
+
+/* 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;   /* Since CreateDirectoryW() has the limit of
+                             248 instead of the normal MAX_PATH, we
+                             use 248 as the max_path to satisfy both
+                           */
+    WITH_UNICODE_STRING(env, path, ps) {
+        pathlen = 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 = getPrefixed(ps, pathlen);
+                 } else {
+                     pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
+                     if (pathbuf != 0) {
+                         wcscpy(pathbuf, ps);
+                     }
+                 }
+            } 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 "dirve & 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) {
+                    int abpathlen = dirlen + pathlen + 10;
+                    abpath = (WCHAR*)malloc(abpathlen * sizeof(WCHAR));
+                    if (abpath) {
+                        if (_wfullpath(abpath, ps, 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, ps);
+                            }
+                        }
+                        free(abpath);
+                    }
+                } else {
+                    pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
+                    if (pathbuf != 0) {
+                        wcscpy(pathbuf, ps);
+                    }
+                }
+            }
+        }
+    } END_UNICODE_STRING(env, ps);
+
+    if (pathlen == 0) {
+        if (throwFNFE == JNI_TRUE) {
+            throwFileNotFoundException(env, path);
+            return NULL;
+        } else {
+            pathbuf = (WCHAR*)malloc(sizeof(WCHAR));
+            pathbuf[0] = L'\0';
+        }
+    }
+    if (pathbuf == 0) {
+        JNU_ThrowOutOfMemoryError(env, 0);
+        return NULL;
+    }
+    return pathbuf;
+}
+
+jlong
+winFileHandleOpen(JNIEnv *env, jstring path, int flags)
+{
+    const DWORD access =
+        (flags & O_RDWR)   ? (GENERIC_WRITE | GENERIC_READ) :
+        (flags & O_WRONLY) ?  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;
+
+    if (onNT) {
+        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);
+    } else {
+        WITH_PLATFORM_STRING(env, path, _ps) {
+            h = CreateFile(_ps, access, sharing, NULL, disposition,
+                           flagsAndAttributes, NULL);
+        } END_PLATFORM_STRING(env, _ps);
+    }
+    if (h == INVALID_HANDLE_VALUE) {
+        int error = GetLastError();
+        if (error == ERROR_TOO_MANY_OPEN_FILES) {
+            JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException",
+                            "Too many open files");
+            return -1;
+        }
+        throwFileNotFoundException(env, path);
+        return -1;
+    }
+    return (jlong) h;
+}
+
+void
+fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
+{
+    jlong 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(jlong, long *);
+static int
+handleStdinAvailable(jlong, long *);
+
+int
+handleAvailable(jlong fd, jlong *pbytes) {
+    jlong current, end;
+    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) {
+        long highPos = 0;
+        DWORD sizeLow = 0;
+        DWORD sizeHigh = 0;
+        DWORD lowPos = SetFilePointer(h, 0, &highPos, FILE_CURRENT);
+        if (lowPos == ((DWORD)-1)) {
+            return FALSE;
+        }
+        current = (((jlong)highPos) << 32) | lowPos;
+        end = GetFileSize(h, &sizeHigh);
+        if (sizeLow == ((DWORD)-1)) {
+            return FALSE;
+        }
+        *pbytes = end - current;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static int
+handleNonSeekAvailable(jlong 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(jlong 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".
+ */
+
+JNIEXPORT int
+handleSync(jlong 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(jlong 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;
+}
+
+int
+handleFileSizeFD(jlong fd, jlong *size)
+{
+    DWORD sizeLow = 0;
+    DWORD sizeHigh = 0;
+    HANDLE h = (HANDLE)fd;
+    if (h == INVALID_HANDLE_VALUE) {
+        return -1;
+    }
+    sizeLow = GetFileSize(h, &sizeHigh);
+    if (sizeLow == ((DWORD)-1)) {
+        if (GetLastError() != ERROR_SUCCESS) {
+            return -1;
+        }
+    }
+    return (((jlong)sizeHigh) << 32) | sizeLow;
+}
+
+JNIEXPORT
+size_t
+handleRead(jlong 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 read;
+}
+
+JNIEXPORT
+size_t
+handleWrite(jlong fd, const void *buf, jint len)
+{
+    BOOL result = 0;
+    DWORD written = 0;
+    HANDLE h = (HANDLE)fd;
+    if (h != INVALID_HANDLE_VALUE) {
+        result = WriteFile(h,           /* File handle to write */
+                      buf,              /* pointers to the buffers */
+                      len,              /* number of bytes to write */
+                      &written,         /* receives number of bytes written */
+                      NULL);            /* no overlapped struct */
+    }
+    if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
+        return -1;
+    }
+    return written;
+}
+
+jint
+handleClose(JNIEnv *env, jobject this, jfieldID fid)
+{
+    FD fd = GET_FD(this, fid);
+    HANDLE h = (HANDLE)fd;
+
+    if (fd == 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 */
+        SET_FD(this, fd, fid); // restore fd
+        JNU_ThrowIOExceptionWithLastError(env, "close failed");
+    }
+    return 0;
+}
+
+jlong
+handleLseek(jlong fd, jlong offset, jint whence)
+{
+    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;
+    }
+
+    lowPos = (DWORD)offset;
+    highPos = (long)(offset >> 32);
+    lowPos = SetFilePointer(h, lowPos, &highPos, op);
+    if (lowPos == ((DWORD)-1)) {
+        if (GetLastError() != ERROR_SUCCESS) {
+            return -1;
+        }
+    }
+    return (((jlong)highPos) << 32) | lowPos;
+}