This is an automated email from the ASF dual-hosted git repository.

bodewig pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git


The following commit(s) were added to refs/heads/master by this push:
     new 80124dd  remove pattern where we first allocate an array and then try 
to fill it
80124dd is described below

commit 80124dd9fe4b0a0b2e203ca19aacac8cd0afc96f
Author: Stefan Bodewig <bode...@apache.org>
AuthorDate: Fri Jul 2 20:00:47 2021 +0200

    remove pattern where we first allocate an array and then try to fill it
---
 .../archivers/ar/ArArchiveInputStream.java         | 16 ++--
 .../archivers/arj/ArjArchiveInputStream.java       | 29 ++++---
 .../archivers/cpio/CpioArchiveInputStream.java     | 19 +++--
 .../archivers/dump/DumpArchiveInputStream.java     | 10 ++-
 .../compress/archivers/dump/TapeInputStream.java   | 11 ++-
 .../compress/archivers/examples/Expander.java      |  2 +-
 .../compress/archivers/sevenz/SevenZFile.java      |  5 +-
 .../commons/compress/archivers/tar/TarUtils.java   |  4 +-
 .../commons/compress/archivers/zip/BinaryTree.java |  5 +-
 .../zip/X0017_StrongEncryptionHeader.java          |  2 +-
 .../archivers/zip/ZipArchiveInputStream.java       | 27 +++---
 .../commons/compress/archivers/zip/ZipFile.java    | 24 ++++--
 .../org/apache/commons/compress/utils/IOUtils.java | 98 ++++++++++++++++++++++
 .../apache/commons/compress/utils/IOUtilsTest.java | 84 +++++++++++++++----
 14 files changed, 258 insertions(+), 78 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
