summaryrefslogtreecommitdiff
path: root/google
diff options
context:
space:
mode:
Diffstat (limited to 'google')
-rw-r--r--google/BUILD.gn2
-rw-r--r--google/compression_utils.cc29
-rw-r--r--google/compression_utils.h2
-rw-r--r--google/compression_utils_portable.cc2
-rw-r--r--google/compression_utils_portable.h2
-rw-r--r--google/compression_utils_unittest.cc2
-rw-r--r--google/redact.h2
-rw-r--r--google/test/data/Mixed Paths.zipbin9793 -> 12814 bytes
-rw-r--r--google/test/data/Windows Special Names.zipbin565 -> 3985 bytes
-rwxr-xr-xgoogle/test/data/create_test_zip.sh2
-rw-r--r--google/zip.cc7
-rw-r--r--google/zip.h8
-rw-r--r--google/zip_internal.cc2
-rw-r--r--google/zip_internal.h2
-rw-r--r--google/zip_reader.cc192
-rw-r--r--google/zip_reader.h73
-rw-r--r--google/zip_reader_unittest.cc20
-rw-r--r--google/zip_unittest.cc257
-rw-r--r--google/zip_writer.cc2
-rw-r--r--google/zip_writer.h2
20 files changed, 457 insertions, 151 deletions
diff --git a/google/BUILD.gn b/google/BUILD.gn
index e996b16..35ba1da 100644
--- a/google/BUILD.gn
+++ b/google/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/google/compression_utils.cc b/google/compression_utils.cc
index 781c805..279ea07 100644
--- a/google/compression_utils.cc
+++ b/google/compression_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -24,8 +24,8 @@ bool GzipCompress(base::span<const char> input,
// uLongf can be larger than size_t.
uLongf compressed_size_long = static_cast<uLongf>(output_buffer_size);
if (zlib_internal::GzipCompressHelper(
- bit_cast<Bytef*>(output_buffer), &compressed_size_long,
- bit_cast<const Bytef*>(input.data()),
+ base::bit_cast<Bytef*>(output_buffer), &compressed_size_long,
+ base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size()), malloc_fn, free_fn) != Z_OK) {
return false;
}
@@ -53,9 +53,10 @@ bool GzipCompress(base::span<const uint8_t> input, std::string* output) {
return false;
}
- if (zlib_internal::GzipCompressHelper(compressed_data, &compressed_data_size,
- bit_cast<const Bytef*>(input.data()),
- input_size, nullptr, nullptr) != Z_OK) {
+ if (zlib_internal::GzipCompressHelper(
+ compressed_data, &compressed_data_size,
+ base::bit_cast<const Bytef*>(input.data()), input_size, nullptr,
+ nullptr) != Z_OK) {
free(compressed_data);
return false;
}
@@ -76,13 +77,13 @@ bool GzipCompress(base::span<const uint8_t> input, std::string* output) {
bool GzipUncompress(const std::string& input, std::string* output) {
std::string uncompressed_output;
uLongf uncompressed_size = static_cast<uLongf>(GetUncompressedSize(input));
- if (uncompressed_size > uncompressed_output.max_size())
+ if (size_t{uncompressed_size} > uncompressed_output.max_size())
return false;
uncompressed_output.resize(uncompressed_size);
if (zlib_internal::GzipUncompressHelper(
- bit_cast<Bytef*>(uncompressed_output.data()), &uncompressed_size,
- bit_cast<const Bytef*>(input.data()),
+ base::bit_cast<Bytef*>(uncompressed_output.data()),
+ &uncompressed_size, base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.length())) == Z_OK) {
output->swap(uncompressed_output);
return true;
@@ -101,8 +102,8 @@ bool GzipUncompress(base::span<const uint8_t> input,
if (uncompressed_size > output.size())
return false;
return zlib_internal::GzipUncompressHelper(
- bit_cast<Bytef*>(output.data()), &uncompressed_size,
- bit_cast<const Bytef*>(input.data()),
+ base::bit_cast<Bytef*>(output.data()), &uncompressed_size,
+ base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
@@ -116,8 +117,8 @@ bool GzipUncompress(base::span<const uint8_t> input, std::string* output) {
uLongf uncompressed_size = GetUncompressedSize(input);
output->resize(uncompressed_size);
return zlib_internal::GzipUncompressHelper(
- bit_cast<Bytef*>(output->data()), &uncompressed_size,
- bit_cast<const Bytef*>(input.data()),
+ base::bit_cast<Bytef*>(output->data()), &uncompressed_size,
+ base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
@@ -127,7 +128,7 @@ uint32_t GetUncompressedSize(base::span<const char> compressed_data) {
uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data) {
return zlib_internal::GetGzipUncompressedSize(
- bit_cast<Bytef*>(compressed_data.data()), compressed_data.size());
+ base::bit_cast<Bytef*>(compressed_data.data()), compressed_data.size());
}
} // namespace compression
diff --git a/google/compression_utils.h b/google/compression_utils.h
index cca47be..ea39981 100644
--- a/google/compression_utils.h
+++ b/google/compression_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/google/compression_utils_portable.cc b/google/compression_utils_portable.cc
index 331e41e..49d6bfe 100644
--- a/google/compression_utils_portable.cc
+++ b/google/compression_utils_portable.cc
@@ -1,6 +1,6 @@
/* compression_utils_portable.cc
*
- * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
diff --git a/google/compression_utils_portable.h b/google/compression_utils_portable.h
index c1f3775..92b033e 100644
--- a/google/compression_utils_portable.h
+++ b/google/compression_utils_portable.h
@@ -1,6 +1,6 @@
/* compression_utils_portable.h
*
- * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
diff --git a/google/compression_utils_unittest.cc b/google/compression_utils_unittest.cc
index 76572e5..8e24387 100644
--- a/google/compression_utils_unittest.cc
+++ b/google/compression_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/google/redact.h b/google/redact.h
index ea7da16..df6bcaf 100644
--- a/google/redact.h
+++ b/google/redact.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_
diff --git a/google/test/data/Mixed Paths.zip b/google/test/data/Mixed Paths.zip
index 2af418b..881f498 100644
--- a/google/test/data/Mixed Paths.zip
+++ b/google/test/data/Mixed Paths.zip
Binary files differ
diff --git a/google/test/data/Windows Special Names.zip b/google/test/data/Windows Special Names.zip
index 3b8a3ab..4fac907 100644
--- a/google/test/data/Windows Special Names.zip
+++ b/google/test/data/Windows Special Names.zip
Binary files differ
diff --git a/google/test/data/create_test_zip.sh b/google/test/data/create_test_zip.sh
index f4cc635..620e8f6 100755
--- a/google/test/data/create_test_zip.sh
+++ b/google/test/data/create_test_zip.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Copyright 2011 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
diff --git a/google/zip.cc b/google/zip.cc
index 1a43196..490dcee 100644
--- a/google/zip.cc
+++ b/google/zip.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -229,7 +229,10 @@ bool Unzip(const base::PlatformFile& src_file,
// It's a file.
std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry->path);
- if (!writer || !reader.ExtractCurrentEntry(writer.get())) {
+ if (!writer ||
+ (options.progress ? !reader.ExtractCurrentEntryWithListener(
+ writer.get(), options.progress)
+ : !reader.ExtractCurrentEntry(writer.get()))) {
LOG(ERROR) << "Cannot extract file " << Redact(entry->path)
<< " from ZIP";
if (!options.continue_on_error)
diff --git a/google/zip.h b/google/zip.h
index 25ec655..e3036c8 100644
--- a/google/zip.h
+++ b/google/zip.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -170,6 +170,9 @@ bool ZipFiles(const base::FilePath& src_dir,
int dest_fd);
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Callback reporting the number of bytes written during Unzip.
+using UnzipProgressCallback = base::RepeatingCallback<void(uint64_t bytes)>;
+
// Options of the Unzip function, with valid default values.
struct UnzipOptions {
// Encoding of entry paths in the ZIP archive. By default, paths are assumed
@@ -180,6 +183,9 @@ struct UnzipOptions {
// everything gets extracted.
FilterCallback filter;
+ // Callback to report bytes extracted from the ZIP.
+ UnzipProgressCallback progress;
+
// Password to decrypt the encrypted files.
std::string password;
diff --git a/google/zip_internal.cc b/google/zip_internal.cc
index 1adf2e6..e65d7ce 100644
--- a/google/zip_internal.cc
+++ b/google/zip_internal.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/google/zip_internal.h b/google/zip_internal.h
index 92833fa..f107d7f 100644
--- a/google/zip_internal.h
+++ b/google/zip_internal.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/google/zip_reader.cc b/google/zip_reader.cc
index b8143cd..e97027a 100644
--- a/google/zip_reader.cc
+++ b/google/zip_reader.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -59,6 +59,26 @@ std::ostream& operator<<(std::ostream& out, UnzipError error) {
#undef SWITCH_ERR
}
+bool IsValidFileNameCharacterOnWindows(char16_t c) {
+ if (c < 32)
+ return false;
+
+ switch (c) {
+ case '<': // Less than
+ case '>': // Greater than
+ case ':': // Colon
+ case '"': // Double quote
+ case '|': // Vertical bar or pipe
+ case '?': // Question mark
+ case '*': // Asterisk
+ case '/': // Forward slash
+ case '\\': // Backslash
+ return false;
+ }
+
+ return true;
+}
+
// A writer delegate that writes to a given string.
class StringWriterDelegate : public WriterDelegate {
public:
@@ -210,21 +230,19 @@ bool ZipReader::OpenEntry() {
return false;
}
- entry_.path = base::FilePath::FromUTF16Unsafe(path_in_utf16);
- entry_.original_size = info.uncompressed_size;
-
- // Directory entries in ZIP have a path ending with "/".
- entry_.is_directory = base::EndsWith(path_in_utf16, u"/");
+ // Normalize path.
+ Normalize(path_in_utf16);
- // Check the entry path for directory traversal issues. We consider entry
- // paths unsafe if they are absolute or if they contain "..". On Windows,
- // IsAbsolute() returns false for paths starting with "/".
- entry_.is_unsafe = entry_.path.ReferencesParent() ||
- entry_.path.IsAbsolute() ||
- base::StartsWith(path_in_utf16, u"/");
+ entry_.original_size = info.uncompressed_size;
// The file content of this entry is encrypted if flag bit 0 is set.
entry_.is_encrypted = info.flag & 1;
+ if (entry_.is_encrypted) {
+ // Is the entry AES encrypted.
+ entry_.uses_aes_encryption = info.compression_method == 99;
+ } else {
+ entry_.uses_aes_encryption = false;
+ }
// Construct the last modified time. The timezone info is not present in ZIP
// archives, so we construct the time as UTC.
@@ -249,7 +267,88 @@ bool ZipReader::OpenEntry() {
return true;
}
+void ZipReader::Normalize(base::StringPiece16 in) {
+ entry_.is_unsafe = true;
+
+ // Directory entries in ZIP have a path ending with "/".
+ entry_.is_directory = base::EndsWith(in, u"/");
+
+ std::u16string normalized_path;
+ if (base::StartsWith(in, u"/")) {
+ normalized_path = u"ROOT";
+ entry_.is_unsafe = false;
+ }
+
+ for (;;) {
+ // Consume initial path separators.
+ const base::StringPiece16::size_type i = in.find_first_not_of(u'/');
+ if (i == base::StringPiece16::npos)
+ break;
+
+ in.remove_prefix(i);
+ DCHECK(!in.empty());
+
+ // Isolate next path component.
+ const base::StringPiece16 part = in.substr(0, in.find_first_of(u'/'));
+ DCHECK(!part.empty());
+
+ in.remove_prefix(part.size());
+
+ if (!normalized_path.empty())
+ normalized_path += u'/';
+
+ if (part == u".") {
+ normalized_path += u"DOT";
+ entry_.is_unsafe = true;
+ continue;
+ }
+
+ if (part == u"..") {
+ normalized_path += u"UP";
+ entry_.is_unsafe = true;
+ continue;
+ }
+
+ // Windows has more restrictions than other systems when it comes to valid
+ // file paths. Replace Windows-invalid characters on all systems for
+ // consistency. In particular, this prevents a path component containing
+ // colon and backslash from being misinterpreted as an absolute path on
+ // Windows.
+ for (const char16_t c : part) {
+ normalized_path += IsValidFileNameCharacterOnWindows(c) ? c : 0xFFFD;
+ }
+
+ entry_.is_unsafe = false;
+ }
+
+ // If the entry is a directory, add the final path separator to the entry
+ // path.
+ if (entry_.is_directory && !normalized_path.empty()) {
+ normalized_path += u'/';
+ entry_.is_unsafe = false;
+ }
+
+ entry_.path = base::FilePath::FromUTF16Unsafe(normalized_path);
+
+ // By construction, we should always get a relative path.
+ DCHECK(!entry_.path.IsAbsolute()) << entry_.path;
+}
+
+void ZipReader::ReportProgress(ListenerCallback listener_callback,
+ uint64_t bytes) const {
+ delta_bytes_read_ += bytes;
+
+ const base::TimeTicks now = base::TimeTicks::Now();
+ if (next_progress_report_time_ > now)
+ return;
+
+ next_progress_report_time_ = now + progress_period_;
+ listener_callback.Run(delta_bytes_read_);
+ delta_bytes_read_ = 0;
+}
+
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
+ ListenerCallback listener_callback,
uint64_t num_bytes_to_extract) const {
DCHECK(zip_file_);
DCHECK_LT(0, next_index_);
@@ -290,6 +389,10 @@ bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
break;
}
+ if (listener_callback) {
+ ReportProgress(listener_callback, num_bytes_read);
+ }
+
DCHECK_LT(0, num_bytes_read);
CHECK_LE(num_bytes_read, internal::kZipBufSize);
@@ -325,9 +428,26 @@ bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
delegate->OnError();
}
+ if (listener_callback) {
+ listener_callback.Run(delta_bytes_read_);
+ delta_bytes_read_ = 0;
+ }
+
return entire_file_extracted;
}
+bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
+ uint64_t num_bytes_to_extract) const {
+ return ExtractCurrentEntry(delegate, ListenerCallback(),
+ num_bytes_to_extract);
+}
+
+bool ZipReader::ExtractCurrentEntryWithListener(
+ WriterDelegate* delegate,
+ ListenerCallback listener_callback) const {
+ return ExtractCurrentEntry(delegate, listener_callback);
+}
+
void ZipReader::ExtractCurrentEntryToFilePathAsync(
const base::FilePath& output_file_path,
SuccessCallback success_callback,
@@ -439,6 +559,7 @@ void ZipReader::Reset() {
next_index_ = 0;
reached_end_ = true;
ok_ = false;
+ delta_bytes_read_ = 0;
entry_ = {};
}
@@ -504,12 +625,26 @@ FileWriterDelegate::~FileWriterDelegate() {}
bool FileWriterDelegate::PrepareOutput() {
DCHECK(file_);
- const bool ok = file_->IsValid();
- if (ok) {
- DCHECK_EQ(file_->GetLength(), 0)
- << " The output file should be initially empty";
+
+ if (!file_->IsValid()) {
+ LOG(ERROR) << "File is not valid";
+ return false;
}
- return ok;
+
+ const int64_t length = file_->GetLength();
+ if (length < 0) {
+ PLOG(ERROR) << "Cannot get length of file handle "
+ << file_->GetPlatformFile();
+ return false;
+ }
+
+ // Just log a warning if the file is not empty.
+ // See crbug.com/1309879 and crbug.com/774762.
+ LOG_IF(WARNING, length > 0)
+ << "File handle " << file_->GetPlatformFile()
+ << " is not empty: Its length is " << length << " bytes";
+
+ return true;
}
bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
@@ -553,10 +688,25 @@ bool FilePathWriterDelegate::PrepareOutput() {
owned_file_.Initialize(output_file_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
- PLOG_IF(ERROR, !owned_file_.IsValid())
- << "Cannot create file " << Redact(output_file_path_) << ": "
- << base::File::ErrorToString(owned_file_.error_details());
- return FileWriterDelegate::PrepareOutput();
+ if (!owned_file_.IsValid()) {
+ PLOG(ERROR) << "Cannot create file " << Redact(output_file_path_) << ": "
+ << base::File::ErrorToString(owned_file_.error_details());
+ return false;
+ }
+
+ const int64_t length = owned_file_.GetLength();
+ if (length < 0) {
+ PLOG(ERROR) << "Cannot get length of file " << Redact(output_file_path_);
+ return false;
+ }
+
+ if (length > 0) {
+ LOG(ERROR) << "File " << Redact(output_file_path_)
+ << " is not empty: Its length is " << length << " bytes";
+ return false;
+ }
+
+ return true;
}
void FilePathWriterDelegate::OnError() {
diff --git a/google/zip_reader.h b/google/zip_reader.h
index df7452a..48244c8 100644
--- a/google/zip_reader.h
+++ b/google/zip_reader.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
@@ -85,6 +85,9 @@ class ZipReader {
// A callback that is called periodically during the operation with the number
// of bytes that have been processed so far.
using ProgressCallback = base::RepeatingCallback<void(int64_t)>;
+ // A callback that is called periodically during the operation with the number
+ // of bytes that have been processed since the previous call (i.e. delta).
+ using ListenerCallback = base::RepeatingCallback<void(uint64_t)>;
// Information of an entry (file or directory) in a ZIP archive.
struct Entry {
@@ -94,9 +97,14 @@ class ZipReader {
// if it wants to interpret this path correctly.
std::string path_in_original_encoding;
- // Path of the entry, converted to Unicode. This path is usually relative
- // (eg "foo/bar.txt"), but it can also be absolute (eg "/foo/bar.txt") or
- // parent-relative (eg "../foo/bar.txt"). See also |is_unsafe|.
+ // Path of the entry, converted to Unicode. This path is relative (eg
+ // "foo/bar.txt"). Absolute paths (eg "/foo/bar.txt") or paths containing
+ // ".." or "." components (eg "../foo/bar.txt") are converted to safe
+ // relative paths. Eg:
+ // (In ZIP) -> (Entry.path)
+ // /foo/bar -> ROOT/foo/bar
+ // ../a -> UP/a
+ // ./a -> DOT/a
base::FilePath path;
// Size of the original uncompressed file, or 0 if the entry is a directory.
@@ -120,14 +128,17 @@ class ZipReader {
// True if the entry is a directory.
// False if the entry is a file.
- bool is_directory;
+ bool is_directory = false;
- // True if the entry path is considered unsafe, ie if it is absolute or if
- // it contains "..".
- bool is_unsafe;
+ // True if the entry path cannot be converted to a safe relative path. This
+ // happens if a file entry (not a directory) has a filename "." or "..".
+ bool is_unsafe = false;
// True if the file content is encrypted.
- bool is_encrypted;
+ bool is_encrypted = false;
+
+ // True if the encryption scheme is AES.
+ bool uses_aes_encryption = false;
// Entry POSIX permissions (POSIX systems only).
int posix_mode;
@@ -200,6 +211,17 @@ class ZipReader {
uint64_t num_bytes_to_extract =
std::numeric_limits<uint64_t>::max()) const;
+ // Extracts the current entry to |delegate|, starting from the beginning
+ // of the entry, calling |listener_callback| regularly with the number of
+ // bytes extracted.
+ //
+ // Returns true if the entire file was extracted without error.
+ //
+ // Precondition: Next() returned a non-null Entry.
+ bool ExtractCurrentEntryWithListener(
+ WriterDelegate* delegate,
+ ListenerCallback listener_callback) const;
+
// Asynchronously extracts the current entry to the given output file path. If
// the current entry is a directory it just creates the directory
// synchronously instead.
@@ -257,6 +279,25 @@ class ZipReader {
// reset automatically as needed.
bool OpenEntry();
+ // Normalizes the given path passed as UTF-16 string piece. Sets entry_.path,
+ // entry_.is_directory and entry_.is_unsafe.
+ void Normalize(base::StringPiece16 in);
+
+ // Runs the ListenerCallback at a throttled rate.
+ void ReportProgress(ListenerCallback listener_callback, uint64_t bytes) const;
+
+ // Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
+ // starting from the beginning of the entry calling |listener_callback| if
+ // its supplied.
+ //
+ // Returns true if the entire file was extracted without error.
+ //
+ // Precondition: Next() returned a non-null Entry.
+ bool ExtractCurrentEntry(WriterDelegate* delegate,
+ ListenerCallback listener_callback,
+ uint64_t num_bytes_to_extract =
+ std::numeric_limits<uint64_t>::max()) const;
+
// Extracts a chunk of the file to the target. Will post a task for the next
// chunk and success/failure/progress callbacks as necessary.
void ExtractChunk(base::File target_file,
@@ -274,11 +315,21 @@ class ZipReader {
bool ok_;
Entry entry_;
+ // Next time to report progress.
+ mutable base::TimeTicks next_progress_report_time_ = base::TimeTicks::Now();
+
+ // Progress time delta.
+ // TODO(crbug.com/953256) Add this as parameter to the unzip options.
+ base::TimeDelta progress_period_ = base::Milliseconds(1000);
+
+ // Number of bytes read since last progress report callback executed.
+ mutable uint64_t delta_bytes_read_ = 0;
+
base::WeakPtrFactory<ZipReader> weak_ptr_factory_{this};
};
-// A writer delegate that writes to a given File. This file is expected to be
-// initially empty.
+// A writer delegate that writes to a given File. It is recommended that this
+// file be initially empty.
class FileWriterDelegate : public WriterDelegate {
public:
// Constructs a FileWriterDelegate that manipulates |file|. The delegate will
diff --git a/google/zip_reader_unittest.cc b/google/zip_reader_unittest.cc
index fc80637..52dab20 100644
--- a/google/zip_reader_unittest.cc
+++ b/google/zip_reader_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -308,19 +308,18 @@ TEST_F(ZipReaderTest, DotDotFile) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil.zip")));
base::FilePath target_path(FILE_PATH_LITERAL(
- "../levilevilevilevilevilevilevilevilevilevilevilevil"));
+ "UP/levilevilevilevilevilevilevilevilevilevilevilevil"));
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
- // This file is unsafe because of ".." in the file name.
- EXPECT_TRUE(entry->is_unsafe);
+ EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
TEST_F(ZipReaderTest, InvalidUTF8File) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil_via_invalid_utf8.zip")));
- base::FilePath target_path = base::FilePath::FromUTF8Unsafe(".�.\\evil.txt");
+ base::FilePath target_path = base::FilePath::FromUTF8Unsafe(".�.�evil.txt");
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
@@ -337,7 +336,7 @@ TEST_F(ZipReaderTest, EncodingSjisAsUtf8) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip")),
ElementsAre(
- base::FilePath::FromUTF8Unsafe("�V�����t�H���_/SJIS_835C_�\\.txt"),
+ base::FilePath::FromUTF8Unsafe("�V�����t�H���_/SJIS_835C_��.txt"),
base::FilePath::FromUTF8Unsafe(
"�V�����t�H���_/�V�����e�L�X�g �h�L�������g.txt")));
}
@@ -349,7 +348,7 @@ TEST_F(ZipReaderTest, EncodingSjisAs1252) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "windows-1252"),
ElementsAre(base::FilePath::FromUTF8Unsafe(
- "\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/SJIS_835C_ƒ\\.txt"),
+ "\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/SJIS_835C_ƒ�.txt"),
base::FilePath::FromUTF8Unsafe(
"\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/\u0090V‚µ‚¢ƒeƒLƒXƒg "
"ƒhƒLƒ…ƒ\u0081ƒ“ƒg.txt")));
@@ -361,7 +360,7 @@ TEST_F(ZipReaderTest, EncodingSjisAsIbm866) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "IBM866"),
ElementsAre(
- base::FilePath::FromUTF8Unsafe("РVВ╡ВвГtГHГЛГ_/SJIS_835C_Г\\.txt"),
+ base::FilePath::FromUTF8Unsafe("РVВ╡ВвГtГHГЛГ_/SJIS_835C_Г�.txt"),
base::FilePath::FromUTF8Unsafe(
"РVВ╡ВвГtГHГЛГ_/РVВ╡ВвГeГLГXГg ГhГLГЕГБГУГg.txt")));
}
@@ -380,12 +379,11 @@ TEST_F(ZipReaderTest, AbsoluteFile) {
ZipReader reader;
ASSERT_TRUE(
reader.Open(data_dir_.AppendASCII("evil_via_absolute_file_name.zip")));
- base::FilePath target_path(FILE_PATH_LITERAL("/evil.txt"));
+ base::FilePath target_path(FILE_PATH_LITERAL("ROOT/evil.txt"));
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
- // This file is unsafe because of the absolute file name.
- EXPECT_TRUE(entry->is_unsafe);
+ EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
diff --git a/google/zip_unittest.cc b/google/zip_unittest.cc
index 8fbec32..b639e8e 100644
--- a/google/zip_unittest.cc
+++ b/google/zip_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -20,6 +20,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/path_service.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
@@ -38,6 +39,7 @@
namespace {
using testing::UnorderedElementsAre;
+using testing::UnorderedElementsAreArray;
std::vector<std::string> GetRelativePaths(const base::FilePath& dir,
base::FileEnumerator::FileType type) {
@@ -46,7 +48,7 @@ std::vector<std::string> GetRelativePaths(const base::FilePath& dir,
for (base::FilePath path = files.Next(); !path.empty(); path = files.Next()) {
base::FilePath relative;
EXPECT_TRUE(dir.AppendRelativePath(path, &relative));
- got_paths.push_back(relative.AsUTF8Unsafe());
+ got_paths.push_back(relative.NormalizePathSeparatorsTo('/').AsUTF8Unsafe());
}
EXPECT_EQ(base::File::FILE_OK, files.GetError());
@@ -401,11 +403,9 @@ TEST_F(ZipTest, UnzipEvil) {
// won't create a persistent file outside test_dir_ in case of a
// failure.
base::FilePath output_dir = test_dir_.AppendASCII("out");
- ASSERT_FALSE(zip::Unzip(path, output_dir));
- base::FilePath evil_file = output_dir;
- evil_file = evil_file.AppendASCII(
- "../levilevilevilevilevilevilevilevilevilevilevilevil");
- ASSERT_FALSE(base::PathExists(evil_file));
+ EXPECT_TRUE(zip::Unzip(path, output_dir));
+ EXPECT_TRUE(base::PathExists(output_dir.AppendASCII(
+ "UP/levilevilevilevilevilevilevilevilevilevilevilevil")));
}
TEST_F(ZipTest, UnzipEvil2) {
@@ -416,7 +416,7 @@ TEST_F(ZipTest, UnzipEvil2) {
base::FilePath output_dir = test_dir_.AppendASCII("out");
ASSERT_TRUE(zip::Unzip(path, output_dir));
ASSERT_TRUE(base::PathExists(
- output_dir.Append(base::FilePath::FromUTF8Unsafe(".�.\\evil.txt"))));
+ output_dir.Append(base::FilePath::FromUTF8Unsafe(".�.�evil.txt"))));
ASSERT_FALSE(base::PathExists(output_dir.AppendASCII("../evil.txt")));
}
@@ -588,37 +588,84 @@ TEST_F(ZipTest, UnzipCannotCreateParentDir) {
EXPECT_EQ("First file", contents);
}
+// TODO(crbug.com/1311140) Detect and rename reserved file names on Windows.
TEST_F(ZipTest, UnzipWindowsSpecialNames) {
- EXPECT_TRUE(zip::Unzip(
- GetDataDirectory().AppendASCII("Windows Special Names.zip"), test_dir_));
+ EXPECT_TRUE(
+ zip::Unzip(GetDataDirectory().AppendASCII("Windows Special Names.zip"),
+ test_dir_, {.continue_on_error = true}));
- std::string contents;
- EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Last"), &contents));
- EXPECT_EQ("Last file", contents);
+ std::unordered_set<std::string> want_paths = {
+ "First",
+ "Last",
+ "CLOCK$",
+ " NUL.txt",
+#ifndef OS_WIN
+ "NUL",
+ "NUL ",
+ "NUL.",
+ "NUL .",
+ "NUL.txt",
+ "NUL.tar.gz",
+ "NUL..txt",
+ "NUL...txt",
+ "NUL .txt",
+ "NUL .txt",
+ "NUL ..txt",
+#ifndef OS_MAC
+ "Nul.txt",
+#endif
+ "nul.very long extension",
+ "a/NUL",
+ "CON",
+ "PRN",
+ "AUX",
+ "COM1",
+ "COM2",
+ "COM3",
+ "COM4",
+ "COM5",
+ "COM6",
+ "COM7",
+ "COM8",
+ "COM9",
+ "LPT1",
+ "LPT2",
+ "LPT3",
+ "LPT4",
+ "LPT5",
+ "LPT6",
+ "LPT7",
+ "LPT8",
+ "LPT9",
+#endif
+ };
+
+ const std::vector<std::string> got_paths =
+ GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES);
+
+ for (const std::string& path : got_paths) {
+ const bool ok = want_paths.erase(path);
#ifdef OS_WIN
- // On Windows, the NUL* files are simply missing. No error is reported. Not
- // even an error message in the logs.
- EXPECT_THAT(
- GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
- UnorderedElementsAre("First", "Last"));
+ if (!ok) {
+ // See crbug.com/1313991: Different versions of Windows treat these
+ // filenames differently. No hard error here if there is an unexpected
+ // file.
+ LOG(WARNING) << "Found unexpected file: " << std::quoted(path);
+ continue;
+ }
#else
- EXPECT_THAT(
- GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
- UnorderedElementsAre("First", "Last", "NUL", "Nul.txt",
- "nul.very long extension"));
-
- EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("NUL"), &contents));
- EXPECT_EQ("This is: NUL", contents);
+ EXPECT_TRUE(ok) << "Found unexpected file: " << std::quoted(path);
+#endif
- EXPECT_TRUE(
- base::ReadFileToString(test_dir_.AppendASCII("Nul.txt"), &contents));
- EXPECT_EQ("This is: Nul.txt", contents);
+ std::string contents;
+ EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII(path), &contents));
+ EXPECT_EQ(base::StrCat({"This is: ", path}), contents);
+ }
- EXPECT_TRUE(base::ReadFileToString(
- test_dir_.AppendASCII("nul.very long extension"), &contents));
- EXPECT_EQ("This is: nul.very long extension", contents);
-#endif
+ for (const std::string& path : want_paths) {
+ EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path);
+ }
}
TEST_F(ZipTest, UnzipDifferentCases) {
@@ -695,45 +742,49 @@ TEST_F(ZipTest, UnzipMixedPaths) {
std::unordered_set<std::string> want_paths = {
#ifdef OS_WIN
- "Dot", //
- "Space→", //
- "a\\b", //
- "u\\v\\w\\x\\y\\z", //
- "←Backslash2", //
+ "Dot", //
+ "Space→", //
#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
+ " ", //
+ "AUX", // Disappears on Windows
+ "COM1", // Disappears on Windows
+ "COM2", // Disappears on Windows
+ "COM3", // Disappears on Windows
+ "COM4", // Disappears on Windows
+ "COM5", // Disappears on Windows
+ "COM6", // Disappears on Windows
+ "COM7", // Disappears on Windows
+ "COM8", // Disappears on Windows
+ "COM9", // Disappears on Windows
+ "CON", // Disappears on Windows
+ "Dot .", //
+ "LPT1", // Disappears on Windows
+ "LPT2", // Disappears on Windows
+ "LPT3", // Disappears on Windows
+ "LPT4", // Disappears on Windows
+ "LPT5", // Disappears on Windows
+ "LPT6", // Disappears on Windows
+ "LPT7", // Disappears on Windows
+ "LPT8", // Disappears on Windows
+ "LPT9", // Disappears on Windows
+ "NUL ..txt", // Disappears on Windows
+ "NUL .txt", // Disappears on Windows
+ "NUL ", // Disappears on Windows
+ "NUL .", // Disappears on Windows
"NUL .txt", // Disappears on Windows
"NUL", // Disappears on Windows
+ "NUL.", // Disappears on Windows
+ "NUL...txt", // 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", //
+ "PRN", // Disappears on Windows
+ "Space→ ", //
+ "c/NUL", // Disappears on Windows
+ "nul.very long extension", // Disappears on Windows
#ifndef OS_MAC
- "CASE", //
- "Case", //
+ "CASE", // Conflicts with "Case"
+ "case", // Conflicts with "Case"
#endif
#endif
" NUL.txt", //
@@ -741,46 +792,82 @@ TEST_F(ZipTest, UnzipMixedPaths) {
"$HOME", //
"%TMP", //
"-", //
- "...Tree", //
+ "...Three", //
"..Two", //
".One", //
"Ampersand &", //
+ "Angle ��", //
"At @", //
- "Backslash3→\\←Backslash4", //
+ "Backslash1→�", //
+ "Backslash3→�←Backslash4", //
+ "Backspace �", //
"Backtick `", //
+ "Bell �", //
+ "CLOCK$", //
"Caret ^", //
+ "Carriage Return �", //
+ "Case", //
+ "Colon �", //
"Comma ,", //
"Curly {}", //
+ "C�", //
+ "C��", //
+ "C��Temp", //
+ "C��Temp�", //
+ "C��Temp�File", //
"Dash -", //
"Delete \x7F", //
"Dollar $", //
+ "Double quote �", //
"Equal =", //
+ "Escape �", //
"Euro €", //
"Exclamation !", //
"FileOrDir", //
"First", //
"Hash #", //
"Last", //
+ "Line Feed �", //
"Percent %", //
+ "Pipe �", //
"Plus +", //
+ "Question �", //
"Quote '", //
+ "ROOT/At The Top", //
+ "ROOT/UP/Over The Top", //
+ "ROOT/dev/null", //
"Round ()", //
"Semicolon ;", //
"Smile \U0001F642", //
"Square []", //
+ "Star �", //
"String Terminator \u009C", //
+ "Tab �", //
"Tilde ~", //
+ "UP/One Level Up", //
+ "UP/UP/Two Levels Up", //
"Underscore _", //
- "case", //
+ "a/DOT/b", //
+ "a/UP/b", //
+ "u/v/w/x/y/z", //
"~", //
+ "�←Backslash2", //
+ "��server�share�file", //
};
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);
+ const bool ok = want_paths.erase(path);
+#ifdef OS_WIN
+ // See crbug.com/1313991: Different versions of Windows treat reserved
+ // Windows filenames differently. No hard error here if there is an
+ // unexpected file.
+ LOG_IF(WARNING, !ok) << "Found unexpected file: " << std::quoted(path);
+#else
+ EXPECT_TRUE(ok) << "Found unexpected file: " << std::quoted(path);
+#endif
}
for (const std::string& path : want_paths) {
@@ -789,14 +876,24 @@ TEST_F(ZipTest, UnzipMixedPaths) {
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
- ));
+ UnorderedElementsAreArray({
+ "Empty",
+ "ROOT",
+ "ROOT/Empty",
+ "ROOT/UP",
+ "ROOT/dev",
+ "UP",
+ "UP/UP",
+ "a",
+ "a/DOT",
+ "a/UP",
+ "c",
+ "u",
+ "u/v",
+ "u/v/w",
+ "u/v/w/x",
+ "u/v/w/x/y",
+ }));
}
TEST_F(ZipTest, UnzipWithDelegates) {
@@ -897,7 +994,7 @@ TEST_F(ZipTest, UnzipSjisAsUtf8) {
std::string contents;
ASSERT_TRUE(base::ReadFileToString(
- dir.Append(base::FilePath::FromUTF8Unsafe("SJIS_835C_�\\.txt")),
+ dir.Append(base::FilePath::FromUTF8Unsafe("SJIS_835C_��.txt")),
&contents));
EXPECT_EQ(
"This file's name contains 0x5c (backslash) as the 2nd byte of Japanese "
@@ -1261,10 +1358,10 @@ TEST_F(ZipTest, NestedZip) {
// performing this test (android-asan, android-11-x86-rel,
// android-marshmallow-x86-rel-non-cq).
// Some Mac, Linux and Debug (dbg) bots tend to time out when performing this
-// test (crbug.com/1299736, crbug.com/1300448).
+// test (crbug.com/1299736, crbug.com/1300448, crbug.com/1369958).
#if defined(THREAD_SANITIZER) || BUILDFLAG(IS_FUCHSIA) || \
BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
- BUILDFLAG(IS_CHROMEOS_LACROS) || !defined(NDEBUG)
+ BUILDFLAG(IS_CHROMEOS) || !defined(NDEBUG)
TEST_F(ZipTest, DISABLED_BigFile) {
#else
TEST_F(ZipTest, BigFile) {
diff --git a/google/zip_writer.cc b/google/zip_writer.cc
index e3f677f..31161ae 100644
--- a/google/zip_writer.cc
+++ b/google/zip_writer.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/google/zip_writer.h b/google/zip_writer.h
index aa3c965..dd10929 100644
--- a/google/zip_writer.h
+++ b/google/zip_writer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.