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