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