xref: /aosp_15_r20/external/zlib/google/zip_reader.cc (revision 86ee64e75fa5f8bce2c8c356138035642429cd05)
1*86ee64e7SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*86ee64e7SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*86ee64e7SAndroid Build Coastguard Worker // found in the LICENSE file.
4*86ee64e7SAndroid Build Coastguard Worker 
5*86ee64e7SAndroid Build Coastguard Worker #include "third_party/zlib/google/zip_reader.h"
6*86ee64e7SAndroid Build Coastguard Worker 
7*86ee64e7SAndroid Build Coastguard Worker #include <algorithm>
8*86ee64e7SAndroid Build Coastguard Worker #include <utility>
9*86ee64e7SAndroid Build Coastguard Worker 
10*86ee64e7SAndroid Build Coastguard Worker #include "base/check.h"
11*86ee64e7SAndroid Build Coastguard Worker #include "base/files/file.h"
12*86ee64e7SAndroid Build Coastguard Worker #include "base/files/file_util.h"
13*86ee64e7SAndroid Build Coastguard Worker #include "base/functional/bind.h"
14*86ee64e7SAndroid Build Coastguard Worker #include "base/i18n/icu_string_conversions.h"
15*86ee64e7SAndroid Build Coastguard Worker #include "base/logging.h"
16*86ee64e7SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
17*86ee64e7SAndroid Build Coastguard Worker #include "base/strings/strcat.h"
18*86ee64e7SAndroid Build Coastguard Worker #include "base/strings/string_piece.h"
19*86ee64e7SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
20*86ee64e7SAndroid Build Coastguard Worker #include "base/strings/utf_string_conversions.h"
21*86ee64e7SAndroid Build Coastguard Worker #include "base/task/sequenced_task_runner.h"
22*86ee64e7SAndroid Build Coastguard Worker #include "build/build_config.h"
23*86ee64e7SAndroid Build Coastguard Worker #include "third_party/zlib/google/redact.h"
24*86ee64e7SAndroid Build Coastguard Worker #include "third_party/zlib/google/zip_internal.h"
25*86ee64e7SAndroid Build Coastguard Worker 
26*86ee64e7SAndroid Build Coastguard Worker #if defined(USE_SYSTEM_MINIZIP)
27*86ee64e7SAndroid Build Coastguard Worker #include <minizip/unzip.h>
28*86ee64e7SAndroid Build Coastguard Worker #else
29*86ee64e7SAndroid Build Coastguard Worker #include "third_party/zlib/contrib/minizip/unzip.h"
30*86ee64e7SAndroid Build Coastguard Worker #if defined(OS_WIN)
31*86ee64e7SAndroid Build Coastguard Worker #include "third_party/zlib/contrib/minizip/iowin32.h"
32*86ee64e7SAndroid Build Coastguard Worker #endif  // defined(OS_WIN)
33*86ee64e7SAndroid Build Coastguard Worker #endif  // defined(USE_SYSTEM_MINIZIP)
34*86ee64e7SAndroid Build Coastguard Worker 
35*86ee64e7SAndroid Build Coastguard Worker #if defined(OS_POSIX)
36*86ee64e7SAndroid Build Coastguard Worker #include <sys/stat.h>
37*86ee64e7SAndroid Build Coastguard Worker #endif
38*86ee64e7SAndroid Build Coastguard Worker 
39*86ee64e7SAndroid Build Coastguard Worker namespace zip {
40*86ee64e7SAndroid Build Coastguard Worker namespace {
41*86ee64e7SAndroid Build Coastguard Worker 
42*86ee64e7SAndroid Build Coastguard Worker enum UnzipError : int;
43*86ee64e7SAndroid Build Coastguard Worker 
operator <<(std::ostream & out,UnzipError error)44*86ee64e7SAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& out, UnzipError error) {
45*86ee64e7SAndroid Build Coastguard Worker #define SWITCH_ERR(X) \
46*86ee64e7SAndroid Build Coastguard Worker   case X:             \
47*86ee64e7SAndroid Build Coastguard Worker     return out << #X;
48*86ee64e7SAndroid Build Coastguard Worker   switch (error) {
49*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_OK);
50*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_END_OF_LIST_OF_FILE);
51*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_ERRNO);
52*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_PARAMERROR);
53*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_BADZIPFILE);
54*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_INTERNALERROR);
55*86ee64e7SAndroid Build Coastguard Worker     SWITCH_ERR(UNZ_CRCERROR);
56*86ee64e7SAndroid Build Coastguard Worker     default:
57*86ee64e7SAndroid Build Coastguard Worker       return out << "UNZ" << static_cast<int>(error);
58*86ee64e7SAndroid Build Coastguard Worker   }
59*86ee64e7SAndroid Build Coastguard Worker #undef SWITCH_ERR
60*86ee64e7SAndroid Build Coastguard Worker }
61*86ee64e7SAndroid Build Coastguard Worker 
IsValidFileNameCharacterOnWindows(char16_t c)62*86ee64e7SAndroid Build Coastguard Worker bool IsValidFileNameCharacterOnWindows(char16_t c) {
63*86ee64e7SAndroid Build Coastguard Worker   if (c < 32)
64*86ee64e7SAndroid Build Coastguard Worker     return false;
65*86ee64e7SAndroid Build Coastguard Worker 
66*86ee64e7SAndroid Build Coastguard Worker   switch (c) {
67*86ee64e7SAndroid Build Coastguard Worker     case '<':   // Less than
68*86ee64e7SAndroid Build Coastguard Worker     case '>':   // Greater than
69*86ee64e7SAndroid Build Coastguard Worker     case ':':   // Colon
70*86ee64e7SAndroid Build Coastguard Worker     case '"':   // Double quote
71*86ee64e7SAndroid Build Coastguard Worker     case '|':   // Vertical bar or pipe
72*86ee64e7SAndroid Build Coastguard Worker     case '?':   // Question mark
73*86ee64e7SAndroid Build Coastguard Worker     case '*':   // Asterisk
74*86ee64e7SAndroid Build Coastguard Worker     case '/':   // Forward slash
75*86ee64e7SAndroid Build Coastguard Worker     case '\\':  // Backslash
76*86ee64e7SAndroid Build Coastguard Worker       return false;
77*86ee64e7SAndroid Build Coastguard Worker   }
78*86ee64e7SAndroid Build Coastguard Worker 
79*86ee64e7SAndroid Build Coastguard Worker   return true;
80*86ee64e7SAndroid Build Coastguard Worker }
81*86ee64e7SAndroid Build Coastguard Worker 
82*86ee64e7SAndroid Build Coastguard Worker // A writer delegate that writes to a given string.
83*86ee64e7SAndroid Build Coastguard Worker class StringWriterDelegate : public WriterDelegate {
84*86ee64e7SAndroid Build Coastguard Worker  public:
StringWriterDelegate(std::string * output)85*86ee64e7SAndroid Build Coastguard Worker   explicit StringWriterDelegate(std::string* output) : output_(output) {}
86*86ee64e7SAndroid Build Coastguard Worker 
87*86ee64e7SAndroid Build Coastguard Worker   // WriterDelegate methods:
WriteBytes(const char * data,int num_bytes)88*86ee64e7SAndroid Build Coastguard Worker   bool WriteBytes(const char* data, int num_bytes) override {
89*86ee64e7SAndroid Build Coastguard Worker     output_->append(data, num_bytes);
90*86ee64e7SAndroid Build Coastguard Worker     return true;
91*86ee64e7SAndroid Build Coastguard Worker   }
92*86ee64e7SAndroid Build Coastguard Worker 
93*86ee64e7SAndroid Build Coastguard Worker  private:
94*86ee64e7SAndroid Build Coastguard Worker   std::string* const output_;
95*86ee64e7SAndroid Build Coastguard Worker };
96*86ee64e7SAndroid Build Coastguard Worker 
97*86ee64e7SAndroid Build Coastguard Worker #if defined(OS_POSIX)
SetPosixFilePermissions(int fd,int mode)98*86ee64e7SAndroid Build Coastguard Worker void SetPosixFilePermissions(int fd, int mode) {
99*86ee64e7SAndroid Build Coastguard Worker   base::stat_wrapper_t sb;
100*86ee64e7SAndroid Build Coastguard Worker   if (base::File::Fstat(fd, &sb)) {
101*86ee64e7SAndroid Build Coastguard Worker     return;
102*86ee64e7SAndroid Build Coastguard Worker   }
103*86ee64e7SAndroid Build Coastguard Worker   mode_t new_mode = sb.st_mode;
104*86ee64e7SAndroid Build Coastguard Worker   // Transfer the executable bit only if the file is readable.
105*86ee64e7SAndroid Build Coastguard Worker   if ((sb.st_mode & S_IRUSR) == S_IRUSR && (mode & S_IXUSR) == S_IXUSR) {
106*86ee64e7SAndroid Build Coastguard Worker     new_mode |= S_IXUSR;
107*86ee64e7SAndroid Build Coastguard Worker   }
108*86ee64e7SAndroid Build Coastguard Worker   if ((sb.st_mode & S_IRGRP) == S_IRGRP && (mode & S_IXGRP) == S_IXGRP) {
109*86ee64e7SAndroid Build Coastguard Worker     new_mode |= S_IXGRP;
110*86ee64e7SAndroid Build Coastguard Worker   }
111*86ee64e7SAndroid Build Coastguard Worker   if ((sb.st_mode & S_IROTH) == S_IROTH && (mode & S_IXOTH) == S_IXOTH) {
112*86ee64e7SAndroid Build Coastguard Worker     new_mode |= S_IXOTH;
113*86ee64e7SAndroid Build Coastguard Worker   }
114*86ee64e7SAndroid Build Coastguard Worker   if (new_mode != sb.st_mode) {
115*86ee64e7SAndroid Build Coastguard Worker     fchmod(fd, new_mode);
116*86ee64e7SAndroid Build Coastguard Worker   }
117*86ee64e7SAndroid Build Coastguard Worker }
118*86ee64e7SAndroid Build Coastguard Worker #endif
119*86ee64e7SAndroid Build Coastguard Worker 
120*86ee64e7SAndroid Build Coastguard Worker }  // namespace
121*86ee64e7SAndroid Build Coastguard Worker 
ZipReader()122*86ee64e7SAndroid Build Coastguard Worker ZipReader::ZipReader() {
123*86ee64e7SAndroid Build Coastguard Worker   Reset();
124*86ee64e7SAndroid Build Coastguard Worker }
125*86ee64e7SAndroid Build Coastguard Worker 
~ZipReader()126*86ee64e7SAndroid Build Coastguard Worker ZipReader::~ZipReader() {
127*86ee64e7SAndroid Build Coastguard Worker   Close();
128*86ee64e7SAndroid Build Coastguard Worker }
129*86ee64e7SAndroid Build Coastguard Worker 
Open(const base::FilePath & zip_path)130*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::Open(const base::FilePath& zip_path) {
131*86ee64e7SAndroid Build Coastguard Worker   DCHECK(!zip_file_);
132*86ee64e7SAndroid Build Coastguard Worker 
133*86ee64e7SAndroid Build Coastguard Worker   // Use of "Unsafe" function does not look good, but there is no way to do
134*86ee64e7SAndroid Build Coastguard Worker   // this safely on Linux. See file_util.h for details.
135*86ee64e7SAndroid Build Coastguard Worker   zip_file_ = internal::OpenForUnzipping(zip_path.AsUTF8Unsafe());
136*86ee64e7SAndroid Build Coastguard Worker   if (!zip_file_) {
137*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path);
138*86ee64e7SAndroid Build Coastguard Worker     return false;
139*86ee64e7SAndroid Build Coastguard Worker   }
140*86ee64e7SAndroid Build Coastguard Worker 
141*86ee64e7SAndroid Build Coastguard Worker   return OpenInternal();
142*86ee64e7SAndroid Build Coastguard Worker }
143*86ee64e7SAndroid Build Coastguard Worker 
OpenFromPlatformFile(base::PlatformFile zip_fd)144*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::OpenFromPlatformFile(base::PlatformFile zip_fd) {
145*86ee64e7SAndroid Build Coastguard Worker   DCHECK(!zip_file_);
146*86ee64e7SAndroid Build Coastguard Worker 
147*86ee64e7SAndroid Build Coastguard Worker #if defined(OS_POSIX) || defined(OS_FUCHSIA)
148*86ee64e7SAndroid Build Coastguard Worker   zip_file_ = internal::OpenFdForUnzipping(zip_fd);
149*86ee64e7SAndroid Build Coastguard Worker #elif defined(OS_WIN)
150*86ee64e7SAndroid Build Coastguard Worker   zip_file_ = internal::OpenHandleForUnzipping(zip_fd);
151*86ee64e7SAndroid Build Coastguard Worker #endif
152*86ee64e7SAndroid Build Coastguard Worker   if (!zip_file_) {
153*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot open ZIP from file handle " << zip_fd;
154*86ee64e7SAndroid Build Coastguard Worker     return false;
155*86ee64e7SAndroid Build Coastguard Worker   }
156*86ee64e7SAndroid Build Coastguard Worker 
157*86ee64e7SAndroid Build Coastguard Worker   return OpenInternal();
158*86ee64e7SAndroid Build Coastguard Worker }
159*86ee64e7SAndroid Build Coastguard Worker 
OpenFromString(const std::string & data)160*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::OpenFromString(const std::string& data) {
161*86ee64e7SAndroid Build Coastguard Worker   zip_file_ = internal::PrepareMemoryForUnzipping(data);
162*86ee64e7SAndroid Build Coastguard Worker   if (!zip_file_)
163*86ee64e7SAndroid Build Coastguard Worker     return false;
164*86ee64e7SAndroid Build Coastguard Worker   return OpenInternal();
165*86ee64e7SAndroid Build Coastguard Worker }
166*86ee64e7SAndroid Build Coastguard Worker 
Close()167*86ee64e7SAndroid Build Coastguard Worker void ZipReader::Close() {
168*86ee64e7SAndroid Build Coastguard Worker   if (zip_file_) {
169*86ee64e7SAndroid Build Coastguard Worker     if (const UnzipError err{unzClose(zip_file_)}; err != UNZ_OK) {
170*86ee64e7SAndroid Build Coastguard Worker       LOG(ERROR) << "Error while closing ZIP archive: " << err;
171*86ee64e7SAndroid Build Coastguard Worker     }
172*86ee64e7SAndroid Build Coastguard Worker   }
173*86ee64e7SAndroid Build Coastguard Worker   Reset();
174*86ee64e7SAndroid Build Coastguard Worker }
175*86ee64e7SAndroid Build Coastguard Worker 
Next()176*86ee64e7SAndroid Build Coastguard Worker const ZipReader::Entry* ZipReader::Next() {
177*86ee64e7SAndroid Build Coastguard Worker   DCHECK(zip_file_);
178*86ee64e7SAndroid Build Coastguard Worker 
179*86ee64e7SAndroid Build Coastguard Worker   if (reached_end_)
180*86ee64e7SAndroid Build Coastguard Worker     return nullptr;
181*86ee64e7SAndroid Build Coastguard Worker 
182*86ee64e7SAndroid Build Coastguard Worker   DCHECK(ok_);
183*86ee64e7SAndroid Build Coastguard Worker 
184*86ee64e7SAndroid Build Coastguard Worker   // Move to the next entry if we're not trying to open the first entry.
185*86ee64e7SAndroid Build Coastguard Worker   if (next_index_ > 0) {
186*86ee64e7SAndroid Build Coastguard Worker     if (const UnzipError err{unzGoToNextFile(zip_file_)}; err != UNZ_OK) {
187*86ee64e7SAndroid Build Coastguard Worker       reached_end_ = true;
188*86ee64e7SAndroid Build Coastguard Worker       if (err != UNZ_END_OF_LIST_OF_FILE) {
189*86ee64e7SAndroid Build Coastguard Worker         LOG(ERROR) << "Cannot go to next entry in ZIP: " << err;
190*86ee64e7SAndroid Build Coastguard Worker         ok_ = false;
191*86ee64e7SAndroid Build Coastguard Worker       }
192*86ee64e7SAndroid Build Coastguard Worker       return nullptr;
193*86ee64e7SAndroid Build Coastguard Worker     }
194*86ee64e7SAndroid Build Coastguard Worker   }
195*86ee64e7SAndroid Build Coastguard Worker 
196*86ee64e7SAndroid Build Coastguard Worker   next_index_++;
197*86ee64e7SAndroid Build Coastguard Worker 
198*86ee64e7SAndroid Build Coastguard Worker   if (!OpenEntry()) {
199*86ee64e7SAndroid Build Coastguard Worker     reached_end_ = true;
200*86ee64e7SAndroid Build Coastguard Worker     ok_ = false;
201*86ee64e7SAndroid Build Coastguard Worker     return nullptr;
202*86ee64e7SAndroid Build Coastguard Worker   }
203*86ee64e7SAndroid Build Coastguard Worker 
204*86ee64e7SAndroid Build Coastguard Worker   return &entry_;
205*86ee64e7SAndroid Build Coastguard Worker }
206*86ee64e7SAndroid Build Coastguard Worker 
OpenEntry()207*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::OpenEntry() {
208*86ee64e7SAndroid Build Coastguard Worker   DCHECK(zip_file_);
209*86ee64e7SAndroid Build Coastguard Worker 
210*86ee64e7SAndroid Build Coastguard Worker   // Get entry info.
211*86ee64e7SAndroid Build Coastguard Worker   unz_file_info64 info = {};
212*86ee64e7SAndroid Build Coastguard Worker   char path_in_zip[internal::kZipMaxPath] = {};
213*86ee64e7SAndroid Build Coastguard Worker   if (const UnzipError err{unzGetCurrentFileInfo64(
214*86ee64e7SAndroid Build Coastguard Worker           zip_file_, &info, path_in_zip, sizeof(path_in_zip) - 1, nullptr, 0,
215*86ee64e7SAndroid Build Coastguard Worker           nullptr, 0)};
216*86ee64e7SAndroid Build Coastguard Worker       err != UNZ_OK) {
217*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot get entry from ZIP: " << err;
218*86ee64e7SAndroid Build Coastguard Worker     return false;
219*86ee64e7SAndroid Build Coastguard Worker   }
220*86ee64e7SAndroid Build Coastguard Worker 
221*86ee64e7SAndroid Build Coastguard Worker   entry_.path_in_original_encoding = path_in_zip;
222*86ee64e7SAndroid Build Coastguard Worker 
223*86ee64e7SAndroid Build Coastguard Worker   // Convert path from original encoding to Unicode.
224*86ee64e7SAndroid Build Coastguard Worker   std::u16string path_in_utf16;
225*86ee64e7SAndroid Build Coastguard Worker   const char* const encoding = encoding_.empty() ? "UTF-8" : encoding_.c_str();
226*86ee64e7SAndroid Build Coastguard Worker   if (!base::CodepageToUTF16(entry_.path_in_original_encoding, encoding,
227*86ee64e7SAndroid Build Coastguard Worker                              base::OnStringConversionError::SUBSTITUTE,
228*86ee64e7SAndroid Build Coastguard Worker                              &path_in_utf16)) {
229*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot convert path from encoding " << encoding;
230*86ee64e7SAndroid Build Coastguard Worker     return false;
231*86ee64e7SAndroid Build Coastguard Worker   }
232*86ee64e7SAndroid Build Coastguard Worker 
233*86ee64e7SAndroid Build Coastguard Worker   // Normalize path.
234*86ee64e7SAndroid Build Coastguard Worker   Normalize(path_in_utf16);
235*86ee64e7SAndroid Build Coastguard Worker 
236*86ee64e7SAndroid Build Coastguard Worker   entry_.original_size = info.uncompressed_size;
237*86ee64e7SAndroid Build Coastguard Worker 
238*86ee64e7SAndroid Build Coastguard Worker   // The file content of this entry is encrypted if flag bit 0 is set.
239*86ee64e7SAndroid Build Coastguard Worker   entry_.is_encrypted = info.flag & 1;
240*86ee64e7SAndroid Build Coastguard Worker   if (entry_.is_encrypted) {
241*86ee64e7SAndroid Build Coastguard Worker     // Is the entry AES encrypted.
242*86ee64e7SAndroid Build Coastguard Worker     entry_.uses_aes_encryption = info.compression_method == 99;
243*86ee64e7SAndroid Build Coastguard Worker   } else {
244*86ee64e7SAndroid Build Coastguard Worker     entry_.uses_aes_encryption = false;
245*86ee64e7SAndroid Build Coastguard Worker   }
246*86ee64e7SAndroid Build Coastguard Worker 
247*86ee64e7SAndroid Build Coastguard Worker   // Construct the last modified time. The timezone info is not present in ZIP
248*86ee64e7SAndroid Build Coastguard Worker   // archives, so we construct the time as UTC.
249*86ee64e7SAndroid Build Coastguard Worker   const base::Time::Exploded exploded_time = {
250*86ee64e7SAndroid Build Coastguard Worker       .year = static_cast<int>(info.tmu_date.tm_year),
251*86ee64e7SAndroid Build Coastguard Worker       .month =
252*86ee64e7SAndroid Build Coastguard Worker           static_cast<int>(info.tmu_date.tm_mon + 1),  // 0-based vs 1-based
253*86ee64e7SAndroid Build Coastguard Worker       .day_of_month = static_cast<int>(info.tmu_date.tm_mday),
254*86ee64e7SAndroid Build Coastguard Worker       .hour = static_cast<int>(info.tmu_date.tm_hour),
255*86ee64e7SAndroid Build Coastguard Worker       .minute = static_cast<int>(info.tmu_date.tm_min),
256*86ee64e7SAndroid Build Coastguard Worker       .second = static_cast<int>(info.tmu_date.tm_sec)};
257*86ee64e7SAndroid Build Coastguard Worker 
258*86ee64e7SAndroid Build Coastguard Worker   if (!base::Time::FromUTCExploded(exploded_time, &entry_.last_modified))
259*86ee64e7SAndroid Build Coastguard Worker     entry_.last_modified = base::Time::UnixEpoch();
260*86ee64e7SAndroid Build Coastguard Worker 
261*86ee64e7SAndroid Build Coastguard Worker #if defined(OS_POSIX)
262*86ee64e7SAndroid Build Coastguard Worker   entry_.posix_mode = (info.external_fa >> 16L) & (S_IRWXU | S_IRWXG | S_IRWXO);
263*86ee64e7SAndroid Build Coastguard Worker #else
264*86ee64e7SAndroid Build Coastguard Worker   entry_.posix_mode = 0;
265*86ee64e7SAndroid Build Coastguard Worker #endif
266*86ee64e7SAndroid Build Coastguard Worker 
267*86ee64e7SAndroid Build Coastguard Worker   return true;
268*86ee64e7SAndroid Build Coastguard Worker }
269*86ee64e7SAndroid Build Coastguard Worker 
Normalize(base::StringPiece16 in)270*86ee64e7SAndroid Build Coastguard Worker void ZipReader::Normalize(base::StringPiece16 in) {
271*86ee64e7SAndroid Build Coastguard Worker   entry_.is_unsafe = true;
272*86ee64e7SAndroid Build Coastguard Worker 
273*86ee64e7SAndroid Build Coastguard Worker   // Directory entries in ZIP have a path ending with "/".
274*86ee64e7SAndroid Build Coastguard Worker   entry_.is_directory = base::EndsWith(in, u"/");
275*86ee64e7SAndroid Build Coastguard Worker 
276*86ee64e7SAndroid Build Coastguard Worker   std::u16string normalized_path;
277*86ee64e7SAndroid Build Coastguard Worker   if (base::StartsWith(in, u"/")) {
278*86ee64e7SAndroid Build Coastguard Worker     normalized_path = u"ROOT";
279*86ee64e7SAndroid Build Coastguard Worker     entry_.is_unsafe = false;
280*86ee64e7SAndroid Build Coastguard Worker   }
281*86ee64e7SAndroid Build Coastguard Worker 
282*86ee64e7SAndroid Build Coastguard Worker   for (;;) {
283*86ee64e7SAndroid Build Coastguard Worker     // Consume initial path separators.
284*86ee64e7SAndroid Build Coastguard Worker     const base::StringPiece16::size_type i = in.find_first_not_of(u'/');
285*86ee64e7SAndroid Build Coastguard Worker     if (i == base::StringPiece16::npos)
286*86ee64e7SAndroid Build Coastguard Worker       break;
287*86ee64e7SAndroid Build Coastguard Worker 
288*86ee64e7SAndroid Build Coastguard Worker     in.remove_prefix(i);
289*86ee64e7SAndroid Build Coastguard Worker     DCHECK(!in.empty());
290*86ee64e7SAndroid Build Coastguard Worker 
291*86ee64e7SAndroid Build Coastguard Worker     // Isolate next path component.
292*86ee64e7SAndroid Build Coastguard Worker     const base::StringPiece16 part = in.substr(0, in.find_first_of(u'/'));
293*86ee64e7SAndroid Build Coastguard Worker     DCHECK(!part.empty());
294*86ee64e7SAndroid Build Coastguard Worker 
295*86ee64e7SAndroid Build Coastguard Worker     in.remove_prefix(part.size());
296*86ee64e7SAndroid Build Coastguard Worker 
297*86ee64e7SAndroid Build Coastguard Worker     if (!normalized_path.empty())
298*86ee64e7SAndroid Build Coastguard Worker       normalized_path += u'/';
299*86ee64e7SAndroid Build Coastguard Worker 
300*86ee64e7SAndroid Build Coastguard Worker     if (part == u".") {
301*86ee64e7SAndroid Build Coastguard Worker       normalized_path += u"DOT";
302*86ee64e7SAndroid Build Coastguard Worker       entry_.is_unsafe = true;
303*86ee64e7SAndroid Build Coastguard Worker       continue;
304*86ee64e7SAndroid Build Coastguard Worker     }
305*86ee64e7SAndroid Build Coastguard Worker 
306*86ee64e7SAndroid Build Coastguard Worker     if (part == u"..") {
307*86ee64e7SAndroid Build Coastguard Worker       normalized_path += u"UP";
308*86ee64e7SAndroid Build Coastguard Worker       entry_.is_unsafe = true;
309*86ee64e7SAndroid Build Coastguard Worker       continue;
310*86ee64e7SAndroid Build Coastguard Worker     }
311*86ee64e7SAndroid Build Coastguard Worker 
312*86ee64e7SAndroid Build Coastguard Worker     // Windows has more restrictions than other systems when it comes to valid
313*86ee64e7SAndroid Build Coastguard Worker     // file paths. Replace Windows-invalid characters on all systems for
314*86ee64e7SAndroid Build Coastguard Worker     // consistency. In particular, this prevents a path component containing
315*86ee64e7SAndroid Build Coastguard Worker     // colon and backslash from being misinterpreted as an absolute path on
316*86ee64e7SAndroid Build Coastguard Worker     // Windows.
317*86ee64e7SAndroid Build Coastguard Worker     for (const char16_t c : part) {
318*86ee64e7SAndroid Build Coastguard Worker       normalized_path += IsValidFileNameCharacterOnWindows(c) ? c : 0xFFFD;
319*86ee64e7SAndroid Build Coastguard Worker     }
320*86ee64e7SAndroid Build Coastguard Worker 
321*86ee64e7SAndroid Build Coastguard Worker     entry_.is_unsafe = false;
322*86ee64e7SAndroid Build Coastguard Worker   }
323*86ee64e7SAndroid Build Coastguard Worker 
324*86ee64e7SAndroid Build Coastguard Worker   // If the entry is a directory, add the final path separator to the entry
325*86ee64e7SAndroid Build Coastguard Worker   // path.
326*86ee64e7SAndroid Build Coastguard Worker   if (entry_.is_directory && !normalized_path.empty()) {
327*86ee64e7SAndroid Build Coastguard Worker     normalized_path += u'/';
328*86ee64e7SAndroid Build Coastguard Worker     entry_.is_unsafe = false;
329*86ee64e7SAndroid Build Coastguard Worker   }
330*86ee64e7SAndroid Build Coastguard Worker 
331*86ee64e7SAndroid Build Coastguard Worker   entry_.path = base::FilePath::FromUTF16Unsafe(normalized_path);
332*86ee64e7SAndroid Build Coastguard Worker 
333*86ee64e7SAndroid Build Coastguard Worker   // By construction, we should always get a relative path.
334*86ee64e7SAndroid Build Coastguard Worker   DCHECK(!entry_.path.IsAbsolute()) << entry_.path;
335*86ee64e7SAndroid Build Coastguard Worker }
336*86ee64e7SAndroid Build Coastguard Worker 
ReportProgress(ListenerCallback listener_callback,uint64_t bytes) const337*86ee64e7SAndroid Build Coastguard Worker void ZipReader::ReportProgress(ListenerCallback listener_callback,
338*86ee64e7SAndroid Build Coastguard Worker                                uint64_t bytes) const {
339*86ee64e7SAndroid Build Coastguard Worker   delta_bytes_read_ += bytes;
340*86ee64e7SAndroid Build Coastguard Worker 
341*86ee64e7SAndroid Build Coastguard Worker   const base::TimeTicks now = base::TimeTicks::Now();
342*86ee64e7SAndroid Build Coastguard Worker   if (next_progress_report_time_ > now)
343*86ee64e7SAndroid Build Coastguard Worker     return;
344*86ee64e7SAndroid Build Coastguard Worker 
345*86ee64e7SAndroid Build Coastguard Worker   next_progress_report_time_ = now + progress_period_;
346*86ee64e7SAndroid Build Coastguard Worker   listener_callback.Run(delta_bytes_read_);
347*86ee64e7SAndroid Build Coastguard Worker   delta_bytes_read_ = 0;
348*86ee64e7SAndroid Build Coastguard Worker }
349*86ee64e7SAndroid Build Coastguard Worker 
ExtractCurrentEntry(WriterDelegate * delegate,ListenerCallback listener_callback,uint64_t num_bytes_to_extract) const350*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
351*86ee64e7SAndroid Build Coastguard Worker                                     ListenerCallback listener_callback,
352*86ee64e7SAndroid Build Coastguard Worker                                     uint64_t num_bytes_to_extract) const {
353*86ee64e7SAndroid Build Coastguard Worker   DCHECK(zip_file_);
354*86ee64e7SAndroid Build Coastguard Worker   DCHECK_LT(0, next_index_);
355*86ee64e7SAndroid Build Coastguard Worker   DCHECK(ok_);
356*86ee64e7SAndroid Build Coastguard Worker   DCHECK(!reached_end_);
357*86ee64e7SAndroid Build Coastguard Worker 
358*86ee64e7SAndroid Build Coastguard Worker   // Use password only for encrypted files. For non-encrypted files, no password
359*86ee64e7SAndroid Build Coastguard Worker   // is needed, and must be nullptr.
360*86ee64e7SAndroid Build Coastguard Worker   const char* const password =
361*86ee64e7SAndroid Build Coastguard Worker       entry_.is_encrypted ? password_.c_str() : nullptr;
362*86ee64e7SAndroid Build Coastguard Worker   if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)};
363*86ee64e7SAndroid Build Coastguard Worker       err != UNZ_OK) {
364*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot open file " << Redact(entry_.path)
365*86ee64e7SAndroid Build Coastguard Worker                << " from ZIP: " << err;
366*86ee64e7SAndroid Build Coastguard Worker     return false;
367*86ee64e7SAndroid Build Coastguard Worker   }
368*86ee64e7SAndroid Build Coastguard Worker 
369*86ee64e7SAndroid Build Coastguard Worker   DCHECK(delegate);
370*86ee64e7SAndroid Build Coastguard Worker   if (!delegate->PrepareOutput())
371*86ee64e7SAndroid Build Coastguard Worker     return false;
372*86ee64e7SAndroid Build Coastguard Worker 
373*86ee64e7SAndroid Build Coastguard Worker   uint64_t remaining_capacity = num_bytes_to_extract;
374*86ee64e7SAndroid Build Coastguard Worker   bool entire_file_extracted = false;
375*86ee64e7SAndroid Build Coastguard Worker 
376*86ee64e7SAndroid Build Coastguard Worker   while (remaining_capacity > 0) {
377*86ee64e7SAndroid Build Coastguard Worker     char buf[internal::kZipBufSize];
378*86ee64e7SAndroid Build Coastguard Worker     const int num_bytes_read =
379*86ee64e7SAndroid Build Coastguard Worker         unzReadCurrentFile(zip_file_, buf, internal::kZipBufSize);
380*86ee64e7SAndroid Build Coastguard Worker 
381*86ee64e7SAndroid Build Coastguard Worker     if (num_bytes_read == 0) {
382*86ee64e7SAndroid Build Coastguard Worker       entire_file_extracted = true;
383*86ee64e7SAndroid Build Coastguard Worker       break;
384*86ee64e7SAndroid Build Coastguard Worker     }
385*86ee64e7SAndroid Build Coastguard Worker 
386*86ee64e7SAndroid Build Coastguard Worker     if (num_bytes_read < 0) {
387*86ee64e7SAndroid Build Coastguard Worker       LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
388*86ee64e7SAndroid Build Coastguard Worker                  << " from ZIP: " << UnzipError(num_bytes_read);
389*86ee64e7SAndroid Build Coastguard Worker       break;
390*86ee64e7SAndroid Build Coastguard Worker     }
391*86ee64e7SAndroid Build Coastguard Worker 
392*86ee64e7SAndroid Build Coastguard Worker     if (listener_callback) {
393*86ee64e7SAndroid Build Coastguard Worker       ReportProgress(listener_callback, num_bytes_read);
394*86ee64e7SAndroid Build Coastguard Worker     }
395*86ee64e7SAndroid Build Coastguard Worker 
396*86ee64e7SAndroid Build Coastguard Worker     DCHECK_LT(0, num_bytes_read);
397*86ee64e7SAndroid Build Coastguard Worker     CHECK_LE(num_bytes_read, internal::kZipBufSize);
398*86ee64e7SAndroid Build Coastguard Worker 
399*86ee64e7SAndroid Build Coastguard Worker     uint64_t num_bytes_to_write = std::min<uint64_t>(
400*86ee64e7SAndroid Build Coastguard Worker         remaining_capacity, base::checked_cast<uint64_t>(num_bytes_read));
401*86ee64e7SAndroid Build Coastguard Worker     if (!delegate->WriteBytes(buf, num_bytes_to_write))
402*86ee64e7SAndroid Build Coastguard Worker       break;
403*86ee64e7SAndroid Build Coastguard Worker 
404*86ee64e7SAndroid Build Coastguard Worker     if (remaining_capacity == base::checked_cast<uint64_t>(num_bytes_read)) {
405*86ee64e7SAndroid Build Coastguard Worker       // Ensures function returns true if the entire file has been read.
406*86ee64e7SAndroid Build Coastguard Worker       const int n = unzReadCurrentFile(zip_file_, buf, 1);
407*86ee64e7SAndroid Build Coastguard Worker       entire_file_extracted = (n == 0);
408*86ee64e7SAndroid Build Coastguard Worker       LOG_IF(ERROR, n < 0) << "Cannot read file " << Redact(entry_.path)
409*86ee64e7SAndroid Build Coastguard Worker                            << " from ZIP: " << UnzipError(n);
410*86ee64e7SAndroid Build Coastguard Worker     }
411*86ee64e7SAndroid Build Coastguard Worker 
412*86ee64e7SAndroid Build Coastguard Worker     CHECK_GE(remaining_capacity, num_bytes_to_write);
413*86ee64e7SAndroid Build Coastguard Worker     remaining_capacity -= num_bytes_to_write;
414*86ee64e7SAndroid Build Coastguard Worker   }
415*86ee64e7SAndroid Build Coastguard Worker 
416*86ee64e7SAndroid Build Coastguard Worker   if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) {
417*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot extract file " << Redact(entry_.path)
418*86ee64e7SAndroid Build Coastguard Worker                << " from ZIP: " << err;
419*86ee64e7SAndroid Build Coastguard Worker     entire_file_extracted = false;
420*86ee64e7SAndroid Build Coastguard Worker   }
421*86ee64e7SAndroid Build Coastguard Worker 
422*86ee64e7SAndroid Build Coastguard Worker   if (entire_file_extracted) {
423*86ee64e7SAndroid Build Coastguard Worker     delegate->SetPosixFilePermissions(entry_.posix_mode);
424*86ee64e7SAndroid Build Coastguard Worker     if (entry_.last_modified != base::Time::UnixEpoch()) {
425*86ee64e7SAndroid Build Coastguard Worker       delegate->SetTimeModified(entry_.last_modified);
426*86ee64e7SAndroid Build Coastguard Worker     }
427*86ee64e7SAndroid Build Coastguard Worker   } else {
428*86ee64e7SAndroid Build Coastguard Worker     delegate->OnError();
429*86ee64e7SAndroid Build Coastguard Worker   }
430*86ee64e7SAndroid Build Coastguard Worker 
431*86ee64e7SAndroid Build Coastguard Worker   if (listener_callback) {
432*86ee64e7SAndroid Build Coastguard Worker     listener_callback.Run(delta_bytes_read_);
433*86ee64e7SAndroid Build Coastguard Worker     delta_bytes_read_ = 0;
434*86ee64e7SAndroid Build Coastguard Worker   }
435*86ee64e7SAndroid Build Coastguard Worker 
436*86ee64e7SAndroid Build Coastguard Worker   return entire_file_extracted;
437*86ee64e7SAndroid Build Coastguard Worker }
438*86ee64e7SAndroid Build Coastguard Worker 
ExtractCurrentEntry(WriterDelegate * delegate,uint64_t num_bytes_to_extract) const439*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
440*86ee64e7SAndroid Build Coastguard Worker                                     uint64_t num_bytes_to_extract) const {
441*86ee64e7SAndroid Build Coastguard Worker   return ExtractCurrentEntry(delegate, ListenerCallback(),
442*86ee64e7SAndroid Build Coastguard Worker                              num_bytes_to_extract);
443*86ee64e7SAndroid Build Coastguard Worker }
444*86ee64e7SAndroid Build Coastguard Worker 
ExtractCurrentEntryWithListener(WriterDelegate * delegate,ListenerCallback listener_callback) const445*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::ExtractCurrentEntryWithListener(
446*86ee64e7SAndroid Build Coastguard Worker     WriterDelegate* delegate,
447*86ee64e7SAndroid Build Coastguard Worker     ListenerCallback listener_callback) const {
448*86ee64e7SAndroid Build Coastguard Worker   return ExtractCurrentEntry(delegate, listener_callback);
449*86ee64e7SAndroid Build Coastguard Worker }
450*86ee64e7SAndroid Build Coastguard Worker 
ExtractCurrentEntryToFilePathAsync(const base::FilePath & output_file_path,SuccessCallback success_callback,FailureCallback failure_callback,ProgressCallback progress_callback)451*86ee64e7SAndroid Build Coastguard Worker void ZipReader::ExtractCurrentEntryToFilePathAsync(
452*86ee64e7SAndroid Build Coastguard Worker     const base::FilePath& output_file_path,
453*86ee64e7SAndroid Build Coastguard Worker     SuccessCallback success_callback,
454*86ee64e7SAndroid Build Coastguard Worker     FailureCallback failure_callback,
455*86ee64e7SAndroid Build Coastguard Worker     ProgressCallback progress_callback) {
456*86ee64e7SAndroid Build Coastguard Worker   DCHECK(zip_file_);
457*86ee64e7SAndroid Build Coastguard Worker   DCHECK_LT(0, next_index_);
458*86ee64e7SAndroid Build Coastguard Worker   DCHECK(ok_);
459*86ee64e7SAndroid Build Coastguard Worker   DCHECK(!reached_end_);
460*86ee64e7SAndroid Build Coastguard Worker 
461*86ee64e7SAndroid Build Coastguard Worker   // If this is a directory, just create it and return.
462*86ee64e7SAndroid Build Coastguard Worker   if (entry_.is_directory) {
463*86ee64e7SAndroid Build Coastguard Worker     if (base::CreateDirectory(output_file_path)) {
464*86ee64e7SAndroid Build Coastguard Worker       base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
465*86ee64e7SAndroid Build Coastguard Worker           FROM_HERE, std::move(success_callback));
466*86ee64e7SAndroid Build Coastguard Worker     } else {
467*86ee64e7SAndroid Build Coastguard Worker       LOG(ERROR) << "Cannot create directory " << Redact(output_file_path);
468*86ee64e7SAndroid Build Coastguard Worker       base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
469*86ee64e7SAndroid Build Coastguard Worker           FROM_HERE, std::move(failure_callback));
470*86ee64e7SAndroid Build Coastguard Worker     }
471*86ee64e7SAndroid Build Coastguard Worker     return;
472*86ee64e7SAndroid Build Coastguard Worker   }
473*86ee64e7SAndroid Build Coastguard Worker 
474*86ee64e7SAndroid Build Coastguard Worker   // Use password only for encrypted files. For non-encrypted files, no password
475*86ee64e7SAndroid Build Coastguard Worker   // is needed, and must be nullptr.
476*86ee64e7SAndroid Build Coastguard Worker   const char* const password =
477*86ee64e7SAndroid Build Coastguard Worker       entry_.is_encrypted ? password_.c_str() : nullptr;
478*86ee64e7SAndroid Build Coastguard Worker   if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)};
479*86ee64e7SAndroid Build Coastguard Worker       err != UNZ_OK) {
480*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot open file " << Redact(entry_.path)
481*86ee64e7SAndroid Build Coastguard Worker                << " from ZIP: " << err;
482*86ee64e7SAndroid Build Coastguard Worker     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
483*86ee64e7SAndroid Build Coastguard Worker         FROM_HERE, std::move(failure_callback));
484*86ee64e7SAndroid Build Coastguard Worker     return;
485*86ee64e7SAndroid Build Coastguard Worker   }
486*86ee64e7SAndroid Build Coastguard Worker 
487*86ee64e7SAndroid Build Coastguard Worker   base::FilePath output_dir_path = output_file_path.DirName();
488*86ee64e7SAndroid Build Coastguard Worker   if (!base::CreateDirectory(output_dir_path)) {
489*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot create directory " << Redact(output_dir_path);
490*86ee64e7SAndroid Build Coastguard Worker     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
491*86ee64e7SAndroid Build Coastguard Worker         FROM_HERE, std::move(failure_callback));
492*86ee64e7SAndroid Build Coastguard Worker     return;
493*86ee64e7SAndroid Build Coastguard Worker   }
494*86ee64e7SAndroid Build Coastguard Worker 
495*86ee64e7SAndroid Build Coastguard Worker   const int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
496*86ee64e7SAndroid Build Coastguard Worker   base::File output_file(output_file_path, flags);
497*86ee64e7SAndroid Build Coastguard Worker 
498*86ee64e7SAndroid Build Coastguard Worker   if (!output_file.IsValid()) {
499*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot create file " << Redact(output_file_path);
500*86ee64e7SAndroid Build Coastguard Worker     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
501*86ee64e7SAndroid Build Coastguard Worker         FROM_HERE, std::move(failure_callback));
502*86ee64e7SAndroid Build Coastguard Worker     return;
503*86ee64e7SAndroid Build Coastguard Worker   }
504*86ee64e7SAndroid Build Coastguard Worker 
505*86ee64e7SAndroid Build Coastguard Worker   base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
506*86ee64e7SAndroid Build Coastguard Worker       FROM_HERE,
507*86ee64e7SAndroid Build Coastguard Worker       base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
508*86ee64e7SAndroid Build Coastguard Worker                      std::move(output_file), std::move(success_callback),
509*86ee64e7SAndroid Build Coastguard Worker                      std::move(failure_callback), std::move(progress_callback),
510*86ee64e7SAndroid Build Coastguard Worker                      0 /* initial offset */));
511*86ee64e7SAndroid Build Coastguard Worker }
512*86ee64e7SAndroid Build Coastguard Worker 
ExtractCurrentEntryToString(uint64_t max_read_bytes,std::string * output) const513*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes,
514*86ee64e7SAndroid Build Coastguard Worker                                             std::string* output) const {
515*86ee64e7SAndroid Build Coastguard Worker   DCHECK(output);
516*86ee64e7SAndroid Build Coastguard Worker   DCHECK(zip_file_);
517*86ee64e7SAndroid Build Coastguard Worker   DCHECK_LT(0, next_index_);
518*86ee64e7SAndroid Build Coastguard Worker   DCHECK(ok_);
519*86ee64e7SAndroid Build Coastguard Worker   DCHECK(!reached_end_);
520*86ee64e7SAndroid Build Coastguard Worker 
521*86ee64e7SAndroid Build Coastguard Worker   output->clear();
522*86ee64e7SAndroid Build Coastguard Worker 
523*86ee64e7SAndroid Build Coastguard Worker   if (max_read_bytes == 0)
524*86ee64e7SAndroid Build Coastguard Worker     return true;
525*86ee64e7SAndroid Build Coastguard Worker 
526*86ee64e7SAndroid Build Coastguard Worker   if (entry_.is_directory)
527*86ee64e7SAndroid Build Coastguard Worker     return true;
528*86ee64e7SAndroid Build Coastguard Worker 
529*86ee64e7SAndroid Build Coastguard Worker   // The original_size is the best hint for the real size, so it saves doing
530*86ee64e7SAndroid Build Coastguard Worker   // reallocations for the common case when the uncompressed size is correct.
531*86ee64e7SAndroid Build Coastguard Worker   // However, we need to assume that the uncompressed size could be incorrect
532*86ee64e7SAndroid Build Coastguard Worker   // therefore this function needs to read as much data as possible.
533*86ee64e7SAndroid Build Coastguard Worker   output->reserve(base::checked_cast<size_t>(std::min<uint64_t>(
534*86ee64e7SAndroid Build Coastguard Worker       max_read_bytes, base::checked_cast<uint64_t>(entry_.original_size))));
535*86ee64e7SAndroid Build Coastguard Worker 
536*86ee64e7SAndroid Build Coastguard Worker   StringWriterDelegate writer(output);
537*86ee64e7SAndroid Build Coastguard Worker   return ExtractCurrentEntry(&writer, max_read_bytes);
538*86ee64e7SAndroid Build Coastguard Worker }
539*86ee64e7SAndroid Build Coastguard Worker 
OpenInternal()540*86ee64e7SAndroid Build Coastguard Worker bool ZipReader::OpenInternal() {
541*86ee64e7SAndroid Build Coastguard Worker   DCHECK(zip_file_);
542*86ee64e7SAndroid Build Coastguard Worker 
543*86ee64e7SAndroid Build Coastguard Worker   unz_global_info zip_info = {};  // Zero-clear.
544*86ee64e7SAndroid Build Coastguard Worker   if (const UnzipError err{unzGetGlobalInfo(zip_file_, &zip_info)};
545*86ee64e7SAndroid Build Coastguard Worker       err != UNZ_OK) {
546*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot get ZIP info: " << err;
547*86ee64e7SAndroid Build Coastguard Worker     return false;
548*86ee64e7SAndroid Build Coastguard Worker   }
549*86ee64e7SAndroid Build Coastguard Worker 
550*86ee64e7SAndroid Build Coastguard Worker   num_entries_ = zip_info.number_entry;
551*86ee64e7SAndroid Build Coastguard Worker   reached_end_ = (num_entries_ <= 0);
552*86ee64e7SAndroid Build Coastguard Worker   ok_ = true;
553*86ee64e7SAndroid Build Coastguard Worker   return true;
554*86ee64e7SAndroid Build Coastguard Worker }
555*86ee64e7SAndroid Build Coastguard Worker 
Reset()556*86ee64e7SAndroid Build Coastguard Worker void ZipReader::Reset() {
557*86ee64e7SAndroid Build Coastguard Worker   zip_file_ = nullptr;
558*86ee64e7SAndroid Build Coastguard Worker   num_entries_ = 0;
559*86ee64e7SAndroid Build Coastguard Worker   next_index_ = 0;
560*86ee64e7SAndroid Build Coastguard Worker   reached_end_ = true;
561*86ee64e7SAndroid Build Coastguard Worker   ok_ = false;
562*86ee64e7SAndroid Build Coastguard Worker   delta_bytes_read_ = 0;
563*86ee64e7SAndroid Build Coastguard Worker   entry_ = {};
564*86ee64e7SAndroid Build Coastguard Worker }
565*86ee64e7SAndroid Build Coastguard Worker 
ExtractChunk(base::File output_file,SuccessCallback success_callback,FailureCallback failure_callback,ProgressCallback progress_callback,int64_t offset)566*86ee64e7SAndroid Build Coastguard Worker void ZipReader::ExtractChunk(base::File output_file,
567*86ee64e7SAndroid Build Coastguard Worker                              SuccessCallback success_callback,
568*86ee64e7SAndroid Build Coastguard Worker                              FailureCallback failure_callback,
569*86ee64e7SAndroid Build Coastguard Worker                              ProgressCallback progress_callback,
570*86ee64e7SAndroid Build Coastguard Worker                              int64_t offset) {
571*86ee64e7SAndroid Build Coastguard Worker   char buffer[internal::kZipBufSize];
572*86ee64e7SAndroid Build Coastguard Worker 
573*86ee64e7SAndroid Build Coastguard Worker   const int num_bytes_read =
574*86ee64e7SAndroid Build Coastguard Worker       unzReadCurrentFile(zip_file_, buffer, internal::kZipBufSize);
575*86ee64e7SAndroid Build Coastguard Worker 
576*86ee64e7SAndroid Build Coastguard Worker   if (num_bytes_read == 0) {
577*86ee64e7SAndroid Build Coastguard Worker     if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) {
578*86ee64e7SAndroid Build Coastguard Worker       LOG(ERROR) << "Cannot extract file " << Redact(entry_.path)
579*86ee64e7SAndroid Build Coastguard Worker                  << " from ZIP: " << err;
580*86ee64e7SAndroid Build Coastguard Worker       std::move(failure_callback).Run();
581*86ee64e7SAndroid Build Coastguard Worker       return;
582*86ee64e7SAndroid Build Coastguard Worker     }
583*86ee64e7SAndroid Build Coastguard Worker 
584*86ee64e7SAndroid Build Coastguard Worker     std::move(success_callback).Run();
585*86ee64e7SAndroid Build Coastguard Worker     return;
586*86ee64e7SAndroid Build Coastguard Worker   }
587*86ee64e7SAndroid Build Coastguard Worker 
588*86ee64e7SAndroid Build Coastguard Worker   if (num_bytes_read < 0) {
589*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
590*86ee64e7SAndroid Build Coastguard Worker                << " from ZIP: " << UnzipError(num_bytes_read);
591*86ee64e7SAndroid Build Coastguard Worker     std::move(failure_callback).Run();
592*86ee64e7SAndroid Build Coastguard Worker     return;
593*86ee64e7SAndroid Build Coastguard Worker   }
594*86ee64e7SAndroid Build Coastguard Worker 
595*86ee64e7SAndroid Build Coastguard Worker   if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) {
596*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot write " << num_bytes_read
597*86ee64e7SAndroid Build Coastguard Worker                << " bytes to file at offset " << offset;
598*86ee64e7SAndroid Build Coastguard Worker     std::move(failure_callback).Run();
599*86ee64e7SAndroid Build Coastguard Worker     return;
600*86ee64e7SAndroid Build Coastguard Worker   }
601*86ee64e7SAndroid Build Coastguard Worker 
602*86ee64e7SAndroid Build Coastguard Worker   offset += num_bytes_read;
603*86ee64e7SAndroid Build Coastguard Worker   progress_callback.Run(offset);
604*86ee64e7SAndroid Build Coastguard Worker 
605*86ee64e7SAndroid Build Coastguard Worker   base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
606*86ee64e7SAndroid Build Coastguard Worker       FROM_HERE,
607*86ee64e7SAndroid Build Coastguard Worker       base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
608*86ee64e7SAndroid Build Coastguard Worker                      std::move(output_file), std::move(success_callback),
609*86ee64e7SAndroid Build Coastguard Worker                      std::move(failure_callback), std::move(progress_callback),
610*86ee64e7SAndroid Build Coastguard Worker                      offset));
611*86ee64e7SAndroid Build Coastguard Worker }
612*86ee64e7SAndroid Build Coastguard Worker 
613*86ee64e7SAndroid Build Coastguard Worker // FileWriterDelegate ----------------------------------------------------------
614*86ee64e7SAndroid Build Coastguard Worker 
FileWriterDelegate(base::File * file)615*86ee64e7SAndroid Build Coastguard Worker FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {
616*86ee64e7SAndroid Build Coastguard Worker   DCHECK(file_);
617*86ee64e7SAndroid Build Coastguard Worker }
618*86ee64e7SAndroid Build Coastguard Worker 
FileWriterDelegate(base::File owned_file)619*86ee64e7SAndroid Build Coastguard Worker FileWriterDelegate::FileWriterDelegate(base::File owned_file)
620*86ee64e7SAndroid Build Coastguard Worker     : owned_file_(std::move(owned_file)) {
621*86ee64e7SAndroid Build Coastguard Worker   DCHECK_EQ(file_, &owned_file_);
622*86ee64e7SAndroid Build Coastguard Worker }
623*86ee64e7SAndroid Build Coastguard Worker 
~FileWriterDelegate()624*86ee64e7SAndroid Build Coastguard Worker FileWriterDelegate::~FileWriterDelegate() {}
625*86ee64e7SAndroid Build Coastguard Worker 
PrepareOutput()626*86ee64e7SAndroid Build Coastguard Worker bool FileWriterDelegate::PrepareOutput() {
627*86ee64e7SAndroid Build Coastguard Worker   DCHECK(file_);
628*86ee64e7SAndroid Build Coastguard Worker 
629*86ee64e7SAndroid Build Coastguard Worker   if (!file_->IsValid()) {
630*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "File is not valid";
631*86ee64e7SAndroid Build Coastguard Worker     return false;
632*86ee64e7SAndroid Build Coastguard Worker   }
633*86ee64e7SAndroid Build Coastguard Worker 
634*86ee64e7SAndroid Build Coastguard Worker   const int64_t length = file_->GetLength();
635*86ee64e7SAndroid Build Coastguard Worker   if (length < 0) {
636*86ee64e7SAndroid Build Coastguard Worker     PLOG(ERROR) << "Cannot get length of file handle "
637*86ee64e7SAndroid Build Coastguard Worker                 << file_->GetPlatformFile();
638*86ee64e7SAndroid Build Coastguard Worker     return false;
639*86ee64e7SAndroid Build Coastguard Worker   }
640*86ee64e7SAndroid Build Coastguard Worker 
641*86ee64e7SAndroid Build Coastguard Worker   // Just log a warning if the file is not empty.
642*86ee64e7SAndroid Build Coastguard Worker   // See crbug.com/1309879 and crbug.com/774762.
643*86ee64e7SAndroid Build Coastguard Worker   LOG_IF(WARNING, length > 0)
644*86ee64e7SAndroid Build Coastguard Worker       << "File handle " << file_->GetPlatformFile()
645*86ee64e7SAndroid Build Coastguard Worker       << " is not empty: Its length is " << length << " bytes";
646*86ee64e7SAndroid Build Coastguard Worker 
647*86ee64e7SAndroid Build Coastguard Worker   return true;
648*86ee64e7SAndroid Build Coastguard Worker }
649*86ee64e7SAndroid Build Coastguard Worker 
WriteBytes(const char * data,int num_bytes)650*86ee64e7SAndroid Build Coastguard Worker bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
651*86ee64e7SAndroid Build Coastguard Worker   int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
652*86ee64e7SAndroid Build Coastguard Worker   if (bytes_written > 0)
653*86ee64e7SAndroid Build Coastguard Worker     file_length_ += bytes_written;
654*86ee64e7SAndroid Build Coastguard Worker   return bytes_written == num_bytes;
655*86ee64e7SAndroid Build Coastguard Worker }
656*86ee64e7SAndroid Build Coastguard Worker 
SetTimeModified(const base::Time & time)657*86ee64e7SAndroid Build Coastguard Worker void FileWriterDelegate::SetTimeModified(const base::Time& time) {
658*86ee64e7SAndroid Build Coastguard Worker   file_->SetTimes(base::Time::Now(), time);
659*86ee64e7SAndroid Build Coastguard Worker }
660*86ee64e7SAndroid Build Coastguard Worker 
SetPosixFilePermissions(int mode)661*86ee64e7SAndroid Build Coastguard Worker void FileWriterDelegate::SetPosixFilePermissions(int mode) {
662*86ee64e7SAndroid Build Coastguard Worker #if defined(OS_POSIX)
663*86ee64e7SAndroid Build Coastguard Worker   zip::SetPosixFilePermissions(file_->GetPlatformFile(), mode);
664*86ee64e7SAndroid Build Coastguard Worker #endif
665*86ee64e7SAndroid Build Coastguard Worker }
666*86ee64e7SAndroid Build Coastguard Worker 
OnError()667*86ee64e7SAndroid Build Coastguard Worker void FileWriterDelegate::OnError() {
668*86ee64e7SAndroid Build Coastguard Worker   file_length_ = 0;
669*86ee64e7SAndroid Build Coastguard Worker   file_->SetLength(0);
670*86ee64e7SAndroid Build Coastguard Worker }
671*86ee64e7SAndroid Build Coastguard Worker 
672*86ee64e7SAndroid Build Coastguard Worker // FilePathWriterDelegate ------------------------------------------------------
673*86ee64e7SAndroid Build Coastguard Worker 
FilePathWriterDelegate(base::FilePath output_file_path)674*86ee64e7SAndroid Build Coastguard Worker FilePathWriterDelegate::FilePathWriterDelegate(base::FilePath output_file_path)
675*86ee64e7SAndroid Build Coastguard Worker     : FileWriterDelegate(base::File()),
676*86ee64e7SAndroid Build Coastguard Worker       output_file_path_(std::move(output_file_path)) {}
677*86ee64e7SAndroid Build Coastguard Worker 
~FilePathWriterDelegate()678*86ee64e7SAndroid Build Coastguard Worker FilePathWriterDelegate::~FilePathWriterDelegate() {}
679*86ee64e7SAndroid Build Coastguard Worker 
PrepareOutput()680*86ee64e7SAndroid Build Coastguard Worker bool FilePathWriterDelegate::PrepareOutput() {
681*86ee64e7SAndroid Build Coastguard Worker   // We can't rely on parent directory entries being specified in the
682*86ee64e7SAndroid Build Coastguard Worker   // zip, so we make sure they are created.
683*86ee64e7SAndroid Build Coastguard Worker   if (const base::FilePath dir = output_file_path_.DirName();
684*86ee64e7SAndroid Build Coastguard Worker       !base::CreateDirectory(dir)) {
685*86ee64e7SAndroid Build Coastguard Worker     PLOG(ERROR) << "Cannot create directory " << Redact(dir);
686*86ee64e7SAndroid Build Coastguard Worker     return false;
687*86ee64e7SAndroid Build Coastguard Worker   }
688*86ee64e7SAndroid Build Coastguard Worker 
689*86ee64e7SAndroid Build Coastguard Worker   owned_file_.Initialize(output_file_path_,
690*86ee64e7SAndroid Build Coastguard Worker                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
691*86ee64e7SAndroid Build Coastguard Worker   if (!owned_file_.IsValid()) {
692*86ee64e7SAndroid Build Coastguard Worker     PLOG(ERROR) << "Cannot create file " << Redact(output_file_path_) << ": "
693*86ee64e7SAndroid Build Coastguard Worker                 << base::File::ErrorToString(owned_file_.error_details());
694*86ee64e7SAndroid Build Coastguard Worker     return false;
695*86ee64e7SAndroid Build Coastguard Worker   }
696*86ee64e7SAndroid Build Coastguard Worker 
697*86ee64e7SAndroid Build Coastguard Worker   const int64_t length = owned_file_.GetLength();
698*86ee64e7SAndroid Build Coastguard Worker   if (length < 0) {
699*86ee64e7SAndroid Build Coastguard Worker     PLOG(ERROR) << "Cannot get length of file " << Redact(output_file_path_);
700*86ee64e7SAndroid Build Coastguard Worker     return false;
701*86ee64e7SAndroid Build Coastguard Worker   }
702*86ee64e7SAndroid Build Coastguard Worker 
703*86ee64e7SAndroid Build Coastguard Worker   if (length > 0) {
704*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "File " << Redact(output_file_path_)
705*86ee64e7SAndroid Build Coastguard Worker                << " is not empty: Its length is " << length << " bytes";
706*86ee64e7SAndroid Build Coastguard Worker     return false;
707*86ee64e7SAndroid Build Coastguard Worker   }
708*86ee64e7SAndroid Build Coastguard Worker 
709*86ee64e7SAndroid Build Coastguard Worker   return true;
710*86ee64e7SAndroid Build Coastguard Worker }
711*86ee64e7SAndroid Build Coastguard Worker 
OnError()712*86ee64e7SAndroid Build Coastguard Worker void FilePathWriterDelegate::OnError() {
713*86ee64e7SAndroid Build Coastguard Worker   FileWriterDelegate::OnError();
714*86ee64e7SAndroid Build Coastguard Worker   owned_file_.Close();
715*86ee64e7SAndroid Build Coastguard Worker 
716*86ee64e7SAndroid Build Coastguard Worker   if (!base::DeleteFile(output_file_path_)) {
717*86ee64e7SAndroid Build Coastguard Worker     LOG(ERROR) << "Cannot delete partially extracted file "
718*86ee64e7SAndroid Build Coastguard Worker                << Redact(output_file_path_);
719*86ee64e7SAndroid Build Coastguard Worker   }
720*86ee64e7SAndroid Build Coastguard Worker }
721*86ee64e7SAndroid Build Coastguard Worker 
722*86ee64e7SAndroid Build Coastguard Worker }  // namespace zip
723