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 <[email protected]>
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);
}
}