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/bind.h" | 7 #include "base/bind.h" |
8 #include "base/files/file.h" | 8 #include "base/files/file.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop/message_loop.h" | 10 #include "base/message_loop/message_loop.h" |
11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
13 #include "third_party/zlib/google/zip_internal.h" | 13 #include "third_party/zlib/google/zip_internal.h" |
14 | 14 |
15 #if defined(USE_SYSTEM_MINIZIP) | 15 #if defined(USE_SYSTEM_MINIZIP) |
16 #include <minizip/unzip.h> | 16 #include <minizip/unzip.h> |
17 #else | 17 #else |
18 #include "third_party/zlib/contrib/minizip/unzip.h" | 18 #include "third_party/zlib/contrib/minizip/unzip.h" |
19 #if defined(OS_WIN) | 19 #if defined(OS_WIN) |
20 #include "third_party/zlib/contrib/minizip/iowin32.h" | 20 #include "third_party/zlib/contrib/minizip/iowin32.h" |
21 #endif // defined(OS_WIN) | 21 #endif // defined(OS_WIN) |
22 #endif // defined(USE_SYSTEM_MINIZIP) | 22 #endif // defined(USE_SYSTEM_MINIZIP) |
23 | 23 |
24 namespace zip { | 24 namespace zip { |
25 | 25 |
26 namespace { | |
27 | |
28 // FilePathWriterDelegate ------------------------------------------------------ | |
29 | |
30 // A writer delegate that writes a file at a given path. | |
31 class FilePathWriterDelegate : public WriterDelegate { | |
32 public: | |
33 explicit FilePathWriterDelegate(const base::FilePath& output_file_path); | |
34 ~FilePathWriterDelegate() override; | |
35 | |
36 // WriterDelegate methods: | |
37 | |
38 // Creates the output file and any necessary intermediate directories. | |
39 bool PrepareOutput() override; | |
40 | |
41 // Writes |num_bytes| bytes of |data| to the file, returning false if not all | |
42 // bytes could be written. | |
43 bool WriteBytes(const char* data, int num_bytes) override; | |
44 | |
45 private: | |
46 base::FilePath output_file_path_; | |
47 base::File file_; | |
48 | |
49 DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate); | |
50 }; | |
51 | |
52 FilePathWriterDelegate::FilePathWriterDelegate( | |
53 const base::FilePath& output_file_path) | |
54 : output_file_path_(output_file_path) { | |
55 } | |
56 | |
57 FilePathWriterDelegate::~FilePathWriterDelegate() { | |
58 } | |
59 | |
60 bool FilePathWriterDelegate::PrepareOutput() { | |
61 // We can't rely on parent directory entries being specified in the | |
62 // zip, so we make sure they are created. | |
63 if (!base::CreateDirectory(output_file_path_.DirName())) | |
64 return false; | |
65 | |
66 file_.Initialize(output_file_path_, | |
67 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | |
68 return file_.IsValid(); | |
69 } | |
70 | |
71 bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) { | |
72 return num_bytes == file_.WriteAtCurrentPos(data, num_bytes); | |
73 } | |
74 | |
75 | |
76 // StringWriterDelegate -------------------------------------------------------- | |
77 | |
78 // A writer delegate that writes no more than |max_read_bytes| to a given | |
79 // std::string. | |
80 class StringWriterDelegate : public WriterDelegate { | |
81 public: | |
82 StringWriterDelegate(size_t max_read_bytes, std::string* output); | |
83 ~StringWriterDelegate() override; | |
84 | |
85 // WriterDelegate methods: | |
86 | |
87 // Returns true. | |
88 bool PrepareOutput() override; | |
89 | |
90 // Appends |num_bytes| bytes from |data| to the output string. Returns false | |
91 // if |num_bytes| will cause the string to exceed |max_read_bytes|. | |
92 bool WriteBytes(const char* data, int num_bytes) override; | |
93 | |
94 private: | |
95 size_t max_read_bytes_; | |
96 std::string* output_; | |
97 | |
98 DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate); | |
99 }; | |
100 | |
101 StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes, | |
102 std::string* output) | |
103 : max_read_bytes_(max_read_bytes), | |
104 output_(output) { | |
105 } | |
106 | |
107 StringWriterDelegate::~StringWriterDelegate() { | |
108 } | |
109 | |
110 bool StringWriterDelegate::PrepareOutput() { | |
111 return true; | |
112 } | |
113 | |
114 bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) { | |
115 if (output_->size() + num_bytes > max_read_bytes_) | |
116 return false; | |
117 output_->append(data, num_bytes); | |
118 return true; | |
119 } | |
120 | |
121 } // namespace | |
122 | |
123 // TODO(satorux): The implementation assumes that file names in zip files | 26 // TODO(satorux): The implementation assumes that file names in zip files |
124 // are encoded in UTF-8. This is true for zip files created by Zip() | 27 // are encoded in UTF-8. This is true for zip files created by Zip() |
125 // function in zip.h, but not true for user-supplied random zip files. | 28 // function in zip.h, but not true for user-supplied random zip files. |
126 ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, | 29 ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, |
127 const unz_file_info& raw_file_info) | 30 const unz_file_info& raw_file_info) |
128 : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)), | 31 : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)), |
129 is_directory_(false) { | 32 is_directory_(false) { |
130 original_size_ = raw_file_info.uncompressed_size; | 33 original_size_ = raw_file_info.uncompressed_size; |
131 | 34 |
132 // Directory entries in zip files end with "/". | 35 // Directory entries in zip files end with "/". |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 const int result = unzLocateFile(zip_file_, | 180 const int result = unzLocateFile(zip_file_, |
278 path_in_zip.AsUTF8Unsafe().c_str(), | 181 path_in_zip.AsUTF8Unsafe().c_str(), |
279 kDefaultCaseSensivityOfOS); | 182 kDefaultCaseSensivityOfOS); |
280 if (result != UNZ_OK) | 183 if (result != UNZ_OK) |
281 return false; | 184 return false; |
282 | 185 |
283 // Then Open the entry. | 186 // Then Open the entry. |
284 return OpenCurrentEntryInZip(); | 187 return OpenCurrentEntryInZip(); |
285 } | 188 } |
286 | 189 |
287 bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate) const { | 190 bool ZipReader::ExtractCurrentEntryToFilePath( |
| 191 const base::FilePath& output_file_path) { |
288 DCHECK(zip_file_); | 192 DCHECK(zip_file_); |
289 | 193 |
| 194 // If this is a directory, just create it and return. |
| 195 if (current_entry_info()->is_directory()) |
| 196 return base::CreateDirectory(output_file_path); |
| 197 |
290 const int open_result = unzOpenCurrentFile(zip_file_); | 198 const int open_result = unzOpenCurrentFile(zip_file_); |
291 if (open_result != UNZ_OK) | 199 if (open_result != UNZ_OK) |
292 return false; | 200 return false; |
293 | 201 |
294 if (!delegate->PrepareOutput()) | 202 // We can't rely on parent directory entries being specified in the |
| 203 // zip, so we make sure they are created. |
| 204 base::FilePath output_dir_path = output_file_path.DirName(); |
| 205 if (!base::CreateDirectory(output_dir_path)) |
| 206 return false; |
| 207 |
| 208 base::File file(output_file_path, |
| 209 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| 210 if (!file.IsValid()) |
295 return false; | 211 return false; |
296 | 212 |
297 bool success = true; // This becomes false when something bad happens. | 213 bool success = true; // This becomes false when something bad happens. |
298 scoped_ptr<char[]> buf(new char[internal::kZipBufSize]); | |
299 while (true) { | 214 while (true) { |
300 const int num_bytes_read = unzReadCurrentFile(zip_file_, buf.get(), | 215 char buf[internal::kZipBufSize]; |
| 216 const int num_bytes_read = unzReadCurrentFile(zip_file_, buf, |
301 internal::kZipBufSize); | 217 internal::kZipBufSize); |
302 if (num_bytes_read == 0) { | 218 if (num_bytes_read == 0) { |
303 // Reached the end of the file. | 219 // Reached the end of the file. |
304 break; | 220 break; |
305 } else if (num_bytes_read < 0) { | 221 } else if (num_bytes_read < 0) { |
306 // If num_bytes_read < 0, then it's a specific UNZ_* error code. | 222 // If num_bytes_read < 0, then it's a specific UNZ_* error code. |
307 success = false; | 223 success = false; |
308 break; | 224 break; |
309 } else if (num_bytes_read > 0) { | 225 } else if (num_bytes_read > 0) { |
310 // Some data is read. | 226 // Some data is read. Write it to the output file. |
311 if (!delegate->WriteBytes(buf.get(), num_bytes_read)) { | 227 if (num_bytes_read != file.WriteAtCurrentPos(buf, num_bytes_read)) { |
312 success = false; | 228 success = false; |
313 break; | 229 break; |
314 } | 230 } |
315 } | 231 } |
316 } | 232 } |
317 | 233 |
| 234 file.Close(); |
318 unzCloseCurrentFile(zip_file_); | 235 unzCloseCurrentFile(zip_file_); |
319 | 236 |
| 237 if (current_entry_info()->last_modified() != base::Time::UnixEpoch()) |
| 238 base::TouchFile(output_file_path, |
| 239 base::Time::Now(), |
| 240 current_entry_info()->last_modified()); |
| 241 |
320 return success; | 242 return success; |
321 } | 243 } |
322 | |
323 bool ZipReader::ExtractCurrentEntryToFilePath( | |
324 const base::FilePath& output_file_path) const { | |
325 DCHECK(zip_file_); | |
326 | |
327 // If this is a directory, just create it and return. | |
328 if (current_entry_info()->is_directory()) | |
329 return base::CreateDirectory(output_file_path); | |
330 | |
331 bool success = false; | |
332 { | |
333 FilePathWriterDelegate writer(output_file_path); | |
334 success = ExtractCurrentEntry(&writer); | |
335 } | |
336 | |
337 if (success && | |
338 current_entry_info()->last_modified() != base::Time::UnixEpoch()) { | |
339 base::TouchFile(output_file_path, | |
340 base::Time::Now(), | |
341 current_entry_info()->last_modified()); | |
342 } | |
343 | |
344 return success; | |
345 } | |
346 | 244 |
347 void ZipReader::ExtractCurrentEntryToFilePathAsync( | 245 void ZipReader::ExtractCurrentEntryToFilePathAsync( |
348 const base::FilePath& output_file_path, | 246 const base::FilePath& output_file_path, |
349 const SuccessCallback& success_callback, | 247 const SuccessCallback& success_callback, |
350 const FailureCallback& failure_callback, | 248 const FailureCallback& failure_callback, |
351 const ProgressCallback& progress_callback) { | 249 const ProgressCallback& progress_callback) { |
352 DCHECK(zip_file_); | 250 DCHECK(zip_file_); |
353 DCHECK(current_entry_info_.get()); | 251 DCHECK(current_entry_info_.get()); |
354 | 252 |
355 // If this is a directory, just create it and return. | 253 // If this is a directory, just create it and return. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 base::Bind(&ZipReader::ExtractChunk, | 289 base::Bind(&ZipReader::ExtractChunk, |
392 weak_ptr_factory_.GetWeakPtr(), | 290 weak_ptr_factory_.GetWeakPtr(), |
393 Passed(output_file.Pass()), | 291 Passed(output_file.Pass()), |
394 success_callback, | 292 success_callback, |
395 failure_callback, | 293 failure_callback, |
396 progress_callback, | 294 progress_callback, |
397 0 /* initial offset */)); | 295 0 /* initial offset */)); |
398 } | 296 } |
399 | 297 |
400 bool ZipReader::ExtractCurrentEntryIntoDirectory( | 298 bool ZipReader::ExtractCurrentEntryIntoDirectory( |
401 const base::FilePath& output_directory_path) const { | 299 const base::FilePath& output_directory_path) { |
402 DCHECK(current_entry_info_.get()); | 300 DCHECK(current_entry_info_.get()); |
403 | 301 |
404 base::FilePath output_file_path = output_directory_path.Append( | 302 base::FilePath output_file_path = output_directory_path.Append( |
405 current_entry_info()->file_path()); | 303 current_entry_info()->file_path()); |
406 return ExtractCurrentEntryToFilePath(output_file_path); | 304 return ExtractCurrentEntryToFilePath(output_file_path); |
407 } | 305 } |
408 | 306 |
409 bool ZipReader::ExtractCurrentEntryToFile(base::File* file) const { | 307 #if defined(OS_POSIX) |
| 308 bool ZipReader::ExtractCurrentEntryToFd(const int fd) { |
410 DCHECK(zip_file_); | 309 DCHECK(zip_file_); |
411 | 310 |
412 // If this is a directory, there's nothing to extract to the file, so return | 311 // If this is a directory, there's nothing to extract to the file descriptor, |
413 // false. | 312 // so return false. |
414 if (current_entry_info()->is_directory()) | 313 if (current_entry_info()->is_directory()) |
415 return false; | 314 return false; |
416 | 315 |
417 FileWriterDelegate writer(file); | 316 const int open_result = unzOpenCurrentFile(zip_file_); |
418 return ExtractCurrentEntry(&writer); | 317 if (open_result != UNZ_OK) |
| 318 return false; |
| 319 |
| 320 bool success = true; // This becomes false when something bad happens. |
| 321 while (true) { |
| 322 char buf[internal::kZipBufSize]; |
| 323 const int num_bytes_read = unzReadCurrentFile(zip_file_, buf, |
| 324 internal::kZipBufSize); |
| 325 if (num_bytes_read == 0) { |
| 326 // Reached the end of the file. |
| 327 break; |
| 328 } else if (num_bytes_read < 0) { |
| 329 // If num_bytes_read < 0, then it's a specific UNZ_* error code. |
| 330 success = false; |
| 331 break; |
| 332 } else if (num_bytes_read > 0) { |
| 333 // Some data is read. Write it to the output file descriptor. |
| 334 if (!base::WriteFileDescriptor(fd, buf, num_bytes_read)) { |
| 335 success = false; |
| 336 break; |
| 337 } |
| 338 } |
| 339 } |
| 340 |
| 341 unzCloseCurrentFile(zip_file_); |
| 342 return success; |
419 } | 343 } |
| 344 #endif // defined(OS_POSIX) |
420 | 345 |
421 bool ZipReader::ExtractCurrentEntryToString(size_t max_read_bytes, | 346 bool ZipReader::ExtractCurrentEntryToString( |
422 std::string* output) const { | 347 size_t max_read_bytes, |
| 348 std::string* output) const { |
423 DCHECK(output); | 349 DCHECK(output); |
424 DCHECK(zip_file_); | 350 DCHECK(zip_file_); |
425 DCHECK_NE(0U, max_read_bytes); | 351 DCHECK(max_read_bytes != 0); |
426 | 352 |
427 if (current_entry_info()->is_directory()) { | 353 if (current_entry_info()->is_directory()) { |
428 output->clear(); | 354 output->clear(); |
429 return true; | 355 return true; |
430 } | 356 } |
431 | 357 |
| 358 const int open_result = unzOpenCurrentFile(zip_file_); |
| 359 if (open_result != UNZ_OK) |
| 360 return false; |
| 361 |
432 // The original_size() is the best hint for the real size, so it saves | 362 // The original_size() is the best hint for the real size, so it saves |
433 // doing reallocations for the common case when the uncompressed size is | 363 // doing reallocations for the common case when the uncompressed size is |
434 // correct. However, we need to assume that the uncompressed size could be | 364 // correct. However, we need to assume that the uncompressed size could be |
435 // incorrect therefore this function needs to read as much data as possible. | 365 // incorrect therefore this function needs to read as much data as possible. |
436 std::string contents; | 366 std::string contents; |
437 contents.reserve(static_cast<size_t>(std::min( | 367 contents.reserve(static_cast<size_t>(std::min( |
438 static_cast<int64>(max_read_bytes), | 368 static_cast<int64>(max_read_bytes), |
439 current_entry_info()->original_size()))); | 369 current_entry_info()->original_size()))); |
440 | 370 |
441 StringWriterDelegate writer(max_read_bytes, &contents); | 371 bool success = true; // This becomes false when something bad happens. |
442 if (!ExtractCurrentEntry(&writer)) | 372 char buf[internal::kZipBufSize]; |
443 return false; | 373 while (true) { |
444 output->swap(contents); | 374 const int num_bytes_read = unzReadCurrentFile(zip_file_, buf, |
445 return true; | 375 internal::kZipBufSize); |
| 376 if (num_bytes_read == 0) { |
| 377 // Reached the end of the file. |
| 378 break; |
| 379 } else if (num_bytes_read < 0) { |
| 380 // If num_bytes_read < 0, then it's a specific UNZ_* error code. |
| 381 success = false; |
| 382 break; |
| 383 } else if (num_bytes_read > 0) { |
| 384 if (contents.size() + num_bytes_read > max_read_bytes) { |
| 385 success = false; |
| 386 break; |
| 387 } |
| 388 contents.append(buf, num_bytes_read); |
| 389 } |
| 390 } |
| 391 |
| 392 unzCloseCurrentFile(zip_file_); |
| 393 if (success) |
| 394 output->swap(contents); |
| 395 |
| 396 return success; |
446 } | 397 } |
447 | 398 |
448 bool ZipReader::OpenInternal() { | 399 bool ZipReader::OpenInternal() { |
449 DCHECK(zip_file_); | 400 DCHECK(zip_file_); |
450 | 401 |
451 unz_global_info zip_info = {}; // Zero-clear. | 402 unz_global_info zip_info = {}; // Zero-clear. |
452 if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) { | 403 if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) { |
453 return false; | 404 return false; |
454 } | 405 } |
455 num_entries_ = zip_info.number_entry; | 406 num_entries_ = zip_info.number_entry; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
503 weak_ptr_factory_.GetWeakPtr(), | 454 weak_ptr_factory_.GetWeakPtr(), |
504 Passed(output_file.Pass()), | 455 Passed(output_file.Pass()), |
505 success_callback, | 456 success_callback, |
506 failure_callback, | 457 failure_callback, |
507 progress_callback, | 458 progress_callback, |
508 current_progress)); | 459 current_progress)); |
509 | 460 |
510 } | 461 } |
511 } | 462 } |
512 | 463 |
513 // FileWriterDelegate ---------------------------------------------------------- | |
514 | |
515 FileWriterDelegate::FileWriterDelegate(base::File* file) | |
516 : file_(file), | |
517 file_length_(0) { | |
518 } | |
519 | |
520 FileWriterDelegate::~FileWriterDelegate() { | |
521 #if !defined(NDEBUG) | |
522 const bool success = | |
523 #endif | |
524 file_->SetLength(file_length_); | |
525 DPLOG_IF(ERROR, !success) << "Failed updating length of written file"; | |
526 } | |
527 | |
528 bool FileWriterDelegate::PrepareOutput() { | |
529 return file_->Seek(base::File::FROM_BEGIN, 0) >= 0; | |
530 } | |
531 | |
532 bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) { | |
533 int bytes_written = file_->WriteAtCurrentPos(data, num_bytes); | |
534 if (bytes_written > 0) | |
535 file_length_ += bytes_written; | |
536 return bytes_written == num_bytes; | |
537 } | |
538 | 464 |
539 } // namespace zip | 465 } // namespace zip |
OLD | NEW |