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 #include <unordered_set>
12
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/no_destructor.h"
16 #include "base/notreached.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.tm_year = file_time_parts.year;
264 zip_info.tmz_date.tm_mon = file_time_parts.month - 1;
265 zip_info.tmz_date.tm_mday = file_time_parts.day_of_month;
266 zip_info.tmz_date.tm_hour = file_time_parts.hour;
267 zip_info.tmz_date.tm_min = file_time_parts.minute;
268 zip_info.tmz_date.tm_sec = file_time_parts.second;
269 }
270
271 return zip_info;
272 }
273 } // namespace
274
275 namespace zip {
276 namespace internal {
277
OpenForUnzipping(const std::string & file_name_utf8)278 unzFile OpenForUnzipping(const std::string& file_name_utf8) {
279 zlib_filefunc64_def* zip_func_ptrs = nullptr;
280 #if defined(OS_WIN)
281 zlib_filefunc64_def zip_funcs;
282 fill_win32_filefunc64(&zip_funcs);
283 zip_funcs.zopen64_file = ZipOpenFunc;
284 zip_func_ptrs = &zip_funcs;
285 #endif
286 return unzOpen2_64(file_name_utf8.c_str(), zip_func_ptrs);
287 }
288
289 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
OpenFdForUnzipping(int zip_fd)290 unzFile OpenFdForUnzipping(int zip_fd) {
291 zlib_filefunc64_def zip_funcs;
292 FillFdOpenFileFunc(&zip_funcs, zip_fd);
293 // Passing dummy "fd" filename to zlib.
294 return unzOpen2_64("fd", &zip_funcs);
295 }
296 #endif
297
298 #if defined(OS_WIN)
OpenHandleForUnzipping(HANDLE zip_handle)299 unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
300 zlib_filefunc64_def zip_funcs;
301 fill_win32_filefunc64(&zip_funcs);
302 zip_funcs.zopen64_file = HandleOpenFileFunc;
303 zip_funcs.zclose_file = HandleCloseFileFunc;
304 zip_funcs.opaque = zip_handle;
305 return unzOpen2_64("fd", &zip_funcs);
306 }
307 #endif
308
309 // static
PrepareMemoryForUnzipping(const std::string & data)310 unzFile PrepareMemoryForUnzipping(const std::string& data) {
311 if (data.empty())
312 return NULL;
313
314 ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
315 if (!buffer)
316 return NULL;
317 buffer->data = data.data();
318 buffer->length = data.length();
319 buffer->offset = 0;
320
321 zlib_filefunc64_def zip_functions;
322 zip_functions.zopen64_file = OpenZipBuffer;
323 zip_functions.zread_file = ReadZipBuffer;
324 zip_functions.zwrite_file = WriteZipBuffer;
325 zip_functions.ztell64_file = GetOffsetOfZipBuffer;
326 zip_functions.zseek64_file = SeekZipBuffer;
327 zip_functions.zclose_file = CloseZipBuffer;
328 zip_functions.zerror_file = GetErrorOfZipBuffer;
329 zip_functions.opaque = buffer;
330 return unzOpen2_64(nullptr, &zip_functions);
331 }
332
OpenForZipping(const std::string & file_name_utf8,int append_flag)333 zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) {
334 zlib_filefunc64_def* zip_func_ptrs = nullptr;
335 #if defined(OS_WIN)
336 zlib_filefunc64_def zip_funcs;
337 fill_win32_filefunc64(&zip_funcs);
338 zip_funcs.zopen64_file = ZipOpenFunc;
339 zip_func_ptrs = &zip_funcs;
340 #endif
341 return zipOpen2_64(file_name_utf8.c_str(), append_flag, nullptr,
342 zip_func_ptrs);
343 }
344
345 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
OpenFdForZipping(int zip_fd,int append_flag)346 zipFile OpenFdForZipping(int zip_fd, int append_flag) {
347 zlib_filefunc64_def zip_funcs;
348 FillFdOpenFileFunc(&zip_funcs, zip_fd);
349 // Passing dummy "fd" filename to zlib.
350 return zipOpen2_64("fd", append_flag, nullptr, &zip_funcs);
351 }
352 #endif
353
ZipOpenNewFileInZip(zipFile zip_file,const std::string & str_path,base::Time last_modified_time,Compression compression)354 bool ZipOpenNewFileInZip(zipFile zip_file,
355 const std::string& str_path,
356 base::Time last_modified_time,
357 Compression compression) {
358 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
359 // Setting the Language encoding flag so the file is told to be in utf-8.
360 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
361
362 const zip_fileinfo file_info = TimeToZipFileInfo(last_modified_time);
363 const int err = zipOpenNewFileInZip4_64(
364 /*file=*/zip_file,
365 /*filename=*/str_path.c_str(),
366 /*zip_fileinfo=*/&file_info,
367 /*extrafield_local=*/nullptr,
368 /*size_extrafield_local=*/0u,
369 /*extrafield_global=*/nullptr,
370 /*size_extrafield_global=*/0u,
371 /*comment=*/nullptr,
372 /*method=*/compression,
373 /*level=*/Z_DEFAULT_COMPRESSION,
374 /*raw=*/0,
375 /*windowBits=*/-MAX_WBITS,
376 /*memLevel=*/DEF_MEM_LEVEL,
377 /*strategy=*/Z_DEFAULT_STRATEGY,
378 /*password=*/nullptr,
379 /*crcForCrypting=*/0,
380 /*versionMadeBy=*/0,
381 /*flagBase=*/LANGUAGE_ENCODING_FLAG,
382 /*zip64=*/1);
383
384 if (err != ZIP_OK) {
385 DLOG(ERROR) << "Cannot open ZIP file entry '" << str_path
386 << "': zipOpenNewFileInZip4_64 returned " << err;
387 return false;
388 }
389
390 return true;
391 }
392
GetCompressionMethod(const base::FilePath & path)393 Compression GetCompressionMethod(const base::FilePath& path) {
394 // Get the filename extension in lower case.
395 const base::FilePath::StringType ext =
396 base::ToLowerASCII(path.FinalExtension());
397
398 if (ext.empty())
399 return kDeflated;
400
401 using StringPiece = base::FilePath::StringPieceType;
402
403 // Skip the leading dot.
404 StringPiece ext_without_dot = ext;
405 DCHECK_EQ(ext_without_dot.front(), FILE_PATH_LITERAL('.'));
406 ext_without_dot.remove_prefix(1);
407
408 // Well known filename extensions of files that a likely to be already
409 // compressed. The extensions are in lower case without the leading dot.
410 static const base::NoDestructor<
411 std::unordered_set<StringPiece, base::StringPieceHashImpl<StringPiece>>>
412 exts(std::initializer_list<StringPiece>{
413 FILE_PATH_LITERAL("3g2"), //
414 FILE_PATH_LITERAL("3gp"), //
415 FILE_PATH_LITERAL("7z"), //
416 FILE_PATH_LITERAL("7zip"), //
417 FILE_PATH_LITERAL("aac"), //
418 FILE_PATH_LITERAL("avi"), //
419 FILE_PATH_LITERAL("bz"), //
420 FILE_PATH_LITERAL("bz2"), //
421 FILE_PATH_LITERAL("crx"), //
422 FILE_PATH_LITERAL("gif"), //
423 FILE_PATH_LITERAL("gz"), //
424 FILE_PATH_LITERAL("jar"), //
425 FILE_PATH_LITERAL("jpeg"), //
426 FILE_PATH_LITERAL("jpg"), //
427 FILE_PATH_LITERAL("lz"), //
428 FILE_PATH_LITERAL("m2v"), //
429 FILE_PATH_LITERAL("m4p"), //
430 FILE_PATH_LITERAL("m4v"), //
431 FILE_PATH_LITERAL("mng"), //
432 FILE_PATH_LITERAL("mov"), //
433 FILE_PATH_LITERAL("mp2"), //
434 FILE_PATH_LITERAL("mp3"), //
435 FILE_PATH_LITERAL("mp4"), //
436 FILE_PATH_LITERAL("mpe"), //
437 FILE_PATH_LITERAL("mpeg"), //
438 FILE_PATH_LITERAL("mpg"), //
439 FILE_PATH_LITERAL("mpv"), //
440 FILE_PATH_LITERAL("ogg"), //
441 FILE_PATH_LITERAL("ogv"), //
442 FILE_PATH_LITERAL("png"), //
443 FILE_PATH_LITERAL("qt"), //
444 FILE_PATH_LITERAL("rar"), //
445 FILE_PATH_LITERAL("taz"), //
446 FILE_PATH_LITERAL("tb2"), //
447 FILE_PATH_LITERAL("tbz"), //
448 FILE_PATH_LITERAL("tbz2"), //
449 FILE_PATH_LITERAL("tgz"), //
450 FILE_PATH_LITERAL("tlz"), //
451 FILE_PATH_LITERAL("tz"), //
452 FILE_PATH_LITERAL("tz2"), //
453 FILE_PATH_LITERAL("vob"), //
454 FILE_PATH_LITERAL("webm"), //
455 FILE_PATH_LITERAL("wma"), //
456 FILE_PATH_LITERAL("wmv"), //
457 FILE_PATH_LITERAL("xz"), //
458 FILE_PATH_LITERAL("z"), //
459 FILE_PATH_LITERAL("zip"), //
460 });
461
462 if (exts->count(ext_without_dot))
463 return kStored;
464
465 return kDeflated;
466 }
467
468 } // namespace internal
469 } // namespace zip
470