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