--- /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;
+}