You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2018/08/09 18:30:09 UTC

commons-compress git commit: ensure archive streams really mean EOF if they say so

Repository: commons-compress
Updated Branches:
  refs/heads/master 0fe6ae319 -> 64ed6dde0


ensure archive streams really mean EOF if they say so


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/64ed6dde
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/64ed6dde
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/64ed6dde

Branch: refs/heads/master
Commit: 64ed6dde03afbef6715fdfdeab5fc04be6192899
Parents: 0fe6ae3
Author: Stefan Bodewig <bo...@apache.org>
Authored: Thu Aug 9 20:29:39 2018 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu Aug 9 20:29:39 2018 +0200

----------------------------------------------------------------------
 .../archivers/cpio/CpioArchiveInputStream.java  |   4 +-
 .../compress/archivers/SevenZTestCase.java      | 143 +++++++++++++++++--
 .../archivers/ar/ArArchiveInputStreamTest.java  |  42 ++++++
 .../arj/ArjArchiveInputStreamTest.java          |  26 ++++
 .../cpio/CpioArchiveInputStreamTest.java        |  26 ++++
 .../dump/DumpArchiveInputStreamTest.java        |  25 ++++
 .../tar/TarArchiveInputStreamTest.java          |  24 ++++
 .../zip/ZipArchiveInputStreamTest.java          |  81 +++++++++++
 .../compress/archivers/zip/ZipFileTest.java     |  83 +++++++++++
 9 files changed, 442 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
----------------------------------------------------------------------
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 bdfc802..4cedc27 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
@@ -332,7 +332,9 @@ public class CpioArchiveInputStream extends ArchiveInputStream implements
                 this.crc &= 0xFFFFFFFFL;
             }
         }
-        this.entryBytesRead += tmpread;
+        if (tmpread > 0) {
+            this.entryBytesRead += tmpread;
+        }
 
         return tmpread;
     }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/SevenZTestCase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/SevenZTestCase.java b/src/test/java/org/apache/commons/compress/archivers/SevenZTestCase.java
index c5499c9..0b5bd95 100644
--- a/src/test/java/org/apache/commons/compress/archivers/SevenZTestCase.java
+++ b/src/test/java/org/apache/commons/compress/archivers/SevenZTestCase.java
@@ -22,16 +22,34 @@ import static org.junit.Assert.*;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
 
 import org.apache.commons.compress.AbstractTestCase;
 import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
 import org.apache.commons.compress.archivers.sevenz.SevenZFile;
 import org.apache.commons.compress.archivers.sevenz.SevenZMethod;
 import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
+import org.junit.Assume;
+import org.junit.Before;
 import org.junit.Test;
 
 public class SevenZTestCase extends AbstractTestCase {
 
+    private File output;
+    private final File file1, file2;
+
+    public SevenZTestCase() throws IOException {
+        file1 = getFile("test1.xml");
+        file2 = getFile("test2.xml");
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        output = new File(dir, "bla.7z");
+    }
+
     @Test
     public void testSevenZArchiveCreationUsingCopy() throws Exception {
         testSevenZArchiveCreation(SevenZMethod.COPY);
@@ -58,10 +76,23 @@ public class SevenZTestCase extends AbstractTestCase {
     }
 
     private void testSevenZArchiveCreation(final SevenZMethod method) throws Exception {
-        final File output = new File(dir, "bla.7z");
-        final File file1 = getFile("test1.xml");
-        final File file2 = getFile("test2.xml");
+        createArchive(method);
+        try (SevenZFile archive = new SevenZFile(output)) {
+            SevenZArchiveEntry entry;
+
+            entry = archive.getNextEntry();
+            assert (entry != null);
+            assertEquals(entry.getName(), file1.getName());
+
+            entry = archive.getNextEntry();
+            assert (entry != null);
+            assertEquals(entry.getName(), file2.getName());
+
+            assert (archive.getNextEntry() == null);
+        }
+    }
 
+    private void createArchive(final SevenZMethod method) throws Exception {
         final SevenZOutputFile outArchive = new SevenZOutputFile(output);
         outArchive.setContentCompression(method);
         try {
@@ -79,22 +110,100 @@ public class SevenZTestCase extends AbstractTestCase {
         } finally {
             outArchive.close();
         }
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingCopy() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.COPY);
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingLZMA() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.LZMA);
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingLZMA2() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.LZMA2);
+    }
 
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingBZIP2() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.BZIP2);
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.DEFLATE);
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingAES() throws Exception {
+        assumeStrongCryptoIsAvailable();
+        try (SevenZFile archive = new SevenZFile(getFile("bla.encrypted.7z"), "foo".toCharArray())) {
+            singleByteReadConsistentlyReturnsMinusOneAtEof(archive);
+        }
+    }
+
+    private void singleByteReadConsistentlyReturnsMinusOneAtEof(final SevenZMethod method) throws Exception {
+        createArchive(method);
         try (SevenZFile archive = new SevenZFile(output)) {
-            SevenZArchiveEntry entry;
+            singleByteReadConsistentlyReturnsMinusOneAtEof(archive);
+        }
+    }
 