index 36ef33f..f30951d 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
@@ -101,8 +101,8 @@ public class ArArchiveInputStream extends 
ArchiveInputStream {
 
         if (offset == 0) {
             final byte[] expected = 
ArchiveUtils.toAsciiBytes(ArArchiveEntry.HEADER);
-            final byte[] realized = new byte[expected.length];
-            final int read = IOUtils.readFully(input, realized);
+            final byte[] realized = IOUtils.readRange(input, expected.length);
+            final int read = realized.length;
             trackReadBytes(read);
             if (read != expected.length) {
                 throw new IOException("Failed to read header. Occurred at 
byte: " + getBytesRead());
@@ -133,8 +133,8 @@ public class ArArchiveInputStream extends 
ArchiveInputStream {
 
         {
             final byte[] expected = 
ArchiveUtils.toAsciiBytes(ArArchiveEntry.TRAILER);
-            final byte[] realized = new byte[expected.length];
-            final int read = IOUtils.readFully(input, realized);
+            final byte[] realized = IOUtils.readRange(input, expected.length);
+            final int read = realized.length;
             trackReadBytes(read);
             if (read != expected.length) {
                 throw new IOException("Failed to read entry trailer. Occurred 
at byte: " + getBytesRead());
@@ -340,8 +340,8 @@ public class ArArchiveInputStream extends 
ArchiveInputStream {
     private String getBSDLongName(final String bsdLongName) throws IOException 
{
         final int nameLen =
             Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
-        final byte[] name = new byte[nameLen];
-        final int read = IOUtils.readFully(input, name);
+        final byte[] name = IOUtils.readRange(input, nameLen);
+        final int read = name.length;
         trackReadBytes(read);
         if (read != nameLen) {
             throw new EOFException();
@@ -386,8 +386,8 @@ public class ArArchiveInputStream extends 
ArchiveInputStream {
      */
     private ArArchiveEntry readGNUStringTable(final byte[] length, final int 
offset, final int len) throws IOException {
         final int bufflen = asInt(length, offset, len); // Assume length will 
fit in an int
-        namebuffer = new byte[bufflen];
-        final int read = IOUtils.readFully(input, namebuffer, 0, bufflen);
+        namebuffer = IOUtils.readRange(input, bufflen);
+        final int read = namebuffer.length;
         trackReadBytes(read);
         if (read != bufflen){
             throw new IOException("Failed to read complete // record: 
expected="
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
index b0c16b2..4b20c12 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
@@ -20,6 +20,7 @@ package org.apache.commons.compress.archivers.arj;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -122,10 +123,14 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
         }
     }
 
-    private void readFully(final DataInputStream dataIn, final byte[] b)
+    private byte[] readRange(final InputStream in, final int len)
         throws IOException {
-        dataIn.readFully(b);
+        final byte[] b = IOUtils.readRange(in, len);
         count(b.length);
+        if (b.length < len) {
+            throw new EOFException();
+        }
+        return b;
     }
 
     private byte[] readHeader() throws IOException {
@@ -144,8 +149,7 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
                 return null;
             }
             if (basicHeaderSize <= 2600) {
-                basicHeaderBytes = new byte[basicHeaderSize];
-                readFully(in, basicHeaderBytes);
+                basicHeaderBytes = readRange(in, basicHeaderSize);
                 final long basicHeaderCrc32 = read32(in) & 0xFFFFFFFFL;
                 final CRC32 crc32 = new CRC32();
                 crc32.update(basicHeaderBytes);
@@ -166,8 +170,9 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
                 new ByteArrayInputStream(basicHeaderBytes));
 
         final int firstHeaderSize = basicHeader.readUnsignedByte();
-        final byte[] firstHeaderBytes = new byte[firstHeaderSize - 1];
-        basicHeader.readFully(firstHeaderBytes);
+        final byte[] firstHeaderBytes = readRange(basicHeader, firstHeaderSize 
- 1);
+        pushedBackBytes(firstHeaderBytes.length);
+
         final DataInputStream firstHeader = new DataInputStream(
                 new ByteArrayInputStream(firstHeaderBytes));
 
@@ -185,7 +190,7 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
         hdr.securityEnvelopeFilePosition = read32(firstHeader);
         hdr.fileSpecPosition = read16(firstHeader);
         hdr.securityEnvelopeLength = read16(firstHeader);
-        pushedBackBytes(20); // count has already counted them via readFully
+        pushedBackBytes(20); // count has already counted them via readRange
         hdr.encryptionVersion = firstHeader.readUnsignedByte();
         hdr.lastChapter = firstHeader.readUnsignedByte();
 
@@ -201,8 +206,7 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
 
         final  int extendedHeaderSize = read16(in);
         if (extendedHeaderSize > 0) {
-            hdr.extendedHeaderBytes = new byte[extendedHeaderSize];
-            readFully(in, hdr.extendedHeaderBytes);
+            hdr.extendedHeaderBytes = readRange(in, extendedHeaderSize);
             final long extendedHeaderCrc32 = 0xffffFFFFL & read32(in);
             final CRC32 crc32 = new CRC32();
             crc32.update(hdr.extendedHeaderBytes);
@@ -222,8 +226,8 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
         try (final DataInputStream basicHeader = new DataInputStream(new 
ByteArrayInputStream(basicHeaderBytes))) {
 
             final int firstHeaderSize = basicHeader.readUnsignedByte();
-            final byte[] firstHeaderBytes = new byte[firstHeaderSize - 1];
-            basicHeader.readFully(firstHeaderBytes);
+            final byte[] firstHeaderBytes = readRange(basicHeader, 
firstHeaderSize - 1);
+            pushedBackBytes(firstHeaderBytes.length);
             try (final DataInputStream firstHeader = new DataInputStream(new 
ByteArrayInputStream(firstHeaderBytes))) {
 
                 final LocalFileHeader localFileHeader = new LocalFileHeader();
@@ -252,8 +256,7 @@ public class ArjArchiveInputStream extends 
ArchiveInputStream {
                 final ArrayList<byte[]> extendedHeaders = new ArrayList<>();
                 int extendedHeaderSize;
                 while ((extendedHeaderSize = read16(in)) > 0) {
-                    final byte[] extendedHeaderBytes = new 
byte[extendedHeaderSize];
-                    readFully(in, extendedHeaderBytes);
+                    final byte[] extendedHeaderBytes = readRange(in, 
extendedHeaderSize);
                     final long extendedHeaderCrc32 = 0xffffFFFFL & read32(in);
                     final CRC32 crc32 = new CRC32();
                     crc32.update(extendedHeaderBytes);
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
index 38a8e0c..de57548 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
@@ -354,17 +354,25 @@ public class CpioArchiveInputStream extends 
ArchiveInputStream implements
         return count;
     }
 
+    private final byte[] readRange(final int len)
+            throws IOException {
+        final byte[] b = IOUtils.readRange(in, len);
+        count(b.length);
+        if (b.length < len) {
+            throw new EOFException();
+        }
+        return b;
+    }
+
     private long readBinaryLong(final int length, final boolean swapHalfWord)
             throws IOException {
-        final byte[] tmp = new byte[length];
-        readFully(tmp, 0, tmp.length);
+        final byte[] tmp = readRange(length);
         return CpioUtil.byteArray2long(tmp, swapHalfWord);
     }
 
     private long readAsciiLong(final int length, final int radix)
             throws IOException {
-        final byte[] tmpBuffer = new byte[length];
-        readFully(tmpBuffer, 0, tmpBuffer.length);
+        final byte[] tmpBuffer = readRange(length);
         return Long.parseLong(ArchiveUtils.toAsciiString(tmpBuffer), radix);
     }
 
@@ -481,8 +489,7 @@ public class CpioArchiveInputStream extends 
ArchiveInputStream implements
 
     private String readCString(final int length) throws IOException {
         // don't include trailing NUL in file name to decode
-        final byte[] tmpBuffer = new byte[length - 1];
-        readFully(tmpBuffer, 0, tmpBuffer.length);
+        final byte[] tmpBuffer = readRange(length - 1);
         if (this.in.read() == -1) {
             throw new EOFException();
         }
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
index 9eeae44..59c00b5 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
@@ -22,6 +22,7 @@ import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.archivers.zip.ZipEncoding;
 import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
+import org.apache.commons.compress.utils.IOUtils;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -337,10 +338,11 @@ public class DumpArchiveInputStream extends 
ArchiveInputStream {
             final int datalen = DumpArchiveConstants.TP_SIZE * 
entry.getHeaderCount();
 
             if (blockBuffer.length < datalen) {
-                blockBuffer = new byte[datalen];
-            }
-
-            if (raw.read(blockBuffer, 0, datalen) != datalen) {
+                blockBuffer = IOUtils.readRange(raw, datalen);
+                if (blockBuffer.length != datalen) {
+                    throw new EOFException();
+                }
+            } else if (raw.read(blockBuffer, 0, datalen) != datalen) {
                 throw new EOFException();
             }
 
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/dump/TapeInputStream.java 
b/src/main/java/org/apache/commons/compress/archivers/dump/TapeInputStream.java
index 25829f2..006953f 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/dump/TapeInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/dump/TapeInputStream.java
@@ -297,8 +297,7 @@ class TapeInputStream extends FilterInputStream {
                 // this block is compressed.
                 final int flags = (h >> 1) & 0x07;
                 int length = (h >> 4) & 0x0FFFFFFF;
-                final byte[] compBuffer = new byte[length];
-                readFully(compBuffer, 0, length);
+                final byte[] compBuffer = readRange(length);
                 bytesRead += length;
 
                 if (!decompress) {
@@ -355,6 +354,14 @@ class TapeInputStream extends FilterInputStream {
         }
     }
 
+    private byte[] readRange(final int len) throws IOException {
+        final byte[] ret = IOUtils.readRange(in, len);
+        if (ret.length < len) {
+            throw new ShortFileException();
+        }
+        return ret;
+    }
+
     /**
      * Get number of bytes read.
      */
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java 
b/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java
index bbbae00..d256df9 100644
--- a/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java
@@ -327,7 +327,7 @@ public class Expander {
     public void expand(final SevenZFile archive, final File targetDirectory)
         throws IOException, ArchiveException {
         expand(archive::getNextEntry, (entry, out) -> {
-            final byte[] buffer = new byte[8024];
+            final byte[] buffer = new byte[8192];
             int n;
             while (-1 != (n = archive.read(buffer))) {
                 out.write(buffer, 0, n);
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java 
b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
index fde329e..867ed18 100644
--- a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
@@ -646,7 +646,6 @@ public class SevenZFile implements Closeable {
         int nid =  getUnsignedByte(input);
         while (nid != NID.kEnd) {
             final long propertySize = readUint64(input);
-            assertFitsIntoNonNegativeInt("propertySize", propertySize);
             final byte[] property = new byte[(int)propertySize];
             get(input, property);
             nid = getUnsignedByte(input);
@@ -704,8 +703,8 @@ public class SevenZFile implements Closeable {
                     folder.getUnpackSize(), folder.crc);
         }
         final int unpackSize = assertFitsIntoNonNegativeInt("unpackSize", 
folder.getUnpackSize());
-        final byte[] nextHeader = new byte[unpackSize];
-        if (IOUtils.readFully(inputStreamStack, nextHeader) < unpackSize) {
+        final byte[] nextHeader = IOUtils.readRange(inputStreamStack, 
unpackSize);
+        if (nextHeader.length < unpackSize) {
             throw new IOException("premature end of stream");
         }
         inputStreamStack.close();
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
index ec12f17..4dd0f58 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
@@ -754,8 +754,8 @@ public class TarUtils {
                                 throw new IOException("Paxheader value size " 
+ restLen
                                     + " exceeds size of header record");
                             } else {
-                                final byte[] rest = new byte[restLen];
-                                final int got = IOUtils.readFully(inputStream, 
rest);
+                                final byte[] rest = 
IOUtils.readRange(inputStream, restLen);
+                                final int got = rest.length;
                                 if (got != restLen) {
                                     throw new IOException("Failed to read "
                                             + "Paxheader. Expected "
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/BinaryTree.java 
b/src/main/java/org/apache/commons/compress/archivers/zip/BinaryTree.java
index 31153e1..a0ff91d 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/BinaryTree.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/BinaryTree.java
@@ -123,9 +123,8 @@ class BinaryTree {
             throw new IOException("Cannot read the size of the encoded tree, 
unexpected end of stream");
         }
 
-        final byte[] encodedTree = new byte[size];
-        final int read = IOUtils.readFully(inputStream, encodedTree);
-        if (read != size) {
+        final byte[] encodedTree = IOUtils.readRange(inputStream, size);
+        if (encodedTree.length != size) {
             throw new EOFException();
         }
 
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
 
b/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
index 5bd89f9..d843b89 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/zip/X0017_StrongEncryptionHeader.java
@@ -371,11 +371,11 @@ public class X0017_StrongEncryptionHeader extends 
PKWareExtraHeader {
             this.hashSize = ZipShort.getValue(data, offset + ivSize + 22 + 
erdSize);
             final int resize = ZipShort.getValue(data, offset + ivSize + 24 + 
erdSize);
 
-            this.recipientKeyHash = new byte[this.hashSize];
             if (resize < this.hashSize) {
                 throw new ZipException("Invalid X0017_StrongEncryptionHeader: 
resize " + resize
                     + " is too small to hold hashSize" + this.hashSize);
             }
+            this.recipientKeyHash = new byte[this.hashSize];
             this.keyBlob = new byte[resize - this.hashSize];
             // TODO: this looks suspicious, 26 rather than 24 would be "after" 
resize
             assertDynamicLengthFits("resize", resize, ivSize + 24 + erdSize, 
length);
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
index af3d45f..ab6994a 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
@@ -278,7 +278,7 @@ public class ZipArchiveInputStream extends 
ArchiveInputStream implements InputSt
                 // first local file header - look for it and fail with
                 // the appropriate error message if this is a split
                 // archive.
-                readFirstLocalFileHeader(lfhBuf);
+                readFirstLocalFileHeader();
             } else {
                 readFully(lfhBuf);
             }
@@ -339,15 +339,13 @@ public class ZipArchiveInputStream extends 
ArchiveInputStream implements InputSt
         final int extraLen = ZipShort.getValue(lfhBuf, off);
         off += SHORT; // NOSONAR - assignment as documentation
 
-        final byte[] fileName = new byte[fileNameLen];
-        readFully(fileName);
+        final byte[] fileName = readRange(fileNameLen);
         current.entry.setName(entryEncoding.decode(fileName), fileName);
         if (hasUTF8Flag) {
             
current.entry.setNameSource(ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
         }
 
-        final byte[] extraData = new byte[extraLen];
-        readFully(extraData);
+        final byte[] extraData = readRange(extraLen);
         try {
             current.entry.setExtra(extraData);
         } catch (RuntimeException ex) {
@@ -410,9 +408,9 @@ public class ZipArchiveInputStream extends 
ArchiveInputStream implements InputSt
      * deals with splitting/spanning markers that may prefix the first
      * LFH.
      */
-    private void readFirstLocalFileHeader(final byte[] lfh) throws IOException 
{
-        readFully(lfh);
-        final ZipLong sig = new ZipLong(lfh);
+    private void readFirstLocalFileHeader() throws IOException {
+        readFully(lfhBuf);
+        final ZipLong sig = new ZipLong(lfhBuf);
 
         if (!skipSplitSig && sig.equals(ZipLong.DD_SIG)) {
             throw new 
UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
@@ -423,8 +421,8 @@ public class ZipArchiveInputStream extends 
ArchiveInputStream implements InputSt
             // Just skip over the marker.
             final byte[] missedLfhBytes = new byte[4];
             readFully(missedLfhBytes);
-            System.arraycopy(lfh, 4, lfh, 0, LFH_LEN - 4);
-            System.arraycopy(missedLfhBytes, 0, lfh, LFH_LEN - 4, 4);
+            System.arraycopy(lfhBuf, 4, lfhBuf, 0, LFH_LEN - 4);
+            System.arraycopy(missedLfhBytes, 0, lfhBuf, LFH_LEN - 4, 4);
         }
     }
 
@@ -882,6 +880,15 @@ public class ZipArchiveInputStream extends 
ArchiveInputStream implements InputSt
         }
     }
 
+    private byte[] readRange(int len) throws IOException {
+        final byte[] ret = IOUtils.readRange(in, len);
+        count(ret.length);
+        if (ret.length < len) {
+            throw new EOFException();
+        }
+        return ret;
+    }
+
     private void readDataDescriptor() throws IOException {
         readFully(wordBuf);
         ZipLong val = new ZipLong(wordBuf);
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java 
b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
index 0f2bb53..e433c6e 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
@@ -820,8 +820,10 @@ public class ZipFile implements Closeable {
         ze.setExternalAttributes(ZipLong.getValue(cfhBuf, off));
         off += WORD;
 
-        final byte[] fileName = new byte[fileNameLen];
-        IOUtils.readFully(archive, ByteBuffer.wrap(fileName));
+        final byte[] fileName = IOUtils.readRange(archive, fileNameLen);
+        if (fileName.length < fileNameLen) {
+            throw new EOFException();
+        }
         ze.setName(entryEncoding.decode(fileName), fileName);
 
         // LFH offset,
@@ -829,8 +831,10 @@ public class ZipFile implements Closeable {
         // data offset will be filled later
         entries.add(ze);
 
-        final byte[] cdExtraData = new byte[extraLen];
-        IOUtils.readFully(archive, ByteBuffer.wrap(cdExtraData));
+        final byte[] cdExtraData = IOUtils.readRange(archive, extraLen);
+        if (cdExtraData.length < extraLen) {
+            throw new EOFException();
+        }
         try {
             ze.setCentralDirectoryExtra(cdExtraData);
         } catch (RuntimeException ex) {
@@ -842,8 +846,10 @@ public class ZipFile implements Closeable {
         setSizesAndOffsetFromZip64Extra(ze);
         sanityCheckLFHOffset(ze);
 
-        final byte[] comment = new byte[commentLen];
-        IOUtils.readFully(archive, ByteBuffer.wrap(comment));
+        final byte[] comment = IOUtils.readRange(archive, commentLen);
+        if (comment.length < commentLen) {
+            throw new EOFException();
+        }
         ze.setComment(entryEncoding.decode(comment));
 
         if (!hasUTF8Flag && useUnicodeExtraFields) {
@@ -1306,8 +1312,10 @@ public class ZipFile implements Closeable {
             final int fileNameLen = lens[0];
             final int extraFieldLen = lens[1];
             skipBytes(fileNameLen);
-            final byte[] localExtraData = new byte[extraFieldLen];
-            IOUtils.readFully(archive, ByteBuffer.wrap(localExtraData));
+            final byte[] localExtraData = IOUtils.readRange(archive, 
extraFieldLen);
+            if (localExtraData.length < extraFieldLen) {
+                throw new EOFException();
+            }
             try {
                 ze.setExtra(localExtraData);
             } catch (RuntimeException ex) {
diff --git a/src/main/java/org/apache/commons/compress/utils/IOUtils.java 
b/src/main/java/org/apache/commons/compress/utils/IOUtils.java
index 3587ced..3a1f582 100644
--- a/src/main/java/org/apache/commons/compress/utils/IOUtils.java
+++ b/src/main/java/org/apache/commons/compress/utils/IOUtils.java
@@ -280,4 +280,102 @@ public final class IOUtils {
     public static void copy(final File sourceFile, final OutputStream 
outputStream) throws IOException {
         Files.copy(sourceFile.toPath(), outputStream);
     }
+
+    /**
+     * Copies part of the content of a InputStream into an OutputStream.
+     * Uses a default buffer size of 8024 bytes.
+     *
+     * @param input
+     *            the InputStream to copy
+     * @param output
+     *            the target Stream
+     * @param len
+     *            maximum amount of bytes to copy
+     * @return the number of bytes copied
+     * @throws IOException
+     *             if an error occurs
+     * @since 1.21
+     */
+    public static long copyRange(final InputStream input, final long len, 
final OutputStream output)
+        throws IOException {
+        return copyRange(input, len, output, COPY_BUF_SIZE);
+    }
+
+    /**
+     * Copies part of the content of a InputStream into an OutputStream
+     *
+     * @param input
+     *            the InputStream to copy
+     * @param len
+     *            maximum amount of bytes to copy
+     * @param output
+     *            the target Stream
+     * @param buffersize
+     *            the buffer size to use, must be bigger than 0
+     * @return the number of bytes copied
+     * @throws IOException
+     *             if an error occurs
+     * @throws IllegalArgumentException
+     *             if buffersize is smaller than or equal to 0
+     * @since 1.21
+     */
+    public static long copyRange(final InputStream input, final long len, 
final OutputStream output,
+        final int buffersize) throws IOException {
+        if (buffersize < 1) {
+            throw new IllegalArgumentException("buffersize must be bigger than 
0");
+        }
+        final byte[] buffer = new byte[(int) Math.min(buffersize, len)];
+        int n = 0;
+        long count = 0;
+        while (count < len && -1 != (n = input.read(buffer, 0, (int) 
Math.min(len - count, buffer.length)))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    /**
+     * Gets part of the contents of an <code>InputStream</code> as a 
<code>byte[]</code>.
+     *
+     * @param input  the <code>InputStream</code> to read from
+     * @param len
+     *            maximum amount of bytes to copy
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since 1.21
+     */
+    public static byte[] readRange(final InputStream input, final int len) 
throws IOException {
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copyRange(input, len, output);
+        return output.toByteArray();
+    }
+
+    /**
+     * Gets part of the contents of an <code>ReadableByteChannel</code> as a 
<code>byte[]</code>.
+     *
+     * @param input  the <code>ReadableByteChannel</code> to read from
+     * @param len
+     *            maximum amount of bytes to copy
+     * @return the requested byte array
+     * @throws NullPointerException if the input is null
+     * @throws IOException if an I/O error occurs
+     * @since 1.21
+     */
+    public static byte[] readRange(final ReadableByteChannel input, final int 
len) throws IOException {
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        final ByteBuffer b = ByteBuffer.allocate(Math.min(len, COPY_BUF_SIZE));
+        int read = 0;
+        while (read < len) {
+            final int readNow = input.read(b);
+            if (readNow <= 0) {
+                break;
+            }
+            output.write(b.array(), 0, readNow);
+            b.rewind();
+            read += readNow;
+        }
+        return output.toByteArray();
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java 
b/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java
index 9b2b314..a692ef0 100644
--- a/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java
+++ b/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java
@@ -26,6 +26,8 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -92,24 +94,72 @@ public class IOUtilsTest {
         IOUtils.copy(new ByteArrayInputStream(ByteUtils.EMPTY_BYTE_ARRAY), new 
ByteArrayOutputStream(), 0);
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void copyRangeThrowsOnZeroBufferSize() throws IOException {
+        IOUtils.copyRange(new 
ByteArrayInputStream(ByteUtils.EMPTY_BYTE_ARRAY), 5, new 
ByteArrayOutputStream(), 0);
+    }
+
+    @Test
+    public void copyRangeDoesntCopyMoreThanAskedFor() throws IOException {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[] { 
1, 2, 3, 4, 5 });
+             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            Assert.assertEquals(3, IOUtils.copyRange(in, 3, out));
+            out.close();
+            Assert.assertArrayEquals(new byte[] { 1, 2, 3 }, 
out.toByteArray());
+        }
+    }
+
+    @Test
+    public void copyRangeStopsIfThereIsNothingToCopyAnymore() throws 
IOException {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[] { 
1, 2, 3, 4, 5 });
+             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            Assert.assertEquals(5, IOUtils.copyRange(in, 10, out));
+            out.close();
+            Assert.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, 
out.toByteArray());
+        }
+    }
+
+    @Test
+    public void readRangeFromStreamDoesntReadMoreThanAskedFor() throws 
IOException {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[] { 
1, 2, 3, 4, 5 })) {
+            byte[] read = IOUtils.readRange(in, 3);
+            Assert.assertArrayEquals(new byte[] { 1, 2, 3 }, read);
+            Assert.assertEquals(4, in.read());
+        }
+    }
+
+    @Test
+    public void readRangeFromStreamStopsIfThereIsNothingToReadAnymore() throws 
IOException {
+        try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[] { 
1, 2, 3, 4, 5 })) {
+            byte[] read = IOUtils.readRange(in, 10);
+            Assert.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, read);
+            Assert.assertEquals(-1, in.read());
+        }
+    }
+
+    @Test
+    public void readRangeFromChannelDoesntReadMoreThanAskedFor() throws 
IOException {
+        try (ReadableByteChannel in = new SeekableInMemoryByteChannel(new 
byte[] { 1, 2, 3, 4, 5 })) {
+            byte[] read = IOUtils.readRange(in, 3);
+            Assert.assertArrayEquals(new byte[] { 1, 2, 3 }, read);
+            final ByteBuffer b = ByteBuffer.allocate(1);
+            Assert.assertEquals(1, in.read(b));
+            Assert.assertArrayEquals(new byte[] { 4 }, b.array());
+        }
+    }
+
+    @Test
+    public void readRangeFromChannelStopsIfThereIsNothingToReadAnymore() 
throws IOException {
+        try (ReadableByteChannel in = new SeekableInMemoryByteChannel(new 
byte[] { 1, 2, 3, 4, 5 })) {
+            byte[] read = IOUtils.readRange(in, 10);
+            Assert.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, read);
+            final ByteBuffer b = ByteBuffer.allocate(1);
+            Assert.assertEquals(-1, in.read(b));
+        }
+    }
+
     private static void readFully(final byte[] source, final ByteBuffer b) 
throws IOException {
-        IOUtils.readFully(new ReadableByteChannel() {
-                private int idx;
-                @Override
-                public int read(final ByteBuffer buf) {
-                    if (idx >= source.length) {
-                        return -1;
-                    }
-                    buf.put(source[idx++]);
-                    return 1;
-                }
-                @Override
-                public void close() { }
-                @Override
-                public boolean isOpen() {
-                    return true;
-                }
-            }, b);
+        IOUtils.readFully(new SeekableInMemoryByteChannel(source), b);
     }
 
     private void skip(final StreamWrapper wrapper) throws Exception {

Reply via email to