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