• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/zlib/google/zip_reader.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/check.h"
12 #include "base/files/file.h"
13 #include "base/files/file_util.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/threading/sequenced_task_runner_handle.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 
241   // Construct the last modified time. The timezone info is not present in ZIP
242   // archives, so we construct the time as UTC.
243   base::Time::Exploded exploded_time = {};
244   exploded_time.year = info.tmu_date.tm_year;
245   exploded_time.month = info.tmu_date.tm_mon + 1;  // 0-based vs 1-based
246   exploded_time.day_of_month = info.tmu_date.tm_mday;
247   exploded_time.hour = info.tmu_date.tm_hour;
248   exploded_time.minute = info.tmu_date.tm_min;
249   exploded_time.second = info.tmu_date.tm_sec;
250   exploded_time.millisecond = 0;
251 
252   if (!base::Time::FromUTCExploded(exploded_time, &entry_.last_modified))
253     entry_.last_modified = base::Time::UnixEpoch();
254 
255 #if defined(OS_POSIX)
256   entry_.posix_mode = (info.external_fa >> 16L) & (S_IRWXU | S_IRWXG | S_IRWXO);
257 #else
258   entry_.posix_mode = 0;
259 #endif
260 
261   return true;
262 }
263 
Normalize(base::StringPiece16 in)264 void ZipReader::Normalize(base::StringPiece16 in) {
265   entry_.is_unsafe = true;
266 
267   // Directory entries in ZIP have a path ending with "/".
268   entry_.is_directory = base::EndsWith(in, u"/");
269 
270   std::u16string normalized_path;
271   if (base::StartsWith(in, u"/")) {
272     normalized_path = u"ROOT";
273     entry_.is_unsafe = false;
274   }
275 
276   for (;;) {
277     // Consume initial path separators.
278     const base::StringPiece16::size_type i = in.find_first_not_of(u'/');
279     if (i == base::StringPiece16::npos)
280       break;
281 
282     in.remove_prefix(i);
283     DCHECK(!in.empty());
284 
285     // Isolate next path component.
286     const base::StringPiece16 part = in.substr(0, in.find_first_of(u'/'));
287     DCHECK(!part.empty());
288 
289     in.remove_prefix(part.size());
290 
291     if (!normalized_path.empty())
292       normalized_path += u'/';
293 
294     if (part == u".") {
295       normalized_path += u"DOT";
296       entry_.is_unsafe = true;
297       continue;
298     }
299 
300     if (part == u"..") {
301       normalized_path += u"UP";
302       entry_.is_unsafe = true;
303       continue;
304     }
305 
306     // Windows has more restrictions than other systems when it comes to valid
307     // file paths. Replace Windows-invalid characters on all systems for
308     // consistency. In particular, this prevents a path component containing
309     // colon and backslash from being misinterpreted as an absolute path on
310     // Windows.
311     for (const char16_t c : part) {
312       normalized_path += IsValidFileNameCharacterOnWindows(c) ? c : 0xFFFD;
313     }
314 
315     entry_.is_unsafe = false;
316   }
317 
318   // If the entry is a directory, add the final path separator to the entry
319   // path.
320   if (entry_.is_directory && !normalized_path.empty()) {
321     normalized_path += u'/';
322     entry_.is_unsafe = false;
323   }
324 
325   entry_.path = base::FilePath::FromUTF16Unsafe(normalized_path);
326 
327   // By construction, we should always get a relative path.
328   DCHECK(!entry_.path.IsAbsolute()) << entry_.path;
329 }
330 
ExtractCurrentEntry(WriterDelegate * delegate,uint64_t num_bytes_to_extract) const331 bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
332                                     uint64_t num_bytes_to_extract) const {
333   DCHECK(zip_file_);
334   DCHECK_LT(0, next_index_);
335   DCHECK(ok_);
336   DCHECK(!reached_end_);
337 
338   // Use password only for encrypted files. For non-encrypted files, no password
339   // is needed, and must be nullptr.
340   const char* const password =
341       entry_.is_encrypted ? password_.c_str() : nullptr;
342   if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)};
343       err != UNZ_OK) {
344     LOG(ERROR) << "Cannot open file " << Redact(entry_.path)
345                << " from ZIP: " << err;
346     return false;
347   }
348 
349   DCHECK(delegate);
350   if (!delegate->PrepareOutput())
351     return false;
352 
353   uint64_t remaining_capacity = num_bytes_to_extract;
354   bool entire_file_extracted = false;
355 
356   while (remaining_capacity > 0) {
357     char buf[internal::kZipBufSize];
358     const int num_bytes_read =
359         unzReadCurrentFile(zip_file_, buf, internal::kZipBufSize);
360 
361     if (num_bytes_read == 0) {
362       entire_file_extracted = true;
363       break;
364     }
365 
366     if (num_bytes_read < 0) {
367       LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
368                  << " from ZIP: " << UnzipError(num_bytes_read);
369       break;
370     }
371 
372     DCHECK_LT(0, num_bytes_read);
373     CHECK_LE(num_bytes_read, internal::kZipBufSize);
374 
375     uint64_t num_bytes_to_write = std::min<uint64_t>(
376         remaining_capacity, base::checked_cast<uint64_t>(num_bytes_read));
377     if (!delegate->WriteBytes(buf, num_bytes_to_write))
378       break;
379 
380     if (remaining_capacity == base::checked_cast<uint64_t>(num_bytes_read)) {
381       // Ensures function returns true if the entire file has been read.
382       const int n = unzReadCurrentFile(zip_file_, buf, 1);
383       entire_file_extracted = (n == 0);
384       LOG_IF(ERROR, n < 0) << "Cannot read file " << Redact(entry_.path)
385                            << " from ZIP: " << UnzipError(n);
386     }
387 
388     CHECK_GE(remaining_capacity, num_bytes_to_write);
389     remaining_capacity -= num_bytes_to_write;
390   }
391 
392   if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) {
393     LOG(ERROR) << "Cannot extract file " << Redact(entry_.path)
394                << " from ZIP: " << err;
395     entire_file_extracted = false;
396   }
397 
398   if (entire_file_extracted) {
399     delegate->SetPosixFilePermissions(entry_.posix_mode);
400     if (entry_.last_modified != base::Time::UnixEpoch()) {
401       delegate->SetTimeModified(entry_.last_modified);
402     }
403   } else {
404     delegate->OnError();
405   }
406 
407   return entire_file_extracted;
408 }
409 
ExtractCurrentEntryToFilePathAsync(const base::FilePath & output_file_path,SuccessCallback success_callback,FailureCallback failure_callback,ProgressCallback progress_callback)410 void ZipReader::ExtractCurrentEntryToFilePathAsync(
411     const base::FilePath& output_file_path,
412     SuccessCallback success_callback,
413     FailureCallback failure_callback,
414     ProgressCallback progress_callback) {
415   DCHECK(zip_file_);
416   DCHECK_LT(0, next_index_);
417   DCHECK(ok_);
418   DCHECK(!reached_end_);
419 
420   // If this is a directory, just create it and return.
421   if (entry_.is_directory) {
422     if (base::CreateDirectory(output_file_path)) {
423       base::SequencedTaskRunnerHandle::Get()->PostTask(
424           FROM_HERE, std::move(success_callback));
425     } else {
426       LOG(ERROR) << "Cannot create directory " << Redact(output_file_path);
427       base::SequencedTaskRunnerHandle::Get()->PostTask(
428           FROM_HERE, std::move(failure_callback));
429     }
430     return;
431   }
432 
433   // Use password only for encrypted files. For non-encrypted files, no password
434   // is needed, and must be nullptr.
435   const char* const password =
436       entry_.is_encrypted ? password_.c_str() : nullptr;
437   if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)};
438       err != UNZ_OK) {
439     LOG(ERROR) << "Cannot open file " << Redact(entry_.path)
440                << " from ZIP: " << err;
441     base::SequencedTaskRunnerHandle::Get()->PostTask(
442         FROM_HERE, std::move(failure_callback));
443     return;
444   }
445 
446   base::FilePath output_dir_path = output_file_path.DirName();
447   if (!base::CreateDirectory(output_dir_path)) {
448     LOG(ERROR) << "Cannot create directory " << Redact(output_dir_path);
449     base::SequencedTaskRunnerHandle::Get()->PostTask(
450         FROM_HERE, std::move(failure_callback));
451     return;
452   }
453 
454   const int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
455   base::File output_file(output_file_path, flags);
456 
457   if (!output_file.IsValid()) {
458     LOG(ERROR) << "Cannot create file " << Redact(output_file_path);
459     base::SequencedTaskRunnerHandle::Get()->PostTask(
460         FROM_HERE, std::move(failure_callback));
461     return;
462   }
463 
464   base::SequencedTaskRunnerHandle::Get()->PostTask(
465       FROM_HERE,
466       base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
467                      std::move(output_file), std::move(success_callback),
468                      std::move(failure_callback), std::move(progress_callback),
469                      0 /* initial offset */));
470 }
471 
ExtractCurrentEntryToString(uint64_t max_read_bytes,std::string * output) const472 bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes,
473                                             std::string* output) const {
474   DCHECK(output);
475   DCHECK(zip_file_);
476   DCHECK_LT(0, next_index_);
477   DCHECK(ok_);
478   DCHECK(!reached_end_);
479 
480   output->clear();
481 
482   if (max_read_bytes == 0)
483     return true;
484 
485   if (entry_.is_directory)
486     return true;
487 
488   // The original_size is the best hint for the real size, so it saves doing
489   // reallocations for the common case when the uncompressed size is correct.
490   // However, we need to assume that the uncompressed size could be incorrect
491   // therefore this function needs to read as much data as possible.
492   output->reserve(base::checked_cast<size_t>(std::min<uint64_t>(
493       max_read_bytes, base::checked_cast<uint64_t>(entry_.original_size))));
494 
495   StringWriterDelegate writer(output);
496   return ExtractCurrentEntry(&writer, max_read_bytes);
497 }
498 
OpenInternal()499 bool ZipReader::OpenInternal() {
500   DCHECK(zip_file_);
501 
502   unz_global_info zip_info = {};  // Zero-clear.
503   if (const UnzipError err{unzGetGlobalInfo(zip_file_, &zip_info)};
504       err != UNZ_OK) {
505     LOG(ERROR) << "Cannot get ZIP info: " << err;
506     return false;
507   }
508 
509   num_entries_ = zip_info.number_entry;
510   reached_end_ = (num_entries_ <= 0);
511   ok_ = true;
512   return true;
513 }
514 
Reset()515 void ZipReader::Reset() {
516   zip_file_ = nullptr;
517   num_entries_ = 0;
518   next_index_ = 0;
519   reached_end_ = true;
520   ok_ = false;
521   entry_ = {};
522 }
523 
ExtractChunk(base::File output_file,SuccessCallback success_callback,FailureCallback failure_callback,ProgressCallback progress_callback,int64_t offset)524 void ZipReader::ExtractChunk(base::File output_file,
525                              SuccessCallback success_callback,
526                              FailureCallback failure_callback,
527                              ProgressCallback progress_callback,
528                              int64_t offset) {
529   char buffer[internal::kZipBufSize];
530 
531   const int num_bytes_read =
532       unzReadCurrentFile(zip_file_, buffer, internal::kZipBufSize);
533 
534   if (num_bytes_read == 0) {
535     if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) {
536       LOG(ERROR) << "Cannot extract file " << Redact(entry_.path)
537                  << " from ZIP: " << err;
538       std::move(failure_callback).Run();
539       return;
540     }
541 
542     std::move(success_callback).Run();
543     return;
544   }
545 
546   if (num_bytes_read < 0) {
547     LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
548                << " from ZIP: " << UnzipError(num_bytes_read);
549     std::move(failure_callback).Run();
550     return;
551   }
552 
553   if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) {
554     LOG(ERROR) << "Cannot write " << num_bytes_read
555                << " bytes to file at offset " << offset;
556     std::move(failure_callback).Run();
557     return;
558   }
559 
560   offset += num_bytes_read;
561   progress_callback.Run(offset);
562 
563   base::SequencedTaskRunnerHandle::Get()->PostTask(
564       FROM_HERE,
565       base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
566                      std::move(output_file), std::move(success_callback),
567                      std::move(failure_callback), std::move(progress_callback),
568                      offset));
569 }
570 
571 // FileWriterDelegate ----------------------------------------------------------
572 
FileWriterDelegate(base::File * file)573 FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {
574   DCHECK(file_);
575 }
576 
FileWriterDelegate(base::File owned_file)577 FileWriterDelegate::FileWriterDelegate(base::File owned_file)
578     : owned_file_(std::move(owned_file)) {
579   DCHECK_EQ(file_, &owned_file_);
580 }
581 
~FileWriterDelegate()582 FileWriterDelegate::~FileWriterDelegate() {}
583 
PrepareOutput()584 bool FileWriterDelegate::PrepareOutput() {
585   DCHECK(file_);
586 
587   if (!file_->IsValid()) {
588     LOG(ERROR) << "File is not valid";
589     return false;
590   }
591 
592   const int64_t length = file_->GetLength();
593   if (length < 0) {
594     PLOG(ERROR) << "Cannot get length of file handle "
595                 << file_->GetPlatformFile();
596     return false;
597   }
598 
599   // Just log a warning if the file is not empty.
600   // See crbug.com/1309879 and crbug.com/774762.
601   LOG_IF(WARNING, length > 0)
602       << "File handle " << file_->GetPlatformFile()
603       << " is not empty: Its length is " << length << " bytes";
604 
605   return true;
606 }
607 
WriteBytes(const char * data,int num_bytes)608 bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
609   int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
610   if (bytes_written > 0)
611     file_length_ += bytes_written;
612   return bytes_written == num_bytes;
613 }
614 
SetTimeModified(const base::Time & time)615 void FileWriterDelegate::SetTimeModified(const base::Time& time) {
616   file_->SetTimes(base::Time::Now(), time);
617 }
618 
SetPosixFilePermissions(int mode)619 void FileWriterDelegate::SetPosixFilePermissions(int mode) {
620 #if defined(OS_POSIX)
621   zip::SetPosixFilePermissions(file_->GetPlatformFile(), mode);
622 #endif
623 }
624 
OnError()625 void FileWriterDelegate::OnError() {
626   file_length_ = 0;
627   file_->SetLength(0);
628 }
629 
630 // FilePathWriterDelegate ------------------------------------------------------
631 
FilePathWriterDelegate(base::FilePath output_file_path)632 FilePathWriterDelegate::FilePathWriterDelegate(base::FilePath output_file_path)
633     : FileWriterDelegate(base::File()),
634       output_file_path_(std::move(output_file_path)) {}
635 
~FilePathWriterDelegate()636 FilePathWriterDelegate::~FilePathWriterDelegate() {}
637 
PrepareOutput()638 bool FilePathWriterDelegate::PrepareOutput() {
639   // We can't rely on parent directory entries being specified in the
640   // zip, so we make sure they are created.
641   if (const base::FilePath dir = output_file_path_.DirName();
642       !base::CreateDirectory(dir)) {
643     PLOG(ERROR) << "Cannot create directory " << Redact(dir);
644     return false;
645   }
646 
647   owned_file_.Initialize(output_file_path_,
648                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
649   if (!owned_file_.IsValid()) {
650     PLOG(ERROR) << "Cannot create file " << Redact(output_file_path_) << ": "
651                 << base::File::ErrorToString(owned_file_.error_details());
652     return false;
653   }
654 
655   const int64_t length = owned_file_.GetLength();
656   if (length < 0) {
657     PLOG(ERROR) << "Cannot get length of file " << Redact(output_file_path_);
658     return false;
659   }
660 
661   if (length > 0) {
662     LOG(ERROR) << "File " << Redact(output_file_path_)
663                << " is not empty: Its length is " << length << " bytes";
664     return false;
665   }
666 
667   return true;
668 }
669 
OnError()670 void FilePathWriterDelegate::OnError() {
671   FileWriterDelegate::OnError();
672   owned_file_.Close();
673 
674   if (!base::DeleteFile(output_file_path_)) {
675     LOG(ERROR) << "Cannot delete partially extracted file "
676                << Redact(output_file_path_);
677   }
678 }
679 
680 }  // namespace zip
681