-            entry = archive.getNextEntry();
-            assert (entry != null);
-            assertEquals(entry.getName(), file1.getName());
+    private void singleByteReadConsistentlyReturnsMinusOneAtEof(SevenZFile archive) throws Exception {
+        SevenZArchiveEntry entry = archive.getNextEntry();
+        entry = archive.getNextEntry();
+        readFully(archive);
+        assertEquals(-1, archive.read());
+        assertEquals(-1, archive.read());
+    }
 
-            entry = archive.getNextEntry();
-            assert (entry != null);
-            assertEquals(entry.getName(), file2.getName());
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingLZMA() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.LZMA);
+    }
 
-            assert (archive.getNextEntry() == null);
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingLZMA2() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.LZMA2);
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingBZIP2() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.BZIP2);
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(SevenZMethod.DEFLATE);
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingAES() throws Exception {
+        assumeStrongCryptoIsAvailable();
+        try (SevenZFile archive = new SevenZFile(getFile("bla.encrypted.7z"), "foo".toCharArray())) {
+            multiByteReadConsistentlyReturnsMinusOneAtEof(archive);
+        }
+    }
+
+    private void multiByteReadConsistentlyReturnsMinusOneAtEof(final SevenZMethod method) throws Exception {
+        createArchive(method);
+        try (SevenZFile archive = new SevenZFile(output)) {
+            multiByteReadConsistentlyReturnsMinusOneAtEof(archive);
         }
     }
 
+    private void multiByteReadConsistentlyReturnsMinusOneAtEof(SevenZFile archive) throws Exception {
+        final byte[] buf = new byte[2];
+        SevenZArchiveEntry entry = archive.getNextEntry();
+        entry = archive.getNextEntry();
+        readFully(archive);
+        assertEquals(-1, archive.read(buf));
+        assertEquals(-1, archive.read(buf));
+    }
+
     private void copy(final File src, final SevenZOutputFile dst) throws IOException {
         FileInputStream fis = null;
         try {
@@ -110,4 +219,16 @@ public class SevenZTestCase extends AbstractTestCase {
             }
         }
     }
+
+    private void readFully(final SevenZFile archive) throws IOException {
+        final byte[] buf = new byte[1024];
+        int x = 0;
+        while (0 <= (x = archive.read(buf))) {
+            ;
+        }
+    }
+
+    private static void assumeStrongCryptoIsAvailable() throws NoSuchAlgorithmException {
+        Assume.assumeTrue("test requires strong crypto", Cipher.getMaxAllowedKeyLength("AES/ECB/PKCS5Padding") >= 256);
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
index f93679f..6f2d0d5 100644
--- a/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
@@ -26,6 +26,7 @@ import java.io.FileInputStream;
 import org.apache.commons.compress.AbstractTestCase;
 import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.utils.ArchiveUtils;
+import org.apache.commons.compress.utils.IOUtils;
 import org.junit.Test;
 
 public class ArArchiveInputStreamTest extends AbstractTestCase {
@@ -58,4 +59,45 @@ public class ArArchiveInputStreamTest extends AbstractTestCase {
             assertNull(s.getNextEntry());
         }
     }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.ar"));
+             ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read());
+            assertEquals(-1, archive.read());
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        byte[] buf = new byte[2];
+        try (FileInputStream in = new FileInputStream(getFile("bla.ar"));
+             ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read(buf));
+            assertEquals(-1, archive.read(buf));
+        }
+    }
+
+    @org.junit.Ignore("COMPRESS-462")
+    @Test(expected=IllegalStateException.class)
+    public void cantReadWithoutOpeningAnEntry() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.ar"));
+             ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
+            archive.read();
+        }
+    }
+
+    @Test(expected=java.io.IOException.class)
+    public void cantReadAfterClose() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.ar"));
+             ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
+            archive.close();
+            archive.read();
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStreamTest.java
index 1983f1b..70a8b1f 100644
--- a/src/test/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStreamTest.java
@@ -26,6 +26,8 @@ import java.util.Calendar;
 import java.util.TimeZone;
 
 import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.utils.IOUtils;
 import org.junit.Test;
 
 public class ArjArchiveInputStreamTest extends AbstractTestCase {
@@ -81,4 +83,28 @@ public class ArjArchiveInputStreamTest extends AbstractTestCase {
         assertEquals(cal.getTime(), entry.getLastModifiedDate());
         in.close();
     }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.arj"));
+             ArjArchiveInputStream archive = new ArjArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read());
+            assertEquals(-1, archive.read());
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        byte[] buf = new byte[2];
+        try (FileInputStream in = new FileInputStream(getFile("bla.arj"));
+             ArjArchiveInputStream archive = new ArjArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read(buf));
+            assertEquals(-1, archive.read(buf));
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
index 762d464..55a9f75 100644
--- a/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
@@ -23,6 +23,8 @@ import static org.junit.Assert.*;
 import java.io.FileInputStream;
 
 import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.utils.IOUtils;
 import org.junit.Test;
 
 public class CpioArchiveInputStreamTest extends AbstractTestCase {
@@ -81,4 +83,28 @@ public class CpioArchiveInputStreamTest extends AbstractTestCase {
 
         assertEquals(2, count);
     }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.cpio"));
+             CpioArchiveInputStream archive = new CpioArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read());
+            assertEquals(-1, archive.read());
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        byte[] buf = new byte[2];
+        try (FileInputStream in = new FileInputStream(getFile("bla.cpio"));
+             CpioArchiveInputStream archive = new CpioArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read(buf));
+            assertEquals(-1, archive.read(buf));
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
index a3b6764..89f6f15 100644
--- a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java
@@ -24,7 +24,9 @@ import java.io.FileInputStream;
 import java.io.InputStream;
 
 import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.utils.IOUtils;
 import org.junit.Test;
 
 public class DumpArchiveInputStreamTest extends AbstractTestCase {
@@ -68,4 +70,27 @@ public class DumpArchiveInputStreamTest extends AbstractTestCase {
         dump.close();
     }
 
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.dump"));
+             DumpArchiveInputStream archive = new DumpArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read());
+            assertEquals(-1, archive.read());
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        byte[] buf = new byte[2];
+        try (FileInputStream in = new FileInputStream(getFile("bla.dump"));
+             DumpArchiveInputStream archive = new DumpArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read(buf));
+            assertEquals(-1, archive.read(buf));
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
index f16d584..b7dcb06 100644
--- a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
@@ -41,6 +41,7 @@ import java.util.Map;
 import java.util.TimeZone;
 import java.util.zip.GZIPInputStream;
 
