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