Chromium Code Reviews| 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 |