blob: f0183d3f364034e03dd3abb822f3818604332cc5 [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
joaoe@opera.comce54c1b2013-12-17 14:40:31 +00007#include <string>
8#include <vector>
9
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000010#include "base/bind.h"
hashimoto@chromium.org516d1852014-03-24 09:32:13 +000011#include "base/files/file.h"
brettw@chromium.orgb5aa4e72013-06-08 04:53:36 +000012#include "base/files/file_enumerator.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000013#include "base/logging.h"
avi@chromium.orga9a93952013-06-11 08:04:16 +000014#include "base/strings/string16.h"
15#include "base/strings/string_util.h"
aviaa969482015-12-27 13:36:49 -080016#include "build/build_config.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000017#include "third_party/zlib/google/zip_internal.h"
18#include "third_party/zlib/google/zip_reader.h"
19
20#if defined(USE_SYSTEM_MINIZIP)
21#include <minizip/unzip.h>
22#include <minizip/zip.h>
23#else
24#include "third_party/zlib/contrib/minizip/unzip.h"
25#include "third_party/zlib/contrib/minizip/zip.h"
26#endif
27
28namespace {
29
30bool AddFileToZip(zipFile zip_file, const base::FilePath& src_dir) {
hashimoto@chromium.org516d1852014-03-24 09:32:13 +000031 base::File file(src_dir, base::File::FLAG_OPEN | base::File::FLAG_READ);
32 if (!file.IsValid()) {
33 DLOG(ERROR) << "Could not open file for path " << src_dir.value();
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000034 return false;
35 }
36
37 int num_bytes;
38 char buf[zip::internal::kZipBufSize];
39 do {
hashimoto@chromium.org516d1852014-03-24 09:32:13 +000040 num_bytes = file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000041 if (num_bytes > 0) {
42 if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) {
43 DLOG(ERROR) << "Could not write data to zip for path "
44 << src_dir.value();
45 return false;
46 }
47 }
48 } while (num_bytes > 0);
49
50 return true;
51}
52
53bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
54 const base::FilePath& root_path) {
joaoe@opera.comd62e59c2013-12-09 12:20:40 +000055 base::FilePath relative_path;
56 bool result = root_path.AppendRelativePath(path, &relative_path);
57 DCHECK(result);
58 std::string str_path = relative_path.AsUTF8Unsafe();
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000059#if defined(OS_WIN)
brettw5d237e82015-06-24 13:54:45 -070060 base::ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000061#endif
62
brettw@chromium.org9a352f62013-07-15 20:18:09 +000063 bool is_directory = base::DirectoryExists(path);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000064 if (is_directory)
65 str_path += "/";
66
joaoe@opera.com16b1df32014-05-02 16:50:09 +000067 zip_fileinfo file_info = zip::internal::GetFileInfoForZipping(path);
68 if (!zip::internal::ZipOpenNewFileInZip(zip_file, str_path, &file_info))
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000069 return false;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000070
71 bool success = true;
72 if (!is_directory) {
73 success = AddFileToZip(zip_file, path);
74 }
75
76 if (ZIP_OK != zipCloseFileInZip(zip_file)) {
77 DLOG(ERROR) << "Could not close zip file entry " << str_path;
78 return false;
79 }
80
81 return success;
82}
83
84bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
85 return true;
86}
87
88bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
89 return file_path.BaseName().value()[0] != '.';
90}
91
92} // namespace
93
94namespace zip {
95
96bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
meacerf502ca72016-09-08 20:20:20 -070097 return UnzipWithFilterCallback(src_file, dest_dir,
meacer8086cc82016-11-22 13:25:55 -080098 base::Bind(&ExcludeNoFilesFilter), true);
meacerf502ca72016-09-08 20:20:20 -070099}
100
101bool UnzipWithFilterCallback(const base::FilePath& src_file,
102 const base::FilePath& dest_dir,
meacer8086cc82016-11-22 13:25:55 -0800103 const FilterCallback& filter_cb,
104 bool log_skipped_files) {
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000105 ZipReader reader;
106 if (!reader.Open(src_file)) {
107 DLOG(WARNING) << "Failed to open " << src_file.value();
108 return false;
109 }
110 while (reader.HasMore()) {
111 if (!reader.OpenCurrentEntryInZip()) {
112 DLOG(WARNING) << "Failed to open the current file in zip";
113 return false;
114 }
115 if (reader.current_entry_info()->is_unsafe()) {
116 DLOG(WARNING) << "Found an unsafe file in zip "
117 << reader.current_entry_info()->file_path().value();
118 return false;
119 }
meacerf502ca72016-09-08 20:20:20 -0700120 if (filter_cb.Run(reader.current_entry_info()->file_path())) {
121 if (!reader.ExtractCurrentEntryIntoDirectory(dest_dir)) {
122 DLOG(WARNING) << "Failed to extract "
123 << reader.current_entry_info()->file_path().value();
124 return false;
125 }
meacer8086cc82016-11-22 13:25:55 -0800126 } else if (log_skipped_files) {
meacerf502ca72016-09-08 20:20:20 -0700127 DLOG(WARNING) << "Skipped file "
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000128 << reader.current_entry_info()->file_path().value();
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000129 }
meacerf502ca72016-09-08 20:20:20 -0700130
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000131 if (!reader.AdvanceToNextEntry()) {
132 DLOG(WARNING) << "Failed to advance to the next file";
133 return false;
134 }
135 }
136 return true;
137}
138
139bool ZipWithFilterCallback(const base::FilePath& src_dir,
140 const base::FilePath& dest_file,
141 const FilterCallback& filter_cb) {
brettw@chromium.org9a352f62013-07-15 20:18:09 +0000142 DCHECK(base::DirectoryExists(src_dir));
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000143
144 zipFile zip_file = internal::OpenForZipping(dest_file.AsUTF8Unsafe(),
145 APPEND_STATUS_CREATE);
146
147 if (!zip_file) {
148 DLOG(WARNING) << "couldn't create file " << dest_file.value();
149 return false;
150 }
151
152 bool success = true;
brettw@chromium.orgb5aa4e72013-06-08 04:53:36 +0000153 base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
154 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000155 for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
156 path = file_enumerator.Next()) {
157 if (!filter_cb.Run(path)) {
158 continue;
159 }
160
161 if (!AddEntryToZip(zip_file, path, src_dir)) {
162 success = false;
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000163 break;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000164 }
165 }
166
167 if (ZIP_OK != zipClose(zip_file, NULL)) {
168 DLOG(ERROR) << "Error closing zip file " << dest_file.value();
169 return false;
170 }
171
172 return success;
173}
174
175bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file,
176 bool include_hidden_files) {
177 if (include_hidden_files) {
178 return ZipWithFilterCallback(
179 src_dir, dest_file, base::Bind(&ExcludeNoFilesFilter));
180 } else {
181 return ZipWithFilterCallback(
182 src_dir, dest_file, base::Bind(&ExcludeHiddenFilesFilter));
183 }
184}
185
186#if defined(OS_POSIX)
187bool ZipFiles(const base::FilePath& src_dir,
188 const std::vector<base::FilePath>& src_relative_paths,
189 int dest_fd) {
brettw@chromium.org9a352f62013-07-15 20:18:09 +0000190 DCHECK(base::DirectoryExists(src_dir));
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000191 zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE);
192
193 if (!zip_file) {
194 DLOG(ERROR) << "couldn't create file for fd " << dest_fd;
195 return false;
196 }
197
198 bool success = true;
199 for (std::vector<base::FilePath>::const_iterator iter =
200 src_relative_paths.begin();
201 iter != src_relative_paths.end(); ++iter) {
202 const base::FilePath& path = src_dir.Append(*iter);
203 if (!AddEntryToZip(zip_file, path, src_dir)) {
204 // TODO(hshi): clean up the partial zip file when error occurs.
205 success = false;
206 break;
207 }
208 }
209
210 if (ZIP_OK != zipClose(zip_file, NULL)) {
211 DLOG(ERROR) << "Error closing zip file for fd " << dest_fd;
212 success = false;
213 }
214
215 return success;
216}
217#endif // defined(OS_POSIX)
218
219} // namespace zip