blob: 726df33b477763d8b2825061129657701ba43d46 [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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
27namespace {
28
29bool 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
52bool 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
83bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
84 return true;
85}
86
87bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
88 return file_path.BaseName().value()[0] != '.';
89}
90
91} // namespace
92
93namespace zip {
94
95bool 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
124bool 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
160bool 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)
172bool 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