1 // Copyright 2011 The Chromium Authors
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/containers/fixed_flat_set.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/notreached.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 
20 #if defined(USE_SYSTEM_MINIZIP)
21 #include <minizip/ioapi.h>
22 #include <minizip/unzip.h>
23 #include <minizip/zip.h>
24 #else
25 #include "third_party/zlib/contrib/minizip/unzip.h"
26 #include "third_party/zlib/contrib/minizip/zip.h"
27 #if defined(OS_WIN)
28 #include "third_party/zlib/contrib/minizip/iowin32.h"
29 #elif defined(OS_POSIX)
30 #include "third_party/zlib/contrib/minizip/ioapi.h"
31 #endif  // defined(OS_POSIX)
32 #endif  // defined(USE_SYSTEM_MINIZIP)
33 
34 namespace {
35 
36 #if defined(OS_WIN)
37 typedef struct {
38   HANDLE hf;
39   int error;
40 } WIN32FILE_IOWIN;
41 
42 // This function is derived from third_party/minizip/iowin32.c.
43 // Its only difference is that it treats the filename as UTF-8 and
44 // uses the Unicode version of CreateFile.
ZipOpenFunc(void* opaque, const void* filename, int mode)45 void* ZipOpenFunc(void* opaque, const void* filename, int mode) {
46   DWORD desired_access = 0, creation_disposition = 0;
47   DWORD share_mode = 0, flags_and_attributes = 0;
48   HANDLE file = 0;
49   void* ret = NULL;
50 
51   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
52     desired_access = GENERIC_READ;
53     creation_disposition = OPEN_EXISTING;
54     share_mode = FILE_SHARE_READ;
55   } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
56     desired_access = GENERIC_WRITE | GENERIC_READ;
57     creation_disposition = OPEN_EXISTING;
58   } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
59     desired_access = GENERIC_WRITE | GENERIC_READ;
60     creation_disposition = CREATE_ALWAYS;
61   }
62 
63   if (filename != nullptr && desired_access != 0) {
64     file = CreateFileW(
65         base::UTF8ToWide(static_cast<const char*>(filename)).c_str(),
66         desired_access, share_mode, nullptr, creation_disposition,
67         flags_and_attributes, nullptr);
68   }
69 
70   if (file == INVALID_HANDLE_VALUE)
71     file = NULL;
72 
73   if (file != NULL) {
74     WIN32FILE_IOWIN file_ret;
75     file_ret.hf = file;
76     file_ret.error = 0;
77     ret = malloc(sizeof(WIN32FILE_IOWIN));
78     if (ret == NULL)
79       CloseHandle(file);
80     else
81       *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
82   }
83   return ret;
84 }
85 #endif
86 
87 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
88 // Callback function for zlib that opens a file stream from a file descriptor.
89 // Since we do not own the file descriptor, dup it so that we can fdopen/fclose
90 // a file stream.
FdOpenFileFunc(void* opaque, const void* filename, int mode)91 void* FdOpenFileFunc(void* opaque, const void* filename, int mode) {
92   FILE* file = NULL;
93   const char* mode_fopen = NULL;
94 
95   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
96     mode_fopen = "rb";
97   else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
98     mode_fopen = "r+b";
99   else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
100     mode_fopen = "wb";
101 
102   if ((filename != NULL) && (mode_fopen != NULL)) {
103     int fd = dup(*static_cast<int*>(opaque));
104     if (fd != -1)
105       file = fdopen(fd, mode_fopen);
106   }
107 
108   return file;
109 }
110 
FdCloseFileFunc(void* opaque, void* stream)111 int FdCloseFileFunc(void* opaque, void* stream) {
112   fclose(static_cast<FILE*>(stream));
113   free(opaque);  // malloc'ed in FillFdOpenFileFunc()
114   return 0;
115 }
116 
117 // Fills |pzlib_filecunc_def| appropriately to handle the zip file
118 // referred to by |fd|.
FillFdOpenFileFunc(zlib_filefunc64_def* pzlib_filefunc_def, int fd)119 void FillFdOpenFileFunc(zlib_filefunc64_def* pzlib_filefunc_def, int fd) {
120   fill_fopen64_filefunc(pzlib_filefunc_def);
121   pzlib_filefunc_def->zopen64_file = FdOpenFileFunc;
122   pzlib_filefunc_def->zclose_file = FdCloseFileFunc;
123   int* ptr_fd = static_cast<int*>(malloc(sizeof(fd)));
124   *ptr_fd = fd;
125   pzlib_filefunc_def->opaque = ptr_fd;
126 }
127 #endif  // defined(OS_POSIX)
128 
129 #if defined(OS_WIN)
130 // Callback function for zlib that opens a file stream from a Windows handle.
131 // Does not take ownership of the handle.
HandleOpenFileFunc(void* opaque, const void* , int mode)132 void* HandleOpenFileFunc(void* opaque, const void* /*filename*/, int mode) {
133   WIN32FILE_IOWIN file_ret;
134   file_ret.hf = static_cast<HANDLE>(opaque);
135   file_ret.error = 0;
136   if (file_ret.hf == INVALID_HANDLE_VALUE)
137     return NULL;
138 
139   void* ret = malloc(sizeof(WIN32FILE_IOWIN));
140   if (ret != NULL)
141     *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
142   return ret;
143 }
144 
HandleCloseFileFunc(void* opaque, void* stream)145 int HandleCloseFileFunc(void* opaque, void* stream) {
146   free(stream);  // malloc'ed in HandleOpenFileFunc()
147   return 0;
148 }
149 #endif
150 
151 // A struct that contains data required for zlib functions to extract files from
152 // a zip archive stored in memory directly. The following I/O API functions
153 // expect their opaque parameters refer to this struct.
154 struct ZipBuffer {
155   const char* data;  // weak
156   ZPOS64_T length;
157   ZPOS64_T offset;
158 };
159 
160 // Opens the specified file. When this function returns a non-NULL pointer, zlib
161 // uses this pointer as a stream parameter while compressing or uncompressing
162 // data. (Returning NULL represents an error.) This function initializes the
163 // given opaque parameter and returns it because this parameter stores all
164 // information needed for uncompressing data. (This function does not support
165 // writing compressed data and it returns NULL for this case.)
OpenZipBuffer(void* opaque, const void* , int mode)166 void* OpenZipBuffer(void* opaque, const void* /*filename*/, int mode) {
167   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) {
168     NOTREACHED();
169     return NULL;
170   }
171   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
172   if (!buffer || !buffer->data || !buffer->length)
173     return NULL;
174   buffer->offset = 0;
175   return opaque;
176 }
177 
178 // Reads compressed data from the specified stream. This function copies data
179 // refered by the opaque parameter and returns the size actually copied.
ReadZipBuffer(void* opaque, void* , void* buf, uLong size)180 uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) {
181   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
182   DCHECK_LE(buffer->offset, buffer->length);
183   ZPOS64_T remaining_bytes = buffer->length - buffer->offset;
184   if (!buffer || !buffer->data || !remaining_bytes)
185     return 0;
186   if (size > remaining_bytes)
187     size = remaining_bytes;
188   memcpy(buf, &buffer->data[buffer->offset], size);
189   buffer->offset += size;
190   return size;
191 }
192 
193 // Writes compressed data to the stream. This function always returns zero
194 // because this implementation is only for reading compressed data.
WriteZipBuffer(void* , void* , const void* , uLong )195 uLong WriteZipBuffer(void* /*opaque*/,
196                      void* /*stream*/,
197                      const void* /*buf*/,
198                      uLong /*size*/) {
199   NOTREACHED();
200   return 0;
201 }
202 
203 // Returns the offset from the beginning of the data.
GetOffsetOfZipBuffer(void* opaque, void* )204 ZPOS64_T GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) {
205   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
206   if (!buffer)
207     return -1;
208   return buffer->offset;
209 }
210 
211 // Moves the current offset to the specified position.
SeekZipBuffer(void* opaque, void* , ZPOS64_T offset, int origin)212 long SeekZipBuffer(void* opaque,
213                    void* /*stream*/,
214                    ZPOS64_T offset,
215                    int origin) {
216   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
217   if (!buffer)
218     return -1;
219   if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
220     buffer->offset = std::min(buffer->offset + offset, buffer->length);
221     return 0;
222   }
223   if (origin == ZLIB_FILEFUNC_SEEK_END) {
224     buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0;
225     return 0;
226   }
227   if (origin == ZLIB_FILEFUNC_SEEK_SET) {
228     buffer->offset = std::min(buffer->length, offset);
229     return 0;
230   }
231   NOTREACHED();
232   return -1;
233 }
234 
235 // Closes the input offset and deletes all resources used for compressing or
236 // uncompressing data. This function deletes the ZipBuffer object referred by
237 // the opaque parameter since zlib deletes the unzFile object and it does not
238 // use this object any longer.
CloseZipBuffer(void* opaque, void* )239 int CloseZipBuffer(void* opaque, void* /*stream*/) {
240   if (opaque)
241     free(opaque);
242   return 0;
243 }
244 
245 // Returns the last error happened when reading or writing data. This function
246 // always returns zero, which means there are not any errors.
GetErrorOfZipBuffer(void* , void* )247 int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
248   return 0;
249 }
250 
251 // Returns a zip_fileinfo struct with the time represented by |file_time|.
TimeToZipFileInfo(const base::Time& file_time)252 zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) {
253   base::Time::Exploded file_time_parts;
254   file_time.UTCExplode(&file_time_parts);
255 
256   zip_fileinfo zip_info = {};
257   if (file_time_parts.year >= 1980) {
258     // This if check works around the handling of the year value in
259     // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate
260     // It assumes that dates below 1980 are in the double digit format.
261     // Hence the fail safe option is to leave the date unset. Some programs
262     // might show the unset date as 1980-0-0 which is invalid.
263     zip_info.tmz_date = {
264         .tm_sec = static_cast<uInt>(file_time_parts.second),
265         .tm_min = static_cast<uInt>(file_time_parts.minute),
266         .tm_hour = static_cast<uInt>(file_time_parts.hour),
267         .tm_mday = static_cast<uInt>(file_time_parts.day_of_month),
268         .tm_mon = static_cast<uInt>(file_time_parts.month - 1),
269         .tm_year = static_cast<uInt>(file_time_parts.year)};
270   }
271 
272   return zip_info;
273 }
274 }  // namespace
275 
276 namespace zip {
277 namespace internal {
278 
OpenForUnzipping(const std::string& file_name_utf8)279 unzFile OpenForUnzipping(const std::string& file_name_utf8) {
280   zlib_filefunc64_def* zip_func_ptrs = nullptr;
281 #if defined(OS_WIN)
282   zlib_filefunc64_def zip_funcs;
283   fill_win32_filefunc64(&zip_funcs);
284   zip_funcs.zopen64_file = ZipOpenFunc;
285   zip_func_ptrs = &zip_funcs;
286 #endif
287   return unzOpen2_64(file_name_utf8.c_str(), zip_func_ptrs);
288 }
289 
290 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
OpenFdForUnzipping(int zip_fd)291 unzFile OpenFdForUnzipping(int zip_fd) {
292   zlib_filefunc64_def zip_funcs;
293   FillFdOpenFileFunc(&zip_funcs, zip_fd);
294   // Passing dummy "fd" filename to zlib.
295   return unzOpen2_64("fd", &zip_funcs);
296 }
297 #endif
298 
299 #if defined(OS_WIN)
OpenHandleForUnzipping(HANDLE zip_handle)300 unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
301   zlib_filefunc64_def zip_funcs;
302   fill_win32_filefunc64(&zip_funcs);
303   zip_funcs.zopen64_file = HandleOpenFileFunc;
304   zip_funcs.zclose_file = HandleCloseFileFunc;
305   zip_funcs.opaque = zip_handle;
306   return unzOpen2_64("fd", &zip_funcs);
307 }
308 #endif
309 
310 // static
PrepareMemoryForUnzipping(const std::string& data)311 unzFile PrepareMemoryForUnzipping(const std::string& data) {
312   if (data.empty())
313     return NULL;
314 
315   ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
316   if (!buffer)
317     return NULL;
318   buffer->data = data.data();
319   buffer->length = data.length();
320   buffer->offset = 0;
321 
322   zlib_filefunc64_def zip_functions;
323   zip_functions.zopen64_file = OpenZipBuffer;
324   zip_functions.zread_file = ReadZipBuffer;
325   zip_functions.zwrite_file = WriteZipBuffer;
326   zip_functions.ztell64_file = GetOffsetOfZipBuffer;
327   zip_functions.zseek64_file = SeekZipBuffer;
328   zip_functions.zclose_file = CloseZipBuffer;
329   zip_functions.zerror_file = GetErrorOfZipBuffer;
330   zip_functions.opaque = buffer;
331   return unzOpen2_64(nullptr, &zip_functions);
332 }
333 
OpenForZipping(const std::string& file_name_utf8, int append_flag)334 zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) {
335   zlib_filefunc64_def* zip_func_ptrs = nullptr;
336 #if defined(OS_WIN)
337   zlib_filefunc64_def zip_funcs;
338   fill_win32_filefunc64(&zip_funcs);
339   zip_funcs.zopen64_file = ZipOpenFunc;
340   zip_func_ptrs = &zip_funcs;
341 #endif
342   return zipOpen2_64(file_name_utf8.c_str(), append_flag, nullptr,
343                      zip_func_ptrs);
344 }
345 
346 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
OpenFdForZipping(int zip_fd, int append_flag)347 zipFile OpenFdForZipping(int zip_fd, int append_flag) {
348   zlib_filefunc64_def zip_funcs;
349   FillFdOpenFileFunc(&zip_funcs, zip_fd);
350   // Passing dummy "fd" filename to zlib.
351   return zipOpen2_64("fd", append_flag, nullptr, &zip_funcs);
352 }
353 #endif
354 
ZipOpenNewFileInZip(zipFile zip_file, const std::string& str_path, base::Time last_modified_time, Compression compression)355 bool ZipOpenNewFileInZip(zipFile zip_file,
356                          const std::string& str_path,
357                          base::Time last_modified_time,
358                          Compression compression) {
359   // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
360   // Setting the Language encoding flag so the file is told to be in utf-8.
361   const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
362 
363   const zip_fileinfo file_info = TimeToZipFileInfo(last_modified_time);
364   const int err = zipOpenNewFileInZip4_64(
365       /*file=*/zip_file,
366       /*filename=*/str_path.c_str(),
367       /*zip_fileinfo=*/&file_info,
368       /*extrafield_local=*/nullptr,
369       /*size_extrafield_local=*/0u,
370       /*extrafield_global=*/nullptr,
371       /*size_extrafield_global=*/0u,
372       /*comment=*/nullptr,
373       /*method=*/compression,
374       /*level=*/Z_DEFAULT_COMPRESSION,
375       /*raw=*/0,
376       /*windowBits=*/-MAX_WBITS,
377       /*memLevel=*/DEF_MEM_LEVEL,
378       /*strategy=*/Z_DEFAULT_STRATEGY,
379       /*password=*/nullptr,
380       /*crcForCrypting=*/0,
381       /*versionMadeBy=*/0,
382       /*flagBase=*/LANGUAGE_ENCODING_FLAG,
383       /*zip64=*/1);
384 
385   if (err != ZIP_OK) {
386     DLOG(ERROR) << "Cannot open ZIP file entry '" << str_path
387                 << "': zipOpenNewFileInZip4_64 returned " << err;
388     return false;
389   }
390 
391   return true;
392 }
393 
GetCompressionMethod(const base::FilePath& path)394 Compression GetCompressionMethod(const base::FilePath& path) {
395   // Get the filename extension in lower case.
396   const base::FilePath::StringType ext =
397       base::ToLowerASCII(path.FinalExtension());
398 
399   if (ext.empty())
400     return kDeflated;
401 
402   using StringPiece = base::FilePath::StringPieceType;
403 
404   // Skip the leading dot.
405   StringPiece ext_without_dot = ext;
406   DCHECK_EQ(ext_without_dot.front(), FILE_PATH_LITERAL('.'));
407   ext_without_dot.remove_prefix(1);
408 
409   // Well known filename extensions of files that a likely to be already
410   // compressed. The extensions are in lower case without the leading dot.
411   static constexpr auto kExts = base::MakeFixedFlatSet<StringPiece>({
412       FILE_PATH_LITERAL("3g2"),   //
413       FILE_PATH_LITERAL("3gp"),   //
414       FILE_PATH_LITERAL("7z"),    //
415       FILE_PATH_LITERAL("7zip"),  //
416       FILE_PATH_LITERAL("aac"),   //
417       FILE_PATH_LITERAL("avi"),   //
418       FILE_PATH_LITERAL("bz"),    //
419       FILE_PATH_LITERAL("bz2"),   //
420       FILE_PATH_LITERAL("crx"),   //
421       FILE_PATH_LITERAL("gif"),   //
422       FILE_PATH_LITERAL("gz"),    //
423       FILE_PATH_LITERAL("jar"),   //
424       FILE_PATH_LITERAL("jpeg"),  //
425       FILE_PATH_LITERAL("jpg"),   //
426       FILE_PATH_LITERAL("lz"),    //
427       FILE_PATH_LITERAL("m2v"),   //
428       FILE_PATH_LITERAL("m4p"),   //
429       FILE_PATH_LITERAL("m4v"),   //
430       FILE_PATH_LITERAL("mng"),   //
431       FILE_PATH_LITERAL("mov"),   //
432       FILE_PATH_LITERAL("mp2"),   //
433       FILE_PATH_LITERAL("mp3"),   //
434       FILE_PATH_LITERAL("mp4"),   //
435       FILE_PATH_LITERAL("mpe"),   //
436       FILE_PATH_LITERAL("mpeg"),  //
437       FILE_PATH_LITERAL("mpg"),   //
438       FILE_PATH_LITERAL("mpv"),   //
439       FILE_PATH_LITERAL("ogg"),   //
440       FILE_PATH_LITERAL("ogv"),   //
441       FILE_PATH_LITERAL("png"),   //
442       FILE_PATH_LITERAL("qt"),    //
443       FILE_PATH_LITERAL("rar"),   //
444       FILE_PATH_LITERAL("taz"),   //
445       FILE_PATH_LITERAL("tb2"),   //
446       FILE_PATH_LITERAL("tbz"),   //
447       FILE_PATH_LITERAL("tbz2"),  //
448       FILE_PATH_LITERAL("tgz"),   //
449       FILE_PATH_LITERAL("tlz"),   //
450       FILE_PATH_LITERAL("tz"),    //
451       FILE_PATH_LITERAL("tz2"),   //
452       FILE_PATH_LITERAL("vob"),   //
453       FILE_PATH_LITERAL("webm"),  //
454       FILE_PATH_LITERAL("wma"),   //
455       FILE_PATH_LITERAL("wmv"),   //
456       FILE_PATH_LITERAL("xz"),    //
457       FILE_PATH_LITERAL("z"),     //
458       FILE_PATH_LITERAL("zip"),   //
459   });
460 
461   if (kExts.count(ext_without_dot)) {
462     return kStored;
463   }
464 
465   return kDeflated;
466 }
467 
468 }  // namespace internal
469 }  // namespace zip
470