Commit 5d19ee79 authored by Joshua Pawlicki's avatar Joshua Pawlicki Committed by Commit Bot

Change unzipping library to support cross-process delegates.

This prepares for servicification of the unzipping library, where the
library will not have direct access to the filesystem.

Bug: 792066
Change-Id: I696dd8ef0936f22dc637e078bd8bba565e854ead
Reviewed-on: https://chromium-review.googlesource.com/860996
Commit-Queue: Joshua Pawlicki <waffles@chromium.org>
Reviewed-by: default avatarSatoru Takabayashi <satorux@chromium.org>
Reviewed-by: default avatarJay Civelli <jcivelli@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534778}
parent 37558cb1
...@@ -116,7 +116,9 @@ IN_PROC_BROWSER_TEST_F(ZipFileCreatorTest, SomeFilesZip) { ...@@ -116,7 +116,9 @@ IN_PROC_BROWSER_TEST_F(ZipFileCreatorTest, SomeFilesZip) {
EXPECT_EQ(kRandomDataSize, entry->original_size()); EXPECT_EQ(kRandomDataSize, entry->original_size());
const base::FilePath out = dir_.GetPath().AppendASCII("archived_content"); const base::FilePath out = dir_.GetPath().AppendASCII("archived_content");
EXPECT_TRUE(reader.ExtractCurrentEntryToFilePath(out)); zip::FilePathWriterDelegate writer(out);
EXPECT_TRUE(reader.ExtractCurrentEntry(
&writer, std::numeric_limits<uint64_t>::max()));
EXPECT_TRUE(base::ContentsEqual(zip_base_dir().Append(kFile2), out)); EXPECT_TRUE(base::ContentsEqual(zip_base_dir().Append(kFile2), out));
} else { } else {
ADD_FAILURE(); ADD_FAILURE();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/files/file.h" #include "base/files/file.h"
#include "base/files/file_enumerator.h" #include "base/files/file_enumerator.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "third_party/zlib/google/zip_internal.h" #include "third_party/zlib/google/zip_internal.h"
...@@ -33,6 +34,20 @@ bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) { ...@@ -33,6 +34,20 @@ bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
return !IsHiddenFile(file_path); return !IsHiddenFile(file_path);
} }
// Creates a directory at |extract_dir|/|entry_path|, including any parents.
bool CreateDirectory(const base::FilePath& extract_dir,
const base::FilePath& entry_path) {
return base::CreateDirectory(extract_dir.Append(entry_path));
}
// Creates a WriterDelegate that can write a file at |extract_dir|/|entry_path|.
std::unique_ptr<WriterDelegate> CreateFilePathWriterDelegate(
const base::FilePath& extract_dir,
const base::FilePath& entry_path) {
return std::make_unique<FilePathWriterDelegate>(
extract_dir.Append(entry_path));
}
class DirectFileAccessor : public FileAccessor { class DirectFileAccessor : public FileAccessor {
public: public:
explicit DirectFileAccessor(base::FilePath src_dir) : src_dir_(src_dir) {} explicit DirectFileAccessor(base::FilePath src_dir) : src_dir_(src_dir) {}
...@@ -166,30 +181,52 @@ bool UnzipWithFilterCallback(const base::FilePath& src_file, ...@@ -166,30 +181,52 @@ bool UnzipWithFilterCallback(const base::FilePath& src_file,
const base::FilePath& dest_dir, const base::FilePath& dest_dir,
const FilterCallback& filter_cb, const FilterCallback& filter_cb,
bool log_skipped_files) { bool log_skipped_files) {
ZipReader reader; base::File file(src_file, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!reader.Open(src_file)) { if (!file.IsValid()) {
DLOG(WARNING) << "Failed to open " << src_file.value(); DLOG(WARNING) << "Failed to open " << src_file.value();
return false; return false;
} }
return UnzipWithFilterAndWriters(
file.GetPlatformFile(),
base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir),
base::BindRepeating(&CreateDirectory, dest_dir), filter_cb,
log_skipped_files);
}
bool UnzipWithFilterAndWriters(const base::PlatformFile& src_file,
const WriterFactory& writer_factory,
const DirectoryCreator& directory_creator,
const FilterCallback& filter_cb,
bool log_skipped_files) {
ZipReader reader;
if (!reader.OpenFromPlatformFile(src_file)) {
DLOG(WARNING) << "Failed to open src_file " << src_file;
return false;
}
while (reader.HasMore()) { while (reader.HasMore()) {
if (!reader.OpenCurrentEntryInZip()) { if (!reader.OpenCurrentEntryInZip()) {
DLOG(WARNING) << "Failed to open the current file in zip"; DLOG(WARNING) << "Failed to open the current file in zip";
return false; return false;
} }
const base::FilePath& entry_path = reader.current_entry_info()->file_path();
if (reader.current_entry_info()->is_unsafe()) { if (reader.current_entry_info()->is_unsafe()) {
DLOG(WARNING) << "Found an unsafe file in zip " DLOG(WARNING) << "Found an unsafe file in zip " << entry_path;
<< reader.current_entry_info()->file_path().value();
return false; return false;
} }
if (filter_cb.Run(reader.current_entry_info()->file_path())) { if (filter_cb.Run(entry_path)) {
if (!reader.ExtractCurrentEntryIntoDirectory(dest_dir)) { if (reader.current_entry_info()->is_directory()) {
DLOG(WARNING) << "Failed to extract " if (!directory_creator.Run(entry_path))
<< reader.current_entry_info()->file_path().value(); return false;
return false; } else {
std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry_path);
if (!reader.ExtractCurrentEntry(writer.get(),
std::numeric_limits<uint64_t>::max())) {
DLOG(WARNING) << "Failed to extract " << entry_path;
return false;
}
} }
} else if (log_skipped_files) { } else if (log_skipped_files) {
DLOG(WARNING) << "Skipped file " DLOG(WARNING) << "Skipped file " << entry_path;
<< reader.current_entry_info()->file_path().value();
} }
if (!reader.AdvanceToNextEntry()) { if (!reader.AdvanceToNextEntry()) {
......
...@@ -19,6 +19,8 @@ class File; ...@@ -19,6 +19,8 @@ class File;
namespace zip { namespace zip {
class WriterDelegate;
// Abstraction for file access operation required by Zip(). // Abstraction for file access operation required by Zip().
// Can be passed to the ZipParams for providing custom access to the files, // Can be passed to the ZipParams for providing custom access to the files,
// for example over IPC. // for example over IPC.
...@@ -156,6 +158,21 @@ bool UnzipWithFilterCallback(const base::FilePath& zip_file, ...@@ -156,6 +158,21 @@ bool UnzipWithFilterCallback(const base::FilePath& zip_file,
const FilterCallback& filter_cb, const FilterCallback& filter_cb,
bool log_skipped_files); bool log_skipped_files);
// Unzip the contents of zip_file, using the writers provided by writer_factory.
// For each file in zip_file, include it only if the callback |filter_cb|
// returns true. Otherwise omit it.
// If |log_skipped_files| is true, files skipped during extraction are printed
// to debug log.
typedef base::RepeatingCallback<std::unique_ptr<WriterDelegate>(
const base::FilePath&)>
WriterFactory;
typedef base::RepeatingCallback<bool(const base::FilePath&)> DirectoryCreator;
bool UnzipWithFilterAndWriters(const base::PlatformFile& zip_file,
const WriterFactory& writer_factory,
const DirectoryCreator& directory_creator,
const FilterCallback& filter_cb,
bool log_skipped_files);
// Unzip the contents of zip_file into dest_dir. // Unzip the contents of zip_file into dest_dir.
bool Unzip(const base::FilePath& zip_file, const base::FilePath& dest_dir); bool Unzip(const base::FilePath& zip_file, const base::FilePath& dest_dir);
......
...@@ -30,54 +30,6 @@ namespace zip { ...@@ -30,54 +30,6 @@ namespace zip {
namespace { namespace {
// FilePathWriterDelegate ------------------------------------------------------
// A writer delegate that writes a file at a given path.
class FilePathWriterDelegate : public WriterDelegate {
public:
explicit FilePathWriterDelegate(const base::FilePath& output_file_path);
~FilePathWriterDelegate() override;
// WriterDelegate methods:
// Creates the output file and any necessary intermediate directories.
bool PrepareOutput() override;
// Writes |num_bytes| bytes of |data| to the file, returning false if not all
// bytes could be written.
bool WriteBytes(const char* data, int num_bytes) override;
private:
base::FilePath output_file_path_;
base::File file_;
DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate);
};
FilePathWriterDelegate::FilePathWriterDelegate(
const base::FilePath& output_file_path)
: output_file_path_(output_file_path) {
}
FilePathWriterDelegate::~FilePathWriterDelegate() {
}
bool FilePathWriterDelegate::PrepareOutput() {
// We can't rely on parent directory entries being specified in the
// zip, so we make sure they are created.
if (!base::CreateDirectory(output_file_path_.DirName()))
return false;
file_.Initialize(output_file_path_,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
return file_.IsValid();
}
bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
}
// StringWriterDelegate -------------------------------------------------------- // StringWriterDelegate --------------------------------------------------------
// A writer delegate that writes no more than |max_read_bytes| to a given // A writer delegate that writes no more than |max_read_bytes| to a given
...@@ -96,6 +48,8 @@ class StringWriterDelegate : public WriterDelegate { ...@@ -96,6 +48,8 @@ class StringWriterDelegate : public WriterDelegate {
// if |num_bytes| will cause the string to exceed |max_read_bytes|. // if |num_bytes| will cause the string to exceed |max_read_bytes|.
bool WriteBytes(const char* data, int num_bytes) override; bool WriteBytes(const char* data, int num_bytes) override;
void SetTimeModified(const base::Time& time) override;
private: private:
size_t max_read_bytes_; size_t max_read_bytes_;
std::string* output_; std::string* output_;
...@@ -123,6 +77,10 @@ bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) { ...@@ -123,6 +77,10 @@ bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) {
return true; return true;
} }
void StringWriterDelegate::SetTimeModified(const base::Time& time) {
// Do nothing.
}
} // namespace } // namespace
// TODO(satorux): The implementation assumes that file names in zip files // TODO(satorux): The implementation assumes that file names in zip files
...@@ -273,22 +231,6 @@ bool ZipReader::OpenCurrentEntryInZip() { ...@@ -273,22 +231,6 @@ bool ZipReader::OpenCurrentEntryInZip() {
return true; return true;
} }
bool ZipReader::LocateAndOpenEntry(const base::FilePath& path_in_zip) {
DCHECK(zip_file_);
current_entry_info_.reset();
reached_end_ = false;
const int kDefaultCaseSensivityOfOS = 0;
const int result = unzLocateFile(zip_file_,
path_in_zip.AsUTF8Unsafe().c_str(),
kDefaultCaseSensivityOfOS);
if (result != UNZ_OK)
return false;
// Then Open the entry.
return OpenCurrentEntryInZip();
}
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate, bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
uint64_t num_bytes_to_extract) const { uint64_t num_bytes_to_extract) const {
DCHECK(zip_file_); DCHECK(zip_file_);
...@@ -331,32 +273,12 @@ bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate, ...@@ -331,32 +273,12 @@ bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
unzCloseCurrentFile(zip_file_); unzCloseCurrentFile(zip_file_);
return entire_file_extracted; if (entire_file_extracted &&
}
bool ZipReader::ExtractCurrentEntryToFilePath(
const base::FilePath& output_file_path) const {
DCHECK(zip_file_);
// If this is a directory, just create it and return.
if (current_entry_info()->is_directory())
return base::CreateDirectory(output_file_path);
bool success = false;
{
FilePathWriterDelegate writer(output_file_path);
success =
ExtractCurrentEntry(&writer, std::numeric_limits<uint64_t>::max());
}
if (success &&
current_entry_info()->last_modified() != base::Time::UnixEpoch()) { current_entry_info()->last_modified() != base::Time::UnixEpoch()) {
base::TouchFile(output_file_path, delegate->SetTimeModified(current_entry_info()->last_modified());
base::Time::Now(),
current_entry_info()->last_modified());
} }
return success; return entire_file_extracted;
} }
void ZipReader::ExtractCurrentEntryToFilePathAsync( void ZipReader::ExtractCurrentEntryToFilePathAsync(
...@@ -408,27 +330,6 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( ...@@ -408,27 +330,6 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync(
failure_callback, progress_callback, 0 /* initial offset */)); failure_callback, progress_callback, 0 /* initial offset */));
} }
bool ZipReader::ExtractCurrentEntryIntoDirectory(
const base::FilePath& output_directory_path) const {
DCHECK(current_entry_info_.get());
base::FilePath output_file_path = output_directory_path.Append(
current_entry_info()->file_path());
return ExtractCurrentEntryToFilePath(output_file_path);
}
bool ZipReader::ExtractCurrentEntryToFile(base::File* file) const {
DCHECK(zip_file_);
// If this is a directory, there's nothing to extract to the file, so return
// false.
if (current_entry_info()->is_directory())
return false;
FileWriterDelegate writer(file);
return ExtractCurrentEntry(&writer, std::numeric_limits<uint64_t>::max());
}
bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes, bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes,
std::string* output) const { std::string* output) const {
DCHECK(output); DCHECK(output);
...@@ -533,10 +434,10 @@ void ZipReader::ExtractChunk(base::File output_file, ...@@ -533,10 +434,10 @@ void ZipReader::ExtractChunk(base::File output_file,
// FileWriterDelegate ---------------------------------------------------------- // FileWriterDelegate ----------------------------------------------------------
FileWriterDelegate::FileWriterDelegate(base::File* file) FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {}
: file_(file),
file_length_(0) { FileWriterDelegate::FileWriterDelegate(std::unique_ptr<base::File> file)
} : file_(file.get()), owned_file_(std::move(file)) {}
FileWriterDelegate::~FileWriterDelegate() { FileWriterDelegate::~FileWriterDelegate() {
if (!file_->SetLength(file_length_)) { if (!file_->SetLength(file_length_)) {
...@@ -555,4 +456,36 @@ bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) { ...@@ -555,4 +456,36 @@ bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
return bytes_written == num_bytes; return bytes_written == num_bytes;
} }
void FileWriterDelegate::SetTimeModified(const base::Time& time) {
file_->SetTimes(base::Time::Now(), time);
}
// FilePathWriterDelegate ------------------------------------------------------
FilePathWriterDelegate::FilePathWriterDelegate(
const base::FilePath& output_file_path)
: output_file_path_(output_file_path) {}
FilePathWriterDelegate::~FilePathWriterDelegate() {}
bool FilePathWriterDelegate::PrepareOutput() {
// We can't rely on parent directory entries being specified in the
// zip, so we make sure they are created.
if (!base::CreateDirectory(output_file_path_.DirName()))
return false;
file_.Initialize(output_file_path_,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
return file_.IsValid();
}
bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
}
void FilePathWriterDelegate::SetTimeModified(const base::Time& time) {
file_.Close();
base::TouchFile(output_file_path_, base::Time::Now(), time);
}
} // namespace zip } // namespace zip
...@@ -39,6 +39,9 @@ class WriterDelegate { ...@@ -39,6 +39,9 @@ class WriterDelegate {
// Invoked to write the next chunk of data. Return false on failure to cancel // Invoked to write the next chunk of data. Return false on failure to cancel
// extraction. // extraction.
virtual bool WriteBytes(const char* data, int num_bytes) = 0; virtual bool WriteBytes(const char* data, int num_bytes) = 0;
// Sets the last-modified time of the data.
virtual void SetTimeModified(const base::Time& time) = 0;
}; };
// This class is used for reading zip files. A typical use case of this // This class is used for reading zip files. A typical use case of this
...@@ -49,16 +52,16 @@ class WriterDelegate { ...@@ -49,16 +52,16 @@ class WriterDelegate {
// reader.Open(zip_file_path); // reader.Open(zip_file_path);
// while (reader.HasMore()) { // while (reader.HasMore()) {
// reader.OpenCurrentEntryInZip(); // reader.OpenCurrentEntryInZip();
// reader.ExtractCurrentEntryToDirectory(output_directory_path); // const base::FilePath& entry_path =
// reader.current_entry_info()->file_path();
// auto writer = CreateFilePathWriterDelegate(extract_dir, entry_path);
// reader.ExtractCurrentEntry(writer, std::numeric_limits<uint64_t>::max());
// reader.AdvanceToNextEntry(); // reader.AdvanceToNextEntry();
// } // }
// //
// For simplicity, error checking is omitted in the example code above. The // For simplicity, error checking is omitted in the example code above. The
// production code should check return values from all of these functions. // production code should check return values from all of these functions.
// //
// This calls can also be used for random access of contents in a zip file
// using LocateAndOpenEntry().
//
class ZipReader { class ZipReader {
public: public:
// A callback that is called when the operation is successful. // A callback that is called when the operation is successful.
...@@ -154,27 +157,12 @@ class ZipReader { ...@@ -154,27 +157,12 @@ class ZipReader {
// state is reset automatically as needed. // state is reset automatically as needed.
bool OpenCurrentEntryInZip(); bool OpenCurrentEntryInZip();
// Locates an entry in the zip file and opens it. Returns true on
// success. This function internally calls OpenCurrentEntryInZip() on
// success. On failure, current_entry_info() becomes NULL.
bool LocateAndOpenEntry(const base::FilePath& path_in_zip);
// Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|, // Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
// starting from the beginning of the entry. Return value specifies whether // starting from the beginning of the entry. Return value specifies whether
// the entire file was extracted. // the entire file was extracted.
bool ExtractCurrentEntry(WriterDelegate* delegate, bool ExtractCurrentEntry(WriterDelegate* delegate,
uint64_t num_bytes_to_extract) const; uint64_t num_bytes_to_extract) const;
// Extracts the current entry to the given output file path. If the
// current file is a directory, just creates a directory
// instead. Returns true on success. OpenCurrentEntryInZip() must be
// called beforehand.
//
// This function preserves the timestamp of the original entry. If that
// timestamp is not valid, the timestamp will be set to the current time.
bool ExtractCurrentEntryToFilePath(
const base::FilePath& output_file_path) const;
// Asynchronously extracts the current entry to the given output file path. // Asynchronously extracts the current entry to the given output file path.
// If the current entry is a directory it just creates the directory // If the current entry is a directory it just creates the directory
// synchronously instead. OpenCurrentEntryInZip() must be called beforehand. // synchronously instead. OpenCurrentEntryInZip() must be called beforehand.
...@@ -187,24 +175,6 @@ class ZipReader { ...@@ -187,24 +175,6 @@ class ZipReader {
const FailureCallback& failure_callback, const FailureCallback& failure_callback,
const ProgressCallback& progress_callback); const ProgressCallback& progress_callback);
// Extracts the current entry to the given output directory path using
// ExtractCurrentEntryToFilePath(). Sub directories are created as needed
// based on the file path of the current entry. For example, if the file
// path in zip is "foo/bar.txt", and the output directory is "output",
// "output/foo/bar.txt" will be created.
//
// Returns true on success. OpenCurrentEntryInZip() must be called
// beforehand.
//
// This function preserves the timestamp of the original entry. If that
// timestamp is not valid, the timestamp will be set to the current time.
bool ExtractCurrentEntryIntoDirectory(
const base::FilePath& output_directory_path) const;
// Extracts the current entry by writing directly to a platform file.
// Does not close the file. Returns true on success.
bool ExtractCurrentEntryToFile(base::File* file) const;
// Extracts the current entry into memory. If the current entry is a // Extracts the current entry into memory. If the current entry is a
// directory, the |output| parameter is set to the empty string. If the // directory, the |output| parameter is set to the empty string. If the
// current entry is a file, the |output| parameter is filled with its // current entry is a file, the |output| parameter is filled with its
...@@ -258,8 +228,14 @@ class ZipReader { ...@@ -258,8 +228,14 @@ class ZipReader {
// A writer delegate that writes to a given File. // A writer delegate that writes to a given File.
class FileWriterDelegate : public WriterDelegate { class FileWriterDelegate : public WriterDelegate {
public: public:
// Constructs a FileWriterDelegate that manipulates |file|. The delegate will
// not own |file|, therefore the caller must guarantee |file| will outlive the
// delegate.
explicit FileWriterDelegate(base::File* file); explicit FileWriterDelegate(base::File* file);
// Constructs a FileWriterDelegate that takes ownership of |file|.
explicit FileWriterDelegate(std::unique_ptr<base::File> file);
// Truncates the file to the number of bytes written. // Truncates the file to the number of bytes written.
~FileWriterDelegate() override; ~FileWriterDelegate() override;
...@@ -272,13 +248,47 @@ class FileWriterDelegate : public WriterDelegate { ...@@ -272,13 +248,47 @@ class FileWriterDelegate : public WriterDelegate {
// if not all bytes could be written. // if not all bytes could be written.
bool WriteBytes(const char* data, int num_bytes) override; bool WriteBytes(const char* data, int num_bytes) override;
// Sets the last-modified time of the data.
void SetTimeModified(const base::Time& time) override;
private: private:
// The file the delegate modifies.
base::File* file_; base::File* file_;
int64_t file_length_;
// The delegate can optionally own the file it modifies, in which case
// owned_file_ is set and file_ is an alias for owned_file_.
std::unique_ptr<base::File> owned_file_;
int64_t file_length_ = 0;
DISALLOW_COPY_AND_ASSIGN(FileWriterDelegate); DISALLOW_COPY_AND_ASSIGN(FileWriterDelegate);
}; };
// A writer delegate that writes a file at a given path.
class FilePathWriterDelegate : public WriterDelegate {
public:
explicit FilePathWriterDelegate(const base::FilePath& output_file_path);
~FilePathWriterDelegate() override;
// WriterDelegate methods:
// Creates the output file and any necessary intermediate directories.
bool PrepareOutput() override;
// Writes |num_bytes| bytes of |data| to the file, returning false if not all
// bytes could be written.
bool WriteBytes(const char* data, int num_bytes) override;
// Sets the last-modified time of the data.
void SetTimeModified(const base::Time& time) override;
private:
base::FilePath output_file_path_;
base::File file_;
DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate);
};
} // namespace zip } // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_ #endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/bind.h"
#include "base/files/file.h" #include "base/files/file.h"
#include "base/files/file_enumerator.h" #include "base/files/file_enumerator.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
...@@ -338,6 +339,75 @@ TEST_F(ZipTest, UnzipEvil2) { ...@@ -338,6 +339,75 @@ TEST_F(ZipTest, UnzipEvil2) {
ASSERT_FALSE(base::PathExists(evil_file)); ASSERT_FALSE(base::PathExists(evil_file));
} }
TEST_F(ZipTest, UnzipWithFilter) {
auto filter = base::BindRepeating([](const base::FilePath& path) {
return path.BaseName().MaybeAsASCII() == "foo.txt";
});
base::FilePath path;
ASSERT_TRUE(GetTestDataDirectory(&path));
ASSERT_TRUE(zip::UnzipWithFilterCallback(path.AppendASCII("test.zip"),
test_dir_, filter, false));
// Only foo.txt should have been extracted. The following paths should not
// be extracted:
// foo/
// foo/bar.txt
// foo/bar/
// foo/bar/.hidden
// foo/bar/baz.txt
// foo/bar/quux.txt
ASSERT_TRUE(base::PathExists(test_dir_.AppendASCII("foo.txt")));
base::FileEnumerator extractedFiles(
test_dir_,
false, // Do not enumerate recursively - the file must be in the root.
base::FileEnumerator::FileType::FILES);
int extracted_count = 0;
while (!extractedFiles.Next().empty())
++extracted_count;
ASSERT_EQ(1, extracted_count);
base::FileEnumerator extractedDirs(
test_dir_,
false, // Do not enumerate recursively - we require zero directories.
base::FileEnumerator::FileType::DIRECTORIES);
extracted_count = 0;
while (!extractedDirs.Next().empty())
++extracted_count;
ASSERT_EQ(0, extracted_count);
}
TEST_F(ZipTest, UnzipWithDelegates) {
auto filter =
base::BindRepeating([](const base::FilePath& path) { return true; });
auto dir_creator = base::BindRepeating(
[](const base::FilePath& extract_dir, const base::FilePath& entry_path) {
return base::CreateDirectory(extract_dir.Append(entry_path));
},
test_dir_);
auto writer = base::BindRepeating(
[](const base::FilePath& extract_dir, const base::FilePath& entry_path)
-> std::unique_ptr<zip::WriterDelegate> {
return std::make_unique<zip::FilePathWriterDelegate>(
extract_dir.Append(entry_path));
},
test_dir_);
base::FilePath path;
ASSERT_TRUE(GetTestDataDirectory(&path));
base::File file(path.AppendASCII("test.zip"),
base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
ASSERT_TRUE(zip::UnzipWithFilterAndWriters(file.GetPlatformFile(), writer,
dir_creator, filter, false));
base::FilePath dir = test_dir_;
base::FilePath dir_foo = dir.AppendASCII("foo");
base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar");
ASSERT_TRUE(base::PathExists(dir.AppendASCII("foo.txt")));
ASSERT_TRUE(base::PathExists(dir_foo));
ASSERT_TRUE(base::PathExists(dir_foo.AppendASCII("bar.txt")));
ASSERT_TRUE(base::PathExists(dir_foo_bar));
ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII(".hidden")));
ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt")));
ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt")));
}
TEST_F(ZipTest, Zip) { TEST_F(ZipTest, Zip) {
base::FilePath src_dir; base::FilePath src_dir;
ASSERT_TRUE(GetTestDataDirectory(&src_dir)); ASSERT_TRUE(GetTestDataDirectory(&src_dir));
...@@ -424,10 +494,11 @@ TEST_F(ZipTest, ZipFiles) { ...@@ -424,10 +494,11 @@ TEST_F(ZipTest, ZipFiles) {
EXPECT_TRUE(reader.Open(zip_name)); EXPECT_TRUE(reader.Open(zip_name));
EXPECT_EQ(zip_file_list_.size(), static_cast<size_t>(reader.num_entries())); EXPECT_EQ(zip_file_list_.size(), static_cast<size_t>(reader.num_entries()));
for (size_t i = 0; i < zip_file_list_.size(); ++i) { for (size_t i = 0; i < zip_file_list_.size(); ++i) {
EXPECT_TRUE(reader.LocateAndOpenEntry(zip_file_list_[i])); EXPECT_TRUE(reader.HasMore());
// Check the path in the entry just in case. EXPECT_TRUE(reader.OpenCurrentEntryInZip());
const zip::ZipReader::EntryInfo* entry_info = reader.current_entry_info(); const zip::ZipReader::EntryInfo* entry_info = reader.current_entry_info();
EXPECT_EQ(entry_info->file_path(), zip_file_list_[i]); EXPECT_EQ(entry_info->file_path(), zip_file_list_[i]);
reader.AdvanceToNextEntry();
} }
} }
#endif // defined(OS_POSIX) #endif // defined(OS_POSIX)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment