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

Unified Diff: third_party/zlib/google/zip.cc

Issue 179963002: New Zip::ZipFromMemory API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixup! New Zip::ZipFromMemory API. Created 6 years, 10 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 side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698