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;