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