• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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 "chrome/common/zip.h"
6 
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/string_split.h"
10 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h"
12 #include "net/base/file_stream.h"
13 #include "third_party/zlib/contrib/minizip/unzip.h"
14 #include "third_party/zlib/contrib/minizip/zip.h"
15 #if defined(OS_WIN)
16 #include "third_party/zlib/contrib/minizip/iowin32.h"
17 #endif
18 
19 static const int kZipMaxPath = 256;
20 static const int kZipBufSize = 8192;
21 
22 // Extract the 'current' selected file from the zip into dest_dir.
23 // Output filename is stored in out_file.  Returns true on success.
ExtractCurrentFile(unzFile zip_file,const FilePath & dest_dir)24 static bool ExtractCurrentFile(unzFile zip_file,
25                                const FilePath& dest_dir) {
26   char filename_inzip[kZipMaxPath] = {0};
27   unz_file_info file_info;
28   int err = unzGetCurrentFileInfo(zip_file, &file_info, filename_inzip,
29                                   sizeof(filename_inzip) - 1, NULL, 0, NULL, 0);
30   if (err != UNZ_OK)
31     return false;
32   if (filename_inzip[0] == '\0')
33     return false;
34 
35   err = unzOpenCurrentFile(zip_file);
36   if (err != UNZ_OK)
37     return false;
38 
39   FilePath::StringType filename;
40   std::vector<FilePath::StringType> filename_parts;
41 #if defined(OS_WIN)
42   filename = UTF8ToWide(filename_inzip);
43 #elif defined(OS_POSIX)
44   filename = filename_inzip;
45 #endif
46 
47   // Check the filename here for directory traversal issues. In the name of
48   // simplicity and security, we might reject a valid filename such as "a..b".
49   if (filename.find(FILE_PATH_LITERAL("..")) != FilePath::StringType::npos)
50     return false;
51 
52   base::SplitString(filename, '/', &filename_parts);
53 
54   FilePath dest_file(dest_dir);
55   std::vector<FilePath::StringType>::iterator iter;
56   for (iter = filename_parts.begin(); iter != filename_parts.end(); ++iter)
57     dest_file = dest_file.Append(*iter);
58 
59   // If this is a directory, just create it and return.
60   if (filename_inzip[strlen(filename_inzip) - 1] == '/') {
61     if (!file_util::CreateDirectory(dest_file))
62       return false;
63     return true;
64   }
65 
66   // We can't rely on parent directory entries being specified in the zip, so we
67   // make sure they are created.
68   FilePath dir = dest_file.DirName();
69   if (!file_util::CreateDirectory(dir))
70     return false;
71 
72   net::FileStream stream;
73   int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
74   if (stream.Open(dest_file, flags) != 0)
75     return false;
76 
77   bool ret = true;
78   int num_bytes = 0;
79   char buf[kZipBufSize];
80   do {
81     num_bytes = unzReadCurrentFile(zip_file, buf, kZipBufSize);
82     if (num_bytes < 0) {
83       // If num_bytes < 0, then it's a specific UNZ_* error code.
84       // While we're not currently handling these codes specifically, save
85       // it away in case we want to in the future.
86       err = num_bytes;
87       break;
88     }
89     if (num_bytes > 0) {
90       if (num_bytes != stream.Write(buf, num_bytes, NULL)) {
91         ret = false;
92         break;
93       }
94     }
95   } while (num_bytes > 0);
96 
97   stream.Close();
98   if (err == UNZ_OK)
99     err = unzCloseCurrentFile(zip_file);
100   else
101     unzCloseCurrentFile(zip_file);  // Don't lose the original error code.
102   if (err != UNZ_OK)
103     ret = false;
104   return ret;
105 }
106 
107 #if defined(OS_WIN)
108 typedef struct {
109   HANDLE hf;
110   int error;
111 } WIN32FILE_IOWIN;
112 
113 // This function is derived from third_party/minizip/iowin32.c.
114 // Its only difference is that it treats the char* as UTF8 and
115 // uses the Unicode version of CreateFile.
ZipOpenFunc(void * opaque,const char * filename,int mode)116 static void* ZipOpenFunc(void *opaque, const char* filename, int mode) {
117   DWORD desired_access, creation_disposition;
118   DWORD share_mode, flags_and_attributes;
119   HANDLE file = 0;
120   void* ret = NULL;
121 
122   desired_access = share_mode = flags_and_attributes = 0;
123 
124   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
125     desired_access = GENERIC_READ;
126     creation_disposition = OPEN_EXISTING;
127     share_mode = FILE_SHARE_READ;
128   } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
129     desired_access = GENERIC_WRITE | GENERIC_READ;
130     creation_disposition = OPEN_EXISTING;
131   } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
132     desired_access = GENERIC_WRITE | GENERIC_READ;
133     creation_disposition = CREATE_ALWAYS;
134   }
135 
136   std::wstring filename_wstr = UTF8ToWide(filename);
137   if ((filename != NULL) && (desired_access != 0)) {
138     file = CreateFile(filename_wstr.c_str(), desired_access, share_mode,
139         NULL, creation_disposition, flags_and_attributes, NULL);
140   }
141 
142   if (file == INVALID_HANDLE_VALUE)
143     file = NULL;
144 
145   if (file != NULL) {
146     WIN32FILE_IOWIN file_ret;
147     file_ret.hf = file;
148     file_ret.error = 0;
149     ret = malloc(sizeof(WIN32FILE_IOWIN));
150     if (ret == NULL)
151       CloseHandle(file);
152     else
153       *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
154   }
155   return ret;
156 }
157 #endif
158 
Unzip(const FilePath & src_file,const FilePath & dest_dir)159 bool Unzip(const FilePath& src_file, const FilePath& dest_dir) {
160 #if defined(OS_WIN)
161   zlib_filefunc_def zip_funcs;
162   fill_win32_filefunc(&zip_funcs);
163   zip_funcs.zopen_file = ZipOpenFunc;
164 #endif
165 
166 #if defined(OS_POSIX)
167   std::string src_file_str = src_file.value();
168   unzFile zip_file = unzOpen(src_file_str.c_str());
169 #elif defined(OS_WIN)
170   std::string src_file_str = WideToUTF8(src_file.value());
171   unzFile zip_file = unzOpen2(src_file_str.c_str(), &zip_funcs);
172 #endif
173   if (!zip_file) {
174     LOG(WARNING) << "couldn't create file " << src_file_str;
175     return false;
176   }
177   unz_global_info zip_info;
178   int err;
179   err = unzGetGlobalInfo(zip_file, &zip_info);
180   if (err != UNZ_OK) {
181     LOG(WARNING) << "couldn't open zip " << src_file_str;
182     return false;
183   }
184   bool ret = true;
185   for (unsigned int i = 0; i < zip_info.number_entry; ++i) {
186     if (!ExtractCurrentFile(zip_file, dest_dir)) {
187       ret = false;
188       break;
189     }
190 
191     if (i + 1 < zip_info.number_entry) {
192       err = unzGoToNextFile(zip_file);
193       if (err != UNZ_OK) {
194         LOG(WARNING) << "error %d in unzGoToNextFile";
195         ret = false;
196         break;
197       }
198     }
199   }
200   unzClose(zip_file);
201   return ret;
202 }
203 
AddFileToZip(zipFile zip_file,const FilePath & src_dir)204 static bool AddFileToZip(zipFile zip_file, const FilePath& src_dir) {
205   net::FileStream stream;
206   int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
207   if (stream.Open(src_dir, flags) != 0) {
208     LOG(ERROR) << "Could not open stream for path "
209                << src_dir.value();
210     return false;
211   }
212 
213   int num_bytes;
214   char buf[kZipBufSize];
215   do {
216     num_bytes = stream.Read(buf, kZipBufSize, NULL);
217     if (num_bytes > 0) {
218       if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) {
219         LOG(ERROR) << "Could not write data to zip for path "
220                    << src_dir.value();
221         return false;
222       }
223     }
224   } while (num_bytes > 0);
225 
226   return true;
227 }
228 
AddEntryToZip(zipFile zip_file,const FilePath & path,const FilePath & root_path)229 static bool AddEntryToZip(zipFile zip_file, const FilePath& path,
230                           const FilePath& root_path) {
231 #if defined(OS_WIN)
232   std::string str_path =
233       WideToUTF8(path.value().substr(root_path.value().length() + 1));
234   ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
235 #else
236   std::string str_path = path.value().substr(root_path.value().length() + 1);
237 #endif
238 
239   bool is_directory = file_util::DirectoryExists(path);
240   if (is_directory)
241     str_path += "/";
242 
243   if (ZIP_OK != zipOpenNewFileInZip(
244       zip_file, str_path.c_str(),
245       NULL, NULL, 0u, NULL, 0u, NULL,  // file info, extrafield local, length,
246                                        // extrafield global, length, comment
247       Z_DEFLATED, Z_DEFAULT_COMPRESSION)) {
248     LOG(ERROR) << "Could not open zip file entry " << str_path;
249     return false;
250   }
251 
252   bool success = true;
253   if (!is_directory) {
254     success = AddFileToZip(zip_file, path);
255   }
256 
257   if (ZIP_OK != zipCloseFileInZip(zip_file)) {
258     LOG(ERROR) << "Could not close zip file entry " << str_path;
259     return false;
260   }
261 
262   return success;
263 }
264 
Zip(const FilePath & src_dir,const FilePath & dest_file,bool include_hidden_files)265 bool Zip(const FilePath& src_dir, const FilePath& dest_file,
266          bool include_hidden_files) {
267   DCHECK(file_util::DirectoryExists(src_dir));
268 
269 #if defined(OS_WIN)
270   zlib_filefunc_def zip_funcs;
271   fill_win32_filefunc(&zip_funcs);
272   zip_funcs.zopen_file = ZipOpenFunc;
273 #endif
274 
275 #if defined(OS_POSIX)
276   std::string dest_file_str = dest_file.value();
277   std::string src_dir_str = src_dir.value();
278   zipFile zip_file = zipOpen(dest_file_str.c_str(), APPEND_STATUS_CREATE);
279 #elif defined(OS_WIN)
280   std::string dest_file_str = WideToUTF8(dest_file.value());
281   zipFile zip_file = zipOpen2(dest_file_str.c_str(), APPEND_STATUS_CREATE,
282                               NULL,  // global comment
283                               &zip_funcs);
284 #endif
285 
286   if (!zip_file) {
287     LOG(WARNING) << "couldn't create file " << dest_file_str;
288     return false;
289   }
290 
291   bool success = true;
292   file_util::FileEnumerator file_enumerator(
293       src_dir, true,  // recursive
294       static_cast<file_util::FileEnumerator::FILE_TYPE>(
295           file_util::FileEnumerator::FILES |
296           file_util::FileEnumerator::DIRECTORIES));
297   for (FilePath path = file_enumerator.Next(); !path.value().empty();
298        path = file_enumerator.Next()) {
299     if (!include_hidden_files && path.BaseName().value()[0] == '.')
300       continue;
301 
302     if (!AddEntryToZip(zip_file, path, src_dir)) {
303       success = false;
304       return false;
305     }
306   }
307 
308   if (ZIP_OK != zipClose(zip_file, NULL)) {  // global comment
309     LOG(ERROR) << "Error closing zip file " << dest_file_str;
310     return false;
311   }
312 
313   return success;
314 }
315