jdk
1/*
2* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4*
5* This code is free software; you can redistribute it and/or modify it
6* under the terms of the GNU General Public License version 2 only, as
7* published by the Free Software Foundation. Oracle designates this
8* particular file as subject to the "Classpath" exception as provided
9* by Oracle in the LICENSE file that accompanied this code.
10*
11* This code is distributed in the hope that it will be useful, but WITHOUT
12* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14* version 2 for more details (a copy is included in the LICENSE file that
15* accompanied this code).
16*
17* You should have received a copy of the GNU General Public License version
18* 2 along with this work; if not, write to the Free Software Foundation,
19* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20*
21* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22* or visit www.oracle.com if you need additional information or have any
23* questions.
24*/
25
26#include "jni.h"27#include "jni_util.h"28#include "jvm.h"29#include "io_util.h"30#include "io_util_md.h"31#include <stdio.h>32#include <windows.h>33
34#include <wchar.h>35#include <io.h>36#include <fcntl.h>37#include <errno.h>38#include <string.h>39#include <sys/types.h>40#include <sys/stat.h>41#include <limits.h>42#include <wincon.h>43
44
45static DWORD MAX_INPUT_EVENTS = 2000;46
47/* If this returns NULL then an exception is pending */
48WCHAR*49fileToNTPath(JNIEnv *env, jobject file, jfieldID id) {50jstring path = NULL;51if (file != NULL) {52path = (*env)->GetObjectField(env, file, id);53}54return pathToNTPath(env, path, JNI_FALSE);55}
56
57/* Returns the working directory for the given drive, or NULL */
58WCHAR*59currentDir(int di) {60UINT dt;61WCHAR root[4];62// verify drive is valid as _wgetdcwd in the VC++ 2010 runtime63// library does not handle invalid drives.64root[0] = L'A' + (WCHAR)(di - 1);65root[1] = L':';66root[2] = L'\\';67root[3] = L'\0';68dt = GetDriveTypeW(root);69if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) {70return NULL;71} else {72return _wgetdcwd(di, NULL, MAX_PATH);73}74}
75
76/* We cache the length of current working dir here to avoid
77calling _wgetcwd() every time we need to resolve a relative
78path. This piece of code needs to be revisited if chdir
79makes its way into java runtime.
80*/
81
82int
83currentDirLength(const WCHAR* ps, int pathlen) {84WCHAR *dir;85if (pathlen > 2 && ps[1] == L':' && ps[2] != L'\\') {86//drive-relative87WCHAR d = ps[0];88int dirlen = 0;89int di = 0;90if ((d >= L'a') && (d <= L'z')) di = d - L'a' + 1;91else if ((d >= L'A') && (d <= L'Z')) di = d - L'A' + 1;92else return 0; /* invalid drive name. */93dir = currentDir(di);94if (dir != NULL){95dirlen = (int)wcslen(dir);96free(dir);97}98return dirlen;99} else {100static int curDirLenCached = -1;101//relative to both drive and directory102if (curDirLenCached == -1) {103dir = _wgetcwd(NULL, MAX_PATH);104if (dir != NULL) {105curDirLenCached = (int)wcslen(dir);106free(dir);107}108}109return curDirLenCached;110}111}
112
113/*
114The "abpathlen" is the size of the buffer needed by _wfullpath. If the
115"path" is a relative path, it is "the length of the current dir" + "the
116length of the path", if it's "absolute" already, it's the same as
117pathlen which is the length of "path".
118*/
119WCHAR* prefixAbpath(const WCHAR* path, int pathlen, int abpathlen) {120WCHAR* pathbuf = NULL;121WCHAR* abpath = NULL;122
123abpathlen += 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 the
134ps into the return buffer, the subsequent win32 API will
135probably fail with FileNotFoundException, which is expected
136*/
137pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));138if (pathbuf != 0) {139wcscpy(pathbuf, path);140}141}142free(abpath);143}144return pathbuf;145}
146
147/* 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 */153
154WITH_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 yes
172need to (1)convert it to absolute and (2)prefix. This is
173obviously a burden to all relative paths (The current dir/len
174for "drive & directory" relative path is cached, so we only
175calculate it once but for "drive-relative path we call
176_wgetdcwd() and wcslen() every time), but a hit we have
177to take if we want to support relative path beyond max_path.
178There is no way to predict how long the absolute path will be
179(therefore allocate the sufficient memory block) before calling
180_wfullpath(), we have to get the length of "current" dir first.
181*/
182int dirlen = currentDirLength(ps, pathlen);183if (dirlen + pathlen + 1 > max_path - 1) {184pathbuf = prefixAbpath(ps, pathlen, dirlen + pathlen);185} else {186pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));187if (pathbuf != 0) {188wcscpy(pathbuf, ps);189}190}191}192}193} END_UNICODE_STRING(env, ps);194
195if (pathlen == 0) {196if (throwFNFE == JNI_TRUE) {197if (!(*env)->ExceptionCheck(env)) {198throwFileNotFoundException(env, path);199}200return NULL;201} else {202pathbuf = (WCHAR*)malloc(sizeof(WCHAR));203if (pathbuf != NULL) {204pathbuf[0] = L'\0';205}206}207}208if (pathbuf == 0) {209JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");210}211return pathbuf;212}
213
214FD winFileHandleOpen(JNIEnv *env, jstring path, int flags)215{
216const DWORD access =217(flags & O_WRONLY) ? GENERIC_WRITE :218(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :219GENERIC_READ;220const DWORD sharing =221FILE_SHARE_READ | FILE_SHARE_WRITE;222const DWORD disposition =223/* Note: O_TRUNC overrides O_CREAT */224(flags & O_TRUNC) ? CREATE_ALWAYS :225(flags & O_CREAT) ? OPEN_ALWAYS :226OPEN_EXISTING;227const DWORD maybeWriteThrough =228(flags & (O_SYNC | O_DSYNC)) ?229FILE_FLAG_WRITE_THROUGH :230FILE_ATTRIBUTE_NORMAL;231const DWORD maybeDeleteOnClose =232(flags & O_TEMPORARY) ?233FILE_FLAG_DELETE_ON_CLOSE :234FILE_ATTRIBUTE_NORMAL;235const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;236HANDLE h = NULL;237
238WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);239if (pathbuf == NULL) {240/* Exception already pending */241return -1;242}243h = CreateFileW(244pathbuf, /* Wide char path name */245access, /* Read and/or write permission */246sharing, /* File sharing flags */247NULL, /* Security attributes */248disposition, /* creation disposition */249flagsAndAttributes, /* flags and attributes */250NULL);251free(pathbuf);252
253if (h == INVALID_HANDLE_VALUE) {254throwFileNotFoundException(env, path);255return -1;256}257return (jlong) h;258}
259
260FD getFD(JNIEnv *env, jobject obj, jfieldID fid) {261jobject fdo = (*env)->GetObjectField(env, obj, fid);262if (fdo == NULL) {263return -1;264}265return (*env)->GetLongField(env, fdo, IO_handle_fdID);266}
267
268void
269fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)270{
271FD h = winFileHandleOpen(env, path, flags);272if (h >= 0) {273jobject fdobj;274jboolean append;275fdobj = (*env)->GetObjectField(env, this, fid);276if (fdobj != NULL) {277// Set FD278(*env)->SetLongField(env, fdobj, IO_handle_fdID, h);279append = (flags & O_APPEND) == 0 ? JNI_FALSE : JNI_TRUE;280(*env)->SetBooleanField(env, fdobj, IO_append_fdID, append);281}282}283}
284
285/* These are functions that use a handle fd instead of the
286old C style int fd as is used in HPI layer */
287
288static int289handleNonSeekAvailable(FD, long *);290static int291handleStdinAvailable(FD, long *);292
293int
294handleAvailable(FD fd, jlong *pbytes) {295HANDLE h = (HANDLE)fd;296DWORD type = 0;297
298type = GetFileType(h);299/* Handle is for keyboard or pipe */300if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE) {301int ret;302long lpbytes;303HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE);304if (stdInHandle == h) {305ret = handleStdinAvailable(fd, &lpbytes); /* keyboard */306} else {307ret = handleNonSeekAvailable(fd, &lpbytes); /* pipe */308}309(*pbytes) = (jlong)(lpbytes);310return ret;311}312/* Handle is for regular file */313if (type == FILE_TYPE_DISK) {314jlong current, end;315
316LARGE_INTEGER filesize;317current = handleLseek(fd, 0, SEEK_CUR);318if (current < 0) {319return FALSE;320}321if (GetFileSizeEx(h, &filesize) == 0) {322return FALSE;323}324end = long_to_jlong(filesize.QuadPart);325*pbytes = end - current;326return TRUE;327}328return FALSE;329}
330
331static int332handleNonSeekAvailable(FD fd, long *pbytes) {333/* This is used for available on non-seekable devices334* (like both named and anonymous pipes, such as pipes
335* connected to an exec'd process).
336* Standard Input is a special case.
337*
338*/
339HANDLE han;340
341if ((han = (HANDLE) fd) == INVALID_HANDLE_VALUE) {342return FALSE;343}344
345if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) {346/* PeekNamedPipe fails when at EOF. In that case we347* simply make *pbytes = 0 which is consistent with the
348* behavior we get on Solaris when an fd is at EOF.
349* The only alternative is to raise and Exception,
350* which isn't really warranted.
351*/
352if (GetLastError() != ERROR_BROKEN_PIPE) {353return FALSE;354}355*pbytes = 0;356}357return TRUE;358}
359
360static int361handleStdinAvailable(FD fd, long *pbytes) {362HANDLE han;363DWORD numEventsRead = 0; /* Number of events read from buffer */364DWORD numEvents = 0; /* Number of events in buffer */365DWORD i = 0; /* Loop index */366DWORD curLength = 0; /* Position marker */367DWORD actualLength = 0; /* Number of bytes readable */368BOOL error = FALSE; /* Error holder */369INPUT_RECORD *lpBuffer; /* Pointer to records of input events */370DWORD bufferSize = 0;371
372if ((han = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {373return FALSE;374}375
376/* Construct an array of input records in the console buffer */377error = GetNumberOfConsoleInputEvents(han, &numEvents);378if (error == 0) {379return handleNonSeekAvailable(fd, pbytes);380}381
382/* lpBuffer must fit into 64K or else PeekConsoleInput fails */383if (numEvents > MAX_INPUT_EVENTS) {384numEvents = MAX_INPUT_EVENTS;385}386
387bufferSize = numEvents * sizeof(INPUT_RECORD);388if (bufferSize == 0)389bufferSize = 1;390lpBuffer = malloc(bufferSize);391if (lpBuffer == NULL) {392return FALSE;393}394
395error = PeekConsoleInput(han, lpBuffer, numEvents, &numEventsRead);396if (error == 0) {397free(lpBuffer);398return FALSE;399}400
401/* Examine input records for the number of bytes available */402for(i=0; i<numEvents; i++) {403if (lpBuffer[i].EventType == KEY_EVENT) {404KEY_EVENT_RECORD *keyRecord = (KEY_EVENT_RECORD *)405&(lpBuffer[i].Event);406if (keyRecord->bKeyDown == TRUE) {407CHAR *keyPressed = (CHAR *) &(keyRecord->uChar);408curLength++;409if (*keyPressed == '\r')410actualLength = curLength;411}412}413}414if(lpBuffer != NULL)415free(lpBuffer);416*pbytes = (long) actualLength;417return TRUE;418}
419
420/*
421* This is documented to succeed on read-only files, but Win32's
422* FlushFileBuffers functions fails with "access denied" in such a
423* case. So we only signal an error if the error is *not* "access
424* denied".
425*/
426
427int
428handleSync(FD fd) {429/*430* From the documentation:
431*
432* On Windows NT, the function FlushFileBuffers fails if hFile
433* is a handle to console output. That is because console
434* output is not buffered. The function returns FALSE, and
435* GetLastError returns ERROR_INVALID_HANDLE.
436*
437* On the other hand, on Win95, it returns without error. I cannot
438* assume that 0, 1, and 2 are console, because if someone closes
439* System.out and then opens a file, they might get file descriptor
440* 1. An error on *that* version of 1 should be reported, whereas
441* an error on System.out (which was the original 1) should be
442* ignored. So I use isatty() to ensure that such an error was due
443* to this bogosity, and if it was, I ignore the error.
444*/
445
446HANDLE handle = (HANDLE)fd;447
448if (!FlushFileBuffers(handle)) {449if (GetLastError() != ERROR_ACCESS_DENIED) { /* from winerror.h */450return -1;451}452}453return 0;454}
455
456jint
457handleSetLength(FD fd, jlong length) {458HANDLE h = (HANDLE)fd;459FILE_END_OF_FILE_INFO eofInfo;460
461eofInfo.EndOfFile.QuadPart = length;462
463if (h == INVALID_HANDLE_VALUE) {464return -1;465}466if (!SetFileInformationByHandle(h, FileEndOfFileInfo, &eofInfo,467sizeof(FILE_END_OF_FILE_INFO))) {468return -1;469}470return 0;471}
472
473JNIEXPORT
474jint
475handleRead(FD fd, void *buf, jint len)476{
477DWORD read = 0;478BOOL result = 0;479HANDLE h = (HANDLE)fd;480if (h == INVALID_HANDLE_VALUE) {481return -1;482}483result = ReadFile(h, /* File handle to read */484buf, /* address to put data */485len, /* number of bytes to read */486&read, /* number of bytes read */487NULL); /* no overlapped struct */488if (result == 0) {489int error = GetLastError();490if (error == ERROR_BROKEN_PIPE) {491return 0; /* EOF */492}493return -1;494}495return (jint)read;496}
497
498static jint writeInternal(FD fd, const void *buf, jint len, jboolean append)499{
500BOOL result = 0;501DWORD written = 0;502HANDLE h = (HANDLE)fd;503if (h != INVALID_HANDLE_VALUE) {504OVERLAPPED ov;505LPOVERLAPPED lpOv;506if (append == JNI_TRUE) {507ov.Offset = (DWORD)0xFFFFFFFF;508ov.OffsetHigh = (DWORD)0xFFFFFFFF;509ov.hEvent = NULL;510lpOv = &ov;511} else {512lpOv = NULL;513}514result = WriteFile(h, /* File handle to write */515buf, /* pointers to the buffers */516len, /* number of bytes to write */517&written, /* receives number of bytes written */518lpOv); /* overlapped struct */519}520if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {521return -1;522}523return (jint)written;524}
525
526jint handleWrite(FD fd, const void *buf, jint len) {527return writeInternal(fd, buf, len, JNI_FALSE);528}
529
530jint handleAppend(FD fd, const void *buf, jint len) {531return writeInternal(fd, buf, len, JNI_TRUE);532}
533
534// Function to close the fd held by this FileDescriptor and set fd to -1.
535void
536fileDescriptorClose(JNIEnv *env, jobject this)537{
538FD fd = (*env)->GetLongField(env, this, IO_handle_fdID);539HANDLE h = (HANDLE)fd;540if ((*env)->ExceptionOccurred(env)) {541return;542}543
544if (h == INVALID_HANDLE_VALUE) {545return;546}547
548/* Set the fd to -1 before closing it so that the timing window549* of other threads using the wrong fd (closed but recycled fd,
550* that gets re-opened with some other filename) is reduced.
551* Practically the chance of its occurrence is low, however, we are
552* taking extra precaution over here.
553*/
554(*env)->SetLongField(env, this, IO_handle_fdID, -1);555if ((*env)->ExceptionOccurred(env)) {556return;557}558
559if (CloseHandle(h) == 0) { /* Returns zero on failure */560JNU_ThrowIOExceptionWithLastError(env, "close failed");561}562}
563
564JNIEXPORT jlong JNICALL
565handleLseek(FD fd, jlong offset, jint whence)566{
567LARGE_INTEGER pos, distance;568DWORD op = FILE_CURRENT;569HANDLE h = (HANDLE)fd;570
571if (whence == SEEK_END) {572op = FILE_END;573}574if (whence == SEEK_CUR) {575op = FILE_CURRENT;576}577if (whence == SEEK_SET) {578op = FILE_BEGIN;579}580
581distance.QuadPart = offset;582if (SetFilePointerEx(h, distance, &pos, op) == 0) {583return -1;584}585return long_to_jlong(pos.QuadPart);586}
587
588jlong
589handleGetLength(FD fd) {590HANDLE h = (HANDLE) fd;591LARGE_INTEGER length;592if (GetFileSizeEx(h, &length) != 0) {593return long_to_jlong(length.QuadPart);594} else {595return -1;596}597}
598