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