8233451: (fs) Files.newInputStream() cannot be used with character special files

Reviewed-by: alanb
This commit is contained in:
Brian Burkhalter 2024-10-23 18:53:30 +00:00
parent 002de86081
commit de92fe3757
8 changed files with 415 additions and 31 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@ -38,6 +38,7 @@ import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Objects;
import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.Stable;
/**
* An InputStream that reads bytes from a channel.
@ -53,6 +54,10 @@ class ChannelInputStream extends InputStream {
private byte[] bs; // Invoker's previous array
private byte[] b1;
// if isOther is true, then the file being read is not a regular file,
// nor a directory, nor a symbolic link, hence possibly not seekable
private @Stable Boolean isOther;
/**
* Initialize a ChannelInputStream that reads from the given channel.
*/
@ -60,6 +65,17 @@ class ChannelInputStream extends InputStream {
this.ch = ch;
}
private boolean isOther() throws IOException {
Boolean isOther = this.isOther;
if (isOther == null) {
if (ch instanceof FileChannelImpl fci)
this.isOther = isOther = fci.isOther();
else
this.isOther = isOther = Boolean.FALSE;
}
return isOther;
}
/**
* Reads a sequence of bytes from the channel into the given buffer.
*/
@ -105,7 +121,8 @@ class ChannelInputStream extends InputStream {
@Override
public byte[] readAllBytes() throws IOException {
if (!(ch instanceof SeekableByteChannel sbc))
if (!(ch instanceof SeekableByteChannel sbc) ||
(ch instanceof FileChannelImpl fci && isOther()))
return super.readAllBytes();
long length = sbc.size();
@ -156,7 +173,8 @@ class ChannelInputStream extends InputStream {
if (len == 0)
return new byte[0];
if (!(ch instanceof SeekableByteChannel sbc))
if (!(ch instanceof SeekableByteChannel sbc) ||
(ch instanceof FileChannelImpl fci && isOther()))
return super.readNBytes(len);
long length = sbc.size();
@ -192,7 +210,9 @@ class ChannelInputStream extends InputStream {
@Override
public int available() throws IOException {
// special case where the channel is to a file
if (ch instanceof SeekableByteChannel sbc) {
if (ch instanceof FileChannelImpl fci) {
return fci.available();
} else if (ch instanceof SeekableByteChannel sbc) {
long rem = Math.max(0, sbc.size() - sbc.position());
return (rem > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)rem;
}
@ -202,7 +222,8 @@ class ChannelInputStream extends InputStream {
@Override
public synchronized long skip(long n) throws IOException {
// special case where the channel is to a file
if (ch instanceof SeekableByteChannel sbc) {
if (ch instanceof SeekableByteChannel sbc &&
!(ch instanceof FileChannelImpl fci && isOther())) {
long pos = sbc.position();
long newPos;
if (n > 0) {
@ -224,7 +245,8 @@ class ChannelInputStream extends InputStream {
public long transferTo(OutputStream out) throws IOException {
Objects.requireNonNull(out, "out");
if (ch instanceof FileChannel fc) {
if (ch instanceof FileChannel fc &&
!(fc instanceof FileChannelImpl fci && isOther())) {
// FileChannel -> SocketChannel
if (out instanceof SocketOutputStream sos) {
SocketChannelImpl sc = sos.channel();

View file

@ -454,6 +454,7 @@ public class FileChannelImpl
}
return bytesWritten;
}
// -- Other operations --
@Override
@ -529,6 +530,49 @@ public class FileChannelImpl
}
}
/**
* Returns an estimate of the number of remaining bytes that can be read
* from this channel without blocking.
*/
int available() throws IOException {
ensureOpen();
synchronized (positionLock) {
int a = -1;
int ti = -1;
try {
beginBlocking();
ti = threads.add();
if (!isOpen())
return -1;
a = nd.available(fd);
} finally {
threads.remove(ti);
endBlocking(a > -1);
}
return a;
}
}
/**
* Tells whether the channel represents something other than a regular
* file, directory, or symbolic link.
*/
boolean isOther() throws IOException {
ensureOpen();
int ti = -1;
Boolean isOther = null;
try {
beginBlocking();
ti = threads.add();
if (!isOpen())
return false;
return isOther = nd.isOther(fd);
} finally {
threads.remove(ti);
endBlocking(isOther != null);
}
}
@Override
public FileChannel truncate(long newSize) throws IOException {
ensureOpen();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2024, 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
@ -49,6 +49,10 @@ abstract class FileDispatcher extends NativeDispatcher {
abstract long size(FileDescriptor fd) throws IOException;
abstract int available(FileDescriptor fd) throws IOException;
abstract boolean isOther(FileDescriptor fd) throws IOException;
abstract int lock(FileDescriptor fd, boolean blocking, long pos, long size,
boolean shared) throws IOException;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2024, 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
@ -93,6 +93,14 @@ class UnixFileDispatcherImpl extends FileDispatcher {
return size0(fd);
}
int available(FileDescriptor fd) throws IOException {
return available0(fd);
}
boolean isOther(FileDescriptor fd) throws IOException {
return isOther0(fd);
}
int lock(FileDescriptor fd, boolean blocking, long pos, long size,
boolean shared) throws IOException
{
@ -196,6 +204,10 @@ class UnixFileDispatcherImpl extends FileDispatcher {
static native long size0(FileDescriptor fd) throws IOException;
static native int available0(FileDescriptor fd) throws IOException;
static native boolean isOther0(FileDescriptor fd) throws IOException;
static native int lock0(FileDescriptor fd, boolean blocking, long pos,
long size, boolean shared) throws IOException;

View file

@ -23,6 +23,7 @@
* questions.
*/
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <sys/stat.h>
@ -41,8 +42,10 @@
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_UnixFileDispatcherImpl.h"
#include "java_lang_Integer.h"
#include "java_lang_Long.h"
#include <assert.h>
#include "io_util_md.h"
#if defined(_AIX)
#define statvfs statvfs64
@ -178,6 +181,57 @@ Java_sun_nio_ch_UnixFileDispatcherImpl_size0(JNIEnv *env, jobject this, jobject
return fbuf.st_size;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_UnixFileDispatcherImpl_available0(JNIEnv *env, jobject this, jobject fdo)
{
jint fd = fdval(env, fdo);
struct stat fbuf;
jlong size = -1;
if (fstat(fd, &fbuf) != -1) {
int mode = fbuf.st_mode;
if (S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
int n = ioctl(fd, FIONREAD, &n);
if (n >= 0) {
return n;
}
} else if (S_ISREG(mode)) {
size = fbuf.st_size;
}
}
jlong position;
if ((position = lseek(fd, 0, SEEK_CUR)) == -1) {
return 0;
}
if (size < position) {
if ((size = lseek(fd, 0, SEEK_END)) == -1)
return 0;
else if (lseek(fd, position, SEEK_SET) == -1)
return 0;
}
jlong available = size - position;
return available > java_lang_Integer_MAX_VALUE ?
java_lang_Integer_MAX_VALUE : (jint)available;
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_UnixFileDispatcherImpl_isOther0(JNIEnv *env, jobject this, jobject fdo)
{
jint fd = fdval(env, fdo);
struct stat fbuf;
if (fstat(fd, &fbuf) == -1)
handle(env, -1, "isOther failed");
if (S_ISREG(fbuf.st_mode) || S_ISDIR(fbuf.st_mode) || S_ISLNK(fbuf.st_mode))
return JNI_FALSE;
return JNI_TRUE;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_UnixFileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo,
jboolean block, jlong pos, jlong size,

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2024, 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
@ -101,6 +101,14 @@ class FileDispatcherImpl extends FileDispatcher {
return size0(fd);
}
int available(FileDescriptor fd) throws IOException {
return available0(fd);
}
boolean isOther(FileDescriptor fd) throws IOException {
return isOther0(fd);
}
int lock(FileDescriptor fd, boolean blocking, long pos, long size,
boolean shared) throws IOException
{
@ -223,6 +231,10 @@ class FileDispatcherImpl extends FileDispatcher {
static native long size0(FileDescriptor fd) throws IOException;
static native int available0(FileDescriptor fd) throws IOException;
static native boolean isOther0(FileDescriptor fd) throws IOException;
static native int lock0(FileDescriptor fd, boolean blocking, long pos,
long size, boolean shared) throws IOException;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2024, 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
@ -24,6 +24,7 @@
*/
#include <windows.h>
#include <winioctl.h>
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
@ -33,6 +34,7 @@
#include "nio_util.h"
#include "java_lang_Integer.h"
#include "sun_nio_ch_FileDispatcherImpl.h"
#include "io_util_md.h"
#include <Mswsock.h> // Requires Mswsock.lib
@ -392,6 +394,75 @@ Java_sun_nio_ch_FileDispatcherImpl_size0(JNIEnv *env, jobject this, jobject fdo)
return (jlong)size.QuadPart;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_available0(JNIEnv *env, jobject this, jobject fdo)
{
HANDLE handle = (HANDLE)(handleval(env, fdo));
DWORD type = GetFileType(handle);
jlong available = 0;
// Calculate the number of bytes available for a regular file,
// and return the default (zero) for other types.
if (type == FILE_TYPE_DISK) {
jlong current, end;
LARGE_INTEGER distance, pos, filesize;
distance.QuadPart = 0;
if (SetFilePointerEx(handle, distance, &pos, FILE_CURRENT) == 0) {
JNU_ThrowIOExceptionWithLastError(env, "Available failed");
return IOS_THROWN;
}
current = (jlong)pos.QuadPart;
if (GetFileSizeEx(handle, &filesize) == 0) {
JNU_ThrowIOExceptionWithLastError(env, "Available failed");
return IOS_THROWN;
}
end = (jlong)filesize.QuadPart;
available = end - current;
if (available > java_lang_Integer_MAX_VALUE) {
available = java_lang_Integer_MAX_VALUE;
} else if (available < 0) {
available = 0;
}
}
return (jint)available;
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_FileDispatcherImpl_isOther0(JNIEnv *env, jobject this, jobject fdo)
{
HANDLE handle = (HANDLE)(handleval(env, fdo));
BY_HANDLE_FILE_INFORMATION finfo;
if (!GetFileInformationByHandle(handle, &finfo))
JNU_ThrowIOExceptionWithLastError(env, "isOther failed");
DWORD fattr = finfo.dwFileAttributes;
if ((fattr & FILE_ATTRIBUTE_DEVICE) != 0)
return (jboolean)JNI_TRUE;
if ((fattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
void* lpOutBuffer = (void*)malloc(size*sizeof(char));
if (lpOutBuffer == NULL)
JNU_ThrowOutOfMemoryError(env, "isOther failed");
DWORD bytesReturned;
if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
lpOutBuffer, (DWORD)size, &bytesReturned, NULL)) {
free(lpOutBuffer);
JNU_ThrowIOExceptionWithLastError(env, "isOther failed");
}
ULONG reparseTag = (*((PULONG)lpOutBuffer));
free(lpOutBuffer);
return reparseTag == IO_REPARSE_TAG_SYMLINK ?
(jboolean)JNI_FALSE : (jboolean)JNI_TRUE;
}
return (jboolean)JNI_FALSE;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo,
jboolean block, jlong pos, jlong size,