• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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_internal.h"
6 
7 #include <algorithm>
8 
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h"
13 
14 #if defined(USE_SYSTEM_MINIZIP)
15 #include <minizip/ioapi.h>
16 #include <minizip/unzip.h>
17 #include <minizip/zip.h>
18 #else
19 #include "third_party/zlib/contrib/minizip/unzip.h"
20 #include "third_party/zlib/contrib/minizip/zip.h"
21 #if defined(OS_WIN)
22 #include "third_party/zlib/contrib/minizip/iowin32.h"
23 #elif defined(OS_POSIX)
24 #include "third_party/zlib/contrib/minizip/ioapi.h"
25 #endif  // defined(OS_POSIX)
26 #endif  // defined(USE_SYSTEM_MINIZIP)
27 
28 namespace {
29 
30 #if defined(OS_WIN)
31 typedef struct {
32   HANDLE hf;
33   int error;
34 } WIN32FILE_IOWIN;
35 
36 // This function is derived from third_party/minizip/iowin32.c.
37 // Its only difference is that it treats the char* as UTF8 and
38 // uses the Unicode version of CreateFile.
ZipOpenFunc(void * opaque,const char * filename,int mode)39 void* ZipOpenFunc(void *opaque, const char* filename, int mode) {
40   DWORD desired_access, creation_disposition;
41   DWORD share_mode, flags_and_attributes;
42   HANDLE file = 0;
43   void* ret = NULL;
44 
45   desired_access = share_mode = flags_and_attributes = 0;
46 
47   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
48     desired_access = GENERIC_READ;
49     creation_disposition = OPEN_EXISTING;
50     share_mode = FILE_SHARE_READ;
51   } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
52     desired_access = GENERIC_WRITE | GENERIC_READ;
53     creation_disposition = OPEN_EXISTING;
54   } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
55     desired_access = GENERIC_WRITE | GENERIC_READ;
56     creation_disposition = CREATE_ALWAYS;
57   }
58 
59   base::string16 filename16 = base::UTF8ToUTF16(filename);
60   if ((filename != NULL) && (desired_access != 0)) {
61     file = CreateFile(filename16.c_str(), desired_access, share_mode,
62         NULL, creation_disposition, flags_and_attributes, NULL);
63   }
64 
65   if (file == INVALID_HANDLE_VALUE)
66     file = NULL;
67 
68   if (file != NULL) {
69     WIN32FILE_IOWIN file_ret;
70     file_ret.hf = file;
71     file_ret.error = 0;
72     ret = malloc(sizeof(WIN32FILE_IOWIN));
73     if (ret == NULL)
74       CloseHandle(file);
75     else
76       *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
77   }
78   return ret;
79 }
80 #endif
81 
82 #if defined(OS_POSIX)
83 // Callback function for zlib that opens a file stream from a file descriptor.
FdOpenFileFunc(void * opaque,const char * filename,int mode)84 void* FdOpenFileFunc(void* opaque, const char* filename, int mode) {
85   FILE* file = NULL;
86   const char* mode_fopen = NULL;
87 
88   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
89     mode_fopen = "rb";
90   else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
91     mode_fopen = "r+b";
92   else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
93     mode_fopen = "wb";
94 
95   if ((filename != NULL) && (mode_fopen != NULL))
96     file = fdopen(*static_cast<int*>(opaque), mode_fopen);
97 
98   return file;
99 }
100 
101 // We don't actually close the file stream since that would close
102 // the underlying file descriptor, and we don't own it. However we do need to
103 // flush buffers and free |opaque| since we malloc'ed it in FillFdOpenFileFunc.
CloseFileFunc(void * opaque,void * stream)104 int CloseFileFunc(void* opaque, void* stream) {
105   fflush(static_cast<FILE*>(stream));
106   free(opaque);
107   return 0;
108 }
109 
110 // Fills |pzlib_filecunc_def| appropriately to handle the zip file
111 // referred to by |fd|.
FillFdOpenFileFunc(zlib_filefunc_def * pzlib_filefunc_def,int fd)112 void FillFdOpenFileFunc(zlib_filefunc_def* pzlib_filefunc_def, int fd) {
113   fill_fopen_filefunc(pzlib_filefunc_def);
114   pzlib_filefunc_def->zopen_file = FdOpenFileFunc;
115   pzlib_filefunc_def->zclose_file = CloseFileFunc;
116   int* ptr_fd = static_cast<int*>(malloc(sizeof(fd)));
117   *ptr_fd = fd;
118   pzlib_filefunc_def->opaque = ptr_fd;
119 }
120 #endif  // defined(OS_POSIX)
121 
122 #if defined(OS_WIN)
123 // Callback function for zlib that opens a file stream from a Windows handle.
HandleOpenFileFunc(void * opaque,const char * filename,int mode)124 void* HandleOpenFileFunc(void* opaque, const char* filename, int mode) {
125   WIN32FILE_IOWIN file_ret;
126   file_ret.hf = static_cast<HANDLE>(opaque);
127   file_ret.error = 0;
128   if (file_ret.hf == INVALID_HANDLE_VALUE)
129     return NULL;
130 
131   void* ret = malloc(sizeof(WIN32FILE_IOWIN));
132   if (ret != NULL)
133     *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
134   return ret;
135 }
136 #endif
137 
138 // A struct that contains data required for zlib functions to extract files from
139 // a zip archive stored in memory directly. The following I/O API functions
140 // expect their opaque parameters refer to this struct.
141 struct ZipBuffer {
142   const char* data;  // weak
143   size_t length;
144   size_t offset;
145 };
146 
147 // Opens the specified file. When this function returns a non-NULL pointer, zlib
148 // uses this pointer as a stream parameter while compressing or uncompressing
149 // data. (Returning NULL represents an error.) This function initializes the
150 // given opaque parameter and returns it because this parameter stores all
151 // information needed for uncompressing data. (This function does not support
152 // writing compressed data and it returns NULL for this case.)
OpenZipBuffer(void * opaque,const char *,int mode)153 void* OpenZipBuffer(void* opaque, const char* /*filename*/, int mode) {
154   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) {
155     NOTREACHED();
156     return NULL;
157   }
158   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
159   if (!buffer || !buffer->data || !buffer->length)
160     return NULL;
161   buffer->offset = 0;
162   return opaque;
163 }
164 
165 // Reads compressed data from the specified stream. This function copies data
166 // refered by the opaque parameter and returns the size actually copied.
ReadZipBuffer(void * opaque,void *,void * buf,uLong size)167 uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) {
168   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
169   DCHECK_LE(buffer->offset, buffer->length);
170   size_t remaining_bytes = buffer->length - buffer->offset;
171   if (!buffer || !buffer->data || !remaining_bytes)
172     return 0;
173   size = std::min(size, static_cast<uLong>(remaining_bytes));
174   memcpy(buf, &buffer->data[buffer->offset], size);
175   buffer->offset += size;
176   return size;
177 }
178 
179 // Writes compressed data to the stream. This function always returns zero
180 // because this implementation is only for reading compressed data.
WriteZipBuffer(void *,void *,const void *,uLong)181 uLong WriteZipBuffer(void* /*opaque*/,
182                      void* /*stream*/,
183                      const void* /*buf*/,
184                      uLong /*size*/) {
185   NOTREACHED();
186   return 0;
187 }
188 
189 // Returns the offset from the beginning of the data.
GetOffsetOfZipBuffer(void * opaque,void *)190 long GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) {
191   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
192   if (!buffer)
193     return -1;
194   return static_cast<long>(buffer->offset);
195 }
196 
197 // Moves the current offset to the specified position.
SeekZipBuffer(void * opaque,void *,uLong offset,int origin)198 long SeekZipBuffer(void* opaque, void* /*stream*/, uLong offset, int origin) {
199   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
200   if (!buffer)
201     return -1;
202   if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
203     buffer->offset = std::min(buffer->offset + static_cast<size_t>(offset),
204                               buffer->length);
205     return 0;
206   }
207   if (origin == ZLIB_FILEFUNC_SEEK_END) {
208     buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0;
209     return 0;
210   }
211   if (origin == ZLIB_FILEFUNC_SEEK_SET) {
212     buffer->offset = std::min(buffer->length, static_cast<size_t>(offset));
213     return 0;
214   }
215   NOTREACHED();
216   return -1;
217 }
218 
219 // Closes the input offset and deletes all resources used for compressing or
220 // uncompressing data. This function deletes the ZipBuffer object referred by
221 // the opaque parameter since zlib deletes the unzFile object and it does not
222 // use this object any longer.
CloseZipBuffer(void * opaque,void *)223 int CloseZipBuffer(void* opaque, void* /*stream*/) {
224   if (opaque)
225     free(opaque);
226   return 0;
227 }
228 
229 // Returns the last error happened when reading or writing data. This function
230 // always returns zero, which means there are not any errors.
GetErrorOfZipBuffer(void *,void *)231 int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
232   return 0;
233 }
234 
235 // Returns a zip_fileinfo struct with the time represented by |file_time|.
TimeToZipFileInfo(const base::Time & file_time)236 zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) {
237   base::Time::Exploded file_time_parts;
238   file_time.LocalExplode(&file_time_parts);
239 
240   zip_fileinfo zip_info = {};
241   if (file_time_parts.year >= 1980) {
242     // This if check works around the handling of the year value in
243     // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate
244     // It assumes that dates below 1980 are in the double digit format.
245     // Hence the fail safe option is to leave the date unset. Some programs
246     // might show the unset date as 1980-0-0 which is invalid.
247     zip_info.tmz_date.tm_year = file_time_parts.year;
248     zip_info.tmz_date.tm_mon = file_time_parts.month - 1;
249     zip_info.tmz_date.tm_mday = file_time_parts.day_of_month;
250     zip_info.tmz_date.tm_hour = file_time_parts.hour;
251     zip_info.tmz_date.tm_min = file_time_parts.minute;
252     zip_info.tmz_date.tm_sec = file_time_parts.second;
253   }
254 
255   return zip_info;
256 }
257 }  // namespace
258 
259 namespace zip {
260 namespace internal {
261 
OpenForUnzipping(const std::string & file_name_utf8)262 unzFile OpenForUnzipping(const std::string& file_name_utf8) {
263   zlib_filefunc_def* zip_func_ptrs = NULL;
264 #if defined(OS_WIN)
265   zlib_filefunc_def zip_funcs;
266   fill_win32_filefunc(&zip_funcs);
267   zip_funcs.zopen_file = ZipOpenFunc;
268   zip_func_ptrs = &zip_funcs;
269 #endif
270   return unzOpen2(file_name_utf8.c_str(), zip_func_ptrs);
271 }
272 
273 #if defined(OS_POSIX)
OpenFdForUnzipping(int zip_fd)274 unzFile OpenFdForUnzipping(int zip_fd) {
275   zlib_filefunc_def zip_funcs;
276   FillFdOpenFileFunc(&zip_funcs, zip_fd);
277   // Passing dummy "fd" filename to zlib.
278   return unzOpen2("fd", &zip_funcs);
279 }
280 #endif
281 
282 #if defined(OS_WIN)
OpenHandleForUnzipping(HANDLE zip_handle)283 unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
284   zlib_filefunc_def zip_funcs;
285   fill_win32_filefunc(&zip_funcs);
286   zip_funcs.zopen_file = HandleOpenFileFunc;
287   zip_funcs.opaque = zip_handle;
288   return unzOpen2("fd", &zip_funcs);
289 }
290 #endif
291 
292 // static
PrepareMemoryForUnzipping(const std::string & data)293 unzFile PrepareMemoryForUnzipping(const std::string& data) {
294   if (data.empty())
295     return NULL;
296 
297   ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
298   if (!buffer)
299     return NULL;
300   buffer->data = data.data();
301   buffer->length = data.length();
302   buffer->offset = 0;
303 
304   zlib_filefunc_def zip_functions;
305   zip_functions.zopen_file = OpenZipBuffer;
306   zip_functions.zread_file = ReadZipBuffer;
307   zip_functions.zwrite_file = WriteZipBuffer;
308   zip_functions.ztell_file = GetOffsetOfZipBuffer;
309   zip_functions.zseek_file = SeekZipBuffer;
310   zip_functions.zclose_file = CloseZipBuffer;
311   zip_functions.zerror_file = GetErrorOfZipBuffer;
312   zip_functions.opaque = static_cast<void*>(buffer);
313   return unzOpen2(NULL, &zip_functions);
314 }
315 
OpenForZipping(const std::string & file_name_utf8,int append_flag)316 zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) {
317   zlib_filefunc_def* zip_func_ptrs = NULL;
318 #if defined(OS_WIN)
319   zlib_filefunc_def zip_funcs;
320   fill_win32_filefunc(&zip_funcs);
321   zip_funcs.zopen_file = ZipOpenFunc;
322   zip_func_ptrs = &zip_funcs;
323 #endif
324   return zipOpen2(file_name_utf8.c_str(),
325                   append_flag,
326                   NULL,  // global comment
327                   zip_func_ptrs);
328 }
329 
330 #if defined(OS_POSIX)
OpenFdForZipping(int zip_fd,int append_flag)331 zipFile OpenFdForZipping(int zip_fd, int append_flag) {
332   zlib_filefunc_def zip_funcs;
333   FillFdOpenFileFunc(&zip_funcs, zip_fd);
334   // Passing dummy "fd" filename to zlib.
335   return zipOpen2("fd", append_flag, NULL, &zip_funcs);
336 }
337 #endif
338 
GetFileInfoForZipping(const base::FilePath & path)339 zip_fileinfo GetFileInfoForZipping(const base::FilePath& path) {
340   base::Time file_time;
341   base::File::Info file_info;
342   if (base::GetFileInfo(path, &file_info))
343     file_time = file_info.last_modified;
344   return TimeToZipFileInfo(file_time);
345 }
346 
ZipOpenNewFileInZip(zipFile zip_file,const std::string & str_path,const zip_fileinfo * file_info)347 bool ZipOpenNewFileInZip(zipFile zip_file,
348                          const std::string& str_path,
349                          const zip_fileinfo* file_info) {
350   // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
351   // Setting the Language encoding flag so the file is told to be in utf-8.
352   const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
353 
354   if (ZIP_OK != zipOpenNewFileInZip4(
355                     zip_file,  // file
356                     str_path.c_str(),  // filename
357                     file_info,  // zipfi
358                     NULL,  // extrafield_local,
359                     0u,  // size_extrafield_local
360                     NULL,  // extrafield_global
361                     0u,  // size_extrafield_global
362                     NULL,  // comment
363                     Z_DEFLATED,  // method
364                     Z_DEFAULT_COMPRESSION,  // level
365                     0,  // raw
366                     -MAX_WBITS,  // windowBits
367                     DEF_MEM_LEVEL,  // memLevel
368                     Z_DEFAULT_STRATEGY,  // strategy
369                     NULL,  // password
370                     0,  // crcForCrypting
371                     0,  // versionMadeBy
372                     LANGUAGE_ENCODING_FLAG)) {  // flagBase
373     DLOG(ERROR) << "Could not open zip file entry " << str_path;
374     return false;
375   }
376   return true;
377 }
378 
379 }  // namespace internal
380 }  // namespace zip
381