• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &params.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