Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: third_party/zlib/google/zip_reader.cc

Issue 2023703002: Beginning work on GN build (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Really add //build. Add dart_bootstrap rule. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/zlib/google/zip_reader.h ('k') | third_party/zlib/google/zip_reader_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <utility>
8
9 #include "base/bind.h"
10 #include "base/files/file.h"
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "build/build_config.h"
18 #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
29 namespace zip {
30
31 namespace {
32
33 // FilePathWriterDelegate ------------------------------------------------------
34
35 // A writer delegate that writes a file at a given path.
36 class FilePathWriterDelegate : public WriterDelegate {
37 public:
38 explicit FilePathWriterDelegate(const base::FilePath& output_file_path);
39 ~FilePathWriterDelegate() override;
40
41 // WriterDelegate methods:
42
43 // Creates the output file and any necessary intermediate directories.
44 bool PrepareOutput() override;
45
46 // Writes |num_bytes| bytes of |data| to the file, returning false if not all
47 // bytes could be written.
48 bool WriteBytes(const char* data, int num_bytes) override;
49
50 private:
51 base::FilePath output_file_path_;
52 base::File file_;
53
54 DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate);
55 };
56
57 FilePathWriterDelegate::FilePathWriterDelegate(
58 const base::FilePath& output_file_path)
59 : output_file_path_(output_file_path) {
60 }
61
62 FilePathWriterDelegate::~FilePathWriterDelegate() {
63 }
64
65 bool FilePathWriterDelegate::PrepareOutput() {
66 // We can't rely on parent directory entries being specified in the
67 // zip, so we make sure they are created.
68 if (!base::CreateDirectory(output_file_path_.DirName()))
69 return false;
70
71 file_.Initialize(output_file_path_,
72 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
73 return file_.IsValid();
74 }
75
76 bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
77 return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
78 }
79
80
81 // StringWriterDelegate --------------------------------------------------------
82
83 // A writer delegate that writes no more than |max_read_bytes| to a given
84 // std::string.
85 class StringWriterDelegate : public WriterDelegate {
86 public:
87 StringWriterDelegate(size_t max_read_bytes, std::string* output);
88 ~StringWriterDelegate() override;
89
90 // WriterDelegate methods:
91
92 // Returns true.
93 bool PrepareOutput() override;
94
95 // Appends |num_bytes| bytes from |data| to the output string. Returns false
96 // if |num_bytes| will cause the string to exceed |max_read_bytes|.
97 bool WriteBytes(const char* data, int num_bytes) override;
98
99 private:
100 size_t max_read_bytes_;
101 std::string* output_;
102
103 DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate);
104 };
105
106 StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes,
107 std::string* output)
108 : max_read_bytes_(max_read_bytes),
109 output_(output) {
110 }
111
112 StringWriterDelegate::~StringWriterDelegate() {
113 }
114
115 bool StringWriterDelegate::PrepareOutput() {
116 return true;
117 }
118
119 bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) {
120 if (output_->size() + num_bytes > max_read_bytes_)
121 return false;
122 output_->append(data, num_bytes);
123 return true;
124 }
125
126 } // namespace
127
128 // TODO(satorux): The implementation assumes that file names in zip files
129 // are encoded in UTF-8. This is true for zip files created by Zip()
130 // function in zip.h, but not true for user-supplied random zip files.
131 ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip,
132 const unz_file_info& raw_file_info)
133 : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)),
134 is_directory_(false) {
135 original_size_ = raw_file_info.uncompressed_size;
136
137 // Directory entries in zip files end with "/".
138 is_directory_ = base::EndsWith(file_name_in_zip, "/",
139 base::CompareCase::INSENSITIVE_ASCII);
140
141 // Check the file name here for directory traversal issues.
142 is_unsafe_ = file_path_.ReferencesParent();
143
144 // We also consider that the file name is unsafe, if it's invalid UTF-8.
145 base::string16 file_name_utf16;
146 if (!base::UTF8ToUTF16(file_name_in_zip.data(), file_name_in_zip.size(),
147 &file_name_utf16)) {
148 is_unsafe_ = true;
149 }
150
151 // We also consider that the file name is unsafe, if it's absolute.
152 // On Windows, IsAbsolute() returns false for paths starting with "/".
153 if (file_path_.IsAbsolute() ||
154 base::StartsWith(file_name_in_zip, "/",
155 base::CompareCase::INSENSITIVE_ASCII))
156 is_unsafe_ = true;
157
158 // Construct the last modified time. The timezone info is not present in
159 // zip files, so we construct the time as local time.
160 base::Time::Exploded exploded_time = {}; // Zero-clear.
161 exploded_time.year = raw_file_info.tmu_date.tm_year;
162 // The month in zip file is 0-based, whereas ours is 1-based.
163 exploded_time.month = raw_file_info.tmu_date.tm_mon + 1;
164 exploded_time.day_of_month = raw_file_info.tmu_date.tm_mday;
165 exploded_time.hour = raw_file_info.tmu_date.tm_hour;
166 exploded_time.minute = raw_file_info.tmu_date.tm_min;
167 exploded_time.second = raw_file_info.tmu_date.tm_sec;
168 exploded_time.millisecond = 0;
169 if (exploded_time.HasValidValues()) {
170 last_modified_ = base::Time::FromLocalExploded(exploded_time);
171 } else {
172 // Use Unix time epoch if the time stamp data is invalid.
173 last_modified_ = base::Time::UnixEpoch();
174 }
175 }
176
177 ZipReader::ZipReader()
178 : weak_ptr_factory_(this) {
179 Reset();
180 }
181
182 ZipReader::~ZipReader() {
183 Close();
184 }
185
186 bool ZipReader::Open(const base::FilePath& zip_file_path) {
187 DCHECK(!zip_file_);
188
189 // Use of "Unsafe" function does not look good, but there is no way to do
190 // this safely on Linux. See file_util.h for details.
191 zip_file_ = internal::OpenForUnzipping(zip_file_path.AsUTF8Unsafe());
192 if (!zip_file_) {
193 return false;
194 }
195
196 return OpenInternal();
197 }
198
199 bool ZipReader::OpenFromPlatformFile(base::PlatformFile zip_fd) {
200 DCHECK(!zip_file_);
201
202 #if defined(OS_POSIX)
203 zip_file_ = internal::OpenFdForUnzipping(zip_fd);
204 #elif defined(OS_WIN)
205 zip_file_ = internal::OpenHandleForUnzipping(zip_fd);
206 #endif
207 if (!zip_file_) {
208 return false;
209 }
210
211 return OpenInternal();
212 }
213
214 bool ZipReader::OpenFromString(const std::string& data) {
215 zip_file_ = internal::PrepareMemoryForUnzipping(data);
216 if (!zip_file_)
217 return false;
218 return OpenInternal();
219 }
220
221 void ZipReader::Close() {
222 if (zip_file_) {
223 unzClose(zip_file_);
224 }
225 Reset();
226 }
227
228 bool ZipReader::HasMore() {
229 return !reached_end_;
230 }
231
232 bool ZipReader::AdvanceToNextEntry() {
233 DCHECK(zip_file_);
234
235 // Should not go further if we already reached the end.
236 if (reached_end_)
237 return false;
238
239 unz_file_pos position = {};
240 if (unzGetFilePos(zip_file_, &position) != UNZ_OK)
241 return false;
242 const int current_entry_index = position.num_of_file;
243 // If we are currently at the last entry, then the next position is the
244 // end of the zip file, so mark that we reached the end.
245 if (current_entry_index + 1 == num_entries_) {
246 reached_end_ = true;
247 } else {
248 DCHECK_LT(current_entry_index + 1, num_entries_);
249 if (unzGoToNextFile(zip_file_) != UNZ_OK) {
250 return false;
251 }
252 }
253 current_entry_info_.reset();
254 return true;
255 }
256
257 bool ZipReader::OpenCurrentEntryInZip() {
258 DCHECK(zip_file_);
259
260 unz_file_info raw_file_info = {};
261 char raw_file_name_in_zip[internal::kZipMaxPath] = {};
262 const int result = unzGetCurrentFileInfo(zip_file_,
263 &raw_file_info,
264 raw_file_name_in_zip,
265 sizeof(raw_file_name_in_zip) - 1,
266 NULL, // extraField.
267 0, // extraFieldBufferSize.
268 NULL, // szComment.
269 0); // commentBufferSize.
270 if (result != UNZ_OK)
271 return false;
272 if (raw_file_name_in_zip[0] == '\0')
273 return false;
274 current_entry_info_.reset(
275 new EntryInfo(raw_file_name_in_zip, raw_file_info));
276 return true;
277 }
278
279 bool ZipReader::LocateAndOpenEntry(const base::FilePath& path_in_zip) {
280 DCHECK(zip_file_);
281
282 current_entry_info_.reset();
283 reached_end_ = false;
284 const int kDefaultCaseSensivityOfOS = 0;
285 const int result = unzLocateFile(zip_file_,
286 path_in_zip.AsUTF8Unsafe().c_str(),
287 kDefaultCaseSensivityOfOS);
288 if (result != UNZ_OK)
289 return false;
290
291 // Then Open the entry.
292 return OpenCurrentEntryInZip();
293 }
294
295 bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate) const {
296 DCHECK(zip_file_);
297
298 const int open_result = unzOpenCurrentFile(zip_file_);
299 if (open_result != UNZ_OK)
300 return false;
301
302 if (!delegate->PrepareOutput())
303 return false;
304
305 bool success = true; // This becomes false when something bad happens.
306 std::unique_ptr<char[]> buf(new char[internal::kZipBufSize]);
307 while (true) {
308 const int num_bytes_read = unzReadCurrentFile(zip_file_, buf.get(),
309 internal::kZipBufSize);
310 if (num_bytes_read == 0) {
311 // Reached the end of the file.
312 break;
313 } else if (num_bytes_read < 0) {
314 // If num_bytes_read < 0, then it's a specific UNZ_* error code.
315 success = false;
316 break;
317 } else if (num_bytes_read > 0) {
318 // Some data is read.
319 if (!delegate->WriteBytes(buf.get(), num_bytes_read)) {
320 success = false;
321 break;
322 }
323 }
324 }
325
326 unzCloseCurrentFile(zip_file_);
327
328 return success;
329 }
330
331 bool ZipReader::ExtractCurrentEntryToFilePath(
332 const base::FilePath& output_file_path) const {
333 DCHECK(zip_file_);
334
335 // If this is a directory, just create it and return.
336 if (current_entry_info()->is_directory())
337 return base::CreateDirectory(output_file_path);
338
339 bool success = false;
340 {
341 FilePathWriterDelegate writer(output_file_path);
342 success = ExtractCurrentEntry(&writer);
343 }
344
345 if (success &&
346 current_entry_info()->last_modified() != base::Time::UnixEpoch()) {
347 base::TouchFile(output_file_path,
348 base::Time::Now(),
349 current_entry_info()->last_modified());
350 }
351
352 return success;
353 }
354
355 void ZipReader::ExtractCurrentEntryToFilePathAsync(
356 const base::FilePath& output_file_path,
357 const SuccessCallback& success_callback,
358 const FailureCallback& failure_callback,
359 const ProgressCallback& progress_callback) {
360 DCHECK(zip_file_);
361 DCHECK(current_entry_info_.get());
362
363 // If this is a directory, just create it and return.
364 if (current_entry_info()->is_directory()) {
365 if (base::CreateDirectory(output_file_path)) {
366 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, success_callback) ;
367 } else {
368 DVLOG(1) << "Unzip failed: unable to create directory.";
369 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback) ;
370 }
371 return;
372 }
373
374 if (unzOpenCurrentFile(zip_file_) != UNZ_OK) {
375 DVLOG(1) << "Unzip failed: unable to open current zip entry.";
376 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
377 return;
378 }
379
380 base::FilePath output_dir_path = output_file_path.DirName();
381 if (!base::CreateDirectory(output_dir_path)) {
382 DVLOG(1) << "Unzip failed: unable to create containing directory.";
383 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
384 return;
385 }
386
387 const int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
388 base::File output_file(output_file_path, flags);
389
390 if (!output_file.IsValid()) {
391 DVLOG(1) << "Unzip failed: unable to create platform file at "
392 << output_file_path.value();
393 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
394 return;
395 }
396
397 base::MessageLoop::current()->PostTask(
398 FROM_HERE,
399 base::Bind(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
400 Passed(std::move(output_file)), success_callback,
401 failure_callback, progress_callback, 0 /* initial offset */));
402 }
403
404 bool ZipReader::ExtractCurrentEntryIntoDirectory(
405 const base::FilePath& output_directory_path) const {
406 DCHECK(current_entry_info_.get());
407
408 base::FilePath output_file_path = output_directory_path.Append(
409 current_entry_info()->file_path());
410 return ExtractCurrentEntryToFilePath(output_file_path);
411 }
412
413 bool ZipReader::ExtractCurrentEntryToFile(base::File* file) const {
414 DCHECK(zip_file_);
415
416 // If this is a directory, there's nothing to extract to the file, so return
417 // false.
418 if (current_entry_info()->is_directory())
419 return false;
420
421 FileWriterDelegate writer(file);
422 return ExtractCurrentEntry(&writer);
423 }
424
425 bool ZipReader::ExtractCurrentEntryToString(size_t max_read_bytes,
426 std::string* output) const {
427 DCHECK(output);
428 DCHECK(zip_file_);
429 DCHECK_NE(0U, max_read_bytes);
430
431 if (current_entry_info()->is_directory()) {
432 output->clear();
433 return true;
434 }
435
436 // The original_size() is the best hint for the real size, so it saves
437 // doing reallocations for the common case when the uncompressed size is
438 // correct. However, we need to assume that the uncompressed size could be
439 // incorrect therefore this function needs to read as much data as possible.
440 std::string contents;
441 contents.reserve(
442 static_cast<size_t>(std::min(static_cast<int64_t>(max_read_bytes),
443 current_entry_info()->original_size())));
444
445 StringWriterDelegate writer(max_read_bytes, &contents);
446 if (!ExtractCurrentEntry(&writer))
447 return false;
448 output->swap(contents);
449 return true;
450 }
451
452 bool ZipReader::OpenInternal() {
453 DCHECK(zip_file_);
454
455 unz_global_info zip_info = {}; // Zero-clear.
456 if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) {
457 return false;
458 }
459 num_entries_ = zip_info.number_entry;
460 if (num_entries_ < 0)
461 return false;
462
463 // We are already at the end if the zip file is empty.
464 reached_end_ = (num_entries_ == 0);
465 return true;
466 }
467
468 void ZipReader::Reset() {
469 zip_file_ = NULL;
470 num_entries_ = 0;
471 reached_end_ = false;
472 current_entry_info_.reset();
473 }
474
475 void ZipReader::ExtractChunk(base::File output_file,
476 const SuccessCallback& success_callback,
477 const FailureCallback& failure_callback,
478 const ProgressCallback& progress_callback,
479 const int64_t offset) {
480 char buffer[internal::kZipBufSize];
481
482 const int num_bytes_read = unzReadCurrentFile(zip_file_,
483 buffer,
484 internal::kZipBufSize);
485
486 if (num_bytes_read == 0) {
487 unzCloseCurrentFile(zip_file_);
488 success_callback.Run();
489 } else if (num_bytes_read < 0) {
490 DVLOG(1) << "Unzip failed: error while reading zipfile "
491 << "(" << num_bytes_read << ")";
492 failure_callback.Run();
493 } else {
494 if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) {
495 DVLOG(1) << "Unzip failed: unable to write all bytes to target.";
496 failure_callback.Run();
497 return;
498 }
499
500 int64_t current_progress = offset + num_bytes_read;
501
502 progress_callback.Run(current_progress);
503
504 base::MessageLoop::current()->PostTask(
505 FROM_HERE,
506 base::Bind(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
507 Passed(std::move(output_file)), success_callback,
508 failure_callback, progress_callback, current_progress));
509 }
510 }
511
512 // FileWriterDelegate ----------------------------------------------------------
513
514 FileWriterDelegate::FileWriterDelegate(base::File* file)
515 : file_(file),
516 file_length_(0) {
517 }
518
519 FileWriterDelegate::~FileWriterDelegate() {
520 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
521 const bool success =
522 #endif
523 file_->SetLength(file_length_);
524 DPLOG_IF(ERROR, !success) << "Failed updating length of written file";
525 }
526
527 bool FileWriterDelegate::PrepareOutput() {
528 return file_->Seek(base::File::FROM_BEGIN, 0) >= 0;
529 }
530
531 bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
532 int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
533 if (bytes_written > 0)
534 file_length_ += bytes_written;
535 return bytes_written == num_bytes;
536 }
537
538 } // namespace zip
OLDNEW
« no previous file with comments | « third_party/zlib/google/zip_reader.h ('k') | third_party/zlib/google/zip_reader_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698