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 3 // found in the LICENSE file. 4 5 #include "third_party/zlib/google/zip.h" 6 7 #include "base/bind.h" 8 #include "base/file_util.h" 9 #include "base/files/file_enumerator.h" 10 #include "base/logging.h" 11 #include "base/strings/string16.h" 12 #include "base/strings/string_util.h" 13 #include "net/base/file_stream.h" 14 #include "third_party/zlib/google/zip_internal.h" 15 #include "third_party/zlib/google/zip_reader.h" 16 17 #if defined(USE_SYSTEM_MINIZIP) 18 #include <minizip/unzip.h> 19 #include <minizip/zip.h> 20 #else 21 #include "third_party/zlib/contrib/minizip/unzip.h" 22 #include "third_party/zlib/contrib/minizip/zip.h" 23 #endif 24 25 namespace { 26 27 bool AddFileToZip(zipFile zip_file, const base::FilePath& src_dir) { 28 net::FileStream stream(NULL); 29 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; 30 if (stream.OpenSync(src_dir, flags) != 0) { 31 DLOG(ERROR) << "Could not open stream for path " 32 << src_dir.value(); 33 return false; 34 } 35 36 int num_bytes; 37 char buf[zip::internal::kZipBufSize]; 38 do { 39 num_bytes = stream.ReadSync(buf, zip::internal::kZipBufSize); 40 if (num_bytes > 0) { 41 if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) { 42 DLOG(ERROR) << "Could not write data to zip for path " 43 << src_dir.value(); 44 return false; 45 } 46 } 47 } while (num_bytes > 0); 48 49 return true; 50 } 51 52 bool AddEntryToZip(zipFile zip_file, const base::FilePath& path, 53 const base::FilePath& root_path) { 54 std::string str_path = 55 path.AsUTF8Unsafe().substr(root_path.AsUTF8Unsafe().length() + 1); 56 #if defined(OS_WIN) 57 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/"); 58 #endif 59 60 bool is_directory = base::DirectoryExists(path); 61 if (is_directory) 62 str_path += "/"; 63 64 if (ZIP_OK != zipOpenNewFileInZip( 65 zip_file, str_path.c_str(), 66 NULL, NULL, 0u, NULL, 0u, NULL, // file info, extrafield local, length, 67 // extrafield global, length, comment 68 Z_DEFLATED, Z_DEFAULT_COMPRESSION)) { 69 DLOG(ERROR) << "Could not open zip file entry " << str_path; 70 return false; 71 } 72 73 bool success = true; 74 if (!is_directory) { 75 success = AddFileToZip(zip_file, path); 76 } 77 78 if (ZIP_OK != zipCloseFileInZip(zip_file)) { 79 DLOG(ERROR) << "Could not close zip file entry " << str_path; 80 return false; 81 } 82 83 return success; 84 } 85 86 bool ExcludeNoFilesFilter(const base::FilePath& file_path) { 87 return true; 88 } 89 90 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) { 91 return file_path.BaseName().value()[0] != '.'; 92 } 93 94 } // namespace 95 96 namespace zip { 97 98 bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) { 99 ZipReader reader; 100 if (!reader.Open(src_file)) { 101 DLOG(WARNING) << "Failed to open " << src_file.value(); 102 return false; 103 } 104 while (reader.HasMore()) { 105 if (!reader.OpenCurrentEntryInZip()) { 106 DLOG(WARNING) << "Failed to open the current file in zip"; 107 return false; 108 } 109 if (reader.current_entry_info()->is_unsafe()) { 110 DLOG(WARNING) << "Found an unsafe file in zip " 111 << reader.current_entry_info()->file_path().value(); 112 return false; 113 } 114 if (!reader.ExtractCurrentEntryIntoDirectory(dest_dir)) { 115 DLOG(WARNING) << "Failed to extract " 116 << reader.current_entry_info()->file_path().value(); 117 return false; 118 } 119 if (!reader.AdvanceToNextEntry()) { 120 DLOG(WARNING) << "Failed to advance to the next file"; 121 return false; 122 } 123 } 124 return true; 125 } 126 127 bool ZipWithFilterCallback(const base::FilePath& src_dir, 128 const base::FilePath& dest_file, 129 const FilterCallback& filter_cb) { 130 DCHECK(base::DirectoryExists(src_dir)); 131 132 zipFile zip_file = internal::OpenForZipping(dest_file.AsUTF8Unsafe(), 133 APPEND_STATUS_CREATE); 134 135 if (!zip_file) { 136 DLOG(WARNING) << "couldn't create file " << dest_file.value(); 137 return false; 138 } 139 140 bool success = true; 141 base::FileEnumerator file_enumerator(src_dir, true /* recursive */, 142 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); 143 for (base::FilePath path = file_enumerator.Next(); !path.value().empty(); 144 path = file_enumerator.Next()) { 145 if (!filter_cb.Run(path)) { 146 continue; 147 } 148 149 if (!AddEntryToZip(zip_file, path, src_dir)) { 150 success = false; 151 return false; 152 } 153 } 154 155 if (ZIP_OK != zipClose(zip_file, NULL)) { 156 DLOG(ERROR) << "Error closing zip file " << dest_file.value(); 157 return false; 158 } 159 160 return success; 161 } 162 163 bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file, 164 bool include_hidden_files) { 165 if (include_hidden_files) { 166 return ZipWithFilterCallback( 167 src_dir, dest_file, base::Bind(&ExcludeNoFilesFilter)); 168 } else { 169 return ZipWithFilterCallback( 170 src_dir, dest_file, base::Bind(&ExcludeHiddenFilesFilter)); 171 } 172 } 173 174 #if defined(OS_POSIX) 175 bool ZipFiles(const base::FilePath& src_dir, 176 const std::vector<base::FilePath>& src_relative_paths, 177 int dest_fd) { 178 DCHECK(base::DirectoryExists(src_dir)); 179 zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE); 180 181 if (!zip_file) { 182 DLOG(ERROR) << "couldn't create file for fd " << dest_fd; 183 return false; 184 } 185 186 bool success = true; 187 for (std::vector<base::FilePath>::const_iterator iter = 188 src_relative_paths.begin(); 189 iter != src_relative_paths.end(); ++iter) { 190 const base::FilePath& path = src_dir.Append(*iter); 191 if (!AddEntryToZip(zip_file, path, src_dir)) { 192 // TODO(hshi): clean up the partial zip file when error occurs. 193 success = false; 194 break; 195 } 196 } 197 198 if (ZIP_OK != zipClose(zip_file, NULL)) { 199 DLOG(ERROR) << "Error closing zip file for fd " << dest_fd; 200 success = false; 201 } 202 203 return success; 204 } 205 #endif // defined(OS_POSIX) 206 207 } // namespace zip 208