OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "third_party/zlib/google/zip_reader.h" | 5 #include "third_party/zlib/google/zip_reader.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/location.h" | |
8 #include "base/logging.h" | 9 #include "base/logging.h" |
9 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
10 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
11 #include "net/base/file_stream.h" | 12 #include "net/base/file_stream.h" |
12 #include "third_party/zlib/google/zip_internal.h" | 13 #include "third_party/zlib/google/zip_internal.h" |
13 | 14 |
14 #if defined(USE_SYSTEM_MINIZIP) | 15 #if defined(USE_SYSTEM_MINIZIP) |
15 #include <minizip/unzip.h> | 16 #include <minizip/unzip.h> |
16 #else | 17 #else |
17 #include "third_party/zlib/contrib/minizip/unzip.h" | 18 #include "third_party/zlib/contrib/minizip/unzip.h" |
18 #if defined(OS_WIN) | 19 #if defined(OS_WIN) |
19 #include "third_party/zlib/contrib/minizip/iowin32.h" | 20 #include "third_party/zlib/contrib/minizip/iowin32.h" |
20 #endif // defined(OS_WIN) | 21 #endif // defined(OS_WIN) |
21 #endif // defined(USE_SYSTEM_MINIZIP) | 22 #endif // defined(USE_SYSTEM_MINIZIP) |
22 | 23 |
23 namespace zip { | 24 namespace zip { |
24 | 25 |
26 namespace { | |
27 | |
28 // A listener that will close a PlatformFile when the operation is complete, | |
29 // with a delegate to pass events to. This is used by some asynchronous | |
30 // functions to ensure a PlatformFile is closed when done. | |
31 class FileClosingListener : public ZipReader::Listener { | |
32 public: | |
33 FileClosingListener(scoped_refptr<ZipReader::Listener> delegate, | |
34 base::PlatformFile platform_file) | |
35 : delegate_(delegate), | |
36 platform_file_(platform_file) { | |
37 } | |
38 | |
39 virtual void OnUnzipProgress(int progress) OVERRIDE { | |
40 delegate_->OnUnzipProgress(progress); | |
41 } | |
42 | |
43 virtual void OnUnzipSuccess() OVERRIDE { | |
44 base::ClosePlatformFile(platform_file_); | |
45 delegate_->OnUnzipSuccess(); | |
46 } | |
47 | |
48 virtual void OnUnzipFailed() OVERRIDE { | |
49 base::ClosePlatformFile(platform_file_); | |
50 delegate_->OnUnzipFailed(); | |
51 } | |
52 | |
53 private: | |
54 scoped_refptr<ZipReader::Listener> delegate_; | |
55 base::PlatformFile platform_file_; | |
56 }; | |
57 | |
58 } // namespace | |
59 | |
25 // TODO(satorux): The implementation assumes that file names in zip files | 60 // TODO(satorux): The implementation assumes that file names in zip files |
26 // are encoded in UTF-8. This is true for zip files created by Zip() | 61 // are encoded in UTF-8. This is true for zip files created by Zip() |
27 // function in zip.h, but not true for user-supplied random zip files. | 62 // function in zip.h, but not true for user-supplied random zip files. |
28 ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, | 63 ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, |
29 const unz_file_info& raw_file_info) | 64 const unz_file_info& raw_file_info) |
30 : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)), | 65 : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)), |
31 is_directory_(false) { | 66 is_directory_(false) { |
32 original_size_ = raw_file_info.uncompressed_size; | 67 original_size_ = raw_file_info.uncompressed_size; |
33 | 68 |
34 // Directory entries in zip files end with "/". | 69 // Directory entries in zip files end with "/". |
(...skipping 27 matching lines...) Expand all Loading... | |
62 exploded_time.second = raw_file_info.tmu_date.tm_sec; | 97 exploded_time.second = raw_file_info.tmu_date.tm_sec; |
63 exploded_time.millisecond = 0; | 98 exploded_time.millisecond = 0; |
64 if (exploded_time.HasValidValues()) { | 99 if (exploded_time.HasValidValues()) { |
65 last_modified_ = base::Time::FromLocalExploded(exploded_time); | 100 last_modified_ = base::Time::FromLocalExploded(exploded_time); |
66 } else { | 101 } else { |
67 // Use Unix time epoch if the time stamp data is invalid. | 102 // Use Unix time epoch if the time stamp data is invalid. |
68 last_modified_ = base::Time::UnixEpoch(); | 103 last_modified_ = base::Time::UnixEpoch(); |
69 } | 104 } |
70 } | 105 } |
71 | 106 |
72 ZipReader::ZipReader() { | 107 ZipReader::ZipReader() |
108 : weak_factory_(this) { | |
73 Reset(); | 109 Reset(); |
74 } | 110 } |
75 | 111 |
76 ZipReader::~ZipReader() { | 112 ZipReader::~ZipReader() { |
77 Close(); | 113 Close(); |
78 } | 114 } |
79 | 115 |
80 bool ZipReader::Open(const base::FilePath& zip_file_path) { | 116 bool ZipReader::Open(const base::FilePath& zip_file_path) { |
81 DCHECK(!zip_file_); | 117 DCHECK(!zip_file_); |
82 | 118 |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
228 success = false; | 264 success = false; |
229 break; | 265 break; |
230 } | 266 } |
231 } | 267 } |
232 } | 268 } |
233 | 269 |
234 unzCloseCurrentFile(zip_file_); | 270 unzCloseCurrentFile(zip_file_); |
235 return success; | 271 return success; |
236 } | 272 } |
237 | 273 |
274 void ZipReader::ExtractCurrentEntryToFilePathAsync( | |
275 const base::FilePath& output_file_path, | |
276 scoped_refptr<base::MessageLoopProxy> message_loop_proxy, | |
277 scoped_refptr<Listener> listener) { | |
278 DCHECK(zip_file_); | |
279 DCHECK(current_entry_info_.get()); | |
280 | |
281 // If this is a directory, just create it and return. | |
282 if (current_entry_info()->is_directory()) { | |
283 if (file_util::CreateDirectory(output_file_path)) { | |
284 listener->OnUnzipSuccess(); | |
285 } else { | |
286 DVLOG(1) << "Unzip failed: unable to create directory."; | |
287 listener->OnUnzipFailed(); | |
288 } | |
289 return; | |
290 } | |
291 | |
292 base::FilePath output_dir_path = output_file_path.DirName(); | |
293 if (!file_util::CreateDirectory(output_dir_path)) { | |
294 DVLOG(1) << "Unzip failed: unable to create containing directory."; | |
295 listener->OnUnzipFailed(); | |
296 return; | |
297 } | |
298 | |
299 const int flags = (base::PLATFORM_FILE_CREATE_ALWAYS | | |
300 base::PLATFORM_FILE_WRITE); | |
301 bool created = false; | |
302 base::PlatformFileError platform_file_error; | |
303 base::PlatformFile output_file = CreatePlatformFile(output_file_path, | |
304 flags, | |
305 &created, | |
306 &platform_file_error); | |
307 | |
308 if (platform_file_error != base::PLATFORM_FILE_OK) { | |
309 DVLOG(1) << "Unzip failed: unable to create platform file at " | |
310 << output_file_path.value(); | |
311 listener->OnUnzipFailed(); | |
312 return; | |
313 } | |
314 | |
315 ExtractCurrentEntryToPlatformFileAsync(output_file, | |
316 message_loop_proxy, | |
317 new FileClosingListener(listener, | |
318 output_file)); | |
319 } | |
320 | |
238 bool ZipReader::ExtractCurrentEntryIntoDirectory( | 321 bool ZipReader::ExtractCurrentEntryIntoDirectory( |
239 const base::FilePath& output_directory_path) { | 322 const base::FilePath& output_directory_path) { |
240 DCHECK(current_entry_info_.get()); | 323 DCHECK(current_entry_info_.get()); |
241 | 324 |
242 base::FilePath output_file_path = output_directory_path.Append( | 325 base::FilePath output_file_path = output_directory_path.Append( |
243 current_entry_info()->file_path()); | 326 current_entry_info()->file_path()); |
244 return ExtractCurrentEntryToFilePath(output_file_path); | 327 return ExtractCurrentEntryToFilePath(output_file_path); |
245 } | 328 } |
246 | 329 |
330 void ZipReader::ExtractCurrentEntryIntoDirectoryAsync( | |
331 const base::FilePath& output_directory_path, | |
332 scoped_refptr<base::MessageLoopProxy> message_loop_proxy, | |
333 scoped_refptr<Listener> listener) { | |
334 DCHECK(current_entry_info_.get()); | |
335 | |
336 base::FilePath output_file_path = output_directory_path.Append( | |
337 current_entry_info()->file_path()); | |
338 ExtractCurrentEntryToFilePathAsync(output_file_path, | |
339 message_loop_proxy, | |
340 listener); | |
341 } | |
342 | |
343 void ZipReader::ExtractCurrentEntryToPlatformFileAsync( | |
344 base::PlatformFile output_file, | |
345 scoped_refptr<base::MessageLoopProxy> message_loop_proxy, | |
346 scoped_refptr<Listener> listener) { | |
347 DCHECK(zip_file_); | |
348 DCHECK(current_entry_info_.get()); | |
349 | |
350 if (current_entry_info()->is_directory()) { | |
351 DVLOG(1) << "Unzip failed: Cannot unzip a directory to an open file."; | |
352 listener->OnUnzipFailed(); | |
353 } | |
354 | |
355 if (unzOpenCurrentFile(zip_file_) != UNZ_OK) { | |
356 DVLOG(1) << "Unzip failed: unable to open current zip entry."; | |
357 listener->OnUnzipFailed(); | |
358 return; | |
359 } | |
360 | |
361 message_loop_proxy->PostTask( | |
362 FROM_HERE, | |
363 base::Bind(&ZipReader::ExtractChunk, | |
364 weak_factory_.GetWeakPtr(), | |
satorux1
2013/12/05 04:39:37
this is worrisome. WeakPtr only works if you are p
Drew Haven
2013/12/09 23:33:12
I left the weakptr in, but I'm using the current m
| |
365 output_file, | |
366 message_loop_proxy, | |
367 listener, | |
368 0, | |
369 current_entry_info()->original_size())); | |
370 } | |
371 | |
372 | |
247 #if defined(OS_POSIX) | 373 #if defined(OS_POSIX) |
248 bool ZipReader::ExtractCurrentEntryToFd(const int fd) { | 374 bool ZipReader::ExtractCurrentEntryToFd(const int fd) { |
249 DCHECK(zip_file_); | 375 DCHECK(zip_file_); |
250 | 376 |
251 // If this is a directory, there's nothing to extract to the file descriptor, | 377 // If this is a directory, there's nothing to extract to the file descriptor, |
252 // so return false. | 378 // so return false. |
253 if (current_entry_info()->is_directory()) | 379 if (current_entry_info()->is_directory()) |
254 return false; | 380 return false; |
255 | 381 |
256 const int open_result = unzOpenCurrentFile(zip_file_); | 382 const int open_result = unzOpenCurrentFile(zip_file_); |
(...skipping 18 matching lines...) Expand all Loading... | |
275 file_util::WriteFileDescriptor(fd, buf, num_bytes_read)) { | 401 file_util::WriteFileDescriptor(fd, buf, num_bytes_read)) { |
276 success = false; | 402 success = false; |
277 break; | 403 break; |
278 } | 404 } |
279 } | 405 } |
280 } | 406 } |
281 | 407 |
282 unzCloseCurrentFile(zip_file_); | 408 unzCloseCurrentFile(zip_file_); |
283 return success; | 409 return success; |
284 } | 410 } |
411 | |
412 void ZipReader::ExtractCurrentEntryToFdAsync( | |
413 int fd, | |
414 scoped_refptr<base::MessageLoopProxy> message_loop_proxy, | |
415 scoped_refptr<Listener> listener) { | |
416 // A POSIX PlatformFile is just an int, so we can use the same internal | |
417 // functions. | |
418 ExtractCurrentEntryToPlatformFileAsync(fd, message_loop_proxy, listener); | |
419 } | |
285 #endif // defined(OS_POSIX) | 420 #endif // defined(OS_POSIX) |
286 | 421 |
287 bool ZipReader::OpenInternal() { | 422 bool ZipReader::OpenInternal() { |
288 DCHECK(zip_file_); | 423 DCHECK(zip_file_); |
289 | 424 |
290 unz_global_info zip_info = {}; // Zero-clear. | 425 unz_global_info zip_info = {}; // Zero-clear. |
291 if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) { | 426 if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) { |
292 return false; | 427 return false; |
293 } | 428 } |
294 num_entries_ = zip_info.number_entry; | 429 num_entries_ = zip_info.number_entry; |
295 if (num_entries_ < 0) | 430 if (num_entries_ < 0) |
296 return false; | 431 return false; |
297 | 432 |
298 // We are already at the end if the zip file is empty. | 433 // We are already at the end if the zip file is empty. |
299 reached_end_ = (num_entries_ == 0); | 434 reached_end_ = (num_entries_ == 0); |
300 return true; | 435 return true; |
301 } | 436 } |
302 | 437 |
303 void ZipReader::Reset() { | 438 void ZipReader::Reset() { |
304 zip_file_ = NULL; | 439 zip_file_ = NULL; |
305 num_entries_ = 0; | 440 num_entries_ = 0; |
306 reached_end_ = false; | 441 reached_end_ = false; |
307 current_entry_info_.reset(); | 442 current_entry_info_.reset(); |
308 } | 443 } |
309 | 444 |
445 void ZipReader::ExtractChunk(base::PlatformFile output_file, | |
446 scoped_refptr<base::MessageLoopProxy> message_loop_ proxy, | |
satorux1
2013/12/05 04:39:37
> 80+ chars.
Drew Haven
2013/12/09 23:33:12
Done.
| |
447 scoped_refptr<Listener> listener, | |
448 int offset, | |
449 int size) { | |
450 char buffer[internal::kZipBufSize]; | |
451 | |
452 const int num_bytes_read = unzReadCurrentFile(zip_file_, | |
453 buffer, | |
454 internal::kZipBufSize); | |
455 | |
456 if (num_bytes_read == 0) { | |
457 unzCloseCurrentFile(zip_file_); | |
458 listener->OnUnzipSuccess(); | |
459 } else if (num_bytes_read < 0) { | |
460 DVLOG(1) << "Unzip failed: error while reading zipfile " | |
461 << "(" << num_bytes_read << ")"; | |
462 listener->OnUnzipFailed(); | |
463 } else { | |
464 if (num_bytes_read != base::WritePlatformFileAtCurrentPos(output_file, | |
465 buffer, | |
466 num_bytes_read)) { | |
467 DVLOG(1) << "Unzip failed: unable to write all bytes to target."; | |
468 listener->OnUnzipFailed(); | |
469 return; | |
470 } | |
471 | |
472 const int prev_progress = offset / size; | |
473 const int curr_progress = (offset + num_bytes_read) / size; | |
474 | |
475 if (curr_progress > prev_progress) { | |
476 listener->OnUnzipProgress(curr_progress); | |
477 } | |
478 | |
479 message_loop_proxy->PostTask( | |
480 FROM_HERE, | |
481 base::Bind(&ZipReader::ExtractChunk, | |
482 weak_factory_.GetWeakPtr(), | |
483 output_file, | |
484 message_loop_proxy, | |
485 listener, | |
486 offset + num_bytes_read, | |
487 size)); | |
488 | |
489 } | |
490 } | |
491 | |
492 | |
310 } // namespace zip | 493 } // namespace zip |
OLD | NEW |