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

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

Issue 179963002: New Zip::ZipFromMemory API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressing comments Created 6 years, 9 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
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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.h" 5 #include "third_party/zlib/google/zip_internal.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
11 #include "base/time/time.h"
12 #include "third_party/zlib/google/zip_reader.h"
11 13
12 #if defined(USE_SYSTEM_MINIZIP) 14 #if defined(USE_SYSTEM_MINIZIP)
13 #include <minizip/ioapi.h> 15 #include <minizip/ioapi.h>
14 #include <minizip/unzip.h> 16 #include <minizip/unzip.h>
15 #include <minizip/zip.h> 17 #include <minizip/zip.h>
16 #else 18 #else
17 #include "third_party/zlib/contrib/minizip/unzip.h" 19 #include "third_party/zlib/contrib/minizip/unzip.h"
18 #include "third_party/zlib/contrib/minizip/zip.h" 20 #include "third_party/zlib/contrib/minizip/zip.h"
19 #if defined(OS_WIN) 21 #if defined(OS_WIN)
20 #include "third_party/zlib/contrib/minizip/iowin32.h" 22 #include "third_party/zlib/contrib/minizip/iowin32.h"
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 free(opaque); 225 free(opaque);
224 return 0; 226 return 0;
225 } 227 }
226 228
227 // Returns the last error happened when reading or writing data. This function 229 // Returns the last error happened when reading or writing data. This function
228 // always returns zero, which means there are not any errors. 230 // always returns zero, which means there are not any errors.
229 int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) { 231 int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
230 return 0; 232 return 0;
231 } 233 }
232 234
235 // Returns a zip_fileinfo struct with the time represented by |file_time|.
236 zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) {
237 base::Time::Exploded file_time_parts;
238 file_time.LocalExplode(&file_time_parts);
239
240 zip_fileinfo zip_info = {};
241 if (file_time_parts.year >= 1980) {
242 // This if check works around the handling of the year value in
243 // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate
244 // It assumes that dates below 1980 are in the double digit format.
245 // Hence the fail safe option is to leave the date unset. Some programs
246 // might show the unset date as 1980-0-0 which is invalid.
247 zip_info.tmz_date.tm_year = file_time_parts.year;
248 zip_info.tmz_date.tm_mon = file_time_parts.month - 1;
249 zip_info.tmz_date.tm_mday = file_time_parts.day_of_month;
250 zip_info.tmz_date.tm_hour = file_time_parts.hour;
251 zip_info.tmz_date.tm_min = file_time_parts.minute;
252 zip_info.tmz_date.tm_sec = file_time_parts.second;
253 }
254
255 return zip_info;
256 }
257
258 // Tells if there is at least one file in the |zip_path| archive which also
259 // exists in |contents|.
260 // Returns false if there was a problem opening the zip file.
261 // The result of whether there is an intersection between the paths in
262 // |zip_path| and |contents| is stored in |has_files|.
263 bool HasFileInZip(const base::FilePath& zip_path,
264 const zip::internal::ZipContents& contents,
265 bool* has_files) {
266 DCHECK(has_files);
267 *has_files = false;
268
269 zip::ZipReader reader;
270 if (!reader.Open(zip_path)) {
271 DLOG(ERROR) << "Can't open zip file '" << zip_path.value();
272 return false;
273 }
274
275 for (zip::internal::ZipContents::const_iterator itr = contents.begin();
276 itr != contents.end(); ++itr)
277 if (reader.LocateAndOpenEntry(itr->first)) {
278 *has_files = true;
279 return true;
280 }
281
282 return true;
283 }
284
285 // Validates the contents in a zip::ZipContents to be used by ZipFromMemory:
286 // Makes sure that all paths are relative and safe. Verifies that folders do not
287 // have data (such would perhaps be a bug when utilizing the API).
288 bool ValidateZipContents(const zip::internal::ZipContents& contents) {
289 for (zip::internal::ZipContents::const_iterator itr = contents.begin();
290 itr != contents.end(); ++itr) {
291 if (itr->first.empty() ||
292 itr->first.IsAbsolute() ||
293 itr->first.ReferencesParent()) {
294 DLOG(ERROR) << "Invalid file path '" << itr->first.value() << "'";
295 return false;
296 }
297 if (itr->first.EndsWithSeparator() &&
298 itr->second.size() != 0) {
299 // Can be NULL (remove) or 0-byte (add).
300 DLOG(ERROR) << "Folder must have empty contents '" <<
301 itr->first.value() << "'";
302 return false;
303 }
304 }
305 return true;
306 }
307
308 // Appends contents to the currently open zip entry in |zip_file|
309 bool AddMemContentsToZip(zipFile zip_file,
310 const base::FilePath& file_path,
311 const void* contents,
312 const size_t contents_length) {
313 if (contents_length != 0 &&
314 ZIP_OK != zipWriteInFileInZip(
315 zip_file, contents, contents_length)) {
316 DCHECK(contents);
317 DLOG(ERROR) << "Could not write data to zip for path "
318 << file_path.value();
319 return false;
320 }
321
322 return true;
323 }
324
325 // Creates a new file or folder entry in |zip_file| with |path| as the file
326 // path and |contents| as the file contents. If |path| ends with a separator
327 // it is considered to be a folder entry. Returns false in case of error.
328 bool AddEntryToZipFromMemory(zipFile zip_file,
329 const base::FilePath& path,
330 const void* contents,
331 const size_t contents_length) {
332 DCHECK(!path.IsAbsolute());
333 std::string str_path = path.AsUTF8Unsafe();
334 #if defined(OS_WIN)
335 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
336 #endif
337
338 zip_fileinfo file_info = TimeToZipFileInfo(base::Time::Now());
339 if (!zip::internal::ZipOpenNewFileInZipWrapper(
340 zip_file, str_path, &file_info))
341 return false;
342
343 bool success = true;
344 if (!path.EndsWithSeparator()) {
345 // Not a folder.
346 success = AddMemContentsToZip(zip_file, path, contents, contents_length);
347 }
348
349 if (ZIP_OK != zipCloseFileInZip(zip_file)) {
350 DLOG(ERROR) << "Could not close zip file entry " << str_path;
351 return false;
352 }
353
354 return success;
355 }
356
357 // Finds all files (path and contents) present in |zip_path| which are not
358 // present in |contents| and adds them to |output_zip_file|
359 // The function returns false in case of error handling the zip file.
360 bool CopyZipContentsNotInMap(const base::FilePath& zip_path,
361 const zip::internal::ZipContents& contents,
362 zipFile output_zip_file) {
363 // Arbitrary big size, but not that big, of the maximum amount of memory this
364 // function can use to load a file's contents into memory.
365 const int64 max_allowed_file_size = 128 * 1024 * 1024;
366
367 zip::ZipReader reader;
368 if (!reader.Open(zip_path)) {
369 DLOG(ERROR) << "Can't open zip file '" << zip_path.value();
370 return false;
371 }
372
373 while (reader.HasMore()) {
374 if (!reader.OpenCurrentEntryInZip()) {
375 DLOG(ERROR) << "Can't open entry in zip file '" << zip_path.value();
376 return false;
377 }
378
379 zip::ZipReader::EntryInfo* entry = reader.current_entry_info();
380 DCHECK(entry);
381 if (contents.find(entry->file_path()) == contents.end()) {
382 std::string mem;
383 if (!reader.ExtractCurrentEntryToString(
384 max_allowed_file_size, &mem)) {
385 DLOG(ERROR) << "Failed to read contents of "
386 << entry->file_path().value();
387 return false;
388 }
389 if (!AddEntryToZipFromMemory(
390 output_zip_file, entry->file_path(), mem.c_str(), mem.size()))
391 return false;
392 }
393 reader.AdvanceToNextEntry();
394 }
395 reader.Close();
396
397 return true;
398 }
399
233 } // namespace 400 } // namespace
234 401
235 namespace zip { 402 namespace zip {
236 namespace internal { 403 namespace internal {
237 404
238 unzFile OpenForUnzipping(const std::string& file_name_utf8) { 405 unzFile OpenForUnzipping(const std::string& file_name_utf8) {
239 zlib_filefunc_def* zip_func_ptrs = NULL; 406 zlib_filefunc_def* zip_func_ptrs = NULL;
240 #if defined(OS_WIN) 407 #if defined(OS_WIN)
241 zlib_filefunc_def zip_funcs; 408 zlib_filefunc_def zip_funcs;
242 fill_win32_filefunc(&zip_funcs); 409 fill_win32_filefunc(&zip_funcs);
(...skipping 16 matching lines...) Expand all
259 unzFile OpenHandleForUnzipping(HANDLE zip_handle) { 426 unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
260 zlib_filefunc_def zip_funcs; 427 zlib_filefunc_def zip_funcs;
261 fill_win32_filefunc(&zip_funcs); 428 fill_win32_filefunc(&zip_funcs);
262 zip_funcs.zopen_file = HandleOpenFileFunc; 429 zip_funcs.zopen_file = HandleOpenFileFunc;
263 zip_funcs.opaque = zip_handle; 430 zip_funcs.opaque = zip_handle;
264 return unzOpen2("fd", &zip_funcs); 431 return unzOpen2("fd", &zip_funcs);
265 } 432 }
266 #endif 433 #endif
267 434
268 // static 435 // static
269 unzFile PreprareMemoryForUnzipping(const std::string& data) { 436 unzFile PrepareMemoryForUnzipping(const std::string& data) {
270 if (data.empty()) 437 if (data.empty())
271 return NULL; 438 return NULL;
272 439
273 ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer))); 440 ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
274 if (!buffer) 441 if (!buffer)
275 return NULL; 442 return NULL;
276 buffer->data = data.data(); 443 buffer->data = data.data();
277 buffer->length = data.length(); 444 buffer->length = data.length();
278 buffer->offset = 0; 445 buffer->offset = 0;
279 446
(...skipping 25 matching lines...) Expand all
305 472
306 #if defined(OS_POSIX) 473 #if defined(OS_POSIX)
307 zipFile OpenFdForZipping(int zip_fd, int append_flag) { 474 zipFile OpenFdForZipping(int zip_fd, int append_flag) {
308 zlib_filefunc_def zip_funcs; 475 zlib_filefunc_def zip_funcs;
309 FillFdOpenFileFunc(&zip_funcs, zip_fd); 476 FillFdOpenFileFunc(&zip_funcs, zip_fd);
310 // Passing dummy "fd" filename to zlib. 477 // Passing dummy "fd" filename to zlib.
311 return zipOpen2("fd", append_flag, NULL, &zip_funcs); 478 return zipOpen2("fd", append_flag, NULL, &zip_funcs);
312 } 479 }
313 #endif 480 #endif
314 481
482 zip_fileinfo GetFileInfoForZipping(const base::FilePath& path) {
483 base::Time file_time;
484 base::File::Info file_info;
485 if (base::GetFileInfo(path, &file_info))
486 file_time = file_info.last_modified;
487 return TimeToZipFileInfo(file_time);
488 }
489
490 bool ZipOpenNewFileInZipWrapper(zipFile zip_file,
491 const std::string& str_path,
492 const zip_fileinfo* file_info) {
493 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
494 // Setting the Language encoding flag so the file is told to be in utf-8.
495 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
496
497 if (ZIP_OK != zipOpenNewFileInZip4(
498 zip_file, // file
499 str_path.c_str(), // filename
500 file_info, // zipfi
501 NULL, // extrafield_local,
502 0u, // size_extrafield_local
503 NULL, // extrafield_global
504 0u, // size_extrafield_global
505 NULL, // comment
506 Z_DEFLATED, // method
507 Z_DEFAULT_COMPRESSION, // level
508 0, // raw
509 -MAX_WBITS, // windowBits
510 DEF_MEM_LEVEL, // memLevel
511 Z_DEFAULT_STRATEGY, // strategy
512 NULL, // password
513 0, // crcForCrypting
514 0, // versionMadeBy
515 LANGUAGE_ENCODING_FLAG)) { // flagBase
516 DLOG(ERROR) << "Could not open zip file entry " << str_path;
517 return false;
518 }
519 return true;
520 }
521
522 bool ZipFromMemory(const base::FilePath& zip_path,
523 const ZipContents& contents,
524 bool append) {
525
526 // There is really no point in calling this with no input, and
527 // there are simpler ways to create a 0 byte file.
528 if (contents.size() == 0)
529 return true;
530
531 CHECK(!zip_path.empty());
532 // This check saves us having to worry with invalid input after.
533 CHECK(ValidateZipContents(contents));
534
535 int64 file_size = 0;
536 if (append &&
537 (!base::PathExists(zip_path) ||
538 (base::GetFileSize(zip_path, &file_size) && file_size < 1))) {
539 // If the size is 0, this might be a new temporary file.
540 append = false;
541 }
542
543 // The path to the zip path that will actually be used to write the new data.
544 base::FilePath zip_path_used = zip_path;
545
546 bool has_intersection = false;
547 if (append) {
548 // NOTE: zipOpenNewFileInZip4 is not clever enough to replace files,
549 // therefore this has to explicitly create a new file and reinsert the
550 // data we want without duplications, which is what the minizip headers
551 // tell to do.
552 if (!HasFileInZip(zip_path, contents, &has_intersection))
553 return false;
554
555 if (has_intersection) {
556 append = false;
557 if (!base::CreateTemporaryFile(&zip_path_used)) {
558 DLOG(WARNING) << "Couldn't create temporary file for "
559 << zip_path.value();
560 return false;
561 }
562 }
563 }
564
565 zipFile zip_file = internal::OpenForZipping(
566 zip_path_used.AsUTF8Unsafe(),
567 append ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE);
568 if (!zip_file) {
569 DLOG(WARNING) << "Couldn't create file " << zip_path_used.value();
570 return false;
571 }
572
573 bool success = true;
574
575 if (has_intersection) {
576 // There was an intersection. There are files in the old archive with
577 // same paths as found in |contents| therefore the old ones need to be
578 // replaced.
579 success = CopyZipContentsNotInMap(zip_path, contents, zip_file);
580 }
581
582 for (ZipContents::const_iterator itr = contents.begin();
583 itr != contents.end() && success;
584 ++itr) {
585 if (itr->second.data() != NULL) {
586 success = AddEntryToZipFromMemory(
587 zip_file, itr->first, itr->second.data(), itr->second.size());
588 }
589 }
590
591 if (ZIP_OK != zipClose(zip_file, NULL)) {
592 DLOG(ERROR) << "Error closing zip file " << zip_path_used.value();
593 success = false;
594 }
595
596 if (zip_path_used != zip_path) {
597 if (success) {
598 if (!base::Move(zip_path_used, zip_path)) {
599 base::DeleteFile(zip_path_used, false);
600 DLOG(ERROR) << "Error moving zip file " << zip_path_used.value();
601 return false;
602 }
603 } else {
604 base::DeleteFile(zip_path_used, false);
605 }
606 }
607
608 return success;
609 }
610
315 } // namespace internal 611 } // namespace internal
316 } // namespace zip 612 } // namespace zip
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698