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 488425c  only try to recover from broken 7z archive if explicitly 
asked to
488425c is described below

commit 488425c1b9fb8c8d0f1ef1ce7d665058880870e2
Author: Stefan Bodewig <bode...@apache.org>
AuthorDate: Sun Jun 27 22:06:22 2021 +0200

    only try to recover from broken 7z archive if explicitly asked to
---
 src/changes/changes.xml                            |  4 +++
 .../compress/archivers/sevenz/SevenZFile.java      |  7 +++-
 .../archivers/sevenz/SevenZFileOptions.java        | 42 ++++++++++++++++++++--
 .../compress/archivers/sevenz/SevenZFileTest.java  | 26 +++++++++++---
 4 files changed, 71 insertions(+), 8 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6225284..9b6cdfb 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -191,6 +191,10 @@ The <action> type attribute can be add,update,fix,remove.
         Also added sanity checks before even trying to parse an
         archive and made SevenZFileOptions' maxMemorySizeInKb apply to
         the stored metadata for an archive.
+
+        And further added an option that needs to be enabled in order
+        to make SevenZFile try to recover a broken archive. This is a
+        backwards incompatible change.
       </action>
       <action issue="COMPRESS-546" type="fix" date="2020-08-15"
               due-to="Maksim Zuev" dev="PeterLee">
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 967d6e9..fde329e 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
@@ -474,7 +474,12 @@ public class SevenZFile implements Closeable {
             return initializeArchive(startHeader, password, true);
         }
         // No valid header found - probably first file of multipart archive 
was removed too early. Scan for end header.
-        return tryToLocateEndHeader(password);
+        if (options.getTryToRecoverBrokenArchives()) {
+            return tryToLocateEndHeader(password);
+        }
+        throw new IOException("archive seems to be invalid.\nYou may want to 
retry and enable the"
+            + " tryToRecoverBrokenArchives if the archive could be a multi 
volume archive that has been closed"
+            + " prematurely.");
     }
 
     private Archive tryToLocateEndHeader(final byte[] password) throws 
IOException {
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFileOptions.java
 
b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFileOptions.java
index ad920b5..d886091 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFileOptions.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFileOptions.java
@@ -26,13 +26,17 @@ package org.apache.commons.compress.archivers.sevenz;
 public class SevenZFileOptions {
     private static final int DEFAUL_MEMORY_LIMIT_IN_KB = Integer.MAX_VALUE;
     private static final boolean DEFAULT_USE_DEFAULTNAME_FOR_UNNAMED_ENTRIES= 
false;
+    private static final boolean DEFAULT_TRY_TO_RECOVER_BROKEN_ARCHIVES = 
false;
 
     private final int maxMemoryLimitInKb;
     private final boolean useDefaultNameForUnnamedEntries;
+    private final boolean tryToRecoverBrokenArchives;
 
-    private SevenZFileOptions(final int maxMemoryLimitInKb, final boolean 
useDefaultNameForUnnamedEntries) {
+    private SevenZFileOptions(final int maxMemoryLimitInKb, final boolean 
useDefaultNameForUnnamedEntries,
+        final boolean tryToRecoverBrokenArchives) {
         this.maxMemoryLimitInKb = maxMemoryLimitInKb;
         this.useDefaultNameForUnnamedEntries = useDefaultNameForUnnamedEntries;
+        this.tryToRecoverBrokenArchives = tryToRecoverBrokenArchives;
     }
 
     /**
@@ -44,7 +48,8 @@ public class SevenZFileOptions {
      * </ul>
      */
     public static final SevenZFileOptions DEFAULT = new 
SevenZFileOptions(DEFAUL_MEMORY_LIMIT_IN_KB,
-        DEFAULT_USE_DEFAULTNAME_FOR_UNNAMED_ENTRIES);
+        DEFAULT_USE_DEFAULTNAME_FOR_UNNAMED_ENTRIES,
+        DEFAULT_TRY_TO_RECOVER_BROKEN_ARCHIVES);
 
     /**
      * Obtains a builder for SevenZFileOptions.
@@ -78,6 +83,15 @@ public class SevenZFileOptions {
     }
 
     /**
+     * Whether {@link SevenZFile} shall try to recover from a certain type of 
broken archive.
+     * @return whether SevenZFile shall try to recover from a certain type of 
broken archive.
+     * @since 1.21
+     */
+    public boolean getTryToRecoverBrokenArchives() {
+        return tryToRecoverBrokenArchives;
+    }
+
+    /**
      * Mutable builder for the immutable {@link SevenZFileOptions}.
      *
      * @since 1.19
@@ -85,6 +99,8 @@ public class SevenZFileOptions {
     public static class Builder {
         private int maxMemoryLimitInKb = DEFAUL_MEMORY_LIMIT_IN_KB;
         private boolean useDefaultNameForUnnamedEntries = 
DEFAULT_USE_DEFAULTNAME_FOR_UNNAMED_ENTRIES;
+        private boolean tryToRecoverBrokenArchives = 
DEFAULT_TRY_TO_RECOVER_BROKEN_ARCHIVES;
+
         /**
          * Sets the maximum amount of memory to use for parsing the
          * archive and during extraction.
@@ -114,12 +130,32 @@ public class SevenZFileOptions {
         }
 
         /**
+         * Sets whether {@link SevenZFile} will try to revover broken archives 
where the CRC of the file's metadata is
+         * 0.
+         *
+         * <p>This special kind of broken archive is encountered when mutli 
volume archives are closed prematurely. If
+         * you enable this option SevenZFile will trust data that looks as if 
it could contain metadata of an archive
+         * and allocate big amounts of memory. It is strongly recommended to 
not enable this option without setting
+         * {@link #withMaxMemoryLimitInKb} at the same time.
+         *
+         * @param tryToRecoverBrokenArchives if true SevenZFile will try to 
recover archives that are broken in the
+         * specific way
+         * @return the reconfigured builder
+         * @since 1.21
+         */
+        public Builder withTryToRecoverBrokenArchives(final boolean 
tryToRecoverBrokenArchives) {
+            this.tryToRecoverBrokenArchives = tryToRecoverBrokenArchives;
+            return this;
+        }
+
+        /**
          * Create the {@link SevenZFileOptions}.
          *
          * @return configured {@link SevenZFileOptions}.
          */
         public SevenZFileOptions build() {
-            return new SevenZFileOptions(maxMemoryLimitInKb, 
useDefaultNameForUnnamedEntries);
+            return new SevenZFileOptions(maxMemoryLimitInKb, 
useDefaultNameForUnnamedEntries,
+                tryToRecoverBrokenArchives);
         }
     }
 }