+import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.utils.CharsetNames;
 import org.apache.commons.compress.utils.IOUtils;
 import org.junit.Test;
@@ -349,6 +350,29 @@ public class TarArchiveInputStreamTest {
         }
     }
 
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        try (FileInputStream in = new FileInputStream(getFile("bla.tar"));
+             TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read());
+            assertEquals(-1, archive.read());
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
+        byte[] buf = new byte[2];
+        try (FileInputStream in = new FileInputStream(getFile("bla.tar"));
+             TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read(buf));
+            assertEquals(-1, archive.read(buf));
+        }
+    }
+
     private TarArchiveInputStream getTestStream(final String name) {
         return new TarArchiveInputStream(
                 TarArchiveInputStreamTest.class.getResourceAsStream(name));

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java
index 5d1cdb1..5b65956 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStreamTest.java
@@ -340,6 +340,87 @@ public class ZipArchiveInputStreamTest {
         }
     }
 
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
+    }
+
+    private void singleByteReadConsistentlyReturnsMinusOneAtEof(File file) throws Exception {
+        try (FileInputStream in = new FileInputStream(file);
+             ZipArchiveInputStream archive = new ZipArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read());
+            assertEquals(-1, archive.read());
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
+    }
+
+    private void multiByteReadConsistentlyReturnsMinusOneAtEof(File file) throws Exception {
+        byte[] buf = new byte[2];
+        try (FileInputStream in = new FileInputStream(getFile("bla.zip"));
+             ZipArchiveInputStream archive = new ZipArchiveInputStream(in)) {
+            ArchiveEntry e = archive.getNextEntry();
+            IOUtils.toByteArray(archive);
+            assertEquals(-1, archive.read(buf));
+            assertEquals(-1, archive.read(buf));
+        }
+    }
+
     private static byte[] readEntry(ZipArchiveInputStream zip, ZipArchiveEntry zae) throws IOException {
         final int len = (int)zae.getSize();
         final byte[] buff = new byte[len];

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/64ed6dde/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
index 8e07d7e..cb6d197 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
@@ -616,6 +616,89 @@ public class ZipFileTest {
         }
     }
 
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
+    }
+
+    @Test
+    public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
+        singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
+    }
+
+    private void singleByteReadConsistentlyReturnsMinusOneAtEof(File file) throws Exception {
+        try (ZipFile archive = new ZipFile(file)) {
+            ZipArchiveEntry e = archive.getEntries().nextElement();
+            try (InputStream is = archive.getInputStream(e)) {
+                IOUtils.toByteArray(is);
+                assertEquals(-1, is.read());
+                assertEquals(-1, is.read());
+            }
+        }
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
+    }
+
+    @Test
+    public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
+        multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
+    }
+
+    private void multiByteReadConsistentlyReturnsMinusOneAtEof(File file) throws Exception {
+        byte[] buf = new byte[2];
+        try (ZipFile archive = new ZipFile(file)) {
+            ZipArchiveEntry e = archive.getEntries().nextElement();
+            try (InputStream is = archive.getInputStream(e)) {
+                IOUtils.toByteArray(is);
+                assertEquals(-1, is.read(buf));
+                assertEquals(-1, is.read(buf));
+            }
+        }
+    }
+
     private void assertAllReadMethods(byte[] expected, ZipFile zipFile, ZipArchiveEntry entry) {
         // simple IOUtil read
         try (InputStream stream = zf.getInputStream(entry)) {