alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 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 | |
joaoe@opera.com | ce54c1b | 2013-12-17 14:40:31 +0000 | [diff] [blame] | 7 | #include <string> |
| 8 | #include <vector> |
| 9 | |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 10 | #include "base/bind.h" |
hashimoto@chromium.org | 516d185 | 2014-03-24 09:32:13 +0000 | [diff] [blame] | 11 | #include "base/files/file.h" |
brettw@chromium.org | b5aa4e7 | 2013-06-08 04:53:36 +0000 | [diff] [blame] | 12 | #include "base/files/file_enumerator.h" |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 13 | #include "base/logging.h" |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 14 | #include "base/memory/ptr_util.h" |
avi@chromium.org | a9a9395 | 2013-06-11 08:04:16 +0000 | [diff] [blame] | 15 | #include "base/strings/string_util.h" |
avi | aa96948 | 2015-12-27 13:36:49 -0800 | [diff] [blame] | 16 | #include "build/build_config.h" |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 17 | #include "third_party/zlib/google/zip_internal.h" |
| 18 | #include "third_party/zlib/google/zip_reader.h" |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 19 | #include "third_party/zlib/google/zip_writer.h" |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 20 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 21 | namespace zip { |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 22 | namespace { |
| 23 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 24 | bool IsHiddenFile(const base::FilePath& file_path) { |
| 25 | return file_path.BaseName().value()[0] == '.'; |
| 26 | } |
| 27 | |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 28 | bool ExcludeNoFilesFilter(const base::FilePath& file_path) { |
| 29 | return true; |
| 30 | } |
| 31 | |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 32 | // Creates a directory at |extract_dir|/|entry_path|, including any parents. |
| 33 | bool CreateDirectory(const base::FilePath& extract_dir, |
| 34 | const base::FilePath& entry_path) { |
| 35 | return base::CreateDirectory(extract_dir.Append(entry_path)); |
| 36 | } |
| 37 | |
| 38 | // Creates a WriterDelegate that can write a file at |extract_dir|/|entry_path|. |
| 39 | std::unique_ptr<WriterDelegate> CreateFilePathWriterDelegate( |
| 40 | const base::FilePath& extract_dir, |
| 41 | const base::FilePath& entry_path) { |
| 42 | return std::make_unique<FilePathWriterDelegate>( |
| 43 | extract_dir.Append(entry_path)); |
| 44 | } |
| 45 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 46 | class DirectFileAccessor : public FileAccessor { |
| 47 | public: |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 48 | explicit DirectFileAccessor(base::FilePath src_dir) |
| 49 | : src_dir_(std::move(src_dir)) {} |
| 50 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 51 | ~DirectFileAccessor() override = default; |
| 52 | |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 53 | bool Open(const Paths paths, std::vector<base::File>* const files) override { |
| 54 | DCHECK(files); |
| 55 | files->reserve(files->size() + paths.size()); |
François Degros | 5b8d433 | 2021-05-18 05:56:20 +0000 | [diff] [blame] | 56 | |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 57 | for (const base::FilePath& path : paths) { |
| 58 | DCHECK(!path.IsAbsolute()); |
| 59 | const base::FilePath absolute_path = src_dir_.Append(path); |
| 60 | if (base::DirectoryExists(absolute_path)) { |
| 61 | files->emplace_back(); |
| 62 | LOG(ERROR) << "Cannot open '" << path << "': It is a directory"; |
| 63 | } else { |
| 64 | files->emplace_back(absolute_path, |
| 65 | base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 66 | LOG_IF(ERROR, !files->back().IsValid()) |
| 67 | << "Cannot open '" << path << "'"; |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 68 | } |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 69 | } |
François Degros | 5b8d433 | 2021-05-18 05:56:20 +0000 | [diff] [blame] | 70 | |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 71 | return true; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 72 | } |
| 73 | |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 74 | bool List(const base::FilePath& path, |
| 75 | std::vector<base::FilePath>* const files, |
| 76 | std::vector<base::FilePath>* const subdirs) override { |
| 77 | DCHECK(!path.IsAbsolute()); |
| 78 | DCHECK(files); |
| 79 | DCHECK(subdirs); |
François Degros | 5b8d433 | 2021-05-18 05:56:20 +0000 | [diff] [blame] | 80 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 81 | base::FileEnumerator file_enumerator( |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 82 | src_dir_.Append(path), false /* recursive */, |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 83 | base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 84 | |
| 85 | while (!file_enumerator.Next().empty()) { |
| 86 | const base::FileEnumerator::FileInfo info = file_enumerator.GetInfo(); |
| 87 | (info.IsDirectory() ? subdirs : files) |
| 88 | ->push_back(path.Append(info.GetName())); |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 89 | } |
François Degros | 5b8d433 | 2021-05-18 05:56:20 +0000 | [diff] [blame] | 90 | |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 91 | return true; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 92 | } |
| 93 | |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 94 | bool GetInfo(const base::FilePath& path, Info* const info) override { |
| 95 | DCHECK(!path.IsAbsolute()); |
| 96 | DCHECK(info); |
| 97 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 98 | base::File::Info file_info; |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 99 | if (!base::GetFileInfo(src_dir_.Append(path), &file_info)) { |
| 100 | LOG(ERROR) << "Cannot get info of '" << path << "'"; |
| 101 | return false; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 102 | } |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 103 | |
| 104 | info->is_directory = file_info.is_directory; |
| 105 | info->last_modified = file_info.last_modified; |
| 106 | |
| 107 | return true; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 108 | } |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 109 | |
| 110 | private: |
| 111 | const base::FilePath src_dir_; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 112 | }; |
| 113 | |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 114 | } // namespace |
| 115 | |
François Degros | 2b99b37 | 2021-05-14 07:01:47 +0000 | [diff] [blame] | 116 | std::ostream& operator<<(std::ostream& out, const Progress& progress) { |
| 117 | return out << progress.bytes << " bytes, " << progress.files << " files, " |
| 118 | << progress.directories << " dirs"; |
| 119 | } |
| 120 | |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 121 | bool Zip(const ZipParams& params) { |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 122 | DirectFileAccessor default_accessor(params.src_dir); |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 123 | FileAccessor* const file_accessor = params.file_accessor ?: &default_accessor; |
| 124 | |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 125 | std::unique_ptr<internal::ZipWriter> zip_writer; |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 126 | |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 127 | #if defined(OS_POSIX) |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 128 | if (params.dest_fd != base::kInvalidPlatformFile) { |
| 129 | DCHECK(params.dest_file.empty()); |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 130 | zip_writer = |
| 131 | internal::ZipWriter::CreateWithFd(params.dest_fd, file_accessor); |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 132 | if (!zip_writer) |
| 133 | return false; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 134 | } |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 135 | #endif |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 136 | |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 137 | if (!zip_writer) { |
François Degros | 4d0fedc | 2021-05-25 01:58:46 +0000 | [diff] [blame] | 138 | zip_writer = internal::ZipWriter::Create(params.dest_file, file_accessor); |
Jay Civelli | 945bc9f | 2017-10-18 05:30:37 +0000 | [diff] [blame] | 139 | if (!zip_writer) |
| 140 | return false; |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 141 | } |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 142 | |
François Degros | 2b99b37 | 2021-05-14 07:01:47 +0000 | [diff] [blame] | 143 | zip_writer->SetProgressCallback(params.progress_callback, |
| 144 | params.progress_period); |
François Degros | 2865d44 | 2021-05-25 03:35:57 +0000 | [diff] [blame^] | 145 | zip_writer->SetRecursive(params.recursive); |
François Degros | 2b99b37 | 2021-05-14 07:01:47 +0000 | [diff] [blame] | 146 | |
François Degros | 2865d44 | 2021-05-25 03:35:57 +0000 | [diff] [blame^] | 147 | if (!params.include_hidden_files || params.filter_callback) |
| 148 | zip_writer->SetFilterCallback(base::BindRepeating( |
| 149 | [](const ZipParams* const params, const base::FilePath& path) -> bool { |
| 150 | return (params->include_hidden_files || !IsHiddenFile(path)) && |
| 151 | (!params->filter_callback || |
| 152 | params->filter_callback.Run(params->src_dir.Append(path))); |
| 153 | }, |
| 154 | ¶ms)); |
| 155 | |
| 156 | if (params.src_files.empty()) { |
| 157 | // No source items are specified. Zip the entire source directory. |
| 158 | zip_writer->SetRecursive(true); |
| 159 | if (!zip_writer->AddDirectoryContents(base::FilePath())) |
| 160 | return false; |
| 161 | } else { |
| 162 | // Only zip the specified source items. |
| 163 | if (!zip_writer->AddMixedEntries(params.src_files)) |
| 164 | return false; |
| 165 | } |
| 166 | |
| 167 | return zip_writer->Close(); |
Jay Civelli | 4f9e8e8 | 2017-10-13 16:38:08 +0000 | [diff] [blame] | 168 | } |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 169 | |
| 170 | bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) { |
Sylvain Defresne | ddebad2 | 2019-10-01 15:40:56 +0000 | [diff] [blame] | 171 | return UnzipWithFilterCallback( |
| 172 | src_file, dest_dir, base::BindRepeating(&ExcludeNoFilesFilter), true); |
meacer | f502ca7 | 2016-09-08 20:20:20 -0700 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | bool UnzipWithFilterCallback(const base::FilePath& src_file, |
| 176 | const base::FilePath& dest_dir, |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 177 | FilterCallback filter_cb, |
meacer | 8086cc8 | 2016-11-22 13:25:55 -0800 | [diff] [blame] | 178 | bool log_skipped_files) { |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 179 | base::File file(src_file, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 180 | if (!file.IsValid()) { |
François Degros | 2865d44 | 2021-05-25 03:35:57 +0000 | [diff] [blame^] | 181 | DLOG(WARNING) << "Cannot open '" << src_file << "'"; |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 182 | return false; |
| 183 | } |
François Degros | 2865d44 | 2021-05-25 03:35:57 +0000 | [diff] [blame^] | 184 | |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 185 | return UnzipWithFilterAndWriters( |
| 186 | file.GetPlatformFile(), |
| 187 | base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir), |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 188 | base::BindRepeating(&CreateDirectory, dest_dir), std::move(filter_cb), |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 189 | log_skipped_files); |
| 190 | } |
| 191 | |
| 192 | bool UnzipWithFilterAndWriters(const base::PlatformFile& src_file, |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 193 | WriterFactory writer_factory, |
| 194 | DirectoryCreator directory_creator, |
| 195 | FilterCallback filter_cb, |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 196 | bool log_skipped_files) { |
| 197 | ZipReader reader; |
| 198 | if (!reader.OpenFromPlatformFile(src_file)) { |
François Degros | 2865d44 | 2021-05-25 03:35:57 +0000 | [diff] [blame^] | 199 | DLOG(WARNING) << "Cannot open '" << src_file << "'"; |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 200 | return false; |
| 201 | } |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 202 | while (reader.HasMore()) { |
| 203 | if (!reader.OpenCurrentEntryInZip()) { |
| 204 | DLOG(WARNING) << "Failed to open the current file in zip"; |
| 205 | return false; |
| 206 | } |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 207 | const base::FilePath& entry_path = reader.current_entry_info()->file_path(); |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 208 | if (reader.current_entry_info()->is_unsafe()) { |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 209 | DLOG(WARNING) << "Found an unsafe file in zip " << entry_path; |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 210 | return false; |
| 211 | } |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 212 | if (filter_cb.Run(entry_path)) { |
| 213 | if (reader.current_entry_info()->is_directory()) { |
| 214 | if (!directory_creator.Run(entry_path)) |
| 215 | return false; |
| 216 | } else { |
| 217 | std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry_path); |
| 218 | if (!reader.ExtractCurrentEntry(writer.get(), |
| 219 | std::numeric_limits<uint64_t>::max())) { |
| 220 | DLOG(WARNING) << "Failed to extract " << entry_path; |
| 221 | return false; |
| 222 | } |
meacer | f502ca7 | 2016-09-08 20:20:20 -0700 | [diff] [blame] | 223 | } |
meacer | 8086cc8 | 2016-11-22 13:25:55 -0800 | [diff] [blame] | 224 | } else if (log_skipped_files) { |
Joshua Pawlicki | e31b503 | 2018-02-06 20:24:51 +0000 | [diff] [blame] | 225 | DLOG(WARNING) << "Skipped file " << entry_path; |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 226 | } |
meacer | f502ca7 | 2016-09-08 20:20:20 -0700 | [diff] [blame] | 227 | |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 228 | if (!reader.AdvanceToNextEntry()) { |
| 229 | DLOG(WARNING) << "Failed to advance to the next file"; |
| 230 | return false; |
| 231 | } |
| 232 | } |
| 233 | return true; |
| 234 | } |
| 235 | |
| 236 | bool ZipWithFilterCallback(const base::FilePath& src_dir, |
| 237 | const base::FilePath& dest_file, |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 238 | FilterCallback filter_cb) { |
brettw@chromium.org | 9a352f6 | 2013-07-15 20:18:09 +0000 | [diff] [blame] | 239 | DCHECK(base::DirectoryExists(src_dir)); |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 240 | return Zip({.src_dir = src_dir, |
| 241 | .dest_file = dest_file, |
| 242 | .filter_callback = std::move(filter_cb)}); |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 243 | } |
| 244 | |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 245 | bool Zip(const base::FilePath& src_dir, |
| 246 | const base::FilePath& dest_file, |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 247 | bool include_hidden_files) { |
François Degros | 2865d44 | 2021-05-25 03:35:57 +0000 | [diff] [blame^] | 248 | return Zip({.src_dir = src_dir, |
| 249 | .dest_file = dest_file, |
| 250 | .include_hidden_files = include_hidden_files}); |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 251 | } |
| 252 | |
| 253 | #if defined(OS_POSIX) |
| 254 | bool ZipFiles(const base::FilePath& src_dir, |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 255 | Paths src_relative_paths, |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 256 | int dest_fd) { |
brettw@chromium.org | 9a352f6 | 2013-07-15 20:18:09 +0000 | [diff] [blame] | 257 | DCHECK(base::DirectoryExists(src_dir)); |
François Degros | 16cd46c | 2021-05-14 05:07:27 +0000 | [diff] [blame] | 258 | return Zip({.src_dir = src_dir, |
| 259 | .dest_fd = dest_fd, |
| 260 | .src_files = src_relative_paths}); |
alecflett@chromium.org | d6d082e | 2013-05-03 23:02:57 +0000 | [diff] [blame] | 261 | } |
| 262 | #endif // defined(OS_POSIX) |
| 263 | |
| 264 | } // namespace zip |