diff --git 
a/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZFileTest.java
 
b/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZFileTest.java
index 7dde40a..16ddd78 100644
--- 
a/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZFileTest.java
+++ 
b/src/test/java/org/apache/commons/compress/archivers/sevenz/SevenZFileTest.java
@@ -173,7 +173,12 @@ public class SevenZFileTest extends AbstractTestCase {
     }
 
     private void test7zUnarchive(final File f, final SevenZMethod m) throws 
Exception {
-        test7zUnarchive(f, m, (char[]) null);
+        test7zUnarchive(f, m, false);
+    }
+
+    private void test7zUnarchive(final File f, final SevenZMethod m, boolean 
tryToRecoverBrokenArchives)
+        throws Exception {
+        test7zUnarchive(f, m, (char[]) null, tryToRecoverBrokenArchives);
     }
 
     @Test
@@ -413,11 +418,17 @@ public class SevenZFileTest extends AbstractTestCase {
     }
 
     @Test
-    public void test7zUnarchiveWithDefectHeader() throws Exception {
+    public void test7zUnarchiveWithDefectHeaderFailsByDefault() throws 
Exception {
+        thrown.expect(IOException.class);
         test7zUnarchive(getFile("bla.noendheaderoffset.7z"), 
SevenZMethod.LZMA);
     }
 
     @Test
+    public void test7zUnarchiveWithDefectHeader() throws Exception {
+        test7zUnarchive(getFile("bla.noendheaderoffset.7z"), 
SevenZMethod.LZMA, true);
+    }
+
+    @Test
     public void extractSpecifiedFile() throws Exception {
         try (SevenZFile sevenZFile = new 
SevenZFile(getFile("COMPRESS-256.7z"))) {
             final String testTxtContents = 
"111111111111111111111111111000101011\n" +
@@ -725,7 +736,8 @@ public class SevenZFileTest extends AbstractTestCase {
         testFiles.add(getPath("COMPRESS-542-endheadercorrupted2.7z"));
 
         for (final Path file : testFiles) {
-            try (SevenZFile sevenZFile = new 
SevenZFile(Files.newByteChannel(file))) {
+            try (SevenZFile sevenZFile = new 
SevenZFile(Files.newByteChannel(file),
+                     
SevenZFileOptions.builder().withTryToRecoverBrokenArchives(true).build())) {
                 fail("Expected IOException: start header corrupt and unable to 
guess end header");
             } catch (final IOException e) {
                 assertEquals("Start header corrupt and unable to guess end 
header", e.getMessage());
@@ -751,7 +763,13 @@ public class SevenZFileTest extends AbstractTestCase {
     }
 
     private void test7zUnarchive(final File f, final SevenZMethod m, final 
char[] password) throws Exception {
-        try (SevenZFile sevenZFile = new SevenZFile(f, password)) {
+        test7zUnarchive(f, m, password, false);
+    }
+
+    private void test7zUnarchive(final File f, final SevenZMethod m, final 
char[] password,
+        final boolean tryToRecoverBrokenArchives) throws Exception {
+        try (SevenZFile sevenZFile = new SevenZFile(f, password,
+                 
SevenZFileOptions.builder().withTryToRecoverBrokenArchives(tryToRecoverBrokenArchives).build()))
 {
             test7zUnarchive(sevenZFile, m);
         }
     }

Reply via email to