1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/zlib/google/zip.h"
6
7 #include <list>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/files/file.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/string_util.h"
17 #include "build/build_config.h"
18 #include "third_party/zlib/google/zip_internal.h"
19 #include "third_party/zlib/google/zip_reader.h"
20 #include "third_party/zlib/google/zip_writer.h"
21
22 namespace zip {
23 namespace {
24
IsHiddenFile(const base::FilePath & file_path)25 bool IsHiddenFile(const base::FilePath& file_path) {
26 return file_path.BaseName().value()[0] == '.';
27 }
28
ExcludeNoFilesFilter(const base::FilePath & file_path)29 bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
30 return true;
31 }
32
ExcludeHiddenFilesFilter(const base::FilePath & file_path)33 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
34 return !IsHiddenFile(file_path);
35 }
36
37 // Creates a directory at |extract_dir|/|entry_path|, including any parents.
CreateDirectory(const base::FilePath & extract_dir,const base::FilePath & entry_path)38 bool CreateDirectory(const base::FilePath& extract_dir,
39 const base::FilePath& entry_path) {
40 return base::CreateDirectory(extract_dir.Append(entry_path));
41 }
42
43 // Creates a WriterDelegate that can write a file at |extract_dir|/|entry_path|.
CreateFilePathWriterDelegate(const base::FilePath & extract_dir,const base::FilePath & entry_path)44 std::unique_ptr<WriterDelegate> CreateFilePathWriterDelegate(
45 const base::FilePath& extract_dir,
46 const base::FilePath& entry_path) {
47 return std::make_unique<FilePathWriterDelegate>(
48 extract_dir.Append(entry_path));
49 }
50
51 class DirectFileAccessor : public FileAccessor {
52 public:
DirectFileAccessor(base::FilePath src_dir)53 explicit DirectFileAccessor(base::FilePath src_dir) : src_dir_(src_dir) {}
54 ~DirectFileAccessor() override = default;
55
OpenFilesForReading(const std::vector<base::FilePath> & paths)56 std::vector<base::File> OpenFilesForReading(
57 const std::vector<base::FilePath>& paths) override {
58 std::vector<base::File> files;
59 for (const auto& path : paths) {
60 base::File file;
61 if (base::PathExists(path) && !base::DirectoryExists(path)) {
62 file = base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
63 }
64 files.push_back(std::move(file));
65 }
66 return files;
67 }
68
DirectoryExists(const base::FilePath & file)69 bool DirectoryExists(const base::FilePath& file) override {
70 return base::DirectoryExists(file);
71 }
72
ListDirectoryContent(const base::FilePath & dir)73 std::vector<DirectoryContentEntry> ListDirectoryContent(
74 const base::FilePath& dir) override {
75 std::vector<DirectoryContentEntry> files;
76 base::FileEnumerator file_enumerator(
77 dir, false /* recursive */,
78 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
79 for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
80 path = file_enumerator.Next()) {
81 files.push_back(DirectoryContentEntry(path, base::DirectoryExists(path)));
82 }
83 return files;
84 }
85
GetLastModifiedTime(const base::FilePath & path)86 base::Time GetLastModifiedTime(const base::FilePath& path) override {
87 base::File::Info file_info;
88 if (!base::GetFileInfo(path, &file_info)) {
89 LOG(ERROR) << "Failed to retrieve file modification time for "
90 << path.value();
91 }
92 return file_info.last_modified;
93 }
94
95 private:
96 base::FilePath src_dir_;
97
98 DISALLOW_COPY_AND_ASSIGN(DirectFileAccessor);
99 };
100
101 } // namespace
102
ZipParams(const base::FilePath & src_dir,const base::FilePath & dest_file)103 ZipParams::ZipParams(const base::FilePath& src_dir,
104 const base::FilePath& dest_file)
105 : src_dir_(src_dir),
106 dest_file_(dest_file),
107 file_accessor_(new DirectFileAccessor(src_dir)) {}
108
109 #if defined(OS_POSIX)
110 // Does not take ownership of |fd|.
ZipParams(const base::FilePath & src_dir,int dest_fd)111 ZipParams::ZipParams(const base::FilePath& src_dir, int dest_fd)
112 : src_dir_(src_dir),
113 dest_fd_(dest_fd),
114 file_accessor_(new DirectFileAccessor(src_dir)) {}
115 #endif
116
Zip(const ZipParams & params)117 bool Zip(const ZipParams& params) {
118 // Using a pointer to avoid copies of a potentially large array.
119 const std::vector<base::FilePath>* files_to_add = ¶ms.files_to_zip();
120 std::vector<base::FilePath> all_files;
121 if (files_to_add->empty()) {
122 // Include all files from the src_dir (modulo the src_dir itself and
123 // filtered and hidden files).
124
125 files_to_add = &all_files;
126 // Using a list so we can call push_back while iterating.
127 std::list<FileAccessor::DirectoryContentEntry> entries;
128 entries.push_back(FileAccessor::DirectoryContentEntry(
129 params.src_dir(), true /* is directory*/));
130 const FilterCallback& filter_callback = params.filter_callback();
131 for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
132 const base::FilePath& entry_path = iter->path;
133 if (iter != entries.begin() && // Don't filter the root dir.
134 ((!params.include_hidden_files() && IsHiddenFile(entry_path)) ||
135 (filter_callback && !filter_callback.Run(entry_path)))) {
136 continue;
137 }
138
139 if (iter != entries.begin()) { // Exclude the root dir from the ZIP file.
140 // Make the path relative for AddEntryToZip.
141 base::FilePath relative_path;
142 bool success =
143 params.src_dir().AppendRelativePath(entry_path, &relative_path);
144 DCHECK(success);
145 all_files.push_back(relative_path);
146 }
147
148 if (iter->is_directory) {
149 std::vector<FileAccessor::DirectoryContentEntry> subentries =
150 params.file_accessor()->ListDirectoryContent(entry_path);
151 entries.insert(entries.end(), subentries.begin(), subentries.end());
152 }
153 }
154 }
155
156 std::unique_ptr<internal::ZipWriter> zip_writer;
157 #if defined(OS_POSIX)
158 if (params.dest_fd() != base::kInvalidPlatformFile) {
159 DCHECK(params.dest_file().empty());
160 zip_writer = internal::ZipWriter::CreateWithFd(
161 params.dest_fd(), params.src_dir(), params.file_accessor());
162 if (!zip_writer)
163 return false;
164 }
165 #endif
166 if (!zip_writer) {
167 zip_writer = internal::ZipWriter::Create(
168 params.dest_file(), params.src_dir(), params.file_accessor());
169 if (!zip_writer)
170 return false;
171 }
172 return zip_writer->WriteEntries(*files_to_add);
173 }
174
Unzip(const base::FilePath & src_file,const base::FilePath & dest_dir)175 bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
176 return UnzipWithFilterCallback(
177 src_file, dest_dir, base::BindRepeating(&ExcludeNoFilesFilter), true);
178 }
179
UnzipWithFilterCallback(const base::FilePath & src_file,const base::FilePath & dest_dir,const FilterCallback & filter_cb,bool log_skipped_files)180 bool UnzipWithFilterCallback(const base::FilePath& src_file,
181 const base::FilePath& dest_dir,
182 const FilterCallback& filter_cb,
183 bool log_skipped_files) {
184 base::File file(src_file, base::File::FLAG_OPEN | base::File::FLAG_READ);
185 if (!file.IsValid()) {
186 DLOG(WARNING) << "Failed to open " << src_file.value();
187 return false;
188 }
189 return UnzipWithFilterAndWriters(
190 file.GetPlatformFile(),
191 base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir),
192 base::BindRepeating(&CreateDirectory, dest_dir), filter_cb,
193 log_skipped_files);
194 }
195
UnzipWithFilterAndWriters(const base::PlatformFile & src_file,const WriterFactory & writer_factory,const DirectoryCreator & directory_creator,const FilterCallback & filter_cb,bool log_skipped_files)196 bool UnzipWithFilterAndWriters(const base::PlatformFile& src_file,
197 const WriterFactory& writer_factory,
198 const DirectoryCreator& directory_creator,
199 const FilterCallback& filter_cb,
200 bool log_skipped_files) {
201 ZipReader reader;
202 if (!reader.OpenFromPlatformFile(src_file)) {
203 DLOG(WARNING) << "Failed to open src_file " << src_file;
204 return false;
205 }
206 while (reader.HasMore()) {
207 if (!reader.OpenCurrentEntryInZip()) {
208 DLOG(WARNING) << "Failed to open the current file in zip";
209 return false;
210 }
211 const base::FilePath& entry_path = reader.current_entry_info()->file_path();
212 if (reader.current_entry_info()->is_unsafe()) {
213 DLOG(WARNING) << "Found an unsafe file in zip " << entry_path;
214 return false;
215 }
216 if (filter_cb.Run(entry_path)) {
217 if (reader.current_entry_info()->is_directory()) {
218 if (!directory_creator.Run(entry_path))
219 return false;
220 } else {
221 std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry_path);
222 if (!reader.ExtractCurrentEntry(writer.get(),
223 std::numeric_limits<uint64_t>::max())) {
224 DLOG(WARNING) << "Failed to extract " << entry_path;
225 return false;
226 }
227 }
228 } else if (log_skipped_files) {
229 DLOG(WARNING) << "Skipped file " << entry_path;
230 }
231
232 if (!reader.AdvanceToNextEntry()) {
233 DLOG(WARNING) << "Failed to advance to the next file";
234 return false;
235 }
236 }
237 return true;
238 }
239
ZipWithFilterCallback(const base::FilePath & src_dir,const base::FilePath & dest_file,const FilterCallback & filter_cb)240 bool ZipWithFilterCallback(const base::FilePath& src_dir,
241 const base::FilePath& dest_file,
242 const FilterCallback& filter_cb) {
243 DCHECK(base::DirectoryExists(src_dir));
244 ZipParams params(src_dir, dest_file);
245 params.set_filter_callback(filter_cb);
246 return Zip(params);
247 }
248
Zip(const base::FilePath & src_dir,const base::FilePath & dest_file,bool include_hidden_files)249 bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file,
250 bool include_hidden_files) {
251 if (include_hidden_files) {
252 return ZipWithFilterCallback(src_dir, dest_file,
253 base::BindRepeating(&ExcludeNoFilesFilter));
254 } else {
255 return ZipWithFilterCallback(
256 src_dir, dest_file, base::BindRepeating(&ExcludeHiddenFilesFilter));
257 }
258 }
259
260 #if defined(OS_POSIX)
ZipFiles(const base::FilePath & src_dir,const std::vector<base::FilePath> & src_relative_paths,int dest_fd)261 bool ZipFiles(const base::FilePath& src_dir,
262 const std::vector<base::FilePath>& src_relative_paths,
263 int dest_fd) {
264 DCHECK(base::DirectoryExists(src_dir));
265 ZipParams params(src_dir, dest_fd);
266 params.set_files_to_zip(src_relative_paths);
267 return Zip(params);
268 }
269 #endif // defined(OS_POSIX)
270
271 } // namespace zip
272