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