8020669: (fs) Files.readAllBytes() does not read any data when Files.size() is 0
Reviewed-by: alanb, chegar, martin, rriggs
--- a/jdk/src/share/classes/java/nio/file/Files.java Tue Jul 30 21:11:08 2013 +0400
+++ b/jdk/src/share/classes/java/nio/file/Files.java Mon Jul 29 12:35:42 2013 +0400
@@ -25,10 +25,10 @@
package java.nio.file;
-import java.nio.ByteBuffer;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileSystemProvider;
import java.nio.file.spi.FileTypeDetector;
+import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.io.Closeable;
@@ -2965,7 +2965,63 @@
}
/**
- * Read all the bytes from a file. The method ensures that the file is
+ * The maximum size of array to allocate.
+ * Some VMs reserve some header words in an array.
+ * Attempts to allocate larger arrays may result in
+ * OutOfMemoryError: Requested array size exceeds VM limit
+ */
+ private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * Reads all the bytes from an input stream. Uses {@code initialSize} as a hint
+ * about how many bytes the stream will have.
+ *
+ * @param source
+ * the input stream to read from
+ * @param initialSize
+ * the initial size of the byte array to allocate
+ *
+ * @return a byte array containing the bytes read from the file
+ *
+ * @throws IOException
+ * if an I/O error occurs reading from the stream
+ * @throws OutOfMemoryError
+ * if an array of the required size cannot be allocated
+ */
+ private static byte[] read(InputStream source, int initialSize)
+ throws IOException
+ {
+ int capacity = initialSize;
+ byte[] buf = new byte[capacity];
+ int nread = 0;
+ int n;
+ for (;;) {
+ // read to EOF which may read more or less than initialSize (eg: file
+ // is truncated while we are reading)
+ while ((n = source.read(buf, nread, capacity - nread)) > 0)
+ nread += n;
+
+ // if last call to source.read() returned -1, we are done
+ // otherwise, try to read one more byte; if that failed we're done too
+ if (n < 0 || (n = source.read()) < 0)
+ break;
+
+ // one more byte was read; need to allocate a larger buffer
+ if (capacity <= MAX_BUFFER_SIZE - capacity) {
+ capacity = Math.max(capacity << 1, BUFFER_SIZE);
+ } else {
+ if (capacity == MAX_BUFFER_SIZE)
+ throw new OutOfMemoryError("Required array size too large");
+ capacity = MAX_BUFFER_SIZE;
+ }
+ buf = Arrays.copyOf(buf, capacity);
+ buf[nread++] = (byte)n;
+ }
+ return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
+ }
+
+ /**
+ * Reads all the bytes from a file. The method ensures that the file is
* closed when all bytes have been read or an I/O error, or other runtime
* exception, is thrown.
*
@@ -2989,22 +3045,13 @@
* method is invoked to check read access to the file.
*/
public static byte[] readAllBytes(Path path) throws IOException {
- try (FileChannel fc = FileChannel.open(path)) {
+ try (FileChannel fc = FileChannel.open(path);
+ InputStream is = Channels.newInputStream(fc)) {
long size = fc.size();
- if (size > (long)Integer.MAX_VALUE)
+ if (size > (long)MAX_BUFFER_SIZE)
throw new OutOfMemoryError("Required array size too large");
- byte[] arr = new byte[(int)size];
- ByteBuffer bb = ByteBuffer.wrap(arr);
- while (bb.hasRemaining()) {
- if (fc.read(bb) < 0) {
- // truncated
- break;
- }
- }
-
- int nread = bb.position();
- return (nread == size) ? arr : Arrays.copyOf(arr, nread);
+ return read(is, (int)size);
}
}
--- a/jdk/test/java/nio/file/Files/BytesAndLines.java Tue Jul 30 21:11:08 2013 +0400
+++ b/jdk/test/java/nio/file/Files/BytesAndLines.java Mon Jul 29 12:35:42 2013 +0400
@@ -22,7 +22,7 @@
*/
/* @test
- * @bug 7006126
+ * @bug 7006126 8020669
* @summary Unit test for methods for Files readAllBytes, readAllLines and
* and write methods.
*/
@@ -82,6 +82,16 @@
write(file, lines, Charset.defaultCharset(), opts);
throw new RuntimeException("NullPointerException expected");
} catch (NullPointerException ignore) { }
+
+ // read from procfs
+ if (System.getProperty("os.name").equals("Linux")) {
+ // Refer to the Linux proc(5) man page for details about /proc/self/stat file
+ // procfs reports it to be zero sized, even though data can be read from it
+ String statFile = "/proc/self/stat";
+ Path pathStat = Paths.get(statFile);
+ byte[] data = Files.readAllBytes(pathStat);
+ assertTrue(data.length > 0, "Files.readAllBytes('" + statFile + "') failed to read");
+ }
}
@@ -174,6 +184,16 @@
throw new RuntimeException("NullPointerException expected");
} catch (NullPointerException ignore) { }
+ // read from procfs
+ if (System.getProperty("os.name").equals("Linux")) {
+ // Refer to the Linux proc(5) man page for details about /proc/self/status file
+ // procfs reports this file to be zero sized, even though data can be read from it
+ String statusFile = "/proc/self/status";
+ Path pathStatus = Paths.get(statusFile);
+ lines = Files.readAllLines(pathStatus, US_ASCII);
+ assertTrue(lines.size() > 0, "Files.readAllLines('" + pathStatus + "') failed to read");
+ }
+
} finally {
delete(tmpfile);
}
@@ -242,7 +262,6 @@
} finally {
delete(tmpfile);
}
-
}
static void assertTrue(boolean expr, String errmsg) {