Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Author: Frank Warmerdam, warmerdam@pobox.com
5 : * Purpose: Adjusted minizip "zip.c" source code for zip services.
6 : *
7 : * Modified version by Even Rouault. :
8 : * - Decoration of symbol names unz* -> cpl_unz*
9 : * - Undef EXPORT so that we are sure the symbols are not exported
10 : * - Remove old C style function prototypes
11 : * - Added CPL* simplified API at bottom.
12 : *
13 : * Original license available in port/LICENCE_minizip
14 : *
15 : *****************************************************************************/
16 :
17 : /* zip.c -- IO on .zip files using zlib
18 : Version 1.1, February 14h, 2010
19 : part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html
20 : )
21 :
22 : Copyright (C) 1998-2010 Gilles Vollant (minizip) (
23 : http://www.winimage.com/zLibDll/minizip.html )
24 :
25 : Modifications for Zip64 support
26 : Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
27 :
28 : For more info read MiniZip_info.txt
29 :
30 : Changes
31 : Oct-2009 - Mathias Svensson - Remove old C style function prototypes
32 : Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file
33 : archives Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring
34 : to get better overview of some functions. Oct-2009 - Mathias Svensson - Added
35 : zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data It is
36 : used when recreating zip archive with RAW when deleting items from a zip.
37 : ZIP64 data is automatically added to items that
38 : needs it, and existing ZIP64 data need to be removed. Oct-2009 - Mathias
39 : Svensson - Added support for BZIP2 as compression mode (bzip2 lib is
40 : required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with
41 : compatibility layer
42 :
43 : Copyright (c) 2010-2018, Even Rouault <even dot rouault at spatialys.com>
44 :
45 : */
46 :
47 : #include "cpl_port.h"
48 : #include "cpl_minizip_zip.h"
49 :
50 : #include <algorithm>
51 : #include <limits>
52 :
53 : #include <cassert>
54 : #include <cstddef>
55 : #include <cstdlib>
56 : #include <cstring>
57 : #if HAVE_FCNTL_H
58 : #include <fcntl.h>
59 : #endif
60 : #include <time.h>
61 :
62 : #include "cpl_conv.h"
63 : #include "cpl_error.h"
64 : #include "cpl_minizip_unzip.h"
65 : #include "cpl_string.h"
66 : #include "cpl_time.h"
67 : #include "cpl_vsi_virtual.h"
68 :
69 : #ifdef NO_ERRNO_H
70 : extern int errno;
71 : #else
72 : #include <errno.h>
73 : #endif
74 :
75 : #ifndef VERSIONMADEBY
76 : #define VERSIONMADEBY (0x0) /* platform dependent */
77 : #endif
78 :
79 : #ifndef Z_BUFSIZE
80 : #define Z_BUFSIZE (16384)
81 : #endif
82 :
83 : #ifndef ALLOC
84 : #define ALLOC(size) (malloc(size))
85 : #endif
86 : #ifndef TRYFREE
87 : #define TRYFREE(p) \
88 : { \
89 : if (p) \
90 : free(p); \
91 : }
92 : #endif
93 :
94 : /*
95 : #define SIZECENTRALDIRITEM (0x2e)
96 : #define SIZEZIPLOCALHEADER (0x1e)
97 : */
98 :
99 : /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined... */
100 :
101 : #ifndef SEEK_CUR
102 : #define SEEK_CUR 1
103 : #endif
104 :
105 : #ifndef SEEK_END
106 : #define SEEK_END 2
107 : #endif
108 :
109 : #ifndef SEEK_SET
110 : #define SEEK_SET 0
111 : #endif
112 :
113 : #ifndef DEF_MEM_LEVEL
114 : #if MAX_MEM_LEVEL >= 8
115 : #define DEF_MEM_LEVEL 8
116 : #else
117 : #define DEF_MEM_LEVEL MAX_MEM_LEVEL
118 : #endif
119 : #endif
120 :
121 : CPL_UNUSED static const char zip_copyright[] =
122 : " zip 1.01 Copyright 1998-2004 Gilles Vollant - "
123 : "http://www.winimage.com/zLibDll";
124 :
125 : #define SIZEDATA_INDATABLOCK (4096 - (4 * 4))
126 :
127 : #define LOCALHEADERMAGIC (0x04034b50)
128 : #define CENTRALHEADERMAGIC (0x02014b50)
129 : #define ENDHEADERMAGIC (0x06054b50)
130 : #define ZIP64ENDHEADERMAGIC (0x6064b50)
131 : #define ZIP64ENDLOCHEADERMAGIC (0x7064b50)
132 :
133 : #define FLAG_LOCALHEADER_OFFSET (0x06)
134 : #define CRC_LOCALHEADER_OFFSET (0x0e)
135 :
136 : #define SIZECENTRALHEADER (0x2e) /* 46 */
137 :
138 : typedef struct linkedlist_datablock_internal_s
139 : {
140 : struct linkedlist_datablock_internal_s *next_datablock;
141 : uLong avail_in_this_block;
142 : uLong filled_in_this_block;
143 : uLong unused; // For future use and alignment.
144 : unsigned char data[SIZEDATA_INDATABLOCK];
145 : } linkedlist_datablock_internal;
146 :
147 : typedef struct linkedlist_data_s
148 : {
149 : linkedlist_datablock_internal *first_block;
150 : linkedlist_datablock_internal *last_block;
151 : } linkedlist_data;
152 :
153 : typedef struct
154 : {
155 : z_stream stream; /* zLib stream structure for inflate */
156 : int stream_initialised; /* 1 is stream is initialized */
157 : uInt pos_in_buffered_data; /* last written byte in buffered_data */
158 :
159 : ZPOS64_T pos_local_header; /* offset of the local header of the file
160 : currently writing */
161 : char *local_header;
162 : uInt size_local_header;
163 : uInt size_local_header_extrafield;
164 :
165 : char *central_header; /* central header data for the current file */
166 : uLong size_centralExtra;
167 : uLong size_centralheader; /* size of the central header for cur file */
168 : uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader
169 : but that are not used */
170 : uLong flag; /* flag of the file currently writing */
171 :
172 : // TODO: What is "wr"? "to"?
173 : int method; /* compression method of file currently wr.*/
174 : int raw; /* 1 for directly writing raw data */
175 : Byte buffered_data[Z_BUFSIZE]; /* buffer contain compressed data to be
176 : written. */
177 : uLong dosDate;
178 : uLong crc32;
179 : int encrypt;
180 : ZPOS64_T pos_zip64extrainfo;
181 : ZPOS64_T totalCompressedData;
182 : ZPOS64_T totalUncompressedData;
183 : #ifndef NOCRYPT
184 : unsigned long keys[3]; /* keys defining the pseudo-random sequence */
185 : const unsigned long *pcrc_32_tab;
186 : int crypt_header_size;
187 : #endif
188 : } curfile64_info;
189 :
190 : typedef struct
191 : {
192 : zlib_filefunc_def z_filefunc;
193 : voidpf filestream; /* IO structure of the zipfile */
194 : linkedlist_data central_dir; /* datablock with central dir in construction*/
195 : int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/
196 : curfile64_info ci; /* info on the file currently writing */
197 :
198 : ZPOS64_T begin_pos; /* position of the beginning of the zipfile */
199 : ZPOS64_T add_position_when_writing_offset;
200 : ZPOS64_T number_entry;
201 : #ifndef NO_ADDFILEINEXISTINGZIP
202 : char *globalcomment;
203 : #endif
204 : int use_cpl_io;
205 : vsi_l_offset vsi_raw_length_before;
206 : VSIVirtualHandle *vsi_deflate_handle;
207 : size_t nChunkSize;
208 : int nThreads;
209 : size_t nOffsetSize;
210 : std::vector<uint8_t> *sozip_index;
211 : } zip64_internal;
212 :
213 : #ifndef NOCRYPT
214 : #define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
215 : #include "crypt.h"
216 : #endif
217 :
218 397 : static linkedlist_datablock_internal *allocate_new_datablock()
219 : {
220 : linkedlist_datablock_internal *ldi;
221 : ldi = static_cast<linkedlist_datablock_internal *>(
222 397 : ALLOC(sizeof(linkedlist_datablock_internal)));
223 397 : if (ldi != nullptr)
224 : {
225 397 : ldi->next_datablock = nullptr;
226 397 : ldi->filled_in_this_block = 0;
227 397 : ldi->avail_in_this_block = SIZEDATA_INDATABLOCK;
228 : }
229 397 : return ldi;
230 : }
231 :
232 856 : static void free_datablock(linkedlist_datablock_internal *ldi)
233 : {
234 856 : while (ldi != nullptr)
235 : {
236 397 : linkedlist_datablock_internal *ldinext = ldi->next_datablock;
237 397 : TRYFREE(ldi);
238 397 : ldi = ldinext;
239 : }
240 459 : }
241 :
242 459 : static void init_linkedlist(linkedlist_data *ll)
243 : {
244 459 : ll->first_block = ll->last_block = nullptr;
245 459 : }
246 :
247 459 : static void free_linkedlist(linkedlist_data *ll)
248 : {
249 459 : free_datablock(ll->first_block);
250 459 : ll->first_block = ll->last_block = nullptr;
251 459 : }
252 :
253 883 : static int add_data_in_datablock(linkedlist_data *ll, const void *buf,
254 : uLong len)
255 : {
256 : linkedlist_datablock_internal *ldi;
257 : const unsigned char *from_copy;
258 :
259 883 : if (ll == nullptr)
260 0 : return ZIP_INTERNALERROR;
261 :
262 883 : if (ll->last_block == nullptr)
263 : {
264 396 : ll->first_block = ll->last_block = allocate_new_datablock();
265 396 : if (ll->first_block == nullptr)
266 0 : return ZIP_INTERNALERROR;
267 : }
268 :
269 883 : ldi = ll->last_block;
270 883 : from_copy = reinterpret_cast<const unsigned char *>(buf);
271 :
272 1767 : while (len > 0)
273 : {
274 : uInt copy_this;
275 : uInt i;
276 : unsigned char *to_copy;
277 :
278 884 : if (ldi->avail_in_this_block == 0)
279 : {
280 1 : ldi->next_datablock = allocate_new_datablock();
281 1 : if (ldi->next_datablock == nullptr)
282 0 : return ZIP_INTERNALERROR;
283 1 : ldi = ldi->next_datablock;
284 1 : ll->last_block = ldi;
285 : }
286 :
287 884 : if (ldi->avail_in_this_block < len)
288 1 : copy_this = static_cast<uInt>(ldi->avail_in_this_block);
289 : else
290 883 : copy_this = static_cast<uInt>(len);
291 :
292 884 : to_copy = &(ldi->data[ldi->filled_in_this_block]);
293 :
294 79063 : for (i = 0; i < copy_this; i++)
295 78179 : *(to_copy + i) = *(from_copy + i);
296 :
297 884 : ldi->filled_in_this_block += copy_this;
298 884 : ldi->avail_in_this_block -= copy_this;
299 884 : from_copy += copy_this;
300 884 : len -= copy_this;
301 : }
302 883 : return ZIP_OK;
303 : }
304 :
305 : /****************************************************************************/
306 :
307 : #ifndef NO_ADDFILEINEXISTINGZIP
308 : /* ===========================================================================
309 : Inputs a long in LSB order to the given file
310 : nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T)
311 : */
312 :
313 5146 : static int zip64local_putValue(const zlib_filefunc_def *pzlib_filefunc_def,
314 : voidpf filestream, ZPOS64_T x, int nbByte)
315 : {
316 : unsigned char buf[8];
317 21884 : for (int n = 0; n < nbByte; n++)
318 : {
319 16738 : buf[n] = static_cast<unsigned char>(x & 0xff);
320 16738 : x >>= 8;
321 : }
322 5146 : if (x != 0)
323 : { /* data overflow - hack for ZIP64 (X Roche) */
324 0 : for (int n = 0; n < nbByte; n++)
325 : {
326 0 : buf[n] = 0xff;
327 : }
328 : }
329 :
330 5146 : if (ZWRITE64(*pzlib_filefunc_def, filestream, buf, nbByte) !=
331 5146 : static_cast<uLong>(nbByte))
332 38 : return ZIP_ERRNO;
333 : else
334 5108 : return ZIP_OK;
335 : }
336 :
337 22800 : static void zip64local_putValue_inmemory(void *dest, ZPOS64_T x, int nbByte)
338 : {
339 22800 : unsigned char *buf = reinterpret_cast<unsigned char *>(dest);
340 93528 : for (int n = 0; n < nbByte; n++)
341 : {
342 70728 : buf[n] = static_cast<unsigned char>(x & 0xff);
343 70728 : x >>= 8;
344 : }
345 :
346 22800 : if (x != 0)
347 : { /* data overflow - hack for ZIP64 */
348 0 : for (int n = 0; n < nbByte; n++)
349 : {
350 0 : buf[n] = 0xff;
351 : }
352 : }
353 22800 : }
354 :
355 : /****************************************************************************/
356 :
357 747 : static uLong zip64local_TmzDateToDosDate(const tm_zip *ptm)
358 : {
359 747 : uLong year = static_cast<uLong>(ptm->tm_year);
360 747 : if (year > 1980)
361 0 : year -= 1980;
362 747 : else if (year > 80)
363 747 : year -= 80;
364 : return static_cast<uLong>(
365 747 : ((ptm->tm_mday) + (32 * (ptm->tm_mon + 1)) + (512 * year))
366 747 : << 16) |
367 747 : ((ptm->tm_sec / 2) + (32 * ptm->tm_min) +
368 747 : (2048 * static_cast<uLong>(ptm->tm_hour)));
369 : }
370 :
371 : /****************************************************************************/
372 :
373 4532 : static int zip64local_getByte(const zlib_filefunc_def *pzlib_filefunc_def,
374 : voidpf filestream, int *pi)
375 : {
376 4532 : unsigned char c = 0;
377 : const int err =
378 4532 : static_cast<int>(ZREAD64(*pzlib_filefunc_def, filestream, &c, 1));
379 4532 : if (err == 1)
380 : {
381 4510 : *pi = static_cast<int>(c);
382 4510 : return ZIP_OK;
383 : }
384 : else
385 : {
386 22 : if (ZERROR64(*pzlib_filefunc_def, filestream))
387 0 : return ZIP_ERRNO;
388 : else
389 22 : return ZIP_EOF;
390 : }
391 : }
392 :
393 : /* ===========================================================================
394 : Reads a long in LSB order from the given gz_stream. Sets
395 : */
396 1030 : static int zip64local_getShort(const zlib_filefunc_def *pzlib_filefunc_def,
397 : voidpf filestream, uLong *pX)
398 : {
399 1030 : int i = 0;
400 1030 : int err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
401 1030 : uLong x = static_cast<uLong>(i);
402 :
403 1030 : if (err == ZIP_OK)
404 1030 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
405 1030 : x += static_cast<uLong>(i) << 8;
406 :
407 1030 : if (err == ZIP_OK)
408 1030 : *pX = x;
409 : else
410 0 : *pX = 0;
411 1030 : return err;
412 : }
413 :
414 618 : static int zip64local_getLong(const zlib_filefunc_def *pzlib_filefunc_def,
415 : voidpf filestream, uLong *pX)
416 : {
417 618 : int i = 0;
418 618 : int err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
419 618 : uLong x = static_cast<uLong>(i);
420 :
421 618 : if (err == ZIP_OK)
422 618 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
423 618 : x += static_cast<uLong>(i) << 8;
424 :
425 618 : if (err == ZIP_OK)
426 618 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
427 618 : x += static_cast<uLong>(i) << 16;
428 :
429 618 : if (err == ZIP_OK)
430 618 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
431 618 : x += static_cast<uLong>(i) << 24;
432 :
433 618 : if (err == ZIP_OK)
434 618 : *pX = x;
435 : else
436 0 : *pX = 0;
437 618 : return err;
438 : }
439 :
440 0 : static int zip64local_getLong64(const zlib_filefunc_def *pzlib_filefunc_def,
441 : voidpf filestream, ZPOS64_T *pX)
442 : {
443 : ZPOS64_T x;
444 0 : int i = 0;
445 : int err;
446 :
447 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
448 0 : x = static_cast<ZPOS64_T>(i);
449 :
450 0 : if (err == ZIP_OK)
451 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
452 0 : x += static_cast<ZPOS64_T>(i) << 8;
453 :
454 0 : if (err == ZIP_OK)
455 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
456 0 : x += static_cast<ZPOS64_T>(i) << 16;
457 :
458 0 : if (err == ZIP_OK)
459 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
460 0 : x += static_cast<ZPOS64_T>(i) << 24;
461 :
462 0 : if (err == ZIP_OK)
463 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
464 0 : x += static_cast<ZPOS64_T>(i) << 32;
465 :
466 0 : if (err == ZIP_OK)
467 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
468 0 : x += static_cast<ZPOS64_T>(i) << 40;
469 :
470 0 : if (err == ZIP_OK)
471 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
472 0 : x += static_cast<ZPOS64_T>(i) << 48;
473 :
474 0 : if (err == ZIP_OK)
475 0 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
476 0 : x += static_cast<ZPOS64_T>(i) << 56;
477 :
478 0 : if (err == ZIP_OK)
479 0 : *pX = x;
480 : else
481 0 : *pX = 0;
482 :
483 0 : return err;
484 : }
485 :
486 : #ifndef BUFREADCOMMENT
487 : #define BUFREADCOMMENT (0x400)
488 : #endif
489 : /*
490 : Locate the Central directory of a zipfile (at the end, just before
491 : the global comment)
492 : */
493 : static ZPOS64_T
494 206 : zip64local_SearchCentralDir(const zlib_filefunc_def *pzlib_filefunc_def,
495 : voidpf filestream)
496 : {
497 206 : ZPOS64_T uMaxBack = 0xffff; /* maximum size of global comment */
498 206 : ZPOS64_T uPosFound = 0;
499 :
500 206 : if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) !=
501 : 0)
502 0 : return 0;
503 :
504 206 : ZPOS64_T uSizeFile = ZTELL64(*pzlib_filefunc_def, filestream);
505 :
506 206 : if (uMaxBack > uSizeFile)
507 206 : uMaxBack = uSizeFile;
508 :
509 : unsigned char *buf =
510 206 : static_cast<unsigned char *>(ALLOC(BUFREADCOMMENT + 4));
511 206 : if (buf == nullptr)
512 0 : return 0;
513 :
514 206 : ZPOS64_T uBackRead = 4;
515 206 : while (uBackRead < uMaxBack)
516 : {
517 205 : if (uBackRead + BUFREADCOMMENT > uMaxBack)
518 126 : uBackRead = uMaxBack;
519 : else
520 79 : uBackRead += BUFREADCOMMENT;
521 205 : ZPOS64_T uReadPos = uSizeFile - uBackRead;
522 :
523 205 : uLong uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos))
524 : ? (BUFREADCOMMENT + 4)
525 : : static_cast<uLong>(uSizeFile - uReadPos);
526 205 : if (ZSEEK64(*pzlib_filefunc_def, filestream, uReadPos,
527 205 : ZLIB_FILEFUNC_SEEK_SET) != 0)
528 0 : break;
529 :
530 205 : if (ZREAD64(*pzlib_filefunc_def, filestream, buf, uReadSize) !=
531 : uReadSize)
532 0 : break;
533 :
534 3895 : for (int i = static_cast<int>(uReadSize) - 3; (i--) > 0;)
535 3895 : if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) &&
536 205 : ((*(buf + i + 2)) == 0x05) && ((*(buf + i + 3)) == 0x06))
537 : {
538 205 : uPosFound = uReadPos + i;
539 205 : break;
540 : }
541 :
542 205 : if (uPosFound != 0)
543 205 : break;
544 : }
545 206 : TRYFREE(buf);
546 206 : return uPosFound;
547 : }
548 :
549 : /*
550 : Locate the End of Zip64 Central directory locator and from there find the CD of
551 : a zipfile (at the end, just before the global comment)
552 : */
553 : static ZPOS64_T
554 206 : zip64local_SearchCentralDir64(const zlib_filefunc_def *pzlib_filefunc_def,
555 : voidpf filestream)
556 : {
557 : unsigned char *buf;
558 : ZPOS64_T uSizeFile;
559 : ZPOS64_T uBackRead;
560 206 : ZPOS64_T uMaxBack = 0xffff; /* maximum size of global comment */
561 206 : ZPOS64_T uPosFound = 0;
562 : uLong uL;
563 : ZPOS64_T relativeOffset;
564 :
565 206 : if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) !=
566 : 0)
567 0 : return 0;
568 :
569 206 : uSizeFile = ZTELL64(*pzlib_filefunc_def, filestream);
570 :
571 206 : if (uMaxBack > uSizeFile)
572 206 : uMaxBack = uSizeFile;
573 :
574 206 : buf = static_cast<unsigned char *>(ALLOC(BUFREADCOMMENT + 4));
575 206 : if (buf == nullptr)
576 0 : return 0;
577 :
578 206 : uBackRead = 4;
579 491 : while (uBackRead < uMaxBack)
580 : {
581 : uLong uReadSize;
582 : ZPOS64_T uReadPos;
583 : int i;
584 285 : if (uBackRead + BUFREADCOMMENT > uMaxBack)
585 205 : uBackRead = uMaxBack;
586 : else
587 80 : uBackRead += BUFREADCOMMENT;
588 285 : uReadPos = uSizeFile - uBackRead;
589 :
590 285 : uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos))
591 : ? (BUFREADCOMMENT + 4)
592 : : static_cast<uLong>(uSizeFile - uReadPos);
593 285 : if (ZSEEK64(*pzlib_filefunc_def, filestream, uReadPos,
594 285 : ZLIB_FILEFUNC_SEEK_SET) != 0)
595 0 : break;
596 :
597 285 : if (ZREAD64(*pzlib_filefunc_def, filestream, buf, uReadSize) !=
598 : uReadSize)
599 0 : break;
600 :
601 218010 : for (i = static_cast<int>(uReadSize) - 3; (i--) > 0;)
602 : {
603 : // Signature "0x07064b50" Zip64 end of central directory locater
604 217725 : if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) &&
605 1773 : ((*(buf + i + 2)) == 0x06) && ((*(buf + i + 3)) == 0x07))
606 : {
607 0 : uPosFound = uReadPos + i;
608 0 : break;
609 : }
610 : }
611 :
612 285 : if (uPosFound != 0)
613 0 : break;
614 : }
615 :
616 206 : TRYFREE(buf);
617 206 : if (uPosFound == 0)
618 206 : return 0;
619 :
620 : /* Zip64 end of central directory locator */
621 0 : if (ZSEEK64(*pzlib_filefunc_def, filestream, uPosFound,
622 0 : ZLIB_FILEFUNC_SEEK_SET) != 0)
623 0 : return 0;
624 :
625 : /* the signature, already checked */
626 0 : if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
627 0 : return 0;
628 :
629 : /* number of the disk with the start of the zip64 end of central directory
630 : */
631 0 : if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
632 0 : return 0;
633 0 : if (uL != 0)
634 0 : return 0;
635 :
636 : /* relative offset of the zip64 end of central directory record */
637 0 : if (zip64local_getLong64(pzlib_filefunc_def, filestream, &relativeOffset) !=
638 : ZIP_OK)
639 0 : return 0;
640 :
641 : /* total number of disks */
642 0 : if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
643 0 : return 0;
644 : /* Some .zip declare 0 disks, such as in
645 : * http://trac.osgeo.org/gdal/ticket/5615 */
646 0 : if (uL != 0 && uL != 1)
647 0 : return 0;
648 :
649 : /* Goto Zip64 end of central directory record */
650 0 : if (ZSEEK64(*pzlib_filefunc_def, filestream, relativeOffset,
651 0 : ZLIB_FILEFUNC_SEEK_SET) != 0)
652 0 : return 0;
653 :
654 : /* the signature */
655 0 : if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
656 0 : return 0;
657 :
658 0 : if (uL != 0x06064b50) // signature of 'Zip64 end of central directory'
659 0 : return 0;
660 :
661 0 : return relativeOffset;
662 : }
663 :
664 206 : static int LoadCentralDirectoryRecord(zip64_internal *pziinit)
665 : {
666 206 : int err = ZIP_OK;
667 : ZPOS64_T byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx)*/
668 :
669 : ZPOS64_T size_central_dir; /* size of the central directory */
670 : ZPOS64_T offset_central_dir; /* offset of start of central directory */
671 : ZPOS64_T central_pos;
672 : uLong uL;
673 :
674 : uLong number_disk; /* number of the current dist, used for
675 : spanning ZIP, unsupported, always 0*/
676 : uLong number_disk_with_CD; /* number the disk with central dir, used
677 : for spanning ZIP, unsupported, always 0*/
678 : ZPOS64_T number_entry;
679 : ZPOS64_T number_entry_CD; /* total number of entries in
680 : the central dir
681 : (same than number_entry on nospan) */
682 : uLong VersionMadeBy;
683 : uLong VersionNeeded;
684 : uLong size_comment;
685 :
686 206 : int hasZIP64Record = 0;
687 :
688 : // check first if we find a ZIP64 record
689 206 : central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,
690 : pziinit->filestream);
691 206 : if (central_pos > 0)
692 : {
693 0 : hasZIP64Record = 1;
694 : }
695 : else /* if (central_pos == 0) */
696 : {
697 206 : central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,
698 : pziinit->filestream);
699 : }
700 :
701 : /* disable to allow appending to empty ZIP archive
702 : if (central_pos==0)
703 : err=ZIP_ERRNO;
704 : */
705 :
706 206 : if (hasZIP64Record)
707 : {
708 : ZPOS64_T sizeEndOfCentralDirectory;
709 0 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,
710 0 : ZLIB_FILEFUNC_SEEK_SET) != 0)
711 0 : err = ZIP_ERRNO;
712 :
713 : /* the signature, already checked */
714 0 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
715 0 : &uL) != ZIP_OK)
716 0 : err = ZIP_ERRNO;
717 :
718 : /* size of zip64 end of central directory record */
719 0 : if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,
720 0 : &sizeEndOfCentralDirectory) != ZIP_OK)
721 0 : err = ZIP_ERRNO;
722 :
723 : /* version made by */
724 0 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
725 0 : &VersionMadeBy) != ZIP_OK)
726 0 : err = ZIP_ERRNO;
727 :
728 : /* version needed to extract */
729 0 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
730 0 : &VersionNeeded) != ZIP_OK)
731 0 : err = ZIP_ERRNO;
732 :
733 : /* number of this disk */
734 0 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
735 0 : &number_disk) != ZIP_OK)
736 0 : err = ZIP_ERRNO;
737 :
738 : /* number of the disk with the start of the central directory */
739 0 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
740 0 : &number_disk_with_CD) != ZIP_OK)
741 0 : err = ZIP_ERRNO;
742 :
743 : /* total number of entries in the central directory on this disk */
744 0 : if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,
745 0 : &number_entry) != ZIP_OK)
746 0 : err = ZIP_ERRNO;
747 :
748 : /* total number of entries in the central directory */
749 0 : if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,
750 0 : &number_entry_CD) != ZIP_OK)
751 0 : err = ZIP_ERRNO;
752 :
753 0 : if ((number_entry_CD != number_entry) || (number_disk_with_CD != 0) ||
754 0 : (number_disk != 0))
755 0 : err = ZIP_BADZIPFILE;
756 :
757 : /* size of the central directory */
758 0 : if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,
759 0 : &size_central_dir) != ZIP_OK)
760 0 : err = ZIP_ERRNO;
761 :
762 : /* offset of start of central directory with respect to the
763 : starting disk number */
764 0 : if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,
765 0 : &offset_central_dir) != ZIP_OK)
766 0 : err = ZIP_ERRNO;
767 :
768 : // TODO..
769 : // read the comment from the standard central header.
770 0 : size_comment = 0;
771 : }
772 : else
773 : {
774 : // Read End of central Directory info
775 206 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,
776 206 : ZLIB_FILEFUNC_SEEK_SET) != 0)
777 0 : err = ZIP_ERRNO;
778 :
779 : /* the signature, already checked */
780 206 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
781 206 : &uL) != ZIP_OK)
782 0 : err = ZIP_ERRNO;
783 :
784 : /* number of this disk */
785 206 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
786 206 : &number_disk) != ZIP_OK)
787 0 : err = ZIP_ERRNO;
788 :
789 : /* number of the disk with the start of the central directory */
790 206 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
791 206 : &number_disk_with_CD) != ZIP_OK)
792 0 : err = ZIP_ERRNO;
793 :
794 : /* total number of entries in the central dir on this disk */
795 206 : number_entry = 0;
796 206 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
797 206 : &uL) != ZIP_OK)
798 0 : err = ZIP_ERRNO;
799 : else
800 206 : number_entry = uL;
801 :
802 : /* total number of entries in the central dir */
803 206 : number_entry_CD = 0;
804 206 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
805 206 : &uL) != ZIP_OK)
806 0 : err = ZIP_ERRNO;
807 : else
808 206 : number_entry_CD = uL;
809 :
810 206 : if ((number_entry_CD != number_entry) || (number_disk_with_CD != 0) ||
811 206 : (number_disk != 0))
812 0 : err = ZIP_BADZIPFILE;
813 :
814 : /* size of the central directory */
815 206 : size_central_dir = 0;
816 206 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
817 206 : &uL) != ZIP_OK)
818 0 : err = ZIP_ERRNO;
819 : else
820 206 : size_central_dir = uL;
821 :
822 : /* offset of start of central directory with respect to the starting
823 : * disk number */
824 206 : offset_central_dir = 0;
825 206 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
826 206 : &uL) != ZIP_OK)
827 0 : err = ZIP_ERRNO;
828 : else
829 206 : offset_central_dir = uL;
830 :
831 : /* zipfile global comment length */
832 206 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
833 206 : &size_comment) != ZIP_OK)
834 0 : err = ZIP_ERRNO;
835 : }
836 :
837 206 : if ((central_pos < offset_central_dir + size_central_dir) &&
838 : (err == ZIP_OK))
839 0 : err = ZIP_BADZIPFILE;
840 :
841 206 : if (err != ZIP_OK)
842 : {
843 0 : ZCLOSE64(pziinit->z_filefunc, pziinit->filestream);
844 0 : return ZIP_ERRNO;
845 : }
846 :
847 206 : if (size_comment > 0)
848 : {
849 0 : pziinit->globalcomment = static_cast<char *>(ALLOC(size_comment + 1));
850 0 : if (pziinit->globalcomment)
851 : {
852 0 : size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream,
853 : pziinit->globalcomment, size_comment);
854 0 : pziinit->globalcomment[size_comment] = 0;
855 : }
856 : }
857 :
858 206 : byte_before_the_zipfile =
859 206 : central_pos - (offset_central_dir + size_central_dir);
860 206 : pziinit->add_position_when_writing_offset = byte_before_the_zipfile;
861 :
862 : {
863 206 : ZPOS64_T size_central_dir_to_read = size_central_dir;
864 206 : size_t buf_size = SIZEDATA_INDATABLOCK;
865 206 : void *buf_read = ALLOC(buf_size);
866 206 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream,
867 : offset_central_dir + byte_before_the_zipfile,
868 206 : ZLIB_FILEFUNC_SEEK_SET) != 0)
869 0 : err = ZIP_ERRNO;
870 :
871 411 : while ((size_central_dir_to_read > 0) && (err == ZIP_OK))
872 : {
873 205 : ZPOS64_T read_this = SIZEDATA_INDATABLOCK;
874 205 : if (read_this > size_central_dir_to_read)
875 205 : read_this = size_central_dir_to_read;
876 :
877 205 : if (ZREAD64(pziinit->z_filefunc, pziinit->filestream, buf_read,
878 205 : static_cast<uLong>(read_this)) != read_this)
879 0 : err = ZIP_ERRNO;
880 :
881 205 : if (err == ZIP_OK)
882 205 : err = add_data_in_datablock(&pziinit->central_dir, buf_read,
883 : static_cast<uLong>(read_this));
884 :
885 205 : size_central_dir_to_read -= read_this;
886 : }
887 206 : TRYFREE(buf_read);
888 : }
889 206 : pziinit->begin_pos = byte_before_the_zipfile;
890 206 : pziinit->number_entry = number_entry_CD;
891 :
892 206 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream,
893 : offset_central_dir + byte_before_the_zipfile,
894 206 : ZLIB_FILEFUNC_SEEK_SET) != 0)
895 0 : err = ZIP_ERRNO;
896 :
897 206 : return err;
898 : }
899 :
900 : #endif /* !NO_ADDFILEINEXISTINGZIP*/
901 :
902 : /************************************************************/
903 463 : extern zipFile ZEXPORT cpl_zipOpen2(const char *pathname, int append,
904 : zipcharpc *globalcomment,
905 : zlib_filefunc_def *pzlib_filefunc_def)
906 : {
907 : zip64_internal ziinit;
908 463 : memset(&ziinit, 0, sizeof(ziinit));
909 :
910 463 : if (pzlib_filefunc_def == nullptr)
911 463 : cpl_fill_fopen_filefunc(&ziinit.z_filefunc);
912 : else
913 0 : ziinit.z_filefunc = *pzlib_filefunc_def;
914 :
915 463 : ziinit.filestream = (*(ziinit.z_filefunc.zopen_file))(
916 : ziinit.z_filefunc.opaque, pathname,
917 : (append == APPEND_STATUS_CREATE)
918 : ? (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE |
919 : ZLIB_FILEFUNC_MODE_CREATE)
920 : : (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE |
921 : ZLIB_FILEFUNC_MODE_EXISTING));
922 :
923 463 : if (ziinit.filestream == nullptr)
924 4 : return nullptr;
925 :
926 459 : if (append == APPEND_STATUS_CREATEAFTER)
927 0 : ZSEEK64(ziinit.z_filefunc, ziinit.filestream, 0, SEEK_END);
928 :
929 459 : ziinit.begin_pos = ZTELL64(ziinit.z_filefunc, ziinit.filestream);
930 459 : ziinit.in_opened_file_inzip = 0;
931 459 : ziinit.ci.stream_initialised = 0;
932 459 : ziinit.number_entry = 0;
933 459 : ziinit.add_position_when_writing_offset = 0;
934 459 : ziinit.use_cpl_io = (pzlib_filefunc_def == nullptr) ? 1 : 0;
935 459 : ziinit.vsi_raw_length_before = 0;
936 459 : ziinit.vsi_deflate_handle = nullptr;
937 459 : ziinit.nChunkSize = 0;
938 459 : ziinit.nThreads = 0;
939 459 : ziinit.nOffsetSize = 0;
940 459 : ziinit.sozip_index = nullptr;
941 459 : init_linkedlist(&(ziinit.central_dir));
942 :
943 : zip64_internal *zi =
944 459 : static_cast<zip64_internal *>(ALLOC(sizeof(zip64_internal)));
945 459 : if (zi == nullptr)
946 : {
947 0 : ZCLOSE64(ziinit.z_filefunc, ziinit.filestream);
948 0 : return nullptr;
949 : }
950 :
951 : /* now we add file in a zipfile */
952 : #ifndef NO_ADDFILEINEXISTINGZIP
953 459 : ziinit.globalcomment = nullptr;
954 :
955 459 : int err = ZIP_OK;
956 459 : if (append == APPEND_STATUS_ADDINZIP)
957 : {
958 : // Read and Cache Central Directory Records
959 206 : err = LoadCentralDirectoryRecord(&ziinit);
960 : }
961 :
962 459 : if (globalcomment)
963 : {
964 0 : *globalcomment = ziinit.globalcomment;
965 : }
966 : #endif /* !NO_ADDFILEINEXISTINGZIP*/
967 :
968 459 : if (err != ZIP_OK)
969 : {
970 : #ifndef NO_ADDFILEINEXISTINGZIP
971 0 : TRYFREE(ziinit.globalcomment);
972 : #endif /* !NO_ADDFILEINEXISTINGZIP*/
973 0 : TRYFREE(zi);
974 0 : return nullptr;
975 : }
976 : else
977 : {
978 459 : *zi = ziinit;
979 459 : return static_cast<zipFile>(zi);
980 : }
981 : }
982 :
983 463 : extern zipFile ZEXPORT cpl_zipOpen(const char *pathname, int append)
984 : {
985 463 : return cpl_zipOpen2(pathname, append, nullptr, nullptr);
986 : }
987 :
988 8570 : static void zip64local_putValue_inmemory_update(char **dest, ZPOS64_T x,
989 : int nbByte)
990 : {
991 8570 : zip64local_putValue_inmemory(*dest, x, nbByte);
992 8570 : *dest += nbByte;
993 8570 : }
994 :
995 747 : static int Write_LocalFileHeader(zip64_internal *zi, const char *filename,
996 : uInt size_extrafield_local,
997 : const void *extrafield_local, int zip64)
998 : {
999 : /* write the local header */
1000 747 : int err = ZIP_OK;
1001 747 : uInt size_filename = static_cast<uInt>(strlen(filename));
1002 747 : uInt size_extrafield = size_extrafield_local;
1003 :
1004 747 : if (zip64)
1005 : {
1006 275 : size_extrafield += 20;
1007 : }
1008 :
1009 747 : uInt size_local_header = 30 + size_filename + size_extrafield;
1010 747 : char *local_header = static_cast<char *>(ALLOC(size_local_header));
1011 747 : char *p = local_header;
1012 :
1013 747 : zip64local_putValue_inmemory_update(&p, LOCALHEADERMAGIC, 4);
1014 747 : if (zip64)
1015 275 : zip64local_putValue_inmemory_update(&p, 45,
1016 : 2); /* version needed to extract */
1017 : else
1018 472 : zip64local_putValue_inmemory_update(&p, 20,
1019 : 2); /* version needed to extract */
1020 :
1021 747 : zip64local_putValue_inmemory_update(&p, zi->ci.flag, 2);
1022 :
1023 747 : zip64local_putValue_inmemory_update(&p, zi->ci.method, 2);
1024 :
1025 747 : zip64local_putValue_inmemory_update(&p, zi->ci.dosDate, 4);
1026 :
1027 : // CRC / Compressed size / Uncompressed size will be filled in later and
1028 : // rewritten later
1029 747 : zip64local_putValue_inmemory_update(&p, 0, 4); /* crc 32, unknown */
1030 :
1031 747 : if (zip64)
1032 275 : zip64local_putValue_inmemory_update(&p, 0xFFFFFFFFU,
1033 : 4); /* compressed size, unknown */
1034 : else
1035 472 : zip64local_putValue_inmemory_update(&p, 0,
1036 : 4); /* compressed size, unknown */
1037 :
1038 747 : if (zip64)
1039 275 : zip64local_putValue_inmemory_update(&p, 0xFFFFFFFFU,
1040 : 4); /* uncompressed size, unknown */
1041 : else
1042 472 : zip64local_putValue_inmemory_update(&p, 0,
1043 : 4); /* uncompressed size, unknown */
1044 :
1045 747 : zip64local_putValue_inmemory_update(&p, size_filename, 2);
1046 :
1047 747 : zi->ci.size_local_header_extrafield = size_extrafield;
1048 :
1049 747 : zip64local_putValue_inmemory_update(&p, size_extrafield, 2);
1050 :
1051 747 : if (size_filename > 0)
1052 : {
1053 747 : memcpy(p, filename, size_filename);
1054 747 : p += size_filename;
1055 : }
1056 :
1057 747 : if (size_extrafield_local > 0)
1058 : {
1059 6 : memcpy(p, extrafield_local, size_extrafield_local);
1060 6 : p += size_extrafield_local;
1061 : }
1062 :
1063 747 : if (zip64)
1064 : {
1065 : // write the Zip64 extended info
1066 275 : short HeaderID = 1;
1067 275 : short DataSize = 16;
1068 275 : ZPOS64_T CompressedSize = 0;
1069 275 : ZPOS64_T UncompressedSize = 0;
1070 :
1071 : // Remember position of Zip64 extended info for the local file header.
1072 : // (needed when we update size after done with file)
1073 275 : zi->ci.pos_zip64extrainfo =
1074 275 : ZTELL64(zi->z_filefunc, zi->filestream) + p - local_header;
1075 :
1076 275 : zip64local_putValue_inmemory_update(&p, HeaderID, 2);
1077 275 : zip64local_putValue_inmemory_update(&p, DataSize, 2);
1078 :
1079 275 : zip64local_putValue_inmemory_update(&p, UncompressedSize, 8);
1080 275 : zip64local_putValue_inmemory_update(&p, CompressedSize, 8);
1081 : }
1082 747 : assert(p == local_header + size_local_header);
1083 :
1084 747 : if (ZWRITE64(zi->z_filefunc, zi->filestream, local_header,
1085 747 : size_local_header) != size_local_header)
1086 57 : err = ZIP_ERRNO;
1087 :
1088 747 : zi->ci.local_header = local_header;
1089 747 : zi->ci.size_local_header = size_local_header;
1090 :
1091 747 : return err;
1092 : }
1093 :
1094 747 : extern int ZEXPORT cpl_zipOpenNewFileInZip3(
1095 : zipFile file, const char *filename, const zip_fileinfo *zipfi,
1096 : const void *extrafield_local, uInt size_extrafield_local,
1097 : const void *extrafield_global, uInt size_extrafield_global,
1098 : const char *comment, int method, int level, int raw, int windowBits,
1099 : int memLevel, int strategy, const char *password,
1100 : #ifdef NOCRYPT
1101 : uLong /* crcForCrypting */
1102 : #else
1103 : uLong crcForCrypting
1104 : #endif
1105 : ,
1106 : bool bZip64, bool bIncludeInCentralDirectory)
1107 : {
1108 : zip64_internal *zi;
1109 : uInt size_filename;
1110 : uInt size_comment;
1111 : uInt i;
1112 747 : int err = ZIP_OK;
1113 747 : uLong flagBase = 0;
1114 :
1115 : #ifdef NOCRYPT
1116 747 : if (password != nullptr)
1117 0 : return ZIP_PARAMERROR;
1118 : #endif
1119 :
1120 747 : if (file == nullptr)
1121 0 : return ZIP_PARAMERROR;
1122 747 : if ((method != 0) && (method != Z_DEFLATED))
1123 0 : return ZIP_PARAMERROR;
1124 :
1125 747 : zi = reinterpret_cast<zip64_internal *>(file);
1126 :
1127 747 : if (zi->in_opened_file_inzip == 1)
1128 : {
1129 6 : err = cpl_zipCloseFileInZip(file);
1130 6 : if (err != ZIP_OK)
1131 0 : return err;
1132 : }
1133 :
1134 747 : if (filename == nullptr)
1135 0 : filename = "-";
1136 :
1137 : // The filename and comment length must fit in 16 bits.
1138 747 : if ((filename != nullptr) && (strlen(filename) > 0xffff))
1139 0 : return ZIP_PARAMERROR;
1140 747 : if ((comment != nullptr) && (strlen(comment) > 0xffff))
1141 0 : return ZIP_PARAMERROR;
1142 : // The extra field length must fit in 16 bits. If the member also requires
1143 : // a Zip64 extra block, that will also need to fit within that 16-bit
1144 : // length, but that will be checked for later.
1145 747 : if ((size_extrafield_local > 0xffff) || (size_extrafield_global > 0xffff))
1146 0 : return ZIP_PARAMERROR;
1147 :
1148 747 : if (comment == nullptr)
1149 0 : size_comment = 0;
1150 : else
1151 747 : size_comment = static_cast<uInt>(strlen(comment));
1152 :
1153 747 : size_filename = static_cast<uInt>(strlen(filename));
1154 :
1155 747 : if (zipfi == nullptr)
1156 0 : zi->ci.dosDate = 0;
1157 : else
1158 : {
1159 747 : if (zipfi->dosDate != 0)
1160 0 : zi->ci.dosDate = zipfi->dosDate;
1161 : else
1162 747 : zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date);
1163 : }
1164 :
1165 747 : zi->ci.flag = flagBase;
1166 747 : if ((level == 8) || (level == 9))
1167 0 : zi->ci.flag |= 2;
1168 747 : if (level == 2)
1169 0 : zi->ci.flag |= 4;
1170 747 : if (level == 1)
1171 0 : zi->ci.flag |= 6;
1172 : #ifndef NOCRYPT
1173 : if (password != nullptr)
1174 : zi->ci.flag |= 1;
1175 : #endif
1176 :
1177 747 : zi->ci.crc32 = 0;
1178 747 : zi->ci.method = method;
1179 747 : zi->ci.encrypt = 0;
1180 747 : zi->ci.stream_initialised = 0;
1181 747 : zi->ci.pos_in_buffered_data = 0;
1182 747 : zi->ci.raw = raw;
1183 747 : zi->ci.pos_local_header = ZTELL64(zi->z_filefunc, zi->filestream);
1184 :
1185 747 : if (bIncludeInCentralDirectory)
1186 : {
1187 735 : zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename +
1188 735 : size_extrafield_global + size_comment;
1189 735 : zi->ci.size_centralExtraFree =
1190 : 32; // Extra space we have reserved in case we need to add ZIP64
1191 : // extra info data
1192 :
1193 735 : zi->ci.central_header = static_cast<char *>(ALLOC(static_cast<uInt>(
1194 : zi->ci.size_centralheader + zi->ci.size_centralExtraFree)));
1195 :
1196 735 : zi->ci.size_centralExtra = size_extrafield_global;
1197 735 : zip64local_putValue_inmemory(zi->ci.central_header, CENTRALHEADERMAGIC,
1198 : 4);
1199 : /* version info */
1200 735 : zip64local_putValue_inmemory(zi->ci.central_header + 4, VERSIONMADEBY,
1201 : 2);
1202 735 : zip64local_putValue_inmemory(zi->ci.central_header + 6, 20, 2);
1203 735 : zip64local_putValue_inmemory(zi->ci.central_header + 8,
1204 735 : static_cast<uLong>(zi->ci.flag), 2);
1205 735 : zip64local_putValue_inmemory(zi->ci.central_header + 10,
1206 735 : static_cast<uLong>(zi->ci.method), 2);
1207 735 : zip64local_putValue_inmemory(zi->ci.central_header + 12,
1208 735 : static_cast<uLong>(zi->ci.dosDate), 4);
1209 735 : zip64local_putValue_inmemory(zi->ci.central_header + 16, 0, 4); /*crc*/
1210 735 : zip64local_putValue_inmemory(zi->ci.central_header + 20, 0,
1211 : 4); /*compr size*/
1212 735 : zip64local_putValue_inmemory(zi->ci.central_header + 24, 0,
1213 : 4); /*uncompr size*/
1214 735 : zip64local_putValue_inmemory(zi->ci.central_header + 28,
1215 : static_cast<uLong>(size_filename), 2);
1216 735 : zip64local_putValue_inmemory(zi->ci.central_header + 30,
1217 : static_cast<uLong>(size_extrafield_global),
1218 : 2);
1219 735 : zip64local_putValue_inmemory(zi->ci.central_header + 32,
1220 : static_cast<uLong>(size_comment), 2);
1221 735 : zip64local_putValue_inmemory(zi->ci.central_header + 34, 0,
1222 : 2); /*disk nm start*/
1223 :
1224 735 : if (zipfi == nullptr)
1225 0 : zip64local_putValue_inmemory(zi->ci.central_header + 36, 0, 2);
1226 : else
1227 735 : zip64local_putValue_inmemory(zi->ci.central_header + 36,
1228 735 : static_cast<uLong>(zipfi->internal_fa),
1229 : 2);
1230 :
1231 735 : if (zipfi == nullptr)
1232 0 : zip64local_putValue_inmemory(zi->ci.central_header + 38, 0, 4);
1233 : else
1234 735 : zip64local_putValue_inmemory(zi->ci.central_header + 38,
1235 735 : static_cast<uLong>(zipfi->external_fa),
1236 : 4);
1237 :
1238 735 : if (zi->ci.pos_local_header >= 0xffffffff)
1239 0 : zip64local_putValue_inmemory(zi->ci.central_header + 42,
1240 : static_cast<uLong>(0xffffffff), 4);
1241 : else
1242 735 : zip64local_putValue_inmemory(
1243 735 : zi->ci.central_header + 42,
1244 735 : static_cast<uLong>(zi->ci.pos_local_header) -
1245 735 : zi->add_position_when_writing_offset,
1246 : 4);
1247 :
1248 13083 : for (i = 0; i < size_filename; i++)
1249 12348 : *(zi->ci.central_header + SIZECENTRALHEADER + i) = *(filename + i);
1250 :
1251 990 : for (i = 0; i < size_extrafield_global; i++)
1252 255 : *(zi->ci.central_header + SIZECENTRALHEADER + size_filename + i) =
1253 255 : *((reinterpret_cast<const char *>(extrafield_global)) + i);
1254 :
1255 735 : for (i = 0; i < size_comment; i++)
1256 0 : *(zi->ci.central_header + SIZECENTRALHEADER + size_filename +
1257 0 : size_extrafield_global + i) = *(comment + i);
1258 735 : if (zi->ci.central_header == nullptr)
1259 0 : return ZIP_INTERNALERROR;
1260 : }
1261 : else
1262 : {
1263 12 : zi->ci.central_header = nullptr;
1264 : }
1265 :
1266 747 : zi->ci.totalCompressedData = 0;
1267 747 : zi->ci.totalUncompressedData = 0;
1268 747 : zi->ci.pos_zip64extrainfo = 0;
1269 :
1270 : // For now default is to generate zip64 extra fields
1271 747 : err = Write_LocalFileHeader(zi, filename, size_extrafield_local,
1272 : extrafield_local, bZip64 ? 1 : 0);
1273 :
1274 747 : zi->ci.stream.avail_in = 0;
1275 747 : zi->ci.stream.avail_out = Z_BUFSIZE;
1276 747 : zi->ci.stream.next_out = zi->ci.buffered_data;
1277 747 : zi->ci.stream.total_in = 0;
1278 747 : zi->ci.stream.total_out = 0;
1279 747 : zi->ci.stream.data_type = Z_UNKNOWN;
1280 :
1281 747 : if ((err == ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1282 : {
1283 639 : zi->ci.stream.zalloc = nullptr;
1284 639 : zi->ci.stream.zfree = nullptr;
1285 639 : zi->ci.stream.opaque = nullptr;
1286 :
1287 639 : if (windowBits > 0)
1288 0 : windowBits = -windowBits;
1289 :
1290 639 : if (zi->use_cpl_io)
1291 : {
1292 639 : auto fpRaw = reinterpret_cast<VSIVirtualHandle *>(zi->filestream);
1293 639 : zi->vsi_raw_length_before = fpRaw->Tell();
1294 639 : zi->vsi_deflate_handle = VSICreateGZipWritable(
1295 : fpRaw, CPL_DEFLATE_TYPE_RAW_DEFLATE, false, zi->nThreads,
1296 : zi->nChunkSize, zi->nOffsetSize, zi->sozip_index);
1297 639 : err = Z_OK;
1298 : }
1299 : else
1300 : {
1301 0 : err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits,
1302 : memLevel, strategy);
1303 : }
1304 :
1305 639 : if (err == Z_OK)
1306 639 : zi->ci.stream_initialised = 1;
1307 : }
1308 : #ifndef NOCRYPT
1309 : zi->ci.crypt_header_size = 0;
1310 : if ((err == Z_OK) && (password != nullptr))
1311 : {
1312 : unsigned char bufHead[RAND_HEAD_LEN];
1313 : unsigned int sizeHead = 0;
1314 : zi->ci.encrypt = 1;
1315 : zi->ci.pcrc_32_tab = get_crc_table();
1316 : /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/
1317 :
1318 : sizeHead = crypthead(password, bufHead, RAND_HEAD_LEN, zi->ci.keys,
1319 : zi->ci.pcrc_32_tab, crcForCrypting);
1320 : zi->ci.crypt_header_size = sizeHead;
1321 :
1322 : if (ZWRITE64(zi->z_filefunc, zi->filestream, bufHead, sizeHead) !=
1323 : sizeHead)
1324 : err = ZIP_ERRNO;
1325 : }
1326 : #endif
1327 :
1328 747 : if (err == Z_OK)
1329 690 : zi->in_opened_file_inzip = 1;
1330 : else
1331 : {
1332 57 : free(zi->ci.central_header);
1333 57 : zi->ci.central_header = nullptr;
1334 57 : free(zi->ci.local_header);
1335 57 : zi->ci.local_header = nullptr;
1336 : }
1337 :
1338 747 : return err;
1339 : }
1340 :
1341 0 : extern int ZEXPORT cpl_zipOpenNewFileInZip2(
1342 : zipFile file, const char *filename, const zip_fileinfo *zipfi,
1343 : const void *extrafield_local, uInt size_extrafield_local,
1344 : const void *extrafield_global, uInt size_extrafield_global,
1345 : const char *comment, int method, int level, int raw)
1346 : {
1347 0 : return cpl_zipOpenNewFileInZip3(
1348 : file, filename, zipfi, extrafield_local, size_extrafield_local,
1349 : extrafield_global, size_extrafield_global, comment, method, level, raw,
1350 0 : -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, nullptr, 0, true, true);
1351 : }
1352 :
1353 0 : extern int ZEXPORT cpl_zipOpenNewFileInZip(
1354 : zipFile file, const char *filename, const zip_fileinfo *zipfi,
1355 : const void *extrafield_local, uInt size_extrafield_local,
1356 : const void *extrafield_global, uInt size_extrafield_global,
1357 : const char *comment, int method, int level)
1358 : {
1359 0 : return cpl_zipOpenNewFileInZip2(
1360 : file, filename, zipfi, extrafield_local, size_extrafield_local,
1361 0 : extrafield_global, size_extrafield_global, comment, method, level, 0);
1362 : }
1363 :
1364 51 : static int zip64FlushWriteBuffer(zip64_internal *zi)
1365 : {
1366 51 : int err = ZIP_OK;
1367 :
1368 51 : if (zi->ci.encrypt != 0)
1369 : {
1370 : #ifndef NOCRYPT
1371 : int t = 0;
1372 : for (uInt i = 0; i < zi->ci.pos_in_buffered_data; i++)
1373 : zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab,
1374 : zi->ci.buffered_data[i], t);
1375 : #endif
1376 : }
1377 51 : if (ZWRITE64(zi->z_filefunc, zi->filestream, zi->ci.buffered_data,
1378 51 : zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data)
1379 0 : err = ZIP_ERRNO;
1380 :
1381 51 : zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data;
1382 51 : zi->ci.totalUncompressedData += zi->ci.stream.total_in;
1383 51 : zi->ci.stream.total_in = 0;
1384 :
1385 51 : zi->ci.pos_in_buffered_data = 0;
1386 51 : return err;
1387 : }
1388 :
1389 211082 : extern int ZEXPORT cpl_zipWriteInFileInZip(zipFile file, const void *buf,
1390 : unsigned len)
1391 : {
1392 211082 : if (file == nullptr)
1393 0 : return ZIP_PARAMERROR;
1394 :
1395 211082 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(file);
1396 :
1397 211082 : if (zi->in_opened_file_inzip == 0)
1398 0 : return ZIP_PARAMERROR;
1399 :
1400 211082 : zi->ci.stream.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(buf));
1401 211082 : zi->ci.stream.avail_in = len;
1402 211082 : zi->ci.crc32 =
1403 211082 : crc32(zi->ci.crc32, reinterpret_cast<const Bytef *>(buf), len);
1404 :
1405 211082 : int err = ZIP_OK;
1406 422164 : while ((err == ZIP_OK) && (zi->ci.stream.avail_in > 0))
1407 : {
1408 211082 : if (zi->ci.stream.avail_out == 0)
1409 : {
1410 0 : if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
1411 0 : err = ZIP_ERRNO;
1412 0 : zi->ci.stream.avail_out = Z_BUFSIZE;
1413 0 : zi->ci.stream.next_out = zi->ci.buffered_data;
1414 : }
1415 :
1416 211082 : if (err != ZIP_OK)
1417 0 : break;
1418 :
1419 211082 : if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1420 : {
1421 211031 : if (zi->vsi_deflate_handle)
1422 : {
1423 211031 : zi->ci.totalUncompressedData += len;
1424 211031 : if (zi->vsi_deflate_handle->Write(buf, 1, len) < len)
1425 34464 : err = ZIP_INTERNALERROR;
1426 211031 : zi->ci.stream.avail_in = 0;
1427 : }
1428 : else
1429 : {
1430 0 : uLong uTotalOutBefore = zi->ci.stream.total_out;
1431 0 : err = deflate(&zi->ci.stream, Z_NO_FLUSH);
1432 0 : zi->ci.pos_in_buffered_data += static_cast<uInt>(
1433 0 : zi->ci.stream.total_out - uTotalOutBefore);
1434 211031 : }
1435 : }
1436 : else
1437 : {
1438 : uInt copy_this;
1439 51 : if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
1440 51 : copy_this = zi->ci.stream.avail_in;
1441 : else
1442 0 : copy_this = zi->ci.stream.avail_out;
1443 11701 : for (uInt i = 0; i < copy_this; i++)
1444 11650 : *((reinterpret_cast<char *>(zi->ci.stream.next_out)) + i) =
1445 11650 : *((reinterpret_cast<const char *>(zi->ci.stream.next_in)) +
1446 11650 : i);
1447 : {
1448 51 : zi->ci.stream.avail_in -= copy_this;
1449 51 : zi->ci.stream.avail_out -= copy_this;
1450 51 : zi->ci.stream.next_in += copy_this;
1451 51 : zi->ci.stream.next_out += copy_this;
1452 51 : zi->ci.stream.total_in += copy_this;
1453 51 : zi->ci.stream.total_out += copy_this;
1454 51 : zi->ci.pos_in_buffered_data += copy_this;
1455 : }
1456 : }
1457 : }
1458 :
1459 211082 : return err;
1460 : }
1461 :
1462 690 : extern int ZEXPORT cpl_zipCloseFileInZipRaw(zipFile file,
1463 : ZPOS64_T uncompressed_size,
1464 : uLong crc32)
1465 : {
1466 690 : if (file == nullptr)
1467 0 : return ZIP_PARAMERROR;
1468 :
1469 690 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(file);
1470 :
1471 690 : if (zi->in_opened_file_inzip == 0)
1472 0 : return ZIP_PARAMERROR;
1473 690 : zi->ci.stream.avail_in = 0;
1474 :
1475 690 : int err = ZIP_OK;
1476 690 : if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1477 : {
1478 639 : if (zi->vsi_deflate_handle)
1479 : {
1480 639 : auto fpRaw = reinterpret_cast<VSIVirtualHandle *>(zi->filestream);
1481 639 : delete zi->vsi_deflate_handle;
1482 639 : zi->vsi_deflate_handle = nullptr;
1483 639 : zi->ci.totalCompressedData =
1484 639 : fpRaw->Tell() - zi->vsi_raw_length_before;
1485 :
1486 639 : if (zi->sozip_index)
1487 : {
1488 13 : uint64_t nVal =
1489 13 : static_cast<uint64_t>(zi->ci.totalCompressedData);
1490 13 : CPL_LSBPTR64(&nVal);
1491 13 : memcpy(zi->sozip_index->data() + 24, &nVal, sizeof(uint64_t));
1492 : }
1493 : }
1494 : else
1495 : {
1496 0 : while (err == ZIP_OK)
1497 : {
1498 0 : if (zi->ci.stream.avail_out == 0)
1499 : {
1500 0 : if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
1501 : {
1502 0 : err = ZIP_ERRNO;
1503 0 : break;
1504 : }
1505 0 : zi->ci.stream.avail_out = Z_BUFSIZE;
1506 0 : zi->ci.stream.next_out = zi->ci.buffered_data;
1507 : }
1508 0 : uLong uTotalOutBefore = zi->ci.stream.total_out;
1509 0 : err = deflate(&zi->ci.stream, Z_FINISH);
1510 0 : zi->ci.pos_in_buffered_data += static_cast<uInt>(
1511 0 : zi->ci.stream.total_out - uTotalOutBefore);
1512 : }
1513 : }
1514 : }
1515 :
1516 690 : if (err == Z_STREAM_END)
1517 0 : err = ZIP_OK; /* this is normal */
1518 :
1519 690 : if ((zi->ci.pos_in_buffered_data > 0) && (err == ZIP_OK))
1520 51 : if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
1521 0 : err = ZIP_ERRNO;
1522 :
1523 690 : if (!zi->use_cpl_io && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1524 : {
1525 0 : err = deflateEnd(&zi->ci.stream);
1526 0 : zi->ci.stream_initialised = 0;
1527 : }
1528 :
1529 690 : if (!zi->ci.raw)
1530 : {
1531 690 : crc32 = static_cast<uLong>(zi->ci.crc32);
1532 690 : uncompressed_size = zi->ci.totalUncompressedData;
1533 : }
1534 690 : ZPOS64_T compressed_size = zi->ci.totalCompressedData;
1535 : #ifndef NOCRYPT
1536 : compressed_size += zi->ci.crypt_header_size;
1537 : #endif
1538 :
1539 : #ifdef disabled
1540 : // Code finally disabled since it causes compatibility issues with
1541 : // libreoffice for .xlsx or .ods files
1542 : if (zi->ci.pos_zip64extrainfo && uncompressed_size < 0xffffffff &&
1543 : compressed_size < 0xffffffff)
1544 : {
1545 : // Update the LocalFileHeader to be a regular one and not a ZIP64 one
1546 : // by removing its trailing 20 bytes, and moving it in the file
1547 : // 20 bytes further of its original position.
1548 :
1549 : ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
1550 :
1551 : if (ZSEEK64(zi->z_filefunc, zi->filestream, zi->ci.pos_local_header,
1552 : ZLIB_FILEFUNC_SEEK_SET) != 0)
1553 : err = ZIP_ERRNO;
1554 :
1555 : // Insert leading padding
1556 : constexpr uInt nZIP64ExtraBytes = 20;
1557 : char padding[nZIP64ExtraBytes];
1558 : memset(padding, 0, sizeof(padding));
1559 : if (ZWRITE64(zi->z_filefunc, zi->filestream, padding,
1560 : nZIP64ExtraBytes) != nZIP64ExtraBytes)
1561 : err = ZIP_ERRNO;
1562 :
1563 : // Correct version needed to extract
1564 : zip64local_putValue_inmemory(zi->ci.local_header + 4, 20, 2);
1565 :
1566 : // Correct extra field length
1567 : zi->ci.size_local_header_extrafield -= nZIP64ExtraBytes;
1568 : zip64local_putValue_inmemory(zi->ci.local_header + 28,
1569 : zi->ci.size_local_header_extrafield, 2);
1570 :
1571 : zi->ci.size_local_header -= nZIP64ExtraBytes;
1572 :
1573 : // Rewrite local header
1574 : if (ZWRITE64(zi->z_filefunc, zi->filestream, zi->ci.local_header,
1575 : zi->ci.size_local_header) != zi->ci.size_local_header)
1576 : err = ZIP_ERRNO;
1577 :
1578 : if (ZSEEK64(zi->z_filefunc, zi->filestream, cur_pos_inzip,
1579 : ZLIB_FILEFUNC_SEEK_SET) != 0)
1580 : err = ZIP_ERRNO;
1581 :
1582 : zi->ci.pos_zip64extrainfo = 0;
1583 :
1584 : // Correct central header offset to local header
1585 : zi->ci.pos_local_header += nZIP64ExtraBytes;
1586 : if (zi->ci.central_header)
1587 : {
1588 : if (zi->ci.pos_local_header >= 0xffffffff)
1589 : zip64local_putValue_inmemory(zi->ci.central_header + 42,
1590 : static_cast<uLong>(0xffffffff), 4);
1591 : else
1592 : zip64local_putValue_inmemory(
1593 : zi->ci.central_header + 42,
1594 : static_cast<uLong>(zi->ci.pos_local_header) -
1595 : zi->add_position_when_writing_offset,
1596 : 4);
1597 : }
1598 : }
1599 : #endif
1600 :
1601 690 : const bool bInCentralHeader = zi->ci.central_header != nullptr;
1602 690 : if (zi->ci.central_header)
1603 : {
1604 : // update Current Item crc and sizes,
1605 678 : if (zi->ci.pos_zip64extrainfo || compressed_size >= 0xffffffff ||
1606 460 : uncompressed_size >= 0xffffffff ||
1607 460 : zi->ci.pos_local_header >= 0xffffffff)
1608 : {
1609 : /*version Made by*/
1610 218 : zip64local_putValue_inmemory(zi->ci.central_header + 4, 45, 2);
1611 : /*version needed*/
1612 218 : zip64local_putValue_inmemory(zi->ci.central_header + 6, 45, 2);
1613 : }
1614 :
1615 678 : zip64local_putValue_inmemory(zi->ci.central_header + 16, crc32,
1616 : 4); /*crc*/
1617 :
1618 678 : const uLong invalidValue = 0xffffffff;
1619 678 : if (compressed_size >= 0xffffffff)
1620 0 : zip64local_putValue_inmemory(zi->ci.central_header + 20,
1621 : invalidValue, 4); /*compr size*/
1622 : else
1623 678 : zip64local_putValue_inmemory(zi->ci.central_header + 20,
1624 : compressed_size, 4); /*compr size*/
1625 :
1626 : /// set internal file attributes field
1627 678 : if (zi->ci.stream.data_type == Z_ASCII)
1628 0 : zip64local_putValue_inmemory(zi->ci.central_header + 36, Z_ASCII,
1629 : 2);
1630 :
1631 678 : if (uncompressed_size >= 0xffffffff)
1632 0 : zip64local_putValue_inmemory(zi->ci.central_header + 24,
1633 : invalidValue, 4); /*uncompr size*/
1634 : else
1635 678 : zip64local_putValue_inmemory(zi->ci.central_header + 24,
1636 : uncompressed_size, 4); /*uncompr size*/
1637 :
1638 678 : short datasize = 0;
1639 : // Add ZIP64 extra info field for uncompressed size
1640 678 : if (uncompressed_size >= 0xffffffff)
1641 0 : datasize += 8;
1642 :
1643 : // Add ZIP64 extra info field for compressed size
1644 678 : if (compressed_size >= 0xffffffff)
1645 0 : datasize += 8;
1646 :
1647 : // Add ZIP64 extra info field for relative offset to local file header
1648 : // of current file
1649 678 : if (zi->ci.pos_local_header >= 0xffffffff)
1650 0 : datasize += 8;
1651 :
1652 678 : if (datasize > 0)
1653 : {
1654 0 : char *p = nullptr;
1655 :
1656 0 : if (static_cast<uLong>(datasize + 4) > zi->ci.size_centralExtraFree)
1657 : {
1658 : // we can not write more data to the buffer that we have room
1659 : // for.
1660 0 : return ZIP_BADZIPFILE;
1661 : }
1662 :
1663 0 : p = zi->ci.central_header + zi->ci.size_centralheader;
1664 :
1665 : // Add Extra Information Header for 'ZIP64 information'
1666 0 : zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID
1667 0 : p += 2;
1668 0 : zip64local_putValue_inmemory(p, datasize, 2); // DataSize
1669 0 : p += 2;
1670 :
1671 0 : if (uncompressed_size >= 0xffffffff)
1672 : {
1673 0 : zip64local_putValue_inmemory(p, uncompressed_size, 8);
1674 0 : p += 8;
1675 : }
1676 :
1677 0 : if (compressed_size >= 0xffffffff)
1678 : {
1679 0 : zip64local_putValue_inmemory(p, compressed_size, 8);
1680 0 : p += 8;
1681 : }
1682 :
1683 0 : if (zi->ci.pos_local_header >= 0xffffffff)
1684 : {
1685 0 : zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8);
1686 : // p += 8;
1687 : }
1688 :
1689 : // Update how much extra free space we got in the memory buffer
1690 : // and increase the centralheader size so the new ZIP64 fields are
1691 : // included ( 4 below is the size of HeaderID and DataSize field )
1692 0 : zi->ci.size_centralExtraFree -= datasize + 4;
1693 0 : zi->ci.size_centralheader += datasize + 4;
1694 :
1695 : // Update the extra info size field
1696 0 : zi->ci.size_centralExtra += datasize + 4;
1697 0 : zip64local_putValue_inmemory(
1698 0 : zi->ci.central_header + 30,
1699 0 : static_cast<uLong>(zi->ci.size_centralExtra), 2);
1700 : }
1701 :
1702 678 : if (err == ZIP_OK)
1703 678 : err = add_data_in_datablock(
1704 678 : &zi->central_dir, zi->ci.central_header,
1705 678 : static_cast<uLong>(zi->ci.size_centralheader));
1706 678 : free(zi->ci.central_header);
1707 678 : zi->ci.central_header = nullptr;
1708 : }
1709 :
1710 690 : free(zi->ci.local_header);
1711 690 : zi->ci.local_header = nullptr;
1712 :
1713 690 : if (err == ZIP_OK)
1714 : {
1715 : // Update the LocalFileHeader with the new values.
1716 :
1717 690 : ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
1718 :
1719 690 : if (ZSEEK64(zi->z_filefunc, zi->filestream,
1720 690 : zi->ci.pos_local_header + 14, ZLIB_FILEFUNC_SEEK_SET) != 0)
1721 0 : err = ZIP_ERRNO;
1722 :
1723 690 : if (err == ZIP_OK)
1724 690 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, crc32,
1725 : 4); /* crc 32, unknown */
1726 :
1727 690 : if (uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff)
1728 : {
1729 0 : if (zi->ci.pos_zip64extrainfo > 0)
1730 : {
1731 : // Update the size in the ZIP64 extended field.
1732 0 : if (ZSEEK64(zi->z_filefunc, zi->filestream,
1733 : zi->ci.pos_zip64extrainfo + 4,
1734 0 : ZLIB_FILEFUNC_SEEK_SET) != 0)
1735 0 : err = ZIP_ERRNO;
1736 :
1737 0 : if (err == ZIP_OK) /* compressed size, unknown */
1738 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1739 : uncompressed_size, 8);
1740 :
1741 0 : if (err == ZIP_OK) /* uncompressed size, unknown */
1742 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1743 : compressed_size, 8);
1744 : }
1745 : else
1746 0 : err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room
1747 : // for zip64 info -> fatal
1748 : }
1749 : else
1750 : {
1751 690 : if (err == ZIP_OK) /* compressed size, unknown */
1752 690 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1753 : compressed_size, 4);
1754 :
1755 690 : if (err == ZIP_OK) /* uncompressed size, unknown */
1756 690 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1757 : uncompressed_size, 4);
1758 : }
1759 690 : if (ZSEEK64(zi->z_filefunc, zi->filestream, cur_pos_inzip,
1760 690 : ZLIB_FILEFUNC_SEEK_SET) != 0)
1761 0 : err = ZIP_ERRNO;
1762 : }
1763 :
1764 690 : if (bInCentralHeader)
1765 678 : zi->number_entry++;
1766 690 : zi->in_opened_file_inzip = 0;
1767 :
1768 690 : return err;
1769 : }
1770 :
1771 690 : extern int ZEXPORT cpl_zipCloseFileInZip(zipFile file)
1772 : {
1773 690 : return cpl_zipCloseFileInZipRaw(file, 0, 0);
1774 : }
1775 :
1776 0 : static int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal *zi,
1777 : ZPOS64_T zip64eocd_pos_inzip)
1778 : {
1779 0 : int err = ZIP_OK;
1780 0 : ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset;
1781 :
1782 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1783 : ZIP64ENDLOCHEADERMAGIC, 4);
1784 :
1785 : /*num disks*/
1786 0 : if (err ==
1787 : ZIP_OK) /* number of the disk with the start of the central directory */
1788 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 4);
1789 :
1790 : /*relative offset*/
1791 0 : if (err == ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */
1792 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, pos, 8);
1793 :
1794 : /*total disks*/ /* Do not support spawning of disk so always say 1 here*/
1795 0 : if (err ==
1796 : ZIP_OK) /* number of the disk with the start of the central directory */
1797 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 1, 4);
1798 :
1799 0 : return err;
1800 : }
1801 :
1802 0 : static int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal *zi,
1803 : uLong size_centraldir,
1804 : ZPOS64_T centraldir_pos_inzip)
1805 : {
1806 0 : int err = ZIP_OK;
1807 :
1808 0 : uLong Zip64DataSize = 44;
1809 :
1810 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1811 : ZIP64ENDHEADERMAGIC, 4);
1812 :
1813 0 : if (err == ZIP_OK) /* size of this 'zip64 end of central directory' */
1814 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1815 : Zip64DataSize, 8); // why ZPOS64_T of this ?
1816 :
1817 0 : if (err == ZIP_OK) /* version made by */
1818 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 45, 2);
1819 :
1820 0 : if (err == ZIP_OK) /* version needed */
1821 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 45, 2);
1822 :
1823 0 : if (err == ZIP_OK) /* number of this disk */
1824 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 4);
1825 :
1826 0 : if (err ==
1827 : ZIP_OK) /* number of the disk with the start of the central directory */
1828 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 4);
1829 :
1830 0 : if (err ==
1831 : ZIP_OK) /* total number of entries in the central dir on this disk */
1832 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1833 : zi->number_entry, 8);
1834 :
1835 0 : if (err == ZIP_OK) /* total number of entries in the central dir */
1836 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1837 : zi->number_entry, 8);
1838 :
1839 0 : if (err == ZIP_OK) /* size of the central directory */
1840 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1841 : size_centraldir, 8);
1842 :
1843 0 : if (err == ZIP_OK) /* offset of start of central directory with respect to
1844 : the starting disk number */
1845 : {
1846 0 : ZPOS64_T pos =
1847 0 : centraldir_pos_inzip - zi->add_position_when_writing_offset;
1848 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, pos, 8);
1849 : }
1850 0 : return err;
1851 : }
1852 :
1853 403 : static int Write_EndOfCentralDirectoryRecord(zip64_internal *zi,
1854 : uLong size_centraldir,
1855 : ZPOS64_T centraldir_pos_inzip)
1856 : {
1857 403 : int err = ZIP_OK;
1858 :
1859 : /*signature*/
1860 : err =
1861 403 : zip64local_putValue(&zi->z_filefunc, zi->filestream, ENDHEADERMAGIC, 4);
1862 :
1863 403 : if (err == ZIP_OK) /* number of this disk */
1864 395 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 2);
1865 :
1866 403 : if (err ==
1867 : ZIP_OK) /* number of the disk with the start of the central directory */
1868 391 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 2);
1869 :
1870 403 : if (err ==
1871 : ZIP_OK) /* total number of entries in the central dir on this disk */
1872 : {
1873 : {
1874 387 : if (zi->number_entry >= 0xFFFF)
1875 : err =
1876 0 : zip64local_putValue(&zi->z_filefunc, zi->filestream, 0xffff,
1877 : 2); // use value in ZIP64 record
1878 : else
1879 387 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1880 : zi->number_entry, 2);
1881 : }
1882 : }
1883 :
1884 403 : if (err == ZIP_OK) /* total number of entries in the central dir */
1885 : {
1886 383 : if (zi->number_entry >= 0xFFFF)
1887 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0xffff,
1888 : 2); // use value in ZIP64 record
1889 : else
1890 383 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1891 : zi->number_entry, 2);
1892 : }
1893 :
1894 403 : if (err == ZIP_OK) /* size of the central directory */
1895 379 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1896 : size_centraldir, 4);
1897 :
1898 403 : if (err == ZIP_OK) /* offset of start of central directory with respect to
1899 : the starting disk number */
1900 : {
1901 371 : ZPOS64_T pos =
1902 371 : centraldir_pos_inzip - zi->add_position_when_writing_offset;
1903 371 : if (pos >= 0xffffffff)
1904 : {
1905 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1906 : 0xffffffff, 4);
1907 : }
1908 : else
1909 371 : err = zip64local_putValue(
1910 371 : &zi->z_filefunc, zi->filestream,
1911 371 : (centraldir_pos_inzip - zi->add_position_when_writing_offset),
1912 : 4);
1913 : }
1914 :
1915 403 : return err;
1916 : }
1917 :
1918 367 : static int Write_GlobalComment(zip64_internal *zi, const char *global_comment)
1919 : {
1920 367 : int err = ZIP_OK;
1921 367 : uInt size_global_comment = 0;
1922 :
1923 367 : if (global_comment != nullptr)
1924 0 : size_global_comment = static_cast<uInt>(strlen(global_comment));
1925 :
1926 367 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1927 : size_global_comment, 2);
1928 :
1929 367 : if (err == ZIP_OK && size_global_comment > 0)
1930 : {
1931 0 : if (ZWRITE64(zi->z_filefunc, zi->filestream, global_comment,
1932 0 : size_global_comment) != size_global_comment)
1933 0 : err = ZIP_ERRNO;
1934 : }
1935 367 : return err;
1936 : }
1937 :
1938 459 : extern int ZEXPORT cpl_zipClose(zipFile file, const char *global_comment)
1939 : {
1940 459 : int err = 0;
1941 459 : uLong size_centraldir = 0;
1942 : ZPOS64_T centraldir_pos_inzip;
1943 : ZPOS64_T pos;
1944 :
1945 459 : if (file == nullptr)
1946 0 : return ZIP_PARAMERROR;
1947 :
1948 459 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(file);
1949 :
1950 459 : if (zi->in_opened_file_inzip == 1)
1951 : {
1952 0 : err = cpl_zipCloseFileInZip(file);
1953 : }
1954 :
1955 : #ifndef NO_ADDFILEINEXISTINGZIP
1956 459 : if (global_comment == nullptr)
1957 459 : global_comment = zi->globalcomment;
1958 : #endif
1959 :
1960 459 : centraldir_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
1961 459 : if (err == ZIP_OK)
1962 : {
1963 459 : linkedlist_datablock_internal *ldi = zi->central_dir.first_block;
1964 856 : while (ldi != nullptr)
1965 : {
1966 397 : if ((err == ZIP_OK) && (ldi->filled_in_this_block > 0))
1967 397 : if (ZWRITE64(zi->z_filefunc, zi->filestream, ldi->data,
1968 397 : ldi->filled_in_this_block) !=
1969 397 : ldi->filled_in_this_block)
1970 56 : err = ZIP_ERRNO;
1971 :
1972 397 : size_centraldir += ldi->filled_in_this_block;
1973 397 : ldi = ldi->next_datablock;
1974 : }
1975 : }
1976 459 : free_linkedlist(&(zi->central_dir));
1977 :
1978 459 : pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
1979 459 : if (pos >= 0xffffffff || zi->number_entry > 0xFFFF)
1980 : {
1981 0 : ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc, zi->filestream);
1982 0 : Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir,
1983 : centraldir_pos_inzip);
1984 :
1985 0 : Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos);
1986 : }
1987 :
1988 459 : if (err == ZIP_OK)
1989 403 : err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir,
1990 : centraldir_pos_inzip);
1991 :
1992 459 : if (err == ZIP_OK)
1993 367 : err = Write_GlobalComment(zi, global_comment);
1994 :
1995 459 : if (ZCLOSE64(zi->z_filefunc, zi->filestream) != 0)
1996 0 : if (err == ZIP_OK)
1997 0 : err = ZIP_ERRNO;
1998 :
1999 : #ifndef NO_ADDFILEINEXISTINGZIP
2000 459 : TRYFREE(zi->globalcomment);
2001 : #endif
2002 459 : TRYFREE(zi);
2003 :
2004 459 : return err;
2005 : }
2006 :
2007 : /************************************************************************/
2008 : /* ==================================================================== */
2009 : /* The following is a simplified CPL API for creating ZIP files */
2010 : /* exported from cpl_conv.h. */
2011 : /* ==================================================================== */
2012 : /************************************************************************/
2013 :
2014 : #include "cpl_minizip_unzip.h"
2015 :
2016 : typedef struct
2017 : {
2018 : zipFile hZip;
2019 : char **papszFilenames;
2020 : } CPLZip;
2021 :
2022 : /************************************************************************/
2023 : /* CPLCreateZip() */
2024 : /************************************************************************/
2025 :
2026 : /** Create ZIP file */
2027 463 : void *CPLCreateZip(const char *pszZipFilename, char **papszOptions)
2028 :
2029 : {
2030 : const bool bAppend =
2031 463 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "APPEND", "FALSE"));
2032 463 : char **papszFilenames = nullptr;
2033 :
2034 463 : if (bAppend)
2035 : {
2036 206 : zipFile unzF = cpl_unzOpen(pszZipFilename);
2037 206 : if (unzF != nullptr)
2038 : {
2039 205 : if (cpl_unzGoToFirstFile(unzF) == UNZ_OK)
2040 : {
2041 394 : do
2042 : {
2043 : char fileName[8193];
2044 : unz_file_info file_info;
2045 599 : cpl_unzGetCurrentFileInfo(unzF, &file_info, fileName,
2046 : sizeof(fileName) - 1, nullptr, 0,
2047 : nullptr, 0);
2048 599 : fileName[sizeof(fileName) - 1] = '\0';
2049 599 : papszFilenames = CSLAddString(papszFilenames, fileName);
2050 599 : } while (cpl_unzGoToNextFile(unzF) == UNZ_OK);
2051 : }
2052 205 : cpl_unzClose(unzF);
2053 : }
2054 : }
2055 :
2056 463 : zipFile hZip = cpl_zipOpen(pszZipFilename, bAppend ? APPEND_STATUS_ADDINZIP
2057 : : APPEND_STATUS_CREATE);
2058 463 : if (hZip == nullptr)
2059 : {
2060 4 : CSLDestroy(papszFilenames);
2061 4 : return nullptr;
2062 : }
2063 :
2064 459 : CPLZip *psZip = static_cast<CPLZip *>(CPLMalloc(sizeof(CPLZip)));
2065 459 : psZip->hZip = hZip;
2066 459 : psZip->papszFilenames = papszFilenames;
2067 459 : return psZip;
2068 : }
2069 :
2070 : /************************************************************************/
2071 : /* CPLCreateFileInZip() */
2072 : /************************************************************************/
2073 :
2074 : /** Create a file in a ZIP file */
2075 750 : CPLErr CPLCreateFileInZip(void *hZip, const char *pszFilename,
2076 : char **papszOptions)
2077 :
2078 : {
2079 750 : if (hZip == nullptr)
2080 0 : return CE_Failure;
2081 :
2082 750 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2083 :
2084 750 : if (CSLFindString(psZip->papszFilenames, pszFilename) >= 0)
2085 : {
2086 3 : CPLError(CE_Failure, CPLE_AppDefined, "%s already exists in ZIP file",
2087 : pszFilename);
2088 3 : return CE_Failure;
2089 : }
2090 :
2091 : const bool bCompressed =
2092 747 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "COMPRESSED", "TRUE"));
2093 :
2094 747 : char *pszCPFilename = nullptr;
2095 1494 : std::vector<GByte> abyExtra;
2096 : // If the filename is not ASCII only, we need an extended field
2097 747 : if (!CPLIsASCII(pszFilename, strlen(pszFilename)))
2098 : {
2099 2 : const char *pszDestEncoding = CPLGetConfigOption("CPL_ZIP_ENCODING",
2100 : #if defined(_WIN32) && !defined(HAVE_ICONV)
2101 : "CP_OEMCP"
2102 : #else
2103 : "CP437"
2104 : #endif
2105 : );
2106 :
2107 : {
2108 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2109 : pszCPFilename =
2110 2 : CPLRecode(pszFilename, CPL_ENC_UTF8, pszDestEncoding);
2111 :
2112 : char *pszBackToUTF8 =
2113 2 : CPLRecode(pszCPFilename, pszDestEncoding, CPL_ENC_UTF8);
2114 2 : if (strcmp(pszBackToUTF8, pszFilename) != 0)
2115 : {
2116 : // If the UTF-8 name cannot be properly encoded to CPL_ZIP_ENCODING,
2117 : // then generate an ASCII name for it where non-ASCII characters
2118 : // are replaced by an hexadecimal representation
2119 2 : std::string s;
2120 21 : for (int i = 0; pszFilename[i]; ++i)
2121 : {
2122 19 : if (static_cast<unsigned char>(pszFilename[i]) <= 127)
2123 : {
2124 4 : s += pszFilename[i];
2125 : }
2126 : else
2127 : {
2128 15 : s += CPLSPrintf("0x%02X", static_cast<unsigned char>(
2129 15 : pszFilename[i]));
2130 : }
2131 : }
2132 2 : CPLFree(pszCPFilename);
2133 2 : pszCPFilename = CPLStrdup(s.c_str());
2134 : }
2135 2 : CPLFree(pszBackToUTF8);
2136 : }
2137 :
2138 : /* Create a Info-ZIP Unicode Path Extra Field (0x7075) */
2139 2 : const size_t nDataLength =
2140 2 : sizeof(GByte) + sizeof(uint32_t) + strlen(pszFilename);
2141 2 : if (abyExtra.size() + 2 * sizeof(uint16_t) + nDataLength >
2142 2 : std::numeric_limits<uint16_t>::max())
2143 : {
2144 0 : CPLError(CE_Warning, CPLE_AppDefined,
2145 : "Too much content to fit in ZIP ExtraField");
2146 : }
2147 : else
2148 : {
2149 2 : const uint16_t nHeaderIdLE = CPL_LSBWORD16(0x7075);
2150 0 : abyExtra.insert(abyExtra.end(),
2151 : reinterpret_cast<const GByte *>(&nHeaderIdLE),
2152 2 : reinterpret_cast<const GByte *>(&nHeaderIdLE) + 2);
2153 2 : const uint16_t nDataLengthLE =
2154 : CPL_LSBWORD16(static_cast<uint16_t>(nDataLength));
2155 : abyExtra.insert(
2156 0 : abyExtra.end(), reinterpret_cast<const GByte *>(&nDataLengthLE),
2157 2 : reinterpret_cast<const GByte *>(&nDataLengthLE) + 2);
2158 2 : const GByte nVersion = 1;
2159 2 : abyExtra.push_back(nVersion);
2160 : const uint32_t nNameCRC32 = static_cast<uint32_t>(
2161 4 : crc32(0, reinterpret_cast<const Bytef *>(pszCPFilename),
2162 2 : static_cast<uInt>(strlen(pszCPFilename))));
2163 2 : const uint32_t nNameCRC32LE = CPL_LSBWORD32(nNameCRC32);
2164 0 : abyExtra.insert(abyExtra.end(),
2165 : reinterpret_cast<const GByte *>(&nNameCRC32LE),
2166 2 : reinterpret_cast<const GByte *>(&nNameCRC32LE) + 4);
2167 0 : abyExtra.insert(abyExtra.end(),
2168 : reinterpret_cast<const GByte *>(pszFilename),
2169 : reinterpret_cast<const GByte *>(pszFilename) +
2170 2 : strlen(pszFilename));
2171 : }
2172 : }
2173 : else
2174 : {
2175 745 : pszCPFilename = CPLStrdup(pszFilename);
2176 : }
2177 :
2178 : const char *pszContentType =
2179 747 : CSLFetchNameValue(papszOptions, "CONTENT_TYPE");
2180 747 : if (pszContentType)
2181 : {
2182 4 : const size_t nDataLength = strlen("KeyValuePairs") + sizeof(GByte) +
2183 : sizeof(uint16_t) + strlen("Content-Type") +
2184 4 : sizeof(uint16_t) + strlen(pszContentType);
2185 4 : if (abyExtra.size() + 2 * sizeof(uint16_t) + nDataLength >
2186 4 : std::numeric_limits<uint16_t>::max())
2187 : {
2188 0 : CPLError(CE_Warning, CPLE_AppDefined,
2189 : "Too much content to fit in ZIP ExtraField");
2190 : }
2191 : else
2192 : {
2193 4 : abyExtra.push_back(GByte('K'));
2194 4 : abyExtra.push_back(GByte('V'));
2195 4 : const uint16_t nDataLengthLE =
2196 : CPL_LSBWORD16(static_cast<uint16_t>(nDataLength));
2197 : abyExtra.insert(
2198 0 : abyExtra.end(), reinterpret_cast<const GByte *>(&nDataLengthLE),
2199 4 : reinterpret_cast<const GByte *>(&nDataLengthLE) + 2);
2200 0 : abyExtra.insert(abyExtra.end(),
2201 : reinterpret_cast<const GByte *>("KeyValuePairs"),
2202 : reinterpret_cast<const GByte *>("KeyValuePairs") +
2203 4 : strlen("KeyValuePairs"));
2204 4 : abyExtra.push_back(1); // number of key/value pairs
2205 4 : const uint16_t nKeyLen =
2206 : CPL_LSBWORD16(static_cast<uint16_t>(strlen("Content-Type")));
2207 0 : abyExtra.insert(abyExtra.end(),
2208 : reinterpret_cast<const GByte *>(&nKeyLen),
2209 4 : reinterpret_cast<const GByte *>(&nKeyLen) + 2);
2210 0 : abyExtra.insert(abyExtra.end(),
2211 : reinterpret_cast<const GByte *>("Content-Type"),
2212 : reinterpret_cast<const GByte *>("Content-Type") +
2213 4 : strlen("Content-Type"));
2214 4 : const uint16_t nValLen =
2215 4 : CPL_LSBWORD16(static_cast<uint16_t>(strlen(pszContentType)));
2216 0 : abyExtra.insert(abyExtra.end(),
2217 : reinterpret_cast<const GByte *>(&nValLen),
2218 4 : reinterpret_cast<const GByte *>(&nValLen) + 2);
2219 0 : abyExtra.insert(abyExtra.end(),
2220 : reinterpret_cast<const GByte *>(pszContentType),
2221 : reinterpret_cast<const GByte *>(
2222 4 : pszContentType + strlen(pszContentType)));
2223 : }
2224 : }
2225 :
2226 747 : const bool bIncludeInCentralDirectory = CPLTestBool(CSLFetchNameValueDef(
2227 : papszOptions, "INCLUDE_IN_CENTRAL_DIRECTORY", "YES"));
2228 747 : const bool bZip64 = CPLTestBool(CSLFetchNameValueDef(
2229 : papszOptions, "ZIP64", CPLGetConfigOption("CPL_CREATE_ZIP64", "ON")));
2230 :
2231 : // Set datetime to write
2232 : zip_fileinfo fileinfo;
2233 747 : memset(&fileinfo, 0, sizeof(fileinfo));
2234 : const char *pszTimeStamp =
2235 747 : CSLFetchNameValueDef(papszOptions, "TIMESTAMP", "NOW");
2236 : GIntBig unixTime =
2237 747 : EQUAL(pszTimeStamp, "NOW")
2238 747 : ? time(nullptr)
2239 110 : : static_cast<GIntBig>(std::strtoll(pszTimeStamp, nullptr, 10));
2240 : struct tm brokenDown;
2241 747 : CPLUnixTimeToYMDHMS(unixTime, &brokenDown);
2242 747 : fileinfo.tmz_date.tm_year = brokenDown.tm_year;
2243 747 : fileinfo.tmz_date.tm_mon = brokenDown.tm_mon;
2244 747 : fileinfo.tmz_date.tm_mday = brokenDown.tm_mday;
2245 747 : fileinfo.tmz_date.tm_hour = brokenDown.tm_hour;
2246 747 : fileinfo.tmz_date.tm_min = brokenDown.tm_min;
2247 747 : fileinfo.tmz_date.tm_sec = brokenDown.tm_sec;
2248 :
2249 2247 : const int nErr = cpl_zipOpenNewFileInZip3(
2250 : psZip->hZip, pszCPFilename, &fileinfo,
2251 753 : abyExtra.empty() ? nullptr : abyExtra.data(),
2252 747 : static_cast<uInt>(abyExtra.size()),
2253 753 : abyExtra.empty() ? nullptr : abyExtra.data(),
2254 747 : static_cast<uInt>(abyExtra.size()), "", bCompressed ? Z_DEFLATED : 0,
2255 : bCompressed ? Z_DEFAULT_COMPRESSION : 0,
2256 : /* raw = */ 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
2257 : /* password = */ nullptr,
2258 : /* crcForCtypting = */ 0, bZip64, bIncludeInCentralDirectory);
2259 :
2260 747 : CPLFree(pszCPFilename);
2261 :
2262 747 : if (nErr != ZIP_OK)
2263 57 : return CE_Failure;
2264 :
2265 690 : if (bIncludeInCentralDirectory)
2266 678 : psZip->papszFilenames =
2267 678 : CSLAddString(psZip->papszFilenames, pszFilename);
2268 :
2269 690 : return CE_None;
2270 : }
2271 :
2272 : /************************************************************************/
2273 : /* CPLWriteFileInZip() */
2274 : /************************************************************************/
2275 :
2276 : /** Write in current file inside a ZIP file */
2277 211082 : CPLErr CPLWriteFileInZip(void *hZip, const void *pBuffer, int nBufferSize)
2278 :
2279 : {
2280 211082 : if (hZip == nullptr)
2281 0 : return CE_Failure;
2282 :
2283 211082 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2284 :
2285 211082 : int nErr = cpl_zipWriteInFileInZip(psZip->hZip, pBuffer,
2286 : static_cast<unsigned int>(nBufferSize));
2287 :
2288 211082 : if (nErr != ZIP_OK)
2289 34464 : return CE_Failure;
2290 :
2291 176618 : return CE_None;
2292 : }
2293 :
2294 : /************************************************************************/
2295 : /* CPLCloseFileInZip() */
2296 : /************************************************************************/
2297 :
2298 : /** Close current file inside ZIP file */
2299 684 : CPLErr CPLCloseFileInZip(void *hZip)
2300 :
2301 : {
2302 684 : if (hZip == nullptr)
2303 0 : return CE_Failure;
2304 :
2305 684 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2306 :
2307 684 : int nErr = cpl_zipCloseFileInZip(psZip->hZip);
2308 :
2309 684 : if (nErr != ZIP_OK)
2310 0 : return CE_Failure;
2311 :
2312 684 : return CE_None;
2313 : }
2314 :
2315 : /************************************************************************/
2316 : /* CPLAddFileInZip() */
2317 : /************************************************************************/
2318 :
2319 : /** Add a file inside a ZIP file opened/created with CPLCreateZip().
2320 : *
2321 : * This combines calls to CPLCreateFileInZip(), CPLWriteFileInZip(),
2322 : * and CPLCloseFileInZip() in a more convenient and powerful way.
2323 : *
2324 : * In particular, this enables to add a compressed file using the seek
2325 : * optimization extension.
2326 : *
2327 : * Supported options are:
2328 : * <ul>
2329 : * <li>SOZIP_ENABLED=AUTO/YES/NO: whether to generate a SOZip index for the
2330 : * file. The default can be changed with the CPL_SOZIP_ENABLED configuration
2331 : * option.</li>
2332 : * <li>SOZIP_CHUNK_SIZE: chunk size to use for SOZip generation. Defaults to
2333 : * 32768.
2334 : * </li>
2335 : * <li>SOZIP_MIN_FILE_SIZE: minimum file size to consider to enable SOZip index
2336 : * generation in SOZIP_ENABLED=AUTO mode. Defaults to 1 MB.
2337 : * </li>
2338 : * <li>NUM_THREADS: number of threads used for SOZip generation. Defaults to
2339 : * ALL_CPUS.</li>
2340 : * <li>TIMESTAMP=AUTO/NOW/timestamp_as_epoch_since_jan_1_1970: in AUTO mode,
2341 : * the timestamp of pszInputFilename will be used (if available), otherwise
2342 : * it will fallback to NOW.</li>
2343 : * <li>CONTENT_TYPE=string: Content-Type value for the file. This is stored as
2344 : * a key-value pair in the extra field extension 'KV' (0x564b) dedicated to
2345 : * storing key-value pair metadata.</li>
2346 : * </ul>
2347 : *
2348 : * @param hZip ZIP file handle
2349 : * @param pszArchiveFilename Filename (in UTF-8) stored in the archive.
2350 : * @param pszInputFilename Filename of the file to add. If NULL, fpInput must
2351 : * not be NULL
2352 : * @param fpInput File handle opened on the file to add. May be NULL if
2353 : * pszInputFilename is provided.
2354 : * @param papszOptions Options.
2355 : * @param pProgressFunc Progress callback, or NULL.
2356 : * @param pProgressData User data of progress callback, or NULL.
2357 : * @return CE_None in case of success.
2358 : *
2359 : * @since GDAL 3.7
2360 : */
2361 102 : CPLErr CPLAddFileInZip(void *hZip, const char *pszArchiveFilename,
2362 : const char *pszInputFilename, VSILFILE *fpInput,
2363 : CSLConstList papszOptions,
2364 : GDALProgressFunc pProgressFunc, void *pProgressData)
2365 : {
2366 102 : if (!hZip || !pszArchiveFilename || (!pszInputFilename && !fpInput))
2367 0 : return CE_Failure;
2368 :
2369 102 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2370 102 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(psZip->hZip);
2371 :
2372 102 : VSIVirtualHandleUniquePtr poFileHandleAutoClose;
2373 102 : if (!fpInput)
2374 : {
2375 101 : fpInput = VSIFOpenL(pszInputFilename, "rb");
2376 101 : if (!fpInput)
2377 1 : return CE_Failure;
2378 100 : poFileHandleAutoClose.reset(fpInput);
2379 : }
2380 :
2381 101 : VSIFSeekL(fpInput, 0, SEEK_END);
2382 101 : const auto nUncompressedSize = VSIFTellL(fpInput);
2383 101 : VSIFSeekL(fpInput, 0, SEEK_SET);
2384 :
2385 202 : CPLStringList aosNewsOptions(papszOptions);
2386 101 : bool bSeekOptimized = false;
2387 : const char *pszSOZIP =
2388 101 : CSLFetchNameValueDef(papszOptions, "SOZIP_ENABLED",
2389 : CPLGetConfigOption("CPL_SOZIP_ENABLED", "AUTO"));
2390 :
2391 101 : const char *pszChunkSize = CSLFetchNameValueDef(
2392 : papszOptions, "SOZIP_CHUNK_SIZE",
2393 : CPLGetConfigOption("CPL_VSIL_DEFLATE_CHUNK_SIZE", nullptr));
2394 101 : const bool bChunkSizeSpecified = pszChunkSize != nullptr;
2395 101 : if (!pszChunkSize)
2396 34 : pszChunkSize = "1024K";
2397 101 : unsigned nChunkSize = static_cast<unsigned>(atoi(pszChunkSize));
2398 101 : if (strchr(pszChunkSize, 'K'))
2399 34 : nChunkSize *= 1024;
2400 67 : else if (strchr(pszChunkSize, 'M'))
2401 0 : nChunkSize *= 1024 * 1024;
2402 101 : nChunkSize =
2403 202 : std::max(static_cast<unsigned>(1),
2404 101 : std::min(static_cast<unsigned>(UINT_MAX), nChunkSize));
2405 :
2406 101 : const char *pszMinFileSize = CSLFetchNameValueDef(
2407 : papszOptions, "SOZIP_MIN_FILE_SIZE",
2408 : CPLGetConfigOption("CPL_SOZIP_MIN_FILE_SIZE", "1M"));
2409 101 : uint64_t nSOZipMinFileSize = std::strtoull(pszMinFileSize, nullptr, 10);
2410 101 : if (strchr(pszMinFileSize, 'K'))
2411 0 : nSOZipMinFileSize *= 1024;
2412 101 : else if (strchr(pszMinFileSize, 'M'))
2413 99 : nSOZipMinFileSize *= 1024 * 1024;
2414 2 : else if (strchr(pszMinFileSize, 'G'))
2415 0 : nSOZipMinFileSize *= 1024 * 1024 * 1024;
2416 :
2417 202 : std::vector<uint8_t> sozip_index;
2418 101 : uint64_t nExpectedIndexSize = 0;
2419 101 : constexpr unsigned nDefaultSOZipChunkSize = 32 * 1024;
2420 101 : constexpr size_t nOffsetSize = 8;
2421 95 : if (((EQUAL(pszSOZIP, "AUTO") && nUncompressedSize > nSOZipMinFileSize) ||
2422 209 : (!EQUAL(pszSOZIP, "AUTO") && CPLTestBool(pszSOZIP))) &&
2423 11 : ((bChunkSizeSpecified &&
2424 11 : nUncompressedSize > static_cast<unsigned>(nChunkSize)) ||
2425 2 : (!bChunkSizeSpecified && nUncompressedSize > nDefaultSOZipChunkSize)))
2426 : {
2427 13 : if (!bChunkSizeSpecified)
2428 2 : nChunkSize = nDefaultSOZipChunkSize;
2429 :
2430 13 : bSeekOptimized = true;
2431 :
2432 : aosNewsOptions.SetNameValue(
2433 13 : "UNCOMPRESSED_SIZE", CPLSPrintf(CPL_FRMT_GUIB, nUncompressedSize));
2434 :
2435 13 : zi->nOffsetSize = nOffsetSize;
2436 13 : nExpectedIndexSize =
2437 13 : 32 + ((nUncompressedSize - 1) / nChunkSize) * nOffsetSize;
2438 13 : if (nExpectedIndexSize >
2439 13 : static_cast<uint64_t>(std::numeric_limits<int>::max()))
2440 : {
2441 0 : CPLError(CE_Failure, CPLE_AppDefined,
2442 : "Too big file w.r.t CHUNK_SIZE");
2443 0 : return CE_Failure;
2444 : }
2445 : try
2446 : {
2447 13 : sozip_index.reserve(static_cast<size_t>(nExpectedIndexSize));
2448 : }
2449 0 : catch (const std::exception &)
2450 : {
2451 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2452 : "Cannot allocate memory for SOZip index");
2453 0 : return CE_Failure;
2454 : }
2455 13 : sozip_index.resize(32);
2456 : uint32_t nVal32;
2457 : // Version
2458 13 : nVal32 = CPL_LSBWORD32(1);
2459 13 : memcpy(sozip_index.data(), &nVal32, sizeof(nVal32));
2460 : // Extra reserved space after 32 bytes of header
2461 13 : nVal32 = CPL_LSBWORD32(0);
2462 13 : memcpy(sozip_index.data() + 4, &nVal32, sizeof(nVal32));
2463 : // Chunksize
2464 13 : nVal32 = CPL_LSBWORD32(nChunkSize);
2465 13 : memcpy(sozip_index.data() + 8, &nVal32, sizeof(nVal32));
2466 : // SOZIPIndexEltSize
2467 13 : nVal32 = CPL_LSBWORD32(static_cast<uint32_t>(nOffsetSize));
2468 13 : memcpy(sozip_index.data() + 12, &nVal32, sizeof(nVal32));
2469 : // Uncompressed size
2470 13 : uint64_t nVal64 = nUncompressedSize;
2471 13 : CPL_LSBPTR64(&nVal64);
2472 13 : memcpy(sozip_index.data() + 16, &nVal64, sizeof(nVal64));
2473 13 : zi->sozip_index = &sozip_index;
2474 :
2475 13 : zi->nChunkSize = nChunkSize;
2476 :
2477 13 : const char *pszThreads = CSLFetchNameValue(papszOptions, "NUM_THREADS");
2478 13 : if (pszThreads == nullptr || EQUAL(pszThreads, "ALL_CPUS"))
2479 13 : zi->nThreads = CPLGetNumCPUs();
2480 : else
2481 0 : zi->nThreads = atoi(pszThreads);
2482 13 : zi->nThreads = std::max(1, std::min(128, zi->nThreads));
2483 : }
2484 :
2485 : aosNewsOptions.SetNameValue("ZIP64",
2486 101 : nUncompressedSize > 0xFFFFFFFFU ? "YES" : "NO");
2487 :
2488 201 : if (pszInputFilename != nullptr &&
2489 100 : aosNewsOptions.FetchNameValue("TIMESTAMP") == nullptr)
2490 : {
2491 : VSIStatBufL sStat;
2492 100 : if (VSIStatL(pszInputFilename, &sStat) == 0 && sStat.st_mtime != 0)
2493 : {
2494 : aosNewsOptions.SetNameValue(
2495 : "TIMESTAMP",
2496 100 : CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(sStat.st_mtime)));
2497 : }
2498 : }
2499 :
2500 101 : if (CPLCreateFileInZip(hZip, pszArchiveFilename, aosNewsOptions.List()) !=
2501 : CE_None)
2502 : {
2503 1 : zi->sozip_index = nullptr;
2504 1 : zi->nChunkSize = 0;
2505 1 : zi->nThreads = 0;
2506 1 : return CE_Failure;
2507 : }
2508 100 : zi->nChunkSize = 0;
2509 100 : zi->nThreads = 0;
2510 :
2511 100 : constexpr int CHUNK_READ_MAX_SIZE = 1024 * 1024;
2512 200 : std::vector<GByte> abyChunk(CHUNK_READ_MAX_SIZE);
2513 100 : vsi_l_offset nOffset = 0;
2514 : while (true)
2515 : {
2516 : const int nRead = static_cast<int>(
2517 112 : VSIFReadL(abyChunk.data(), 1, abyChunk.size(), fpInput));
2518 224 : if (nRead > 0 &&
2519 112 : CPLWriteFileInZip(hZip, abyChunk.data(), nRead) != CE_None)
2520 : {
2521 0 : CPLCloseFileInZip(hZip);
2522 0 : zi->sozip_index = nullptr;
2523 0 : return CE_Failure;
2524 : }
2525 112 : nOffset += nRead;
2526 122 : if (pProgressFunc &&
2527 20 : !pProgressFunc(nUncompressedSize == 0
2528 : ? 1.0
2529 10 : : double(nOffset) / nUncompressedSize,
2530 : nullptr, pProgressData))
2531 : {
2532 1 : CPLCloseFileInZip(hZip);
2533 1 : zi->sozip_index = nullptr;
2534 1 : return CE_Failure;
2535 : }
2536 111 : if (nRead < CHUNK_READ_MAX_SIZE)
2537 99 : break;
2538 12 : }
2539 :
2540 99 : if (CPLCloseFileInZip(hZip) != CE_None)
2541 : {
2542 0 : zi->sozip_index = nullptr;
2543 0 : return CE_Failure;
2544 : }
2545 :
2546 99 : if (bSeekOptimized && sozip_index.size() != nExpectedIndexSize)
2547 : {
2548 : // shouldn't happen
2549 0 : CPLError(CE_Failure, CPLE_AppDefined,
2550 : "sozip_index.size() (=%u) != nExpectedIndexSize (=%u)",
2551 0 : static_cast<unsigned>(sozip_index.size()),
2552 : static_cast<unsigned>(nExpectedIndexSize));
2553 : }
2554 99 : else if (bSeekOptimized)
2555 : {
2556 12 : std::string osIdxName;
2557 12 : const char *pszLastSlash = strchr(pszArchiveFilename, '/');
2558 12 : if (pszLastSlash)
2559 : {
2560 : osIdxName.assign(pszArchiveFilename,
2561 4 : pszLastSlash - pszArchiveFilename + 1);
2562 4 : osIdxName += '.';
2563 4 : osIdxName += pszLastSlash + 1;
2564 : }
2565 : else
2566 : {
2567 8 : osIdxName = '.';
2568 8 : osIdxName += pszArchiveFilename;
2569 : }
2570 12 : osIdxName += ".sozip.idx";
2571 :
2572 12 : CPLStringList aosIndexOptions;
2573 12 : aosIndexOptions.SetNameValue("COMPRESSED", "NO");
2574 12 : aosIndexOptions.SetNameValue("ZIP64", "NO");
2575 12 : aosIndexOptions.SetNameValue("INCLUDE_IN_CENTRAL_DIRECTORY", "NO");
2576 : aosIndexOptions.SetNameValue(
2577 12 : "TIMESTAMP", aosNewsOptions.FetchNameValue("TIMESTAMP"));
2578 12 : if (CPLCreateFileInZip(hZip, osIdxName.c_str(),
2579 12 : aosIndexOptions.List()) != CE_None)
2580 : {
2581 0 : zi->sozip_index = nullptr;
2582 0 : return CE_Failure;
2583 : }
2584 :
2585 12 : if (CPLWriteFileInZip(hZip, sozip_index.data(),
2586 24 : static_cast<int>(sozip_index.size())) != CE_None)
2587 : {
2588 0 : zi->sozip_index = nullptr;
2589 0 : CPLCloseFileInZip(hZip);
2590 0 : return CE_Failure;
2591 : }
2592 :
2593 12 : zi->sozip_index = nullptr;
2594 12 : if (CPLCloseFileInZip(hZip) != CE_None)
2595 : {
2596 0 : return CE_Failure;
2597 : }
2598 : }
2599 :
2600 99 : zi->sozip_index = nullptr;
2601 :
2602 99 : return CE_None;
2603 : }
2604 :
2605 : /************************************************************************/
2606 : /* CPLCloseZip() */
2607 : /************************************************************************/
2608 :
2609 : /** Close ZIP file */
2610 459 : CPLErr CPLCloseZip(void *hZip)
2611 : {
2612 459 : if (hZip == nullptr)
2613 0 : return CE_Failure;
2614 :
2615 459 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2616 :
2617 459 : int nErr = cpl_zipClose(psZip->hZip, nullptr);
2618 :
2619 459 : psZip->hZip = nullptr;
2620 459 : CSLDestroy(psZip->papszFilenames);
2621 459 : psZip->papszFilenames = nullptr;
2622 459 : CPLFree(psZip);
2623 :
2624 459 : if (nErr != ZIP_OK)
2625 94 : return CE_Failure;
2626 :
2627 365 : return CE_None;
2628 : }
|