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

Side by Side 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, 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) 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.h" 5 #include "third_party/zlib/google/zip.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/files/file_enumerator.h" 12 #include "base/files/file_enumerator.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/memory/ref_counted_memory.h"
14 #include "base/strings/string16.h" 15 #include "base/strings/string16.h"
15 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
16 #include "net/base/file_stream.h" 17 #include "net/base/file_stream.h"
17 #include "third_party/zlib/google/zip_internal.h" 18 #include "third_party/zlib/google/zip_internal.h"
18 #include "third_party/zlib/google/zip_reader.h" 19 #include "third_party/zlib/google/zip_reader.h"
19 20
20 #if defined(USE_SYSTEM_MINIZIP) 21 #if defined(USE_SYSTEM_MINIZIP)
21 #include <minizip/unzip.h> 22 #include <minizip/unzip.h>
22 #include <minizip/zip.h> 23 #include <minizip/zip.h>
23 #else 24 #else
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 DLOG(ERROR) << "Could not write data to zip for path " 78 DLOG(ERROR) << "Could not write data to zip for path "
78 << src_dir.value(); 79 << src_dir.value();
79 return false; 80 return false;
80 } 81 }
81 } 82 }
82 } while (num_bytes > 0); 83 } while (num_bytes > 0);
83 84
84 return true; 85 return true;
85 } 86 }
86 87
87 bool AddEntryToZip(zipFile zip_file, const base::FilePath& path, 88 bool AddMemContentsToZip(zipFile zip_file,
88 const base::FilePath& root_path) { 89 const base::FilePath& file_path,
89 base::FilePath relative_path; 90 base::RefCountedMemory* contents) {
90 bool result = root_path.AppendRelativePath(path, &relative_path); 91 DCHECK(contents);
91 DCHECK(result); 92 if (contents->size() != 0 &&
92 std::string str_path = relative_path.AsUTF8Unsafe(); 93 ZIP_OK != zipWriteInFileInZip(
93 #if defined(OS_WIN) 94 zip_file, contents->front(), contents->size())) {
94 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/"); 95 DLOG(ERROR) << "Could not write data to zip for path "
95 #endif 96 << file_path.value();
97 return false;
98 }
96 99
97 bool is_directory = base::DirectoryExists(path); 100 return true;
98 if (is_directory) 101 }
99 str_path += "/";
100 102
103 bool ZipOpenNewFileWrapper(zipFile zip_file,
104 const std::string& str_path,
105 const zip_fileinfo* file_info) {
101 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT 106 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
102 // Setting the Language encoding flag so the file is told to be in utf-8. 107 // Setting the Language encoding flag so the file is told to be in utf-8.
103 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11; 108 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
104 109
105 zip_fileinfo file_info = GetFileInfoForZipping(path);
106
107 if (ZIP_OK != zipOpenNewFileInZip4( 110 if (ZIP_OK != zipOpenNewFileInZip4(
108 zip_file, // file 111 zip_file, // file
109 str_path.c_str(), // filename 112 str_path.c_str(), // filename
110 &file_info, // zipfi 113 file_info, // zipfi
111 NULL, // extrafield_local, 114 NULL, // extrafield_local,
112 0u, // size_extrafield_local 115 0u, // size_extrafield_local
113 NULL, // extrafield_global 116 NULL, // extrafield_global
114 0u, // size_extrafield_global 117 0u, // size_extrafield_global
115 NULL, // comment 118 NULL, // comment
116 Z_DEFLATED, // method 119 Z_DEFLATED, // method
117 Z_DEFAULT_COMPRESSION, // level 120 Z_DEFAULT_COMPRESSION, // level
118 0, // raw 121 0, // raw
119 -MAX_WBITS, // windowBits 122 -MAX_WBITS, // windowBits
120 DEF_MEM_LEVEL, // memLevel 123 DEF_MEM_LEVEL, // memLevel
121 Z_DEFAULT_STRATEGY, // strategy 124 Z_DEFAULT_STRATEGY, // strategy
122 NULL, // password 125 NULL, // password
123 0, // crcForCrypting 126 0, // crcForCrypting
124 0, // versionMadeBy 127 0, // versionMadeBy
125 LANGUAGE_ENCODING_FLAG)) { // flagBase 128 LANGUAGE_ENCODING_FLAG)) { // flagBase
126 DLOG(ERROR) << "Could not open zip file entry " << str_path; 129 DLOG(ERROR) << "Could not open zip file entry " << str_path;
127 return false; 130 return false;
128 } 131 }
132 return true;
133 }
134
135 bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
136 const base::FilePath& root_path) {
137 base::FilePath relative_path;
138 bool result = root_path.AppendRelativePath(path, &relative_path);
139 DCHECK(result);
140 std::string str_path = relative_path.AsUTF8Unsafe();
141 #if defined(OS_WIN)
142 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
143 #endif
144
145 bool is_directory = base::DirectoryExists(path);
146 if (is_directory)
147 str_path += "/";
148
149 zip_fileinfo file_info = GetFileInfoForZipping(path);
150 if (!ZipOpenNewFileWrapper(zip_file, str_path, &file_info))
151 return false;
129 152
130 bool success = true; 153 bool success = true;
131 if (!is_directory) { 154 if (!is_directory) {
132 success = AddFileToZip(zip_file, path); 155 success = AddFileToZip(zip_file, path);
133 } 156 }
134 157
135 if (ZIP_OK != zipCloseFileInZip(zip_file)) { 158 if (ZIP_OK != zipCloseFileInZip(zip_file)) {
136 DLOG(ERROR) << "Could not close zip file entry " << str_path; 159 DLOG(ERROR) << "Could not close zip file entry " << str_path;
137 return false; 160 return false;
138 } 161 }
139 162
140 return success; 163 return success;
141 } 164 }
142 165
143 bool ExcludeNoFilesFilter(const base::FilePath& file_path) { 166 bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
144 return true; 167 return true;
145 } 168 }
146 169
147 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) { 170 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
148 return file_path.BaseName().value()[0] != '.'; 171 return file_path.BaseName().value()[0] != '.';
149 } 172 }
150 173
174 // Tells if there is at least one file in the |zip_path| archive which also
175 // exists in |contents|.
176 // Returns false if there was a problem opening the zip file.
177 // The result of whether there is an intersection between the paths in
178 // |zip_path| and |contents| is stored in |has_files|.
179 bool HasFileInZip(const base::FilePath& zip_path,
180 const zip::ZipContents& contents,
181 bool* has_files) {
182 DCHECK(has_files);
183 *has_files = false;
184
185 zip::ZipReader reader;
186 if (!reader.Open(zip_path)) {
187 DLOG(ERROR) << "Can't open zip file '" << zip_path.value();
188 return false;
189 }
190
191 for (zip::ZipContents::const_iterator itr = contents.begin();
192 itr != contents.end(); ++itr)
193 if (reader.LocateAndOpenEntry(itr->first)) {
194 *has_files = true;
195 return true;
196 }
197
198 return true;
199 }
200
201 // Validates the contents in a zip::ZipContents to be used by ZipFromMemory:
202 // Makes sure that all paths are relative and safe. Verifies that folders do not
203 // have data (such would perhaps be a bug when utilizing the API).
204 bool ValidateZipContents(const zip::ZipContents& contents) {
205 for (zip::ZipContents::const_iterator itr = contents.begin();
206 itr != contents.end(); ++itr) {
207 if (itr->first.empty() ||
208 itr->first.IsAbsolute() ||
209 itr->first.ReferencesParent()) {
210 DLOG(ERROR) << "Invalid file path '" << itr->first.value() << "'";
211 return false;
212 }
213 if (itr->first.EndsWithSeparator() &&
214 itr->second.get() &&
215 itr->second.get()->size() != 0) {
216 // Can be NULL (remove) or 0-byte (add).
217 DLOG(ERROR) << "Folder must have empty contents '" <<
218 itr->first.value() << "'";
219 return false;
220 }
221 }
222 return true;
223 }
224
225 // Creates a new file or folder entry in |zip_file| with |path| as the file
226 // path and |contents| as the file contents. If |path| ends with a separator
227 // it is considered to be a folder entry. Returns false in case of error.
228 bool AddEntryToZipFromMemory(zipFile zip_file,
229 const base::FilePath& path,
230 base::RefCountedMemory* contents) {
231 DCHECK(!path.IsAbsolute());
232 std::string str_path = path.AsUTF8Unsafe();
233 #if defined(OS_WIN)
234 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
235 #endif
236
237 zip_fileinfo file_info = TimeToZipFileInfo(base::Time::Now());
238 if (!ZipOpenNewFileWrapper(zip_file, str_path, &file_info))
239 return false;
240
241 bool success = true;
242 if (!path.EndsWithSeparator()) {
243 // Not a folder.
244 DCHECK(contents);
245 success = AddMemContentsToZip(zip_file, path, contents);
246 }
247
248 if (ZIP_OK != zipCloseFileInZip(zip_file)) {
249 DLOG(ERROR) << "Could not close zip file entry " << str_path;
250 return false;
251 }
252
253 return success;
254 }
255
256 // Finds all files (path and contents) present in |zip_path| which are not
257 // present in |contents| and adds them to |output_zip_file|
258 // The function returns false in case of error handling the zip file.
259 bool CopyZipContentsNotInMap(const base::FilePath& zip_path,
260 const zip::ZipContents& contents,
261 zipFile output_zip_file) {
262 // Arbitrary big size, but not that big, of the maximum amount of memory this
263 // function can use to load a file's contents into memory.
264 const int64 max_allowed_file_size = 128 * 1024 * 1024;
265
266 zip::ZipContents other_contents;
267
268 zip::ZipReader reader;
269 if (!reader.Open(zip_path)) {
270 DLOG(ERROR) << "Can't open zip file '" << zip_path.value();
271 return false;
272 }
273
274 while (reader.HasMore()) {
275 if (!reader.OpenCurrentEntryInZip()) {
276 DLOG(ERROR) << "Can't open entry in zip file '" << zip_path.value();
277 return false;
278 }
279
280 zip::ZipReader::EntryInfo* entry = reader.current_entry_info();
281 DCHECK(entry);
282 if (contents.find(entry->file_path()) == contents.end()) {
283 scoped_refptr<base::RefCountedMemory> mem;
284 if (!reader.ExtractCurrentEntryToRefCountedMemory(
285 max_allowed_file_size, &mem)) {
286 DLOG(ERROR) << "Failed to read contents of "
287 << entry->file_path().value();
288 return false;
289 }
290 if (!AddEntryToZipFromMemory(
291 output_zip_file, entry->file_path(), mem.get()))
292 return false;
293 }
294 reader.AdvanceToNextEntry();
295 }
296 reader.Close();
297
298 return true;
299 }
300
151 } // namespace 301 } // namespace
152 302
153 namespace zip { 303 namespace zip {
154 304
155 bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) { 305 bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
156 ZipReader reader; 306 ZipReader reader;
157 if (!reader.Open(src_file)) { 307 if (!reader.Open(src_file)) {
158 DLOG(WARNING) << "Failed to open " << src_file.value(); 308 DLOG(WARNING) << "Failed to open " << src_file.value();
159 return false; 309 return false;
160 } 310 }
(...skipping 29 matching lines...) Expand all
190 APPEND_STATUS_CREATE); 340 APPEND_STATUS_CREATE);
191 341
192 if (!zip_file) { 342 if (!zip_file) {
193 DLOG(WARNING) << "couldn't create file " << dest_file.value(); 343 DLOG(WARNING) << "couldn't create file " << dest_file.value();
194 return false; 344 return false;
195 } 345 }
196 346
197 bool success = true; 347 bool success = true;
198 base::FileEnumerator file_enumerator(src_dir, true /* recursive */, 348 base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
199 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); 349 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
200 for (base::FilePath path = file_enumerator.Next(); !path.value().empty(); 350 for (base::FilePath path = file_enumerator.Next();
351 !path.value().empty() && success;
201 path = file_enumerator.Next()) { 352 path = file_enumerator.Next()) {
202 if (!filter_cb.Run(path)) { 353 if (!filter_cb.Run(path)) {
203 continue; 354 continue;
204 } 355 }
205 356
206 if (!AddEntryToZip(zip_file, path, src_dir)) { 357 if (!AddEntryToZip(zip_file, path, src_dir)) {
207 success = false; 358 success = false;
208 return false;
209 } 359 }
210 } 360 }
211 361
212 if (ZIP_OK != zipClose(zip_file, NULL)) { 362 if (ZIP_OK != zipClose(zip_file, NULL)) {
213 DLOG(ERROR) << "Error closing zip file " << dest_file.value(); 363 DLOG(ERROR) << "Error closing zip file " << dest_file.value();
214 return false; 364 return false;
215 } 365 }
216 366
217 return success; 367 return success;
218 } 368 }
(...skipping 17 matching lines...) Expand all
236 zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE); 386 zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE);
237 387
238 if (!zip_file) { 388 if (!zip_file) {
239 DLOG(ERROR) << "couldn't create file for fd " << dest_fd; 389 DLOG(ERROR) << "couldn't create file for fd " << dest_fd;
240 return false; 390 return false;
241 } 391 }
242 392
243 bool success = true; 393 bool success = true;
244 for (std::vector<base::FilePath>::const_iterator iter = 394 for (std::vector<base::FilePath>::const_iterator iter =
245 src_relative_paths.begin(); 395 src_relative_paths.begin();
246 iter != src_relative_paths.end(); ++iter) { 396 iter != src_relative_paths.end() && success; ++iter) {
247 const base::FilePath& path = src_dir.Append(*iter); 397 const base::FilePath& path = src_dir.Append(*iter);
248 if (!AddEntryToZip(zip_file, path, src_dir)) { 398 if (!AddEntryToZip(zip_file, path, src_dir)) {
249 // TODO(hshi): clean up the partial zip file when error occurs. 399 // TODO(hshi): clean up the partial zip file when error occurs.
250 success = false; 400 success = false;
251 break; 401 break;
252 } 402 }
253 } 403 }
254 404
255 if (ZIP_OK != zipClose(zip_file, NULL)) { 405 if (ZIP_OK != zipClose(zip_file, NULL)) {
256 DLOG(ERROR) << "Error closing zip file for fd " << dest_fd; 406 DLOG(ERROR) << "Error closing zip file for fd " << dest_fd;
257 success = false; 407 success = false;
258 } 408 }
259 409
260 return success; 410 return success;
261 } 411 }
262 #endif // defined(OS_POSIX) 412 #endif // defined(OS_POSIX)
263 413
414 bool ZipFromMemory(const base::FilePath& zip_path,
415 const ZipContents& contents,
416 bool append) {
417 DCHECK(!zip_path.empty());
418
419 // There is really no point in calling this with no input, and
420 // there are simpler ways to create a 0 byte file.
421 if (contents.size() == 0)
422 return true;
423
424 // This check saves us having to worry with invalid input after.
425 if (!ValidateZipContents(contents))
426 return false;
427
428 int64 file_size = 0;
429 if (append &&
430 (!base::PathExists(zip_path) ||
431 (base::GetFileSize(zip_path, &file_size) && file_size < 1))) {
432 // If the size is 0, this might be a new temporary file.
433 append = false;
434 }
435
436 // The path to the zip path that will actually be used to write the new data.
437 base::FilePath zip_path_used = zip_path;
438
439 bool has_intersection = false;
440 if (append) {
441 // NOTE: zipOpenNewFileInZip4 is not clever enough to replace files,
442 // therefore this has to explicitly create a new file and reinsert the
443 // data we want without duplications, which is what the minizip headers
444 // tell to do.
445 if (!HasFileInZip(zip_path, contents, &has_intersection))
446 return false;
447
448 if (has_intersection) {
449 append = false;
450 if (!base::CreateTemporaryFile(&zip_path_used)) {
451 DLOG(WARNING) << "Couldn't create temporary file for "
452 << zip_path.value();
453 return false;
454 }
455 }
456 }
457
458 zipFile zip_file = internal::OpenForZipping(
459 zip_path_used.AsUTF8Unsafe(),
460 append ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE);
461 if (!zip_file) {
462 DLOG(WARNING) << "Couldn't create file " << zip_path_used.value();
463 return false;
464 }
465
466 bool success = true;
467
468 if (has_intersection) {
469 // There was an intersection. There are files in the old archive with
470 // same paths as found in |contents| therefore the old ones need to be
471 // replaced.
472 success = CopyZipContentsNotInMap(zip_path, contents, zip_file);
473 }
474
475 for (ZipContents::const_iterator itr = contents.begin();
476 itr != contents.end() && success;
477 ++itr) {
478 if (itr->second.get()) {
479 success = AddEntryToZipFromMemory(
480 zip_file, itr->first, itr->second.get());
481 }
482 }
483
484 if (ZIP_OK != zipClose(zip_file, NULL)) {
485 DLOG(ERROR) << "Error closing zip file " << zip_path_used.value();
486 success = false;
487 }
488
489 if (zip_path_used != zip_path) {
490 if (success) {
491 if (!base::Move(zip_path_used, zip_path)) {
492 base::DeleteFile(zip_path_used, false);
493 DLOG(ERROR) << "Error moving zip file " << zip_path_used.value();
494 return false;
495 }
496 } else {
497 base::DeleteFile(zip_path_used, false);
498 }
499 }
500
501 return success;
502 }
503
264 } // namespace zip 504 } // namespace zip
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698