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
AddFileToZip(zipFile zip_file,const base::FilePath & src_dir)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
AddEntryToZip(zipFile zip_file,const base::FilePath & path,const base::FilePath & root_path)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
ExcludeNoFilesFilter(const base::FilePath & file_path)83 bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
84 return true;
85 }
86
ExcludeHiddenFilesFilter(const base::FilePath & file_path)87 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
88 return file_path.BaseName().value()[0] != '.';
89 }
90
91 } // namespace
92
93 namespace zip {
94
Unzip(const base::FilePath & src_file,const base::FilePath & dest_dir)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
ZipWithFilterCallback(const base::FilePath & src_dir,const base::FilePath & dest_file,const FilterCallback & filter_cb)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
Zip(const base::FilePath & src_dir,const base::FilePath & dest_file,bool include_hidden_files)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)
ZipFiles(const base::FilePath & src_dir,const std::vector<base::FilePath> & src_relative_paths,int dest_fd)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