blob: 9cbb78cb58bcb0b9cc3ab235dd929f10d9c0a072 [file] [log] [blame]
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +00001// 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
joaoe@opera.com16b1df32014-05-02 16:50:09 +00005#include "third_party/zlib/google/zip_internal.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +00006
aviaa969482015-12-27 13:36:49 -08007#include <stddef.h>
Piotr Tworek21c6af62020-04-30 07:12:22 +00008#include <string.h>
aviaa969482015-12-27 13:36:49 -08009
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000010#include <algorithm>
11
12#include "base/logging.h"
Hans Wennborg02daed12020-06-19 21:18:47 +000013#include "base/notreached.h"
avi@chromium.org5cb24772013-06-07 22:40:45 +000014#include "base/strings/utf_string_conversions.h"
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000015
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
30namespace {
31
32#if defined(OS_WIN)
33typedef 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.
41void* ZipOpenFunc(void *opaque, const char* filename, int mode) {
scottmg@chromium.org8796c172014-07-31 03:34:57 +000042 DWORD desired_access = 0, creation_disposition = 0;
43 DWORD share_mode = 0, flags_and_attributes = 0;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000044 HANDLE file = 0;
45 void* ret = NULL;
46
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000047 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
avi@chromium.orgc88f4f32013-12-26 07:07:56 +000059 base::string16 filename16 = base::UTF8ToUTF16(filename);
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000060 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.
jeremysspiegela6bba372014-11-19 15:53:16 -080084// Since we do not own the file descriptor, dup it so that we can fdopen/fclose
85// a file stream.
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +000086void* 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
jeremysspiegela6bba372014-11-19 15:53:16 -080097 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 }
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000102
103 return file;
104}
105
jeremysspiegela6bba372014-11-19 15:53:16 -0800106int FdCloseFileFunc(void* opaque, void* stream) {
107 fclose(static_cast<FILE*>(stream));
108 free(opaque); // malloc'ed in FillFdOpenFileFunc()
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000109 return 0;
110}
111
112// Fills |pzlib_filecunc_def| appropriately to handle the zip file
113// referred to by |fd|.
114void FillFdOpenFileFunc(zlib_filefunc_def* pzlib_filefunc_def, int fd) {
115 fill_fopen_filefunc(pzlib_filefunc_def);
116 pzlib_filefunc_def->zopen_file = FdOpenFileFunc;
jeremysspiegela6bba372014-11-19 15:53:16 -0800117 pzlib_filefunc_def->zclose_file = FdCloseFileFunc;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000118 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.
jeremysspiegela6bba372014-11-19 15:53:16 -0800126// Does not take ownership of the handle.
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000127void* 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}
jeremysspiegela6bba372014-11-19 15:53:16 -0800139
140int HandleCloseFileFunc(void* opaque, void* stream) {
141 free(stream); // malloc'ed in HandleOpenFileFunc()
142 return 0;
143}
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000144#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.
149struct 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.)
161void* 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.
175uLong 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.
189uLong 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.
198long 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.
206long 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.
231int 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.
239int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
240 return 0;
241}
242
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000243// Returns a zip_fileinfo struct with the time represented by |file_time|.
244zip_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}
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000265} // namespace
266
267namespace zip {
268namespace internal {
269
270unzFile 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)
282unzFile 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)
291unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
292 zlib_filefunc_def zip_funcs;
293 fill_win32_filefunc(&zip_funcs);
294 zip_funcs.zopen_file = HandleOpenFileFunc;
jeremysspiegela6bba372014-11-19 15:53:16 -0800295 zip_funcs.zclose_file = HandleCloseFileFunc;
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000296 zip_funcs.opaque = zip_handle;
297 return unzOpen2("fd", &zip_funcs);
298}
299#endif
300
301// static
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000302unzFile PrepareMemoryForUnzipping(const std::string& data) {
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000303 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
325zipFile 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)
340zipFile 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
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000348bool ZipOpenNewFileInZip(zipFile zip_file,
349 const std::string& str_path,
Jay Civelli4f9e8e82017-10-13 16:38:08 +0000350 base::Time last_modified_time) {
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000351 // 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
Jay Civelli4f9e8e82017-10-13 16:38:08 +0000355 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
joaoe@opera.com16b1df32014-05-02 16:50:09 +0000374 DLOG(ERROR) << "Could not open zip file entry " << str_path;
375 return false;
376 }
377 return true;
378}
379
alecflett@chromium.orgd6d082e2013-05-03 23:02:57 +0000380} // namespace internal
381} // namespace zip