Path: blob/master/src/java.base/windows/native/libjava/io_util_md.c
41119 views
/*1* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "jni.h"26#include "jni_util.h"27#include "jvm.h"28#include "io_util.h"29#include "io_util_md.h"30#include <stdio.h>31#include <windows.h>3233#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>424344static DWORD MAX_INPUT_EVENTS = 2000;4546/* If this returns NULL then an exception is pending */47WCHAR*48fileToNTPath(JNIEnv *env, jobject file, jfieldID id) {49jstring path = NULL;50if (file != NULL) {51path = (*env)->GetObjectField(env, file, id);52}53return pathToNTPath(env, path, JNI_FALSE);54}5556/* Returns the working directory for the given drive, or NULL */57WCHAR*58currentDir(int di) {59UINT dt;60WCHAR root[4];61// verify drive is valid as _wgetdcwd in the VC++ 2010 runtime62// library does not handle invalid drives.63root[0] = L'A' + (WCHAR)(di - 1);64root[1] = L':';65root[2] = L'\\';66root[3] = L'\0';67dt = GetDriveTypeW(root);68if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) {69return NULL;70} else {71return _wgetdcwd(di, NULL, MAX_PATH);72}73}7475/* We cache the length of current working dir here to avoid76calling _wgetcwd() every time we need to resolve a relative77path. This piece of code needs to be revisited if chdir78makes its way into java runtime.79*/8081int82currentDirLength(const WCHAR* ps, int pathlen) {83WCHAR *dir;84if (pathlen > 2 && ps[1] == L':' && ps[2] != L'\\') {85//drive-relative86WCHAR d = ps[0];87int dirlen = 0;88int di = 0;89if ((d >= L'a') && (d <= L'z')) di = d - L'a' + 1;90else if ((d >= L'A') && (d <= L'Z')) di = d - L'A' + 1;91else return 0; /* invalid drive name. */92dir = currentDir(di);93if (dir != NULL){94dirlen = (int)wcslen(dir);95free(dir);96}97return dirlen;98} else {99static int curDirLenCached = -1;100//relative to both drive and directory101if (curDirLenCached == -1) {102int dirlen = -1;103dir = _wgetcwd(NULL, MAX_PATH);104if (dir != NULL) {105curDirLenCached = (int)wcslen(dir);106free(dir);107}108}109return curDirLenCached;110}111}112113/*114The "abpathlen" is the size of the buffer needed by _wfullpath. If the115"path" is a relative path, it is "the length of the current dir" + "the116length of the path", if it's "absolute" already, it's the same as117pathlen which is the length of "path".118*/119WCHAR* prefixAbpath(const WCHAR* path, int pathlen, int abpathlen) {120WCHAR* pathbuf = NULL;121WCHAR* abpath = NULL;122123abpathlen += 10; //padding124abpath = (WCHAR*)malloc(abpathlen * sizeof(WCHAR));125if (abpath) {126/* Collapse instances of "foo\.." and ensure absoluteness before127going down to prefixing.128*/129if (_wfullpath(abpath, path, abpathlen)) {130pathbuf = getPrefixed(abpath, abpathlen);131} else {132/* _wfullpath fails if the pathlength exceeds 32k wchar.133Instead of doing more fancy things we simply copy the134ps into the return buffer, the subsequent win32 API will135probably fail with FileNotFoundException, which is expected136*/137pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));138if (pathbuf != 0) {139wcscpy(pathbuf, path);140}141}142free(abpath);143}144return pathbuf;145}146147/* If this returns NULL then an exception is pending */148WCHAR*149pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {150int pathlen = 0;151WCHAR *pathbuf = NULL;152int max_path = 248; /* CreateDirectoryW() has the limit of 248 */153154WITH_UNICODE_STRING(env, path, ps) {155pathlen = (int)wcslen(ps);156if (pathlen != 0) {157if (pathlen > 2 &&158(ps[0] == L'\\' && ps[1] == L'\\' || //UNC159ps[1] == L':' && ps[2] == L'\\')) //absolute160{161if (pathlen > max_path - 1) {162pathbuf = prefixAbpath(ps, pathlen, pathlen);163} else {164pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));165if (pathbuf != 0) {166wcscpy(pathbuf, ps);167}168}169} else {170/* If the path came in as a relative path, need to verify if171its absolute form is bigger than max_path or not, if yes172need to (1)convert it to absolute and (2)prefix. This is173obviously a burden to all relative paths (The current dir/len174for "drive & directory" relative path is cached, so we only175calculate it once but for "drive-relative path we call176_wgetdcwd() and wcslen() everytime), but a hit we have177to take if we want to support relative path beyond max_path.178There is no way to predict how long the absolute path will be179(therefor allocate the sufficient memory block) before calling180_wfullpath(), we have to get the length of "current" dir first.181*/182WCHAR *abpath = NULL;183int dirlen = currentDirLength(ps, pathlen);184if (dirlen + pathlen + 1 > max_path - 1) {185pathbuf = prefixAbpath(ps, pathlen, dirlen + pathlen);186} else {187pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));188if (pathbuf != 0) {189wcscpy(pathbuf, ps);190}191}192}193}194} END_UNICODE_STRING(env, ps);195196if (pathlen == 0) {197if (throwFNFE == JNI_TRUE) {198if (!(*env)->ExceptionCheck(env)) {199throwFileNotFoundException(env, path);200}201return NULL;202} else {203pathbuf = (WCHAR*)malloc(sizeof(WCHAR));204if (pathbuf != NULL) {205pathbuf[0] = L'\0';206}207}208}209if (pathbuf == 0) {210JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");211}212return pathbuf;213}214215FD winFileHandleOpen(JNIEnv *env, jstring path, int flags)216{217const DWORD access =218(flags & O_WRONLY) ? GENERIC_WRITE :219(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :220GENERIC_READ;221const DWORD sharing =222FILE_SHARE_READ | FILE_SHARE_WRITE;223const DWORD disposition =224/* Note: O_TRUNC overrides O_CREAT */225(flags & O_TRUNC) ? CREATE_ALWAYS :226(flags & O_CREAT) ? OPEN_ALWAYS :227OPEN_EXISTING;228const DWORD maybeWriteThrough =229(flags & (O_SYNC | O_DSYNC)) ?230FILE_FLAG_WRITE_THROUGH :231FILE_ATTRIBUTE_NORMAL;232const DWORD maybeDeleteOnClose =233(flags & O_TEMPORARY) ?234FILE_FLAG_DELETE_ON_CLOSE :235FILE_ATTRIBUTE_NORMAL;236const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;237HANDLE h = NULL;238239WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);240if (pathbuf == NULL) {241/* Exception already pending */242return -1;243}244h = CreateFileW(245pathbuf, /* Wide char path name */246access, /* Read and/or write permission */247sharing, /* File sharing flags */248NULL, /* Security attributes */249disposition, /* creation disposition */250flagsAndAttributes, /* flags and attributes */251NULL);252free(pathbuf);253254if (h == INVALID_HANDLE_VALUE) {255throwFileNotFoundException(env, path);256return -1;257}258return (jlong) h;259}260261FD getFD(JNIEnv *env, jobject obj, jfieldID fid) {262jobject fdo = (*env)->GetObjectField(env, obj, fid);263if (fdo == NULL) {264return -1;265}266return (*env)->GetLongField(env, fdo, IO_handle_fdID);267}268269void270fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)271{272FD h = winFileHandleOpen(env, path, flags);273if (h >= 0) {274jobject fdobj;275jboolean append;276fdobj = (*env)->GetObjectField(env, this, fid);277if (fdobj != NULL) {278// Set FD279(*env)->SetLongField(env, fdobj, IO_handle_fdID, h);280append = (flags & O_APPEND) == 0 ? JNI_FALSE : JNI_TRUE;281(*env)->SetBooleanField(env, fdobj, IO_append_fdID, append);282}283}284}285286/* These are functions that use a handle fd instead of the287old C style int fd as is used in HPI layer */288289static int290handleNonSeekAvailable(FD, long *);291static int292handleStdinAvailable(FD, long *);293294int295handleAvailable(FD fd, jlong *pbytes) {296HANDLE h = (HANDLE)fd;297DWORD type = 0;298299type = GetFileType(h);300/* Handle is for keyboard or pipe */301if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) {302int ret;303long lpbytes;304HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE);305if (stdInHandle == h) {306ret = handleStdinAvailable(fd, &lpbytes); /* keyboard */307} else {308ret = handleNonSeekAvailable(fd, &lpbytes); /* pipe */309}310(*pbytes) = (jlong)(lpbytes);311return ret;312}313/* Handle is for regular file */314if (type == FILE_TYPE_DISK) {315jlong current, end;316317LARGE_INTEGER filesize;318current = handleLseek(fd, 0, SEEK_CUR);319if (current < 0) {320return FALSE;321}322if (GetFileSizeEx(h, &filesize) == 0) {323return FALSE;324}325end = long_to_jlong(filesize.QuadPart);326*pbytes = end - current;327return TRUE;328}329return FALSE;330}331332static int333handleNonSeekAvailable(FD fd, long *pbytes) {334/* This is used for available on non-seekable devices335* (like both named and anonymous pipes, such as pipes336* connected to an exec'd process).337* Standard Input is a special case.338*339*/340HANDLE han;341342if ((han = (HANDLE) fd) == INVALID_HANDLE_VALUE) {343return FALSE;344}345346if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) {347/* PeekNamedPipe fails when at EOF. In that case we348* simply make *pbytes = 0 which is consistent with the349* behavior we get on Solaris when an fd is at EOF.350* The only alternative is to raise and Exception,351* which isn't really warranted.352*/353if (GetLastError() != ERROR_BROKEN_PIPE) {354return FALSE;355}356*pbytes = 0;357}358return TRUE;359}360361static int362handleStdinAvailable(FD fd, long *pbytes) {363HANDLE han;364DWORD numEventsRead = 0; /* Number of events read from buffer */365DWORD numEvents = 0; /* Number of events in buffer */366DWORD i = 0; /* Loop index */367DWORD curLength = 0; /* Position marker */368DWORD actualLength = 0; /* Number of bytes readable */369BOOL error = FALSE; /* Error holder */370INPUT_RECORD *lpBuffer; /* Pointer to records of input events */371DWORD bufferSize = 0;372373if ((han = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {374return FALSE;375}376377/* Construct an array of input records in the console buffer */378error = GetNumberOfConsoleInputEvents(han, &numEvents);379if (error == 0) {380return handleNonSeekAvailable(fd, pbytes);381}382383/* lpBuffer must fit into 64K or else PeekConsoleInput fails */384if (numEvents > MAX_INPUT_EVENTS) {385numEvents = MAX_INPUT_EVENTS;386}387388bufferSize = numEvents * sizeof(INPUT_RECORD);389if (bufferSize == 0)390bufferSize = 1;391lpBuffer = malloc(bufferSize);392if (lpBuffer == NULL) {393return FALSE;394}395396error = PeekConsoleInput(han, lpBuffer, numEvents, &numEventsRead);397if (error == 0) {398free(lpBuffer);399return FALSE;400}401402/* Examine input records for the number of bytes available */403for(i=0; i<numEvents; i++) {404if (lpBuffer[i].EventType == KEY_EVENT) {405KEY_EVENT_RECORD *keyRecord = (KEY_EVENT_RECORD *)406&(lpBuffer[i].Event);407if (keyRecord->bKeyDown == TRUE) {408CHAR *keyPressed = (CHAR *) &(keyRecord->uChar);409curLength++;410if (*keyPressed == '\r')411actualLength = curLength;412}413}414}415if(lpBuffer != NULL)416free(lpBuffer);417*pbytes = (long) actualLength;418return TRUE;419}420421/*422* This is documented to succeed on read-only files, but Win32's423* FlushFileBuffers functions fails with "access denied" in such a424* case. So we only signal an error if the error is *not* "access425* denied".426*/427428int429handleSync(FD fd) {430/*431* From the documentation:432*433* On Windows NT, the function FlushFileBuffers fails if hFile434* is a handle to console output. That is because console435* output is not buffered. The function returns FALSE, and436* GetLastError returns ERROR_INVALID_HANDLE.437*438* On the other hand, on Win95, it returns without error. I cannot439* assume that 0, 1, and 2 are console, because if someone closes440* System.out and then opens a file, they might get file descriptor441* 1. An error on *that* version of 1 should be reported, whereas442* an error on System.out (which was the original 1) should be443* ignored. So I use isatty() to ensure that such an error was due444* to this bogosity, and if it was, I ignore the error.445*/446447HANDLE handle = (HANDLE)fd;448449if (!FlushFileBuffers(handle)) {450if (GetLastError() != ERROR_ACCESS_DENIED) { /* from winerror.h */451return -1;452}453}454return 0;455}456457jint458handleSetLength(FD fd, jlong length) {459HANDLE h = (HANDLE)fd;460FILE_END_OF_FILE_INFO eofInfo;461462eofInfo.EndOfFile.QuadPart = length;463464if (h == INVALID_HANDLE_VALUE) {465return -1;466}467if (!SetFileInformationByHandle(h, FileEndOfFileInfo, &eofInfo,468sizeof(FILE_END_OF_FILE_INFO))) {469return -1;470}471return 0;472}473474JNIEXPORT475jint476handleRead(FD fd, void *buf, jint len)477{478DWORD read = 0;479BOOL result = 0;480HANDLE h = (HANDLE)fd;481if (h == INVALID_HANDLE_VALUE) {482return -1;483}484result = ReadFile(h, /* File handle to read */485buf, /* address to put data */486len, /* number of bytes to read */487&read, /* number of bytes read */488NULL); /* no overlapped struct */489if (result == 0) {490int error = GetLastError();491if (error == ERROR_BROKEN_PIPE) {492return 0; /* EOF */493}494return -1;495}496return (jint)read;497}498499static jint writeInternal(FD fd, const void *buf, jint len, jboolean append)500{501BOOL result = 0;502DWORD written = 0;503HANDLE h = (HANDLE)fd;504if (h != INVALID_HANDLE_VALUE) {505OVERLAPPED ov;506LPOVERLAPPED lpOv;507if (append == JNI_TRUE) {508ov.Offset = (DWORD)0xFFFFFFFF;509ov.OffsetHigh = (DWORD)0xFFFFFFFF;510ov.hEvent = NULL;511lpOv = &ov;512} else {513lpOv = NULL;514}515result = WriteFile(h, /* File handle to write */516buf, /* pointers to the buffers */517len, /* number of bytes to write */518&written, /* receives number of bytes written */519lpOv); /* overlapped struct */520}521if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {522return -1;523}524return (jint)written;525}526527jint handleWrite(FD fd, const void *buf, jint len) {528return writeInternal(fd, buf, len, JNI_FALSE);529}530531jint handleAppend(FD fd, const void *buf, jint len) {532return writeInternal(fd, buf, len, JNI_TRUE);533}534535// Function to close the fd held by this FileDescriptor and set fd to -1.536void537fileDescriptorClose(JNIEnv *env, jobject this)538{539FD fd = (*env)->GetLongField(env, this, IO_handle_fdID);540HANDLE h = (HANDLE)fd;541if ((*env)->ExceptionOccurred(env)) {542return;543}544545if (h == INVALID_HANDLE_VALUE) {546return;547}548549/* Set the fd to -1 before closing it so that the timing window550* of other threads using the wrong fd (closed but recycled fd,551* that gets re-opened with some other filename) is reduced.552* Practically the chance of its occurance is low, however, we are553* taking extra precaution over here.554*/555(*env)->SetLongField(env, this, IO_handle_fdID, -1);556if ((*env)->ExceptionOccurred(env)) {557return;558}559560if (CloseHandle(h) == 0) { /* Returns zero on failure */561JNU_ThrowIOExceptionWithLastError(env, "close failed");562}563}564565JNIEXPORT jlong JNICALL566handleLseek(FD fd, jlong offset, jint whence)567{568LARGE_INTEGER pos, distance;569DWORD op = FILE_CURRENT;570HANDLE h = (HANDLE)fd;571572if (whence == SEEK_END) {573op = FILE_END;574}575if (whence == SEEK_CUR) {576op = FILE_CURRENT;577}578if (whence == SEEK_SET) {579op = FILE_BEGIN;580}581582distance.QuadPart = offset;583if (SetFilePointerEx(h, distance, &pos, op) == 0) {584return -1;585}586return long_to_jlong(pos.QuadPart);587}588589jlong590handleGetLength(FD fd) {591HANDLE h = (HANDLE) fd;592LARGE_INTEGER length;593if (GetFileSizeEx(h, &length) != 0) {594return long_to_jlong(length.QuadPart);595} else {596return -1;597}598}599600601