blob: 63d23195a0d3ff0f91a552f586a4dc2b5ccdae07 [file] [log] [blame]
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +00001// 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
dcheng97bdded2015-12-27 18:24:50 -08007#include <utility>
8
hashimoto@chromium.org516d1852014-03-24 09:32:13 +00009#include "base/bind.h"
10#include "base/files/file.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000011#include "base/logging.h"
aviaa969482015-12-27 13:36:49 -080012#include "base/macros.h"
fdoray68752332016-06-29 10:18:59 -070013#include "base/single_thread_task_runner.h"
avi@chromium.orga9a93952013-06-11 08:04:16 +000014#include "base/strings/string_util.h"
avi@chromium.org5cb24772013-06-07 22:40:45 +000015#include "base/strings/utf_string_conversions.h"
gab45763042016-05-11 20:19:19 -070016#include "base/threading/thread_task_runner_handle.h"
aviaa969482015-12-27 13:36:49 -080017#include "build/build_config.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000018#include "third_party/zlib/google/zip_internal.h"
19
20#if defined(USE_SYSTEM_MINIZIP)
21#include <minizip/unzip.h>
22#else
23#include "third_party/zlib/contrib/minizip/unzip.h"
24#if defined(OS_WIN)
25#include "third_party/zlib/contrib/minizip/iowin32.h"
26#endif // defined(OS_WIN)
27#endif // defined(USE_SYSTEM_MINIZIP)
28
29namespace zip {
30
grtebc765a2015-03-18 14:22:34 -070031namespace {
32
grtebc765a2015-03-18 14:22:34 -070033// StringWriterDelegate --------------------------------------------------------
34
35// A writer delegate that writes no more than |max_read_bytes| to a given
36// std::string.
37class StringWriterDelegate : public WriterDelegate {
38 public:
39 StringWriterDelegate(size_t max_read_bytes, std::string* output);
40 ~StringWriterDelegate() override;
41
42 // WriterDelegate methods:
43
44 // Returns true.
45 bool PrepareOutput() override;
46
47 // Appends |num_bytes| bytes from |data| to the output string. Returns false
48 // if |num_bytes| will cause the string to exceed |max_read_bytes|.
49 bool WriteBytes(const char* data, int num_bytes) override;
50
Joshua Pawlickie31b5032018-02-06 20:24:51 +000051 void SetTimeModified(const base::Time& time) override;
52
grtebc765a2015-03-18 14:22:34 -070053 private:
54 size_t max_read_bytes_;
55 std::string* output_;
56
57 DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate);
58};
59
60StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes,
61 std::string* output)
62 : max_read_bytes_(max_read_bytes),
63 output_(output) {
64}
65
66StringWriterDelegate::~StringWriterDelegate() {
67}
68
69bool StringWriterDelegate::PrepareOutput() {
70 return true;
71}
72
73bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) {
74 if (output_->size() + num_bytes > max_read_bytes_)
75 return false;
76 output_->append(data, num_bytes);
77 return true;
78}
79
Joshua Pawlickie31b5032018-02-06 20:24:51 +000080void StringWriterDelegate::SetTimeModified(const base::Time& time) {
81 // Do nothing.
82}
83
grtebc765a2015-03-18 14:22:34 -070084} // namespace
85
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000086// TODO(satorux): The implementation assumes that file names in zip files
87// are encoded in UTF-8. This is true for zip files created by Zip()
88// function in zip.h, but not true for user-supplied random zip files.
89ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip,
90 const unz_file_info& raw_file_info)
91 : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)),
Daniel Ruberyad6f5862018-08-22 16:53:41 +000092 is_directory_(false),
93 is_unsafe_(false),
94 is_encrypted_(false) {
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000095 original_size_ = raw_file_info.uncompressed_size;
96
97 // Directory entries in zip files end with "/".
brettw6b0ea322015-07-16 10:49:29 -070098 is_directory_ = base::EndsWith(file_name_in_zip, "/",
99 base::CompareCase::INSENSITIVE_ASCII);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000100
joaoe@opera.comce54c1b2013-12-17 14:40:31 +0000101 // Check the file name here for directory traversal issues.
102 is_unsafe_ = file_path_.ReferencesParent();
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000103
104 // We also consider that the file name is unsafe, if it's invalid UTF-8.
brettw@chromium.org7d41e882013-12-03 00:39:26 +0000105 base::string16 file_name_utf16;
avi@chromium.orgc88f4f32013-12-26 07:07:56 +0000106 if (!base::UTF8ToUTF16(file_name_in_zip.data(), file_name_in_zip.size(),
107 &file_name_utf16)) {
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000108 is_unsafe_ = true;
109 }
110
111 // We also consider that the file name is unsafe, if it's absolute.
112 // On Windows, IsAbsolute() returns false for paths starting with "/".
brettw47392402015-06-11 18:57:57 -0700113 if (file_path_.IsAbsolute() ||
brettwe88e3c32015-07-16 16:57:33 -0700114 base::StartsWith(file_name_in_zip, "/",
115 base::CompareCase::INSENSITIVE_ASCII))
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000116 is_unsafe_ = true;
117
Daniel Ruberyad6f5862018-08-22 16:53:41 +0000118 // Whether the file is encrypted is bit 0 of the flag.
119 is_encrypted_ = raw_file_info.flag & 1;
120
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000121 // Construct the last modified time. The timezone info is not present in
122 // zip files, so we construct the time as local time.
123 base::Time::Exploded exploded_time = {}; // Zero-clear.
124 exploded_time.year = raw_file_info.tmu_date.tm_year;
125 // The month in zip file is 0-based, whereas ours is 1-based.
126 exploded_time.month = raw_file_info.tmu_date.tm_mon + 1;
127 exploded_time.day_of_month = raw_file_info.tmu_date.tm_mday;
128 exploded_time.hour = raw_file_info.tmu_date.tm_hour;
129 exploded_time.minute = raw_file_info.tmu_date.tm_min;
130 exploded_time.second = raw_file_info.tmu_date.tm_sec;
131 exploded_time.millisecond = 0;
maksim.sisov5a95f9a2016-06-30 02:59:36 -0700132
133 if (!base::Time::FromLocalExploded(exploded_time, &last_modified_))
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000134 last_modified_ = base::Time::UnixEpoch();
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000135}
136
haven@chromium.org84ed2652014-01-17 00:36:28 +0000137ZipReader::ZipReader()
138 : weak_ptr_factory_(this) {
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000139 Reset();
140}
141
142ZipReader::~ZipReader() {
143 Close();
144}
145
146bool ZipReader::Open(const base::FilePath& zip_file_path) {
147 DCHECK(!zip_file_);
148
149 // Use of "Unsafe" function does not look good, but there is no way to do
150 // this safely on Linux. See file_util.h for details.
151 zip_file_ = internal::OpenForUnzipping(zip_file_path.AsUTF8Unsafe());
152 if (!zip_file_) {
153 return false;
154 }
155
156 return OpenInternal();
157}
158
159bool ZipReader::OpenFromPlatformFile(base::PlatformFile zip_fd) {
160 DCHECK(!zip_file_);
161
162#if defined(OS_POSIX)
163 zip_file_ = internal::OpenFdForUnzipping(zip_fd);
164#elif defined(OS_WIN)
165 zip_file_ = internal::OpenHandleForUnzipping(zip_fd);
166#endif
167 if (!zip_file_) {
168 return false;
169 }
170
171 return OpenInternal();
172}
173
174bool ZipReader::OpenFromString(const std::string& data) {
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000175 zip_file_ = internal::PrepareMemoryForUnzipping(data);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000176 if (!zip_file_)
177 return false;
178 return OpenInternal();
179}
180
181void ZipReader::Close() {
182 if (zip_file_) {
183 unzClose(zip_file_);
184 }
185 Reset();
186}
187
188bool ZipReader::HasMore() {
189 return !reached_end_;
190}
191
192bool ZipReader::AdvanceToNextEntry() {
193 DCHECK(zip_file_);
194
195 // Should not go further if we already reached the end.
196 if (reached_end_)
197 return false;
198
199 unz_file_pos position = {};
200 if (unzGetFilePos(zip_file_, &position) != UNZ_OK)
201 return false;
202 const int current_entry_index = position.num_of_file;
203 // If we are currently at the last entry, then the next position is the
204 // end of the zip file, so mark that we reached the end.
205 if (current_entry_index + 1 == num_entries_) {
206 reached_end_ = true;
207 } else {
208 DCHECK_LT(current_entry_index + 1, num_entries_);
209 if (unzGoToNextFile(zip_file_) != UNZ_OK) {
210 return false;
211 }
212 }
213 current_entry_info_.reset();
214 return true;
215}
216
217bool ZipReader::OpenCurrentEntryInZip() {
218 DCHECK(zip_file_);
219
220 unz_file_info raw_file_info = {};
221 char raw_file_name_in_zip[internal::kZipMaxPath] = {};
222 const int result = unzGetCurrentFileInfo(zip_file_,
223 &raw_file_info,
224 raw_file_name_in_zip,
225 sizeof(raw_file_name_in_zip) - 1,
226 NULL, // extraField.
227 0, // extraFieldBufferSize.
228 NULL, // szComment.
229 0); // commentBufferSize.
230 if (result != UNZ_OK)
231 return false;
232 if (raw_file_name_in_zip[0] == '\0')
233 return false;
234 current_entry_info_.reset(
235 new EntryInfo(raw_file_name_in_zip, raw_file_info));
236 return true;
237}
238
mortonmb4298b02017-08-04 07:57:41 -0700239bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
240 uint64_t num_bytes_to_extract) const {
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000241 DCHECK(zip_file_);
242
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000243 const int open_result = unzOpenCurrentFile(zip_file_);
244 if (open_result != UNZ_OK)
245 return false;
246
grtebc765a2015-03-18 14:22:34 -0700247 if (!delegate->PrepareOutput())
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000248 return false;
dchenga90aed52016-04-22 16:49:07 -0700249 std::unique_ptr<char[]> buf(new char[internal::kZipBufSize]);
mortonmb4298b02017-08-04 07:57:41 -0700250
251 uint64_t remaining_capacity = num_bytes_to_extract;
252 bool entire_file_extracted = false;
253
254 while (remaining_capacity > 0) {
255 const int num_bytes_read =
256 unzReadCurrentFile(zip_file_, buf.get(), internal::kZipBufSize);
257
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000258 if (num_bytes_read == 0) {
mortonmb4298b02017-08-04 07:57:41 -0700259 entire_file_extracted = true;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000260 break;
261 } else if (num_bytes_read < 0) {
262 // If num_bytes_read < 0, then it's a specific UNZ_* error code.
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000263 break;
264 } else if (num_bytes_read > 0) {
mortonmb4298b02017-08-04 07:57:41 -0700265 uint64_t num_bytes_to_write = std::min<uint64_t>(
266 remaining_capacity, base::checked_cast<uint64_t>(num_bytes_read));
267 if (!delegate->WriteBytes(buf.get(), num_bytes_to_write))
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000268 break;
mortonmb4298b02017-08-04 07:57:41 -0700269 if (remaining_capacity == base::checked_cast<uint64_t>(num_bytes_read)) {
270 // Ensures function returns true if the entire file has been read.
271 entire_file_extracted =
272 (unzReadCurrentFile(zip_file_, buf.get(), 1) == 0);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000273 }
mortonmb4298b02017-08-04 07:57:41 -0700274 CHECK_GE(remaining_capacity, num_bytes_to_write);
275 remaining_capacity -= num_bytes_to_write;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000276 }
277 }
278
279 unzCloseCurrentFile(zip_file_);
joaoe@opera.comce54c1b2013-12-17 14:40:31 +0000280
Joshua Pawlickie31b5032018-02-06 20:24:51 +0000281 if (entire_file_extracted &&
grtebc765a2015-03-18 14:22:34 -0700282 current_entry_info()->last_modified() != base::Time::UnixEpoch()) {
Joshua Pawlickie31b5032018-02-06 20:24:51 +0000283 delegate->SetTimeModified(current_entry_info()->last_modified());
grtebc765a2015-03-18 14:22:34 -0700284 }
joaoe@opera.comce54c1b2013-12-17 14:40:31 +0000285
Joshua Pawlickie31b5032018-02-06 20:24:51 +0000286 return entire_file_extracted;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000287}
288
haven@chromium.org84ed2652014-01-17 00:36:28 +0000289void ZipReader::ExtractCurrentEntryToFilePathAsync(
290 const base::FilePath& output_file_path,
291 const SuccessCallback& success_callback,
292 const FailureCallback& failure_callback,
293 const ProgressCallback& progress_callback) {
294 DCHECK(zip_file_);
295 DCHECK(current_entry_info_.get());
296
297 // If this is a directory, just create it and return.
298 if (current_entry_info()->is_directory()) {
299 if (base::CreateDirectory(output_file_path)) {
skyostild8e6bfa2015-06-09 12:10:55 -0700300 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, success_callback);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000301 } else {
302 DVLOG(1) << "Unzip failed: unable to create directory.";
skyostild8e6bfa2015-06-09 12:10:55 -0700303 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000304 }
305 return;
306 }
307
308 if (unzOpenCurrentFile(zip_file_) != UNZ_OK) {
309 DVLOG(1) << "Unzip failed: unable to open current zip entry.";
skyostild8e6bfa2015-06-09 12:10:55 -0700310 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000311 return;
312 }
313
314 base::FilePath output_dir_path = output_file_path.DirName();
315 if (!base::CreateDirectory(output_dir_path)) {
316 DVLOG(1) << "Unzip failed: unable to create containing directory.";
skyostild8e6bfa2015-06-09 12:10:55 -0700317 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000318 return;
319 }
320
rvargas@chromium.org0d737652014-02-27 05:58:13 +0000321 const int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
322 base::File output_file(output_file_path, flags);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000323
rvargas@chromium.org0d737652014-02-27 05:58:13 +0000324 if (!output_file.IsValid()) {
haven@chromium.org84ed2652014-01-17 00:36:28 +0000325 DVLOG(1) << "Unzip failed: unable to create platform file at "
326 << output_file_path.value();
skyostild8e6bfa2015-06-09 12:10:55 -0700327 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000328 return;
329 }
330
fdoray4e2773b2016-09-30 13:42:16 -0700331 base::ThreadTaskRunnerHandle::Get()->PostTask(
haven@chromium.org84ed2652014-01-17 00:36:28 +0000332 FROM_HERE,
dcheng97bdded2015-12-27 18:24:50 -0800333 base::Bind(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
334 Passed(std::move(output_file)), success_callback,
335 failure_callback, progress_callback, 0 /* initial offset */));
haven@chromium.org84ed2652014-01-17 00:36:28 +0000336}
337
mortonmb4298b02017-08-04 07:57:41 -0700338bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes,
grtebc765a2015-03-18 14:22:34 -0700339 std::string* output) const {
joaoe@opera.com00024292014-06-20 18:12:13 +0000340 DCHECK(output);
341 DCHECK(zip_file_);
mortonmb4298b02017-08-04 07:57:41 -0700342
343 if (max_read_bytes == 0) {
344 output->clear();
345 return true;
346 }
joaoe@opera.com00024292014-06-20 18:12:13 +0000347
348 if (current_entry_info()->is_directory()) {
349 output->clear();
350 return true;
351 }
352
joaoe@opera.com00024292014-06-20 18:12:13 +0000353 // The original_size() is the best hint for the real size, so it saves
354 // doing reallocations for the common case when the uncompressed size is
355 // correct. However, we need to assume that the uncompressed size could be
356 // incorrect therefore this function needs to read as much data as possible.
357 std::string contents;
aviaa969482015-12-27 13:36:49 -0800358 contents.reserve(
mortonmb4298b02017-08-04 07:57:41 -0700359 static_cast<size_t>(std::min(base::checked_cast<int64_t>(max_read_bytes),
aviaa969482015-12-27 13:36:49 -0800360 current_entry_info()->original_size())));
joaoe@opera.com00024292014-06-20 18:12:13 +0000361
grtebc765a2015-03-18 14:22:34 -0700362 StringWriterDelegate writer(max_read_bytes, &contents);
mortonmb4298b02017-08-04 07:57:41 -0700363 if (!ExtractCurrentEntry(&writer, max_read_bytes)) {
364 if (contents.length() < max_read_bytes) {
365 // There was an error in extracting entry. If ExtractCurrentEntry()
366 // returns false, the entire file was not read - in which case
367 // contents.length() should equal |max_read_bytes| unless an error
368 // occurred which caused extraction to be aborted.
369 output->clear();
370 } else {
371 // |num_bytes| is less than the length of current entry.
372 output->swap(contents);
373 }
grtebc765a2015-03-18 14:22:34 -0700374 return false;
mortonmb4298b02017-08-04 07:57:41 -0700375 }
grtebc765a2015-03-18 14:22:34 -0700376 output->swap(contents);
377 return true;
joaoe@opera.com00024292014-06-20 18:12:13 +0000378}
379
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000380bool ZipReader::OpenInternal() {
381 DCHECK(zip_file_);
382
383 unz_global_info zip_info = {}; // Zero-clear.
384 if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) {
385 return false;
386 }
387 num_entries_ = zip_info.number_entry;
388 if (num_entries_ < 0)
389 return false;
390
391 // We are already at the end if the zip file is empty.
392 reached_end_ = (num_entries_ == 0);
393 return true;
394}
395
396void ZipReader::Reset() {
397 zip_file_ = NULL;
398 num_entries_ = 0;
399 reached_end_ = false;
400 current_entry_info_.reset();
401}
402
rvargas@chromium.org0d737652014-02-27 05:58:13 +0000403void ZipReader::ExtractChunk(base::File output_file,
haven@chromium.org84ed2652014-01-17 00:36:28 +0000404 const SuccessCallback& success_callback,
405 const FailureCallback& failure_callback,
406 const ProgressCallback& progress_callback,
aviaa969482015-12-27 13:36:49 -0800407 const int64_t offset) {
haven@chromium.org84ed2652014-01-17 00:36:28 +0000408 char buffer[internal::kZipBufSize];
409
410 const int num_bytes_read = unzReadCurrentFile(zip_file_,
411 buffer,
412 internal::kZipBufSize);
413
414 if (num_bytes_read == 0) {
415 unzCloseCurrentFile(zip_file_);
haven@chromium.org84ed2652014-01-17 00:36:28 +0000416 success_callback.Run();
417 } else if (num_bytes_read < 0) {
418 DVLOG(1) << "Unzip failed: error while reading zipfile "
419 << "(" << num_bytes_read << ")";
haven@chromium.org84ed2652014-01-17 00:36:28 +0000420 failure_callback.Run();
421 } else {
rvargas@chromium.org0d737652014-02-27 05:58:13 +0000422 if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) {
haven@chromium.org84ed2652014-01-17 00:36:28 +0000423 DVLOG(1) << "Unzip failed: unable to write all bytes to target.";
haven@chromium.org84ed2652014-01-17 00:36:28 +0000424 failure_callback.Run();
425 return;
426 }
427
aviaa969482015-12-27 13:36:49 -0800428 int64_t current_progress = offset + num_bytes_read;
haven@chromium.org84ed2652014-01-17 00:36:28 +0000429
430 progress_callback.Run(current_progress);
431
fdoray4e2773b2016-09-30 13:42:16 -0700432 base::ThreadTaskRunnerHandle::Get()->PostTask(
haven@chromium.org84ed2652014-01-17 00:36:28 +0000433 FROM_HERE,
dcheng97bdded2015-12-27 18:24:50 -0800434 base::Bind(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
435 Passed(std::move(output_file)), success_callback,
436 failure_callback, progress_callback, current_progress));
haven@chromium.org84ed2652014-01-17 00:36:28 +0000437 }
438}
439
grtebc765a2015-03-18 14:22:34 -0700440// FileWriterDelegate ----------------------------------------------------------
441
Joshua Pawlickie31b5032018-02-06 20:24:51 +0000442FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {}
443
444FileWriterDelegate::FileWriterDelegate(std::unique_ptr<base::File> file)
445 : file_(file.get()), owned_file_(std::move(file)) {}
grtebc765a2015-03-18 14:22:34 -0700446
447FileWriterDelegate::~FileWriterDelegate() {
alematefce82322016-11-21 17:23:16 -0800448 if (!file_->SetLength(file_length_)) {
Jochen Eisinger70d07ba2018-04-04 21:21:20 +0000449 DVPLOG(1) << "Failed updating length of written file";
alematefce82322016-11-21 17:23:16 -0800450 }
grtebc765a2015-03-18 14:22:34 -0700451}
452
453bool FileWriterDelegate::PrepareOutput() {
454 return file_->Seek(base::File::FROM_BEGIN, 0) >= 0;
455}
456
457bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
458 int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
459 if (bytes_written > 0)
460 file_length_ += bytes_written;
461 return bytes_written == num_bytes;
462}
haven@chromium.org84ed2652014-01-17 00:36:28 +0000463
Joshua Pawlickie31b5032018-02-06 20:24:51 +0000464void FileWriterDelegate::SetTimeModified(const base::Time& time) {
465 file_->SetTimes(base::Time::Now(), time);
466}
467
468// FilePathWriterDelegate ------------------------------------------------------
469
470FilePathWriterDelegate::FilePathWriterDelegate(
471 const base::FilePath& output_file_path)
472 : output_file_path_(output_file_path) {}
473
474FilePathWriterDelegate::~FilePathWriterDelegate() {}
475
476bool FilePathWriterDelegate::PrepareOutput() {
477 // We can't rely on parent directory entries being specified in the
478 // zip, so we make sure they are created.
479 if (!base::CreateDirectory(output_file_path_.DirName()))
480 return false;
481
482 file_.Initialize(output_file_path_,
483 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
484 return file_.IsValid();
485}
486
487bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
488 return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
489}
490
491void FilePathWriterDelegate::SetTimeModified(const base::Time& time) {
492 file_.Close();
493 base::TouchFile(output_file_path_, base::Time::Now(), time);
494}
495
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000496} // namespace zip