This is an automated email from the ASF dual-hosted git repository. peterlee 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 b927280 COMPRESS-544 : truncated tar detect b927280 is described below commit b92728060ae6b82904d7d1453b05223f2917fc73 Author: PeterAlfredLee <peteralfred...@gmail.com> AuthorDate: Thu Aug 13 16:30:55 2020 +0800 COMPRESS-544 : truncated tar detect TarArchiveInputStream can not detect truncated tar archive when skipping bytes. This is a fix for it. --- src/changes/changes.xml | 4 ++ .../archivers/tar/TarArchiveInputStream.java | 50 ++++++++++++++++++--- .../archivers/tar/TarArchiveInputStreamTest.java | 18 ++++++++ .../COMPRESS-544_truncated_in_content.tar | Bin 0 -> 2067 bytes .../COMPRESS-544_truncated_in_padding.tar | Bin 0 -> 2076 bytes 5 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 934573d..4301093 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -173,6 +173,10 @@ The <action> type attribute can be add,update,fix,remove. Fix for test fails on Windows. The tests are failing because the default charset is not UTF-8. </action> + <action issue="COMPRESS-544" type="fix" date="2020-08-13" due-to="Aditya Prasad" dev="PeterLee"> + TarArchiveInputStream can not detect a truncated tar in skip() + and skipRecordPadding(). + </action> </release> <release version="1.20" date="2020-02-08" description="Release 1.20"> diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java index 5c08ba0..3b99366 100644 --- a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java @@ -24,6 +24,7 @@ package org.apache.commons.compress.archivers.tar; import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -261,8 +262,8 @@ public class TarArchiveInputStream extends ArchiveInputStream { * @param n * the number of bytes to be skipped. * @return the actual number of bytes skipped. - * @throws IOException - * if some other I/O error occurs. + * @throws IOException if a truncated tar archive is detected + * or some other I/O error occurs */ @Override public long skip(final long n) throws IOException { @@ -270,13 +271,19 @@ public class TarArchiveInputStream extends ArchiveInputStream { return 0; } + final long availableOfInputStream = inputStream.available(); final long available = currEntry.getRealSize() - entryOffset; - final long skipped; + final long numToSkip = Math.min(n, available); + long skipped; + if (!currEntry.isSparse()) { - skipped = IOUtils.skip(inputStream, Math.min(n, available)); + skipped = IOUtils.skip(inputStream, numToSkip); } else { - skipped = skipSparse(Math.min(n, available)); + skipped = skipSparse(numToSkip); } + + skipped = getActuallySkipped(availableOfInputStream, skipped, numToSkip); + count(skipped); entryOffset += skipped; return skipped; @@ -436,18 +443,47 @@ public class TarArchiveInputStream extends ArchiveInputStream { /** * The last record block should be written at the full size, so skip any - * additional space used to fill a record after an entry + * additional space used to fill a record after an entry. + * + * @throws IOException if a truncated tar archive is detected */ private void skipRecordPadding() throws IOException { if (!isDirectory() && this.entrySize > 0 && this.entrySize % this.recordSize != 0) { + final long available = inputStream.available(); final long numRecords = (this.entrySize / this.recordSize) + 1; final long padding = (numRecords * this.recordSize) - this.entrySize; - final long skipped = IOUtils.skip(inputStream, padding); + long skipped = IOUtils.skip(inputStream, padding); + + skipped = getActuallySkipped(available, skipped, padding); + count(skipped); } } /** + * For FileInputStream, the skip always return the number you input, so we + * need the available bytes to determine how many bytes are actually skipped + * + * @param available available bytes returned by inputStream.available() + * @param skipped skipped bytes returned by inputStream.skip() + * @param expected bytes expected to skip + * @return number of bytes actually skipped + * @throws IOException if a truncated tar archive is detected + */ + private long getActuallySkipped(final long available, final long skipped, final long expected) throws IOException { + long actuallySkipped = skipped; + if (inputStream instanceof FileInputStream) { + actuallySkipped = Math.min(skipped, available); + } + + if (actuallySkipped != expected) { + throw new IOException("Truncated TAR archive"); + } + + return actuallySkipped; + } + + /** * Get the next entry in this tar archive as longname data. * * @return The next entry in the archive as longname data, or null. 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 8218af5..924542f 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 @@ -442,6 +442,24 @@ public class TarArchiveInputStreamTest extends AbstractTestCase { } } + @Test(expected = IOException.class) + public void testParseTarTruncatedInPadding() throws IOException { + try (FileInputStream in = new FileInputStream(getFile("./COMPRESS-544_truncated_in_padding.tar")); + TarArchiveInputStream archive = new TarArchiveInputStream(in)) { + while (archive.getNextTarEntry() != null) { + } + } + } + + @Test(expected = IOException.class) + public void testParseTarTruncatedInContent() throws IOException { + try (FileInputStream in = new FileInputStream(getFile("./COMPRESS-544_truncated_in_content.tar")); + TarArchiveInputStream archive = new TarArchiveInputStream(in)) { + while (archive.getNextTarEntry() != null) { + } + } + } + private TarArchiveInputStream getTestStream(final String name) { return new TarArchiveInputStream( TarArchiveInputStreamTest.class.getResourceAsStream(name)); diff --git a/src/test/resources/COMPRESS-544_truncated_in_content.tar b/src/test/resources/COMPRESS-544_truncated_in_content.tar new file mode 100644 index 0000000..bfcfb9b Binary files /dev/null and b/src/test/resources/COMPRESS-544_truncated_in_content.tar differ diff --git a/src/test/resources/COMPRESS-544_truncated_in_padding.tar b/src/test/resources/COMPRESS-544_truncated_in_padding.tar new file mode 100644 index 0000000..94caf3f Binary files /dev/null and b/src/test/resources/COMPRESS-544_truncated_in_padding.tar differ