| Index: third_party/zlib/google/zip.cc
|
| diff --git a/third_party/zlib/google/zip.cc b/third_party/zlib/google/zip.cc
|
| index 52fc6948bb6aa38a68c5dec74284786d3faca707..a6661272938b41e75578b8eecd16e3075c2b5178 100644
|
| --- a/third_party/zlib/google/zip.cc
|
| +++ b/third_party/zlib/google/zip.cc
|
| @@ -11,6 +11,7 @@
|
| #include "base/file_util.h"
|
| #include "base/files/file_enumerator.h"
|
| #include "base/logging.h"
|
| +#include "base/memory/ref_counted_memory.h"
|
| #include "base/strings/string16.h"
|
| #include "base/strings/string_util.h"
|
| #include "net/base/file_stream.h"
|
| @@ -84,30 +85,32 @@ bool AddFileToZip(zipFile zip_file, const base::FilePath& src_dir) {
|
| return true;
|
| }
|
|
|
| -bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
|
| - const base::FilePath& root_path) {
|
| - base::FilePath relative_path;
|
| - bool result = root_path.AppendRelativePath(path, &relative_path);
|
| - DCHECK(result);
|
| - std::string str_path = relative_path.AsUTF8Unsafe();
|
| -#if defined(OS_WIN)
|
| - ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
|
| -#endif
|
| +bool AddMemContentsToZip(zipFile zip_file,
|
| + const base::FilePath& file_path,
|
| + base::RefCountedMemory* contents) {
|
| + DCHECK(contents);
|
| + if (contents->size() != 0 &&
|
| + ZIP_OK != zipWriteInFileInZip(
|
| + zip_file, contents->front(), contents->size())) {
|
| + DLOG(ERROR) << "Could not write data to zip for path "
|
| + << file_path.value();
|
| + return false;
|
| + }
|
|
|
| - bool is_directory = base::DirectoryExists(path);
|
| - if (is_directory)
|
| - str_path += "/";
|
| + return true;
|
| +}
|
|
|
| +bool ZipOpenNewFileWrapper(zipFile zip_file,
|
| + const std::string& str_path,
|
| + const zip_fileinfo* file_info) {
|
| // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
| // Setting the Language encoding flag so the file is told to be in utf-8.
|
| const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
|
|
|
| - zip_fileinfo file_info = GetFileInfoForZipping(path);
|
| -
|
| if (ZIP_OK != zipOpenNewFileInZip4(
|
| zip_file, // file
|
| str_path.c_str(), // filename
|
| - &file_info, // zipfi
|
| + file_info, // zipfi
|
| NULL, // extrafield_local,
|
| 0u, // size_extrafield_local
|
| NULL, // extrafield_global
|
| @@ -126,6 +129,26 @@ bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
|
| DLOG(ERROR) << "Could not open zip file entry " << str_path;
|
| return false;
|
| }
|
| + return true;
|
| +}
|
| +
|
| +bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
|
| + const base::FilePath& root_path) {
|
| + base::FilePath relative_path;
|
| + bool result = root_path.AppendRelativePath(path, &relative_path);
|
| + DCHECK(result);
|
| + std::string str_path = relative_path.AsUTF8Unsafe();
|
| +#if defined(OS_WIN)
|
| + ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
|
| +#endif
|
| +
|
| + bool is_directory = base::DirectoryExists(path);
|
| + if (is_directory)
|
| + str_path += "/";
|
| +
|
| + zip_fileinfo file_info = GetFileInfoForZipping(path);
|
| + if (!ZipOpenNewFileWrapper(zip_file, str_path, &file_info))
|
| + return false;
|
|
|
| bool success = true;
|
| if (!is_directory) {
|
| @@ -148,6 +171,133 @@ bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
|
| return file_path.BaseName().value()[0] != '.';
|
| }
|
|
|
| +// Tells if there is at least one file in the |zip_path| archive which also
|
| +// exists in |contents|.
|
| +// Returns false if there was a problem opening the zip file.
|
| +// The result of whether there is an intersection between the paths in
|
| +// |zip_path| and |contents| is stored in |has_files|.
|
| +bool HasFileInZip(const base::FilePath& zip_path,
|
| + const zip::ZipContents& contents,
|
| + bool* has_files) {
|
| + DCHECK(has_files);
|
| + *has_files = false;
|
| +
|
| + zip::ZipReader reader;
|
| + if (!reader.Open(zip_path)) {
|
| + DLOG(ERROR) << "Can't open zip file '" << zip_path.value();
|
| + return false;
|
| + }
|
| +
|
| + for (zip::ZipContents::const_iterator itr = contents.begin();
|
| + itr != contents.end(); ++itr)
|
| + if (reader.LocateAndOpenEntry(itr->first)) {
|
| + *has_files = true;
|
| + return true;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// Validates the contents in a zip::ZipContents to be used by ZipFromMemory:
|
| +// Makes sure that all paths are relative and safe. Verifies that folders do not
|
| +// have data (such would perhaps be a bug when utilizing the API).
|
| +bool ValidateZipContents(const zip::ZipContents& contents) {
|
| + for (zip::ZipContents::const_iterator itr = contents.begin();
|
| + itr != contents.end(); ++itr) {
|
| + if (itr->first.empty() ||
|
| + itr->first.IsAbsolute() ||
|
| + itr->first.ReferencesParent()) {
|
| + DLOG(ERROR) << "Invalid file path '" << itr->first.value() << "'";
|
| + return false;
|
| + }
|
| + if (itr->first.EndsWithSeparator() &&
|
| + itr->second.get() &&
|
| + itr->second.get()->size() != 0) {
|
| + // Can be NULL (remove) or 0-byte (add).
|
| + DLOG(ERROR) << "Folder must have empty contents '" <<
|
| + itr->first.value() << "'";
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// Creates a new file or folder entry in |zip_file| with |path| as the file
|
| +// path and |contents| as the file contents. If |path| ends with a separator
|
| +// it is considered to be a folder entry. Returns false in case of error.
|
| +bool AddEntryToZipFromMemory(zipFile zip_file,
|
| + const base::FilePath& path,
|
| + base::RefCountedMemory* contents) {
|
| + DCHECK(!path.IsAbsolute());
|
| + std::string str_path = path.AsUTF8Unsafe();
|
| +#if defined(OS_WIN)
|
| + ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
|
| +#endif
|
| +
|
| + zip_fileinfo file_info = TimeToZipFileInfo(base::Time::Now());
|
| + if (!ZipOpenNewFileWrapper(zip_file, str_path, &file_info))
|
| + return false;
|
| +
|
| + bool success = true;
|
| + if (!path.EndsWithSeparator()) {
|
| + // Not a folder.
|
| + DCHECK(contents);
|
| + success = AddMemContentsToZip(zip_file, path, contents);
|
| + }
|
| +
|
| + if (ZIP_OK != zipCloseFileInZip(zip_file)) {
|
| + DLOG(ERROR) << "Could not close zip file entry " << str_path;
|
| + return false;
|
| + }
|
| +
|
| + return success;
|
| +}
|
| +
|
| +// Finds all files (path and contents) present in |zip_path| which are not
|
| +// present in |contents| and adds them to |output_zip_file|
|
| +// The function returns false in case of error handling the zip file.
|
| +bool CopyZipContentsNotInMap(const base::FilePath& zip_path,
|
| + const zip::ZipContents& contents,
|
| + zipFile output_zip_file) {
|
| + // Arbitrary big size, but not that big, of the maximum amount of memory this
|
| + // function can use to load a file's contents into memory.
|
| + const int64 max_allowed_file_size = 128 * 1024 * 1024;
|
| +
|
| + zip::ZipContents other_contents;
|
| +
|
| + zip::ZipReader reader;
|
| + if (!reader.Open(zip_path)) {
|
| + DLOG(ERROR) << "Can't open zip file '" << zip_path.value();
|
| + return false;
|
| + }
|
| +
|
| + while (reader.HasMore()) {
|
| + if (!reader.OpenCurrentEntryInZip()) {
|
| + DLOG(ERROR) << "Can't open entry in zip file '" << zip_path.value();
|
| + return false;
|
| + }
|
| +
|
| + zip::ZipReader::EntryInfo* entry = reader.current_entry_info();
|
| + DCHECK(entry);
|
| + if (contents.find(entry->file_path()) == contents.end()) {
|
| + scoped_refptr<base::RefCountedMemory> mem;
|
| + if (!reader.ExtractCurrentEntryToRefCountedMemory(
|
| + max_allowed_file_size, &mem)) {
|
| + DLOG(ERROR) << "Failed to read contents of "
|
| + << entry->file_path().value();
|
| + return false;
|
| + }
|
| + if (!AddEntryToZipFromMemory(
|
| + output_zip_file, entry->file_path(), mem.get()))
|
| + return false;
|
| + }
|
| + reader.AdvanceToNextEntry();
|
| + }
|
| + reader.Close();
|
| +
|
| + return true;
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace zip {
|
| @@ -197,7 +347,8 @@ bool ZipWithFilterCallback(const base::FilePath& src_dir,
|
| bool success = true;
|
| base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
|
| base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
|
| - for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
|
| + for (base::FilePath path = file_enumerator.Next();
|
| + !path.value().empty() && success;
|
| path = file_enumerator.Next()) {
|
| if (!filter_cb.Run(path)) {
|
| continue;
|
| @@ -205,7 +356,6 @@ bool ZipWithFilterCallback(const base::FilePath& src_dir,
|
|
|
| if (!AddEntryToZip(zip_file, path, src_dir)) {
|
| success = false;
|
| - return false;
|
| }
|
| }
|
|
|
| @@ -243,7 +393,7 @@ bool ZipFiles(const base::FilePath& src_dir,
|
| bool success = true;
|
| for (std::vector<base::FilePath>::const_iterator iter =
|
| src_relative_paths.begin();
|
| - iter != src_relative_paths.end(); ++iter) {
|
| + iter != src_relative_paths.end() && success; ++iter) {
|
| const base::FilePath& path = src_dir.Append(*iter);
|
| if (!AddEntryToZip(zip_file, path, src_dir)) {
|
| // TODO(hshi): clean up the partial zip file when error occurs.
|
| @@ -261,4 +411,94 @@ bool ZipFiles(const base::FilePath& src_dir,
|
| }
|
| #endif // defined(OS_POSIX)
|
|
|
| +bool ZipFromMemory(const base::FilePath& zip_path,
|
| + const ZipContents& contents,
|
| + bool append) {
|
| + DCHECK(!zip_path.empty());
|
| +
|
| + // There is really no point in calling this with no input, and
|
| + // there are simpler ways to create a 0 byte file.
|
| + if (contents.size() == 0)
|
| + return true;
|
| +
|
| + // This check saves us having to worry with invalid input after.
|
| + if (!ValidateZipContents(contents))
|
| + return false;
|
| +
|
| + int64 file_size = 0;
|
| + if (append &&
|
| + (!base::PathExists(zip_path) ||
|
| + (base::GetFileSize(zip_path, &file_size) && file_size < 1))) {
|
| + // If the size is 0, this might be a new temporary file.
|
| + append = false;
|
| + }
|
| +
|
| + // The path to the zip path that will actually be used to write the new data.
|
| + base::FilePath zip_path_used = zip_path;
|
| +
|
| + bool has_intersection = false;
|
| + if (append) {
|
| + // NOTE: zipOpenNewFileInZip4 is not clever enough to replace files,
|
| + // therefore this has to explicitly create a new file and reinsert the
|
| + // data we want without duplications, which is what the minizip headers
|
| + // tell to do.
|
| + if (!HasFileInZip(zip_path, contents, &has_intersection))
|
| + return false;
|
| +
|
| + if (has_intersection) {
|
| + append = false;
|
| + if (!base::CreateTemporaryFile(&zip_path_used)) {
|
| + DLOG(WARNING) << "Couldn't create temporary file for "
|
| + << zip_path.value();
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| +
|
| + zipFile zip_file = internal::OpenForZipping(
|
| + zip_path_used.AsUTF8Unsafe(),
|
| + append ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE);
|
| + if (!zip_file) {
|
| + DLOG(WARNING) << "Couldn't create file " << zip_path_used.value();
|
| + return false;
|
| + }
|
| +
|
| + bool success = true;
|
| +
|
| + if (has_intersection) {
|
| + // There was an intersection. There are files in the old archive with
|
| + // same paths as found in |contents| therefore the old ones need to be
|
| + // replaced.
|
| + success = CopyZipContentsNotInMap(zip_path, contents, zip_file);
|
| + }
|
| +
|
| + for (ZipContents::const_iterator itr = contents.begin();
|
| + itr != contents.end() && success;
|
| + ++itr) {
|
| + if (itr->second.get()) {
|
| + success = AddEntryToZipFromMemory(
|
| + zip_file, itr->first, itr->second.get());
|
| + }
|
| + }
|
| +
|
| + if (ZIP_OK != zipClose(zip_file, NULL)) {
|
| + DLOG(ERROR) << "Error closing zip file " << zip_path_used.value();
|
| + success = false;
|
| + }
|
| +
|
| + if (zip_path_used != zip_path) {
|
| + if (success) {
|
| + if (!base::Move(zip_path_used, zip_path)) {
|
| + base::DeleteFile(zip_path_used, false);
|
| + DLOG(ERROR) << "Error moving zip file " << zip_path_used.value();
|
| + return false;
|
| + }
|
| + } else {
|
| + base::DeleteFile(zip_path_used, false);
|
| + }
|
| + }
|
| +
|
| + return success;
|
| +}
|
| +
|
| } // namespace zip
|
|
|