• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/logging.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_util.h"
13 #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 
25 namespace {
26 
AddFileToZip(zipFile zip_file,const base::FilePath & src_dir)27 bool 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 
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   // 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
89     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 
ExcludeNoFilesFilter(const base::FilePath & file_path)106 bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
107   return true;
108 }
109 
ExcludeHiddenFilesFilter(const base::FilePath & file_path)110 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
111   return file_path.BaseName().value()[0] != '.';
112 }
113 
114 }  // namespace
115 
116 namespace zip {
117 
Unzip(const base::FilePath & src_file,const base::FilePath & dest_dir)118 bool 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 
ZipWithFilterCallback(const base::FilePath & src_dir,const base::FilePath & dest_file,const FilterCallback & filter_cb)147 bool ZipWithFilterCallback(const base::FilePath& src_dir,
148                            const base::FilePath& dest_file,
149                            const FilterCallback& filter_cb) {
150   DCHECK(base::DirectoryExists(src_dir));
151 
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;
161   base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
162       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
163   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 
Zip(const base::FilePath & src_dir,const base::FilePath & dest_file,bool include_hidden_files)183 bool 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)
ZipFiles(const base::FilePath & src_dir,const std::vector<base::FilePath> & src_relative_paths,int dest_fd)195 bool ZipFiles(const base::FilePath& src_dir,
196               const std::vector<base::FilePath>& src_relative_paths,
197               int dest_fd) {
198   DCHECK(base::DirectoryExists(src_dir));
199   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
228