summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrançois Degros <fdegros@chromium.org>2022-03-18 11:52:59 +0000
committerCopybara-Service <copybara-worker@google.com>2022-03-18 05:06:23 -0700
commit8b4114dfa1faa320faae92a354f84883f88b46bc (patch)
tree014e922bd6c9e7768d98166b923d31830b95579b
parent923f5eb4bfa403d0f14d9cbca2cdf7fa7416ee67 (diff)
downloadzlib-8b4114dfa1faa320faae92a354f84883f88b46bc.tar.gz
[zip] Add UnzipOptions::continue_on_error
Added an option allowing the ZIP extraction to continue and try to extract as many files as possible even if some file extractions fail. BUG=chromium:953256 TEST=autoninja -C out/Default zlib_unittests && out/Default/zlib_unittests Change-Id: I2ddbfbdb3abe5d0cca3023d93fdff7fcdfc9baab Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3523968 Reviewed-by: Alex Danilo <adanilo@chromium.org> Commit-Queue: François Degros <fdegros@chromium.org> Cr-Commit-Position: refs/heads/main@{#982666} NOKEYCHECK=True GitOrigin-RevId: abc9bcb7f1c7757409f4f0ef6e1089c0276bb7e7
-rw-r--r--google/test/data/Mixed Paths.zipbin0 -> 9793 bytes
-rw-r--r--google/zip.cc10
-rw-r--r--google/zip.h3
-rw-r--r--google/zip_unittest.cc200
4 files changed, 205 insertions, 8 deletions
diff --git a/google/test/data/Mixed Paths.zip b/google/test/data/Mixed Paths.zip
new file mode 100644
index 0000000..2af418b
--- /dev/null
+++ b/google/test/data/Mixed Paths.zip
Binary files differ
diff --git a/google/zip.cc b/google/zip.cc
index 7f9af5b..1a43196 100644
--- a/google/zip.cc
+++ b/google/zip.cc
@@ -206,7 +206,9 @@ bool Unzip(const base::PlatformFile& src_file,
while (const ZipReader::Entry* const entry = reader.Next()) {
if (entry->is_unsafe) {
LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP";
- return false;
+ if (!options.continue_on_error)
+ return false;
+ continue;
}
if (options.filter && !options.filter.Run(entry->path)) {
@@ -218,7 +220,8 @@ bool Unzip(const base::PlatformFile& src_file,
// It's a directory.
if (!directory_creator.Run(entry->path)) {
LOG(ERROR) << "Cannot create directory " << Redact(entry->path);
- return false;
+ if (!options.continue_on_error)
+ return false;
}
continue;
@@ -229,7 +232,8 @@ bool Unzip(const base::PlatformFile& src_file,
if (!writer || !reader.ExtractCurrentEntry(writer.get())) {
LOG(ERROR) << "Cannot extract file " << Redact(entry->path)
<< " from ZIP";
- return false;
+ if (!options.continue_on_error)
+ return false;
}
}
diff --git a/google/zip.h b/google/zip.h
index 621f0d9..25ec655 100644
--- a/google/zip.h
+++ b/google/zip.h
@@ -182,6 +182,9 @@ struct UnzipOptions {
// Password to decrypt the encrypted files.
std::string password;
+
+ // Should ignore errors when extracting files?
+ bool continue_on_error = false;
};
typedef base::RepeatingCallback<std::unique_ptr<WriterDelegate>(
diff --git a/google/zip_unittest.cc b/google/zip_unittest.cc
index cf54991..8fbec32 100644
--- a/google/zip_unittest.cc
+++ b/google/zip_unittest.cc
@@ -471,8 +471,9 @@ TEST_F(ZipTest, UnzipEncryptedWithWrongPassword) {
EXPECT_EQ("This is not encrypted.\n", contents);
// No rubbish file should be left behind.
- EXPECT_FALSE(
- base::PathExists(test_dir_.AppendASCII("Encrypted ZipCrypto.txt")));
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+ UnorderedElementsAre("ClearText.txt"));
}
TEST_F(ZipTest, UnzipEncryptedWithNoPassword) {
@@ -491,8 +492,25 @@ TEST_F(ZipTest, UnzipEncryptedWithNoPassword) {
EXPECT_EQ("This is not encrypted.\n", contents);
// No rubbish file should be left behind.
- EXPECT_FALSE(
- base::PathExists(test_dir_.AppendASCII("Encrypted ZipCrypto.txt")));
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+ UnorderedElementsAre("ClearText.txt"));
+}
+
+TEST_F(ZipTest, UnzipEncryptedContinueOnError) {
+ EXPECT_TRUE(
+ zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"),
+ test_dir_, {.continue_on_error = true}));
+
+ std::string contents;
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"),
+ &contents));
+ EXPECT_EQ("This is not encrypted.\n", contents);
+
+ // No rubbish file should be left behind.
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+ UnorderedElementsAre("ClearText.txt"));
}
TEST_F(ZipTest, UnzipWrongCrc) {
@@ -500,7 +518,9 @@ TEST_F(ZipTest, UnzipWrongCrc) {
zip::Unzip(GetDataDirectory().AppendASCII("Wrong CRC.zip"), test_dir_));
// No rubbish file should be left behind.
- EXPECT_FALSE(base::PathExists(test_dir_.AppendASCII("Corrupted.txt")));
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+ UnorderedElementsAre());
}
TEST_F(ZipTest, UnzipRepeatedDirName) {
@@ -637,6 +657,148 @@ TEST_F(ZipTest, UnzipDifferentCases) {
#endif
}
+TEST_F(ZipTest, UnzipDifferentCasesContinueOnError) {
+ EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII(
+ "Repeated File Name With Different Cases.zip"),
+ test_dir_, {.continue_on_error = true}));
+
+ std::string contents;
+
+#if defined(OS_WIN) || defined(OS_MAC)
+ // Only the first file (with mixed case) has been extracted.
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+ UnorderedElementsAre("Case"));
+
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents));
+ EXPECT_EQ("Mixed case 111", contents);
+#else
+ // All the files have been extracted.
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+ UnorderedElementsAre("Case", "case", "CASE"));
+
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents));
+ EXPECT_EQ("Mixed case 111", contents);
+
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents));
+ EXPECT_EQ("Lower case 22", contents);
+
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents));
+ EXPECT_EQ("Upper case 3", contents);
+#endif
+}
+
+TEST_F(ZipTest, UnzipMixedPaths) {
+ EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("Mixed Paths.zip"),
+ test_dir_, {.continue_on_error = true}));
+
+ std::unordered_set<std::string> want_paths = {
+#ifdef OS_WIN
+ "Dot", //
+ "Space→", //
+ "a\\b", //
+ "u\\v\\w\\x\\y\\z", //
+ "←Backslash2", //
+#else
+ " ", // Invalid on Windows
+ "Angle <>", // Invalid on Windows
+ "Backslash1→\\", //
+ "Backspace \x08", // Invalid on Windows
+ "Bell \a", // Invalid on Windows
+ "C:", // Invalid on Windows
+ "C:\\", // Absolute path on Windows
+ "C:\\Temp", // Absolute path on Windows
+ "C:\\Temp\\", // Absolute path on Windows
+ "C:\\Temp\\File", // Absolute path on Windows
+ "Carriage Return \r", // Invalid on Windows
+ "Colon :", // Invalid on Windows
+ "Dot .", // Becomes "Dot" on Windows
+ "Double quote \"", // Invalid on Windows
+ "Escape \x1B", // Invalid on Windows
+ "Line Feed \n", // Invalid on Windows
+ "NUL .txt", // Disappears on Windows
+ "NUL", // Disappears on Windows
+ "NUL..txt", // Disappears on Windows
+ "NUL.tar.gz", // Disappears on Windows
+ "NUL.txt", // Disappears on Windows
+ "Pipe |", // Invalid on Windows
+ "Question ?", // Invalid on Windows
+ "Space→ ", // Becomes "Space→" on Windows
+ "Star *", // Invalid on Windows
+ "Tab \t", // Invalid on Windows
+ "\\\\server\\share\\file", // Absolute path on Windows
+ "\\←Backslash2", // Becomes "←Backslash2" on Windows
+ "a/b", //
+ "u/v/w/x/y/z", //
+#ifndef OS_MAC
+ "CASE", //
+ "Case", //
+#endif
+#endif
+ " NUL.txt", //
+ " ←Space", //
+ "$HOME", //
+ "%TMP", //
+ "-", //
+ "...Tree", //
+ "..Two", //
+ ".One", //
+ "Ampersand &", //
+ "At @", //
+ "Backslash3→\\←Backslash4", //
+ "Backtick `", //
+ "Caret ^", //
+ "Comma ,", //
+ "Curly {}", //
+ "Dash -", //
+ "Delete \x7F", //
+ "Dollar $", //
+ "Equal =", //
+ "Euro €", //
+ "Exclamation !", //
+ "FileOrDir", //
+ "First", //
+ "Hash #", //
+ "Last", //
+ "Percent %", //
+ "Plus +", //
+ "Quote '", //
+ "Round ()", //
+ "Semicolon ;", //
+ "Smile \U0001F642", //
+ "Square []", //
+ "String Terminator \u009C", //
+ "Tilde ~", //
+ "Underscore _", //
+ "case", //
+ "~", //
+ };
+
+ const std::vector<std::string> got_paths =
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES);
+
+ for (const std::string& path : got_paths) {
+ EXPECT_TRUE(want_paths.erase(path))
+ << "Found unexpected file: " << std::quoted(path);
+ }
+
+ for (const std::string& path : want_paths) {
+ EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path);
+ }
+
+ EXPECT_THAT(
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES),
+ UnorderedElementsAre(
+#ifdef OS_WIN
+ "Backslash3→", "Empty", "a", "u", "u\\v", "u\\v\\w", "u\\v\\w\\x",
+ "u\\v\\w\\x\\y"
+#else
+ "Empty", "a", "u", "u/v", "u/v/w", "u/v/w/x", "u/v/w/x/y"
+#endif
+ ));
+}
+
TEST_F(ZipTest, UnzipWithDelegates) {
auto dir_creator =
base::BindLambdaForTesting([this](const base::FilePath& entry_path) {
@@ -664,6 +826,34 @@ TEST_F(ZipTest, UnzipWithDelegates) {
EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt")));
}
+TEST_F(ZipTest, UnzipOnlyDirectories) {
+ auto dir_creator =
+ base::BindLambdaForTesting([this](const base::FilePath& entry_path) {
+ return base::CreateDirectory(test_dir_.Append(entry_path));
+ });
+
+ // Always return a null WriterDelegate.
+ auto writer =
+ base::BindLambdaForTesting([](const base::FilePath& entry_path) {
+ return std::unique_ptr<zip::WriterDelegate>();
+ });
+
+ base::File file(GetDataDirectory().AppendASCII("test.zip"),
+ base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
+ EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator,
+ {.continue_on_error = true}));
+ base::FilePath dir = test_dir_;
+ base::FilePath dir_foo = dir.AppendASCII("foo");
+ base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar");
+ EXPECT_FALSE(base::PathExists(dir.AppendASCII("foo.txt")));
+ EXPECT_TRUE(base::DirectoryExists(dir_foo));
+ EXPECT_FALSE(base::PathExists(dir_foo.AppendASCII("bar.txt")));
+ EXPECT_TRUE(base::DirectoryExists(dir_foo_bar));
+ EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII(".hidden")));
+ EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt")));
+ EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt")));
+}
+
// Tests that a ZIP archive containing SJIS-encoded file names can be correctly
// extracted if the encoding is specified.
TEST_F(ZipTest, UnzipSjis) {