blob: a7bcc3cc284609b74cef8177c8d679de8811c89c [file] [log] [blame]
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +00001// 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"
brettw@chromium.orgb5aa4e72013-06-08 04:53:36 +00009#include "base/files/file_enumerator.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000010#include "base/logging.h"
avi@chromium.orga9a93952013-06-11 08:04:16 +000011#include "base/strings/string16.h"
12#include "base/strings/string_util.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000013#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
25namespace {
26
27bool 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
52bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
53 const base::FilePath& root_path) {
54 std::string str_path =
KingWeiLiu@gmail.com5bd76ac2013-06-11 21:03:29 +000055 path.AsUTF8Unsafe().substr(root_path.AsUTF8Unsafe().length() + 1);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000056#if defined(OS_WIN)
57 ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
58#endif
59
brettw@chromium.org9a352f62013-07-15 20:18:09 +000060 bool is_directory = base::DirectoryExists(path);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000061 if (is_directory)
62 str_path += "/";
63
joaoe@opera.coma060b142013-12-06 14:41:46 +000064 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
65 // Setting the Language encoding flag so the file is told to be in utf-8.
66 const unsigned long LANGUAGE_ENCODING_FLAG = 0x1 << 11;
67
68 if (ZIP_OK != zipOpenNewFileInZip4(
69 zip_file, //file
70 str_path.c_str(), // filename
71 NULL, // zipfi (file_info)
72 NULL, // extrafield_local,
73 0u, // size_extrafield_local
74 NULL, // extrafield_global
75 0u, // size_extrafield_global
76 NULL, // comment
77 Z_DEFLATED, // method
78 Z_DEFAULT_COMPRESSION, // level
79 0, // raw
80 -MAX_WBITS, // windowBits
81 DEF_MEM_LEVEL, // memLevel
82 Z_DEFAULT_STRATEGY, // strategy
83 NULL, //password
84 0, // crcForCrypting
85 0, // versionMadeBy
86 LANGUAGE_ENCODING_FLAG)) { // flagBase
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000087 DLOG(ERROR) << "Could not open zip file entry " << str_path;
88 return false;
89 }
90
91 bool success = true;
92 if (!is_directory) {
93 success = AddFileToZip(zip_file, path);
94 }
95
96 if (ZIP_OK != zipCloseFileInZip(zip_file)) {
97 DLOG(ERROR) << "Could not close zip file entry " << str_path;
98 return false;
99 }
100
101 return success;
102}
103
104bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
105 return true;
106}
107
108bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
109 return file_path.BaseName().value()[0] != '.';
110}
111
112} // namespace
113
114namespace zip {
115
116bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
117 ZipReader reader;
118 if (!reader.Open(src_file)) {
119 DLOG(WARNING) << "Failed to open " << src_file.value();
120 return false;
121 }
122 while (reader.HasMore()) {
123 if (!reader.OpenCurrentEntryInZip()) {
124 DLOG(WARNING) << "Failed to open the current file in zip";
125 return false;
126 }
127 if (reader.current_entry_info()->is_unsafe()) {
128 DLOG(WARNING) << "Found an unsafe file in zip "
129 << reader.current_entry_info()->file_path().value();
130 return false;
131 }
132 if (!reader.ExtractCurrentEntryIntoDirectory(dest_dir)) {
133 DLOG(WARNING) << "Failed to extract "
134 << reader.current_entry_info()->file_path().value();
135 return false;
136 }
137 if (!reader.AdvanceToNextEntry()) {
138 DLOG(WARNING) << "Failed to advance to the next file";
139 return false;
140 }
141 }
142 return true;
143}
144
145bool ZipWithFilterCallback(const base::FilePath& src_dir,
146 const base::FilePath& dest_file,
147 const FilterCallback& filter_cb) {
brettw@chromium.org9a352f62013-07-15 20:18:09 +0000148 DCHECK(base::DirectoryExists(src_dir));
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000149
150 zipFile zip_file = internal::OpenForZipping(dest_file.AsUTF8Unsafe(),
151 APPEND_STATUS_CREATE);
152
153 if (!zip_file) {
154 DLOG(WARNING) << "couldn't create file " << dest_file.value();
155 return false;
156 }
157
158 bool success = true;
brettw@chromium.orgb5aa4e72013-06-08 04:53:36 +0000159 base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
160 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000161 for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
162 path = file_enumerator.Next()) {
163 if (!filter_cb.Run(path)) {
164 continue;
165 }
166
167 if (!AddEntryToZip(zip_file, path, src_dir)) {
168 success = false;
169 return false;
170 }
171 }
172
173 if (ZIP_OK != zipClose(zip_file, NULL)) {
174 DLOG(ERROR) << "Error closing zip file " << dest_file.value();
175 return false;
176 }
177
178 return success;
179}
180
181bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file,
182 bool include_hidden_files) {
183 if (include_hidden_files) {
184 return ZipWithFilterCallback(
185 src_dir, dest_file, base::Bind(&ExcludeNoFilesFilter));
186 } else {
187 return ZipWithFilterCallback(
188 src_dir, dest_file, base::Bind(&ExcludeHiddenFilesFilter));
189 }
190}
191
192#if defined(OS_POSIX)
193bool ZipFiles(const base::FilePath& src_dir,
194 const std::vector<base::FilePath>& src_relative_paths,
195 int dest_fd) {
brettw@chromium.org9a352f62013-07-15 20:18:09 +0000196 DCHECK(base::DirectoryExists(src_dir));
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000197 zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE);
198
199 if (!zip_file) {
200 DLOG(ERROR) << "couldn't create file for fd " << dest_fd;
201 return false;
202 }
203
204 bool success = true;
205 for (std::vector<base::FilePath>::const_iterator iter =
206 src_relative_paths.begin();
207 iter != src_relative_paths.end(); ++iter) {
208 const base::FilePath& path = src_dir.Append(*iter);
209 if (!AddEntryToZip(zip_file, path, src_dir)) {
210 // TODO(hshi): clean up the partial zip file when error occurs.
211 success = false;
212 break;
213 }
214 }
215
216 if (ZIP_OK != zipClose(zip_file, NULL)) {
217 DLOG(ERROR) << "Error closing zip file for fd " << dest_fd;
218 success = false;
219 }
220
221 return success;
222}
223#endif // defined(OS_POSIX)
224
225} // namespace zip