8316391: (zipfs) ZipFileSystem.readFullyAt does not tolerate short reads

Reviewed-by: jpai, lancea
This commit is contained in:
Alan Bateman 2023-09-17 11:53:12 +00:00
parent 4b8f5d031a
commit f44032969f
3 changed files with 55 additions and 38 deletions

View File

@ -1224,23 +1224,34 @@ class ZipFileSystem extends FileSystem {
close();
}
// Reads len bytes of data from the specified offset into buf.
// Returns the total number of bytes read.
// Each/every byte read from here (except the cen, which is mapped).
final long readFullyAt(byte[] buf, int off, long len, long pos)
throws IOException
{
/**
* Reads len bytes of data at the given file position into the given byte array
* starting at the given array offset. The method blocks until len bytes have been
* read, end of stream is detected, or an exception is thrown. Returns the total
* number of bytes read.
*/
final long readNBytesAt(byte[] buf, int off, long len, long pos) throws IOException {
ByteBuffer bb = ByteBuffer.wrap(buf);
bb.position(off);
bb.limit((int)(off + len));
return readFullyAt(bb, pos);
long totalRead = 0;
while (totalRead < len) {
int n = readAt(bb, pos);
if (n < 0) {
break;
}
pos += n;
totalRead +=n;
}
return totalRead;
}
private long readFullyAt(ByteBuffer bb, long pos) throws IOException {
if (ch instanceof FileChannel fch) {
return fch.read(bb, pos);
private int readAt(ByteBuffer bb, long pos) throws IOException {
if (ch instanceof FileChannel fc) {
return fc.read(bb, pos);
} else {
synchronized(ch) {
synchronized (ch) {
return ch.position(pos).read(bb);
}
}
@ -1264,7 +1275,7 @@ class ZipFileSystem extends FileSystem {
Arrays.fill(buf, 0, off, (byte)0);
}
int len = buf.length - off;
if (readFullyAt(buf, off, len, pos + off) != len)
if (readNBytesAt(buf, off, len, pos + off) != len)
throw new ZipException("zip END header not found");
// Now scan the block backwards for END header signature
@ -1286,14 +1297,14 @@ class ZipFileSystem extends FileSystem {
// try if there is zip64 end;
byte[] loc64 = new byte[ZIP64_LOCHDR];
if (end.endpos < ZIP64_LOCHDR ||
readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
readNBytesAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
!= loc64.length ||
!locator64SigAt(loc64, 0)) {
return end;
}
long end64pos = ZIP64_LOCOFF(loc64);
byte[] end64buf = new byte[ZIP64_ENDHDR];
if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
if (readNBytesAt(end64buf, 0, end64buf.length, end64pos)
!= end64buf.length ||
!end64SigAt(end64buf, 0)) {
return end;
@ -1557,7 +1568,7 @@ class ZipFileSystem extends FileSystem {
// read in the CEN and END
byte[] cen = new byte[(int)(end.cenlen + ENDHDR)];
if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
if (readNBytesAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
throw new ZipException("read CEN tables failed");
}
// Iterate through the entries in the central directory
@ -1706,7 +1717,7 @@ class ZipFileSystem extends FileSystem {
// 'name' field of the loc. if this byte is '/', which means the original
// entry has an absolute path in original zip/jar file, the e.writeLOC()
// is used to output the loc, in which the leading "/" will be removed
if (readFullyAt(buf, 0, LOCHDR + 1 , locoff) != LOCHDR + 1)
if (readNBytesAt(buf, 0, LOCHDR + 1 , locoff) != LOCHDR + 1)
throw new ZipException("loc: reading failed");
if (updateHeader || LOCNAM(buf) > 0 && buf[LOCHDR] == '/') {
@ -1723,7 +1734,7 @@ class ZipFileSystem extends FileSystem {
}
int n;
while (size > 0 &&
(n = (int)readFullyAt(buf, 0, buf.length, locoff)) != -1)
(n = (int)readNBytesAt(buf, 0, buf.length, locoff)) != -1)
{
if (size < n)
n = (int)size;
@ -2338,7 +2349,7 @@ class ZipFileSystem extends FileSystem {
ByteBuffer bb = ByteBuffer.wrap(b);
bb.position(off);
bb.limit(off + len);
long n = readFullyAt(bb, pos);
long n = readAt(bb, pos);
if (n > 0) {
pos += n;
rem -= n;
@ -2383,7 +2394,7 @@ class ZipFileSystem extends FileSystem {
if (pos <= 0) {
pos = -pos + locpos;
byte[] buf = new byte[LOCHDR];
if (readFullyAt(buf, 0, buf.length, pos) != LOCHDR) {
if (readNBytesAt(buf, 0, buf.length, pos) != LOCHDR) {
throw new ZipException("invalid loc " + pos + " for entry reading");
}
pos += LOCHDR + LOCNAM(buf) + LOCEXT(buf);
@ -3224,7 +3235,7 @@ class ZipFileSystem extends FileSystem {
*/
private void readLocEXTT(ZipFileSystem zipfs) throws IOException {
byte[] buf = new byte[LOCHDR];
if (zipfs.readFullyAt(buf, 0, buf.length , locoff)
if (zipfs.readNBytesAt(buf, 0, buf.length , locoff)
!= buf.length)
throw new ZipException("loc: reading failed");
if (!locSigAt(buf, 0))
@ -3235,7 +3246,7 @@ class ZipFileSystem extends FileSystem {
return;
int locNlen = LOCNAM(buf);
buf = new byte[locElen];
if (zipfs.readFullyAt(buf, 0, buf.length , locoff + LOCHDR + locNlen)
if (zipfs.readNBytesAt(buf, 0, buf.length , locoff + LOCHDR + locNlen)
!= buf.length)
throw new ZipException("loc extra: reading failed");
int locPos = 0;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2023, 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
@ -65,12 +65,12 @@ public class ZipInfo {
// loc.extra is bigger than the cen.extra, try to avoid to read
// twice
long len = LOCHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENHDR;
if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
if (zfs.readNBytesAt(buf, 0, len, locoff(cen, pos)) != len)
throw new ZipException("read loc header failed");
if (LOCEXT(buf) > CENEXT(cen, pos) + CENHDR) {
// have to read the second time;
len = LOCHDR + LOCNAM(buf) + LOCEXT(buf);
if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
if (zfs.readNBytesAt(buf, 0, len, locoff(cen, pos)) != len)
throw new ZipException("read loc header failed");
}
printLOC(buf);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, 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
@ -92,18 +92,19 @@ public class LargeGatheringWrite {
// Write the data to a temporary file
Path tempFile = Files.createTempFile("LargeGatheringWrite", ".dat");
printTime("before writing");
System.out.printf("Writing %d bytes of data...%n", totalLength);
try (FileChannel fcw = FileChannel.open(tempFile, CREATE, WRITE);) {
// Print size of individual writes and total number written
long bytesWritten = 0;
long n;
while ((n = fcw.write(bigBuffers)) > 0) {
System.out.printf("Wrote %d bytes\n", n);
bytesWritten += n;
try {
printTime("before writing");
System.out.printf("Writing %d bytes of data...%n", totalLength);
try (FileChannel fcw = FileChannel.open(tempFile, CREATE, WRITE);) {
// Print size of individual writes and total number written
long bytesWritten = 0;
long n;
while ((n = fcw.write(bigBuffers)) > 0) {
System.out.printf("Wrote %d bytes\n", n);
bytesWritten += n;
}
System.out.printf("Total of %d bytes written\n", bytesWritten);
}
System.out.printf("Total of %d bytes written\n", bytesWritten);
printTime("after writing");
// Verify the content written
@ -121,8 +122,13 @@ public class LargeGatheringWrite {
ByteBuffer dst = ByteBuffer.wrap(bytes).slice(0, length);
if (dst.remaining() != length)
throw new RuntimeException("remaining");
if (fcr.read(dst) != length)
throw new RuntimeException("length");
long totalRead = 0;
while (totalRead < length) {
int bytesRead = fcr.read(dst);
if (bytesRead < 0)
throw new RuntimeException("premature EOF");
totalRead += bytesRead;
}
dst.rewind();
// Verify that the bytes read from the file match the buffer