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 <cinttypes>
55 : #include <cstddef>
56 : #include <cstdlib>
57 : #include <cstring>
58 : #include <fcntl.h>
59 : #include <time.h>
60 :
61 : #include "cpl_conv.h"
62 : #include "cpl_error.h"
63 : #include "cpl_minizip_unzip.h"
64 : #include "cpl_multiproc.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 428 : static linkedlist_datablock_internal *allocate_new_datablock()
219 : {
220 : linkedlist_datablock_internal *ldi;
221 : ldi = static_cast<linkedlist_datablock_internal *>(
222 428 : ALLOC(sizeof(linkedlist_datablock_internal)));
223 428 : if (ldi != nullptr)
224 : {
225 428 : ldi->next_datablock = nullptr;
226 428 : ldi->filled_in_this_block = 0;
227 428 : ldi->avail_in_this_block = SIZEDATA_INDATABLOCK;
228 : }
229 428 : return ldi;
230 : }
231 :
232 918 : static void free_datablock(linkedlist_datablock_internal *ldi)
233 : {
234 918 : while (ldi != nullptr)
235 : {
236 428 : linkedlist_datablock_internal *ldinext = ldi->next_datablock;
237 428 : TRYFREE(ldi);
238 428 : ldi = ldinext;
239 : }
240 490 : }
241 :
242 490 : static void init_linkedlist(linkedlist_data *ll)
243 : {
244 490 : ll->first_block = ll->last_block = nullptr;
245 490 : }
246 :
247 490 : static void free_linkedlist(linkedlist_data *ll)
248 : {
249 490 : free_datablock(ll->first_block);
250 490 : ll->first_block = ll->last_block = nullptr;
251 490 : }
252 :
253 942 : 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 942 : if (ll == nullptr)
260 0 : return ZIP_INTERNALERROR;
261 :
262 942 : if (ll->last_block == nullptr)
263 : {
264 427 : ll->first_block = ll->last_block = allocate_new_datablock();
265 427 : if (ll->first_block == nullptr)
266 0 : return ZIP_INTERNALERROR;
267 : }
268 :
269 942 : ldi = ll->last_block;
270 942 : from_copy = reinterpret_cast<const unsigned char *>(buf);
271 :
272 1885 : while (len > 0)
273 : {
274 : uInt copy_this;
275 : uInt i;
276 : unsigned char *to_copy;
277 :
278 943 : 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 943 : if (ldi->avail_in_this_block < len)
288 1 : copy_this = static_cast<uInt>(ldi->avail_in_this_block);
289 : else
290 942 : copy_this = static_cast<uInt>(len);
291 :
292 943 : to_copy = &(ldi->data[ldi->filled_in_this_block]);
293 :
294 93817 : for (i = 0; i < copy_this; i++)
295 92874 : *(to_copy + i) = *(from_copy + i);
296 :
297 943 : ldi->filled_in_this_block += copy_this;
298 943 : ldi->avail_in_this_block -= copy_this;
299 943 : from_copy += copy_this;
300 943 : len -= copy_this;
301 : }
302 942 : 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 5487 : 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 23279 : for (int n = 0; n < nbByte; n++)
318 : {
319 17792 : buf[n] = static_cast<unsigned char>(x & 0xff);
320 17792 : x >>= 8;
321 : }
322 5487 : 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 5487 : if (ZWRITE64(*pzlib_filefunc_def, filestream, buf, nbByte) !=
331 5487 : static_cast<uLong>(nbByte))
332 38 : return ZIP_ERRNO;
333 : else
334 5449 : return ZIP_OK;
335 : }
336 :
337 23885 : static void zip64local_putValue_inmemory(void *dest, ZPOS64_T x, int nbByte)
338 : {
339 23885 : unsigned char *buf = reinterpret_cast<unsigned char *>(dest);
340 98085 : for (int n = 0; n < nbByte; n++)
341 : {
342 74200 : buf[n] = static_cast<unsigned char>(x & 0xff);
343 74200 : x >>= 8;
344 : }
345 :
346 23885 : 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 23885 : }
354 :
355 : /****************************************************************************/
356 :
357 778 : static uLong zip64local_TmzDateToDosDate(const tm_zip *ptm)
358 : {
359 778 : uLong year = static_cast<uLong>(ptm->tm_year);
360 778 : if (year > 1980)
361 0 : year -= 1980;
362 778 : else if (year > 80)
363 778 : year -= 80;
364 : return static_cast<uLong>(
365 778 : ((ptm->tm_mday) + (32 * (ptm->tm_mon + 1)) + (512 * year))
366 778 : << 16) |
367 778 : ((ptm->tm_sec / 2) + (32 * ptm->tm_min) +
368 778 : (2048 * static_cast<uLong>(ptm->tm_hour)));
369 : }
370 :
371 : /****************************************************************************/
372 :
373 5148 : static int zip64local_getByte(const zlib_filefunc_def *pzlib_filefunc_def,
374 : voidpf filestream, int *pi)
375 : {
376 5148 : unsigned char c = 0;
377 : const int err =
378 5148 : static_cast<int>(ZREAD64(*pzlib_filefunc_def, filestream, &c, 1));
379 5148 : if (err == 1)
380 : {
381 5126 : *pi = static_cast<int>(c);
382 5126 : 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 1170 : static int zip64local_getShort(const zlib_filefunc_def *pzlib_filefunc_def,
397 : voidpf filestream, uLong *pX)
398 : {
399 1170 : int i = 0;
400 1170 : int err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
401 1170 : uLong x = static_cast<uLong>(i);
402 :
403 1170 : if (err == ZIP_OK)
404 1170 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
405 1170 : x += static_cast<uLong>(i) << 8;
406 :
407 1170 : if (err == ZIP_OK)
408 1170 : *pX = x;
409 : else
410 0 : *pX = 0;
411 1170 : return err;
412 : }
413 :
414 702 : static int zip64local_getLong(const zlib_filefunc_def *pzlib_filefunc_def,
415 : voidpf filestream, uLong *pX)
416 : {
417 702 : int i = 0;
418 702 : int err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
419 702 : uLong x = static_cast<uLong>(i);
420 :
421 702 : if (err == ZIP_OK)
422 702 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
423 702 : x += static_cast<uLong>(i) << 8;
424 :
425 702 : if (err == ZIP_OK)
426 702 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
427 702 : x += static_cast<uLong>(i) << 16;
428 :
429 702 : if (err == ZIP_OK)
430 702 : err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
431 702 : x += static_cast<uLong>(i) << 24;
432 :
433 702 : if (err == ZIP_OK)
434 702 : *pX = x;
435 : else
436 0 : *pX = 0;
437 702 : 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 234 : zip64local_SearchCentralDir(const zlib_filefunc_def *pzlib_filefunc_def,
495 : voidpf filestream)
496 : {
497 234 : ZPOS64_T uMaxBack = 0xffff; /* maximum size of global comment */
498 234 : ZPOS64_T uPosFound = 0;
499 :
500 234 : if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) !=
501 : 0)
502 0 : return 0;
503 :
504 234 : ZPOS64_T uSizeFile = ZTELL64(*pzlib_filefunc_def, filestream);
505 :
506 234 : if (uMaxBack > uSizeFile)
507 234 : uMaxBack = uSizeFile;
508 :
509 : unsigned char *buf =
510 234 : static_cast<unsigned char *>(ALLOC(BUFREADCOMMENT + 4));
511 234 : if (buf == nullptr)
512 0 : return 0;
513 :
514 234 : ZPOS64_T uBackRead = 4;
515 234 : while (uBackRead < uMaxBack)
516 : {
517 233 : if (uBackRead + BUFREADCOMMENT > uMaxBack)
518 138 : uBackRead = uMaxBack;
519 : else
520 95 : uBackRead += BUFREADCOMMENT;
521 233 : ZPOS64_T uReadPos = uSizeFile - uBackRead;
522 :
523 233 : uLong uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos))
524 : ? (BUFREADCOMMENT + 4)
525 : : static_cast<uLong>(uSizeFile - uReadPos);
526 233 : if (ZSEEK64(*pzlib_filefunc_def, filestream, uReadPos,
527 233 : ZLIB_FILEFUNC_SEEK_SET) != 0)
528 0 : break;
529 :
530 233 : if (ZREAD64(*pzlib_filefunc_def, filestream, buf, uReadSize) !=
531 : uReadSize)
532 0 : break;
533 :
534 4427 : for (int i = static_cast<int>(uReadSize) - 3; (i--) > 0;)
535 4427 : if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) &&
536 233 : ((*(buf + i + 2)) == 0x05) && ((*(buf + i + 3)) == 0x06))
537 : {
538 233 : uPosFound = uReadPos + i;
539 233 : break;
540 : }
541 :
542 233 : if (uPosFound != 0)
543 233 : break;
544 : }
545 234 : TRYFREE(buf);
546 234 : 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 234 : 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 234 : ZPOS64_T uMaxBack = 0xffff; /* maximum size of global comment */
561 234 : ZPOS64_T uPosFound = 0;
562 : uLong uL;
563 : ZPOS64_T relativeOffset;
564 :
565 234 : if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) !=
566 : 0)
567 0 : return 0;
568 :
569 234 : uSizeFile = ZTELL64(*pzlib_filefunc_def, filestream);
570 :
571 234 : if (uMaxBack > uSizeFile)
572 234 : uMaxBack = uSizeFile;
573 :
574 234 : buf = static_cast<unsigned char *>(ALLOC(BUFREADCOMMENT + 4));
575 234 : if (buf == nullptr)
576 0 : return 0;
577 :
578 234 : uBackRead = 4;
579 571 : while (uBackRead < uMaxBack)
580 : {
581 : uLong uReadSize;
582 : ZPOS64_T uReadPos;
583 : int i;
584 337 : if (uBackRead + BUFREADCOMMENT > uMaxBack)
585 233 : uBackRead = uMaxBack;
586 : else
587 104 : uBackRead += BUFREADCOMMENT;
588 337 : uReadPos = uSizeFile - uBackRead;
589 :
590 337 : uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos))
591 : ? (BUFREADCOMMENT + 4)
592 : : static_cast<uLong>(uSizeFile - uReadPos);
593 337 : if (ZSEEK64(*pzlib_filefunc_def, filestream, uReadPos,
594 337 : ZLIB_FILEFUNC_SEEK_SET) != 0)
595 0 : break;
596 :
597 337 : if (ZREAD64(*pzlib_filefunc_def, filestream, buf, uReadSize) !=
598 : uReadSize)
599 0 : break;
600 :
601 264761 : for (i = static_cast<int>(uReadSize) - 3; (i--) > 0;)
602 : {
603 : // Signature "0x07064b50" Zip64 end of central directory locater
604 264424 : if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) &&
605 2291 : ((*(buf + i + 2)) == 0x06) && ((*(buf + i + 3)) == 0x07))
606 : {
607 0 : uPosFound = uReadPos + i;
608 0 : break;
609 : }
610 : }
611 :
612 337 : if (uPosFound != 0)
613 0 : break;
614 : }
615 :
616 234 : TRYFREE(buf);
617 234 : if (uPosFound == 0)
618 234 : 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 234 : static int LoadCentralDirectoryRecord(zip64_internal *pziinit)
665 : {
666 234 : 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 234 : int hasZIP64Record = 0;
687 :
688 : // check first if we find a ZIP64 record
689 234 : central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,
690 : pziinit->filestream);
691 234 : if (central_pos > 0)
692 : {
693 0 : hasZIP64Record = 1;
694 : }
695 : else /* if (central_pos == 0) */
696 : {
697 234 : 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 234 : 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 234 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,
776 234 : ZLIB_FILEFUNC_SEEK_SET) != 0)
777 0 : err = ZIP_ERRNO;
778 :
779 : /* the signature, already checked */
780 234 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
781 234 : &uL) != ZIP_OK)
782 0 : err = ZIP_ERRNO;
783 :
784 : /* number of this disk */
785 234 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
786 234 : &number_disk) != ZIP_OK)
787 0 : err = ZIP_ERRNO;
788 :
789 : /* number of the disk with the start of the central directory */
790 234 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
791 234 : &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 234 : number_entry = 0;
796 234 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
797 234 : &uL) != ZIP_OK)
798 0 : err = ZIP_ERRNO;
799 : else
800 234 : number_entry = uL;
801 :
802 : /* total number of entries in the central dir */
803 234 : number_entry_CD = 0;
804 234 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
805 234 : &uL) != ZIP_OK)
806 0 : err = ZIP_ERRNO;
807 : else
808 234 : number_entry_CD = uL;
809 :
810 234 : if ((number_entry_CD != number_entry) || (number_disk_with_CD != 0) ||
811 234 : (number_disk != 0))
812 0 : err = ZIP_BADZIPFILE;
813 :
814 : /* size of the central directory */
815 234 : size_central_dir = 0;
816 234 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
817 234 : &uL) != ZIP_OK)
818 0 : err = ZIP_ERRNO;
819 : else
820 234 : size_central_dir = uL;
821 :
822 : /* offset of start of central directory with respect to the starting
823 : * disk number */
824 234 : offset_central_dir = 0;
825 234 : if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,
826 234 : &uL) != ZIP_OK)
827 0 : err = ZIP_ERRNO;
828 : else
829 234 : offset_central_dir = uL;
830 :
831 : /* zipfile global comment length */
832 234 : if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,
833 234 : &size_comment) != ZIP_OK)
834 0 : err = ZIP_ERRNO;
835 : }
836 :
837 234 : if ((central_pos < offset_central_dir + size_central_dir) &&
838 : (err == ZIP_OK))
839 0 : err = ZIP_BADZIPFILE;
840 :
841 234 : if (err != ZIP_OK)
842 : {
843 0 : ZCLOSE64(pziinit->z_filefunc, pziinit->filestream);
844 0 : return ZIP_ERRNO;
845 : }
846 :
847 234 : 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 234 : byte_before_the_zipfile =
859 234 : central_pos - (offset_central_dir + size_central_dir);
860 234 : pziinit->add_position_when_writing_offset = byte_before_the_zipfile;
861 :
862 : {
863 234 : ZPOS64_T size_central_dir_to_read = size_central_dir;
864 234 : size_t buf_size = SIZEDATA_INDATABLOCK;
865 234 : void *buf_read = ALLOC(buf_size);
866 234 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream,
867 : offset_central_dir + byte_before_the_zipfile,
868 234 : ZLIB_FILEFUNC_SEEK_SET) != 0)
869 0 : err = ZIP_ERRNO;
870 :
871 467 : while ((size_central_dir_to_read > 0) && (err == ZIP_OK))
872 : {
873 233 : ZPOS64_T read_this = SIZEDATA_INDATABLOCK;
874 233 : if (read_this > size_central_dir_to_read)
875 233 : read_this = size_central_dir_to_read;
876 :
877 233 : if (ZREAD64(pziinit->z_filefunc, pziinit->filestream, buf_read,
878 233 : static_cast<uLong>(read_this)) != read_this)
879 0 : err = ZIP_ERRNO;
880 :
881 233 : if (err == ZIP_OK)
882 233 : err = add_data_in_datablock(&pziinit->central_dir, buf_read,
883 : static_cast<uLong>(read_this));
884 :
885 233 : size_central_dir_to_read -= read_this;
886 : }
887 234 : TRYFREE(buf_read);
888 : }
889 234 : pziinit->begin_pos = byte_before_the_zipfile;
890 234 : pziinit->number_entry = number_entry_CD;
891 :
892 234 : if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream,
893 : offset_central_dir + byte_before_the_zipfile,
894 234 : ZLIB_FILEFUNC_SEEK_SET) != 0)
895 0 : err = ZIP_ERRNO;
896 :
897 234 : return err;
898 : }
899 :
900 : #endif /* !NO_ADDFILEINEXISTINGZIP*/
901 :
902 : /************************************************************/
903 494 : 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 494 : memset(&ziinit, 0, sizeof(ziinit));
909 :
910 494 : if (pzlib_filefunc_def == nullptr)
911 494 : cpl_fill_fopen_filefunc(&ziinit.z_filefunc);
912 : else
913 0 : ziinit.z_filefunc = *pzlib_filefunc_def;
914 :
915 494 : 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 494 : if (ziinit.filestream == nullptr)
924 4 : return nullptr;
925 :
926 490 : if (append == APPEND_STATUS_CREATEAFTER)
927 0 : ZSEEK64(ziinit.z_filefunc, ziinit.filestream, 0, SEEK_END);
928 :
929 490 : ziinit.begin_pos = ZTELL64(ziinit.z_filefunc, ziinit.filestream);
930 490 : ziinit.in_opened_file_inzip = 0;
931 490 : ziinit.ci.stream_initialised = 0;
932 490 : ziinit.number_entry = 0;
933 490 : ziinit.add_position_when_writing_offset = 0;
934 490 : ziinit.use_cpl_io = (pzlib_filefunc_def == nullptr) ? 1 : 0;
935 490 : ziinit.vsi_raw_length_before = 0;
936 490 : ziinit.vsi_deflate_handle = nullptr;
937 490 : ziinit.nChunkSize = 0;
938 490 : ziinit.nThreads = 0;
939 490 : ziinit.nOffsetSize = 0;
940 490 : ziinit.sozip_index = nullptr;
941 490 : init_linkedlist(&(ziinit.central_dir));
942 :
943 : zip64_internal *zi =
944 490 : static_cast<zip64_internal *>(ALLOC(sizeof(zip64_internal)));
945 490 : 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 490 : ziinit.globalcomment = nullptr;
954 :
955 490 : int err = ZIP_OK;
956 490 : if (append == APPEND_STATUS_ADDINZIP)
957 : {
958 : // Read and Cache Central Directory Records
959 234 : err = LoadCentralDirectoryRecord(&ziinit);
960 : }
961 :
962 490 : if (globalcomment)
963 : {
964 0 : *globalcomment = ziinit.globalcomment;
965 : }
966 : #endif /* !NO_ADDFILEINEXISTINGZIP*/
967 :
968 490 : 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 490 : *zi = ziinit;
979 490 : return static_cast<zipFile>(zi);
980 : }
981 : }
982 :
983 494 : extern zipFile ZEXPORT cpl_zipOpen(const char *pathname, int append)
984 : {
985 494 : return cpl_zipOpen2(pathname, append, nullptr, nullptr);
986 : }
987 :
988 9004 : static void zip64local_putValue_inmemory_update(char **dest, ZPOS64_T x,
989 : int nbByte)
990 : {
991 9004 : zip64local_putValue_inmemory(*dest, x, nbByte);
992 9004 : *dest += nbByte;
993 9004 : }
994 :
995 778 : 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 778 : int err = ZIP_OK;
1001 778 : uInt size_filename = static_cast<uInt>(strlen(filename));
1002 778 : uInt size_extrafield = size_extrafield_local;
1003 :
1004 778 : if (zip64)
1005 : {
1006 306 : size_extrafield += 20;
1007 : }
1008 :
1009 778 : uInt size_local_header = 30 + size_filename + size_extrafield;
1010 778 : char *local_header = static_cast<char *>(ALLOC(size_local_header));
1011 778 : char *p = local_header;
1012 :
1013 778 : zip64local_putValue_inmemory_update(&p, LOCALHEADERMAGIC, 4);
1014 778 : if (zip64)
1015 306 : 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 778 : zip64local_putValue_inmemory_update(&p, zi->ci.flag, 2);
1022 :
1023 778 : zip64local_putValue_inmemory_update(&p, zi->ci.method, 2);
1024 :
1025 778 : 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 778 : zip64local_putValue_inmemory_update(&p, 0, 4); /* crc 32, unknown */
1030 :
1031 778 : if (zip64)
1032 306 : 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 778 : if (zip64)
1039 306 : 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 778 : zip64local_putValue_inmemory_update(&p, size_filename, 2);
1046 :
1047 778 : zi->ci.size_local_header_extrafield = size_extrafield;
1048 :
1049 778 : zip64local_putValue_inmemory_update(&p, size_extrafield, 2);
1050 :
1051 778 : if (size_filename > 0)
1052 : {
1053 778 : memcpy(p, filename, size_filename);
1054 778 : p += size_filename;
1055 : }
1056 :
1057 778 : if (size_extrafield_local > 0)
1058 : {
1059 6 : memcpy(p, extrafield_local, size_extrafield_local);
1060 6 : p += size_extrafield_local;
1061 : }
1062 :
1063 778 : if (zip64)
1064 : {
1065 : // write the Zip64 extended info
1066 306 : short HeaderID = 1;
1067 306 : short DataSize = 16;
1068 306 : ZPOS64_T CompressedSize = 0;
1069 306 : 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 306 : zi->ci.pos_zip64extrainfo =
1074 306 : ZTELL64(zi->z_filefunc, zi->filestream) + p - local_header;
1075 :
1076 306 : zip64local_putValue_inmemory_update(&p, HeaderID, 2);
1077 306 : zip64local_putValue_inmemory_update(&p, DataSize, 2);
1078 :
1079 306 : zip64local_putValue_inmemory_update(&p, UncompressedSize, 8);
1080 306 : zip64local_putValue_inmemory_update(&p, CompressedSize, 8);
1081 : }
1082 778 : assert(p == local_header + size_local_header);
1083 :
1084 778 : if (ZWRITE64(zi->z_filefunc, zi->filestream, local_header,
1085 778 : size_local_header) != size_local_header)
1086 57 : err = ZIP_ERRNO;
1087 :
1088 778 : zi->ci.local_header = local_header;
1089 778 : zi->ci.size_local_header = size_local_header;
1090 :
1091 778 : return err;
1092 : }
1093 :
1094 778 : 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 778 : int err = ZIP_OK;
1113 778 : uLong flagBase = 0;
1114 :
1115 : #ifdef NOCRYPT
1116 778 : if (password != nullptr)
1117 0 : return ZIP_PARAMERROR;
1118 : #endif
1119 :
1120 778 : if (file == nullptr)
1121 0 : return ZIP_PARAMERROR;
1122 778 : if ((method != 0) && (method != Z_DEFLATED))
1123 0 : return ZIP_PARAMERROR;
1124 :
1125 778 : zi = reinterpret_cast<zip64_internal *>(file);
1126 :
1127 778 : 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 778 : if (filename == nullptr)
1135 0 : filename = "-";
1136 :
1137 : // The filename and comment length must fit in 16 bits.
1138 778 : if ((filename != nullptr) && (strlen(filename) > 0xffff))
1139 0 : return ZIP_PARAMERROR;
1140 778 : 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 778 : if ((size_extrafield_local > 0xffff) || (size_extrafield_global > 0xffff))
1146 0 : return ZIP_PARAMERROR;
1147 :
1148 778 : if (comment == nullptr)
1149 0 : size_comment = 0;
1150 : else
1151 778 : size_comment = static_cast<uInt>(strlen(comment));
1152 :
1153 778 : size_filename = static_cast<uInt>(strlen(filename));
1154 :
1155 778 : if (zipfi == nullptr)
1156 0 : zi->ci.dosDate = 0;
1157 : else
1158 : {
1159 778 : if (zipfi->dosDate != 0)
1160 0 : zi->ci.dosDate = zipfi->dosDate;
1161 : else
1162 778 : zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date);
1163 : }
1164 :
1165 778 : zi->ci.flag = flagBase;
1166 778 : if ((level == 8) || (level == 9))
1167 0 : zi->ci.flag |= 2;
1168 778 : if (level == 2)
1169 0 : zi->ci.flag |= 4;
1170 778 : 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 778 : zi->ci.crc32 = 0;
1178 778 : zi->ci.method = method;
1179 778 : zi->ci.encrypt = 0;
1180 778 : zi->ci.stream_initialised = 0;
1181 778 : zi->ci.pos_in_buffered_data = 0;
1182 778 : zi->ci.raw = raw;
1183 778 : zi->ci.pos_local_header = ZTELL64(zi->z_filefunc, zi->filestream);
1184 :
1185 778 : if (bIncludeInCentralDirectory)
1186 : {
1187 766 : zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename +
1188 766 : size_extrafield_global + size_comment;
1189 766 : zi->ci.size_centralExtraFree =
1190 : 32; // Extra space we have reserved in case we need to add ZIP64
1191 : // extra info data
1192 :
1193 766 : zi->ci.central_header = static_cast<char *>(ALLOC(static_cast<uInt>(
1194 : zi->ci.size_centralheader + zi->ci.size_centralExtraFree)));
1195 :
1196 766 : zi->ci.size_centralExtra = size_extrafield_global;
1197 766 : zip64local_putValue_inmemory(zi->ci.central_header, CENTRALHEADERMAGIC,
1198 : 4);
1199 : /* version info */
1200 766 : zip64local_putValue_inmemory(zi->ci.central_header + 4, VERSIONMADEBY,
1201 : 2);
1202 766 : zip64local_putValue_inmemory(zi->ci.central_header + 6, 20, 2);
1203 766 : zip64local_putValue_inmemory(zi->ci.central_header + 8,
1204 766 : static_cast<uLong>(zi->ci.flag), 2);
1205 766 : zip64local_putValue_inmemory(zi->ci.central_header + 10,
1206 766 : static_cast<uLong>(zi->ci.method), 2);
1207 766 : zip64local_putValue_inmemory(zi->ci.central_header + 12,
1208 766 : static_cast<uLong>(zi->ci.dosDate), 4);
1209 766 : zip64local_putValue_inmemory(zi->ci.central_header + 16, 0, 4); /*crc*/
1210 766 : zip64local_putValue_inmemory(zi->ci.central_header + 20, 0,
1211 : 4); /*compr size*/
1212 766 : zip64local_putValue_inmemory(zi->ci.central_header + 24, 0,
1213 : 4); /*uncompr size*/
1214 766 : zip64local_putValue_inmemory(zi->ci.central_header + 28,
1215 : static_cast<uLong>(size_filename), 2);
1216 766 : zip64local_putValue_inmemory(zi->ci.central_header + 30,
1217 : static_cast<uLong>(size_extrafield_global),
1218 : 2);
1219 766 : zip64local_putValue_inmemory(zi->ci.central_header + 32,
1220 : static_cast<uLong>(size_comment), 2);
1221 766 : zip64local_putValue_inmemory(zi->ci.central_header + 34, 0,
1222 : 2); /*disk nm start*/
1223 :
1224 766 : if (zipfi == nullptr)
1225 0 : zip64local_putValue_inmemory(zi->ci.central_header + 36, 0, 2);
1226 : else
1227 766 : zip64local_putValue_inmemory(zi->ci.central_header + 36,
1228 766 : static_cast<uLong>(zipfi->internal_fa),
1229 : 2);
1230 :
1231 766 : if (zipfi == nullptr)
1232 0 : zip64local_putValue_inmemory(zi->ci.central_header + 38, 0, 4);
1233 : else
1234 766 : zip64local_putValue_inmemory(zi->ci.central_header + 38,
1235 766 : static_cast<uLong>(zipfi->external_fa),
1236 : 4);
1237 :
1238 766 : 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 766 : zip64local_putValue_inmemory(
1243 766 : zi->ci.central_header + 42,
1244 766 : static_cast<uLong>(zi->ci.pos_local_header) -
1245 766 : zi->add_position_when_writing_offset,
1246 : 4);
1247 :
1248 13623 : for (i = 0; i < size_filename; i++)
1249 12857 : *(zi->ci.central_header + SIZECENTRALHEADER + i) = *(filename + i);
1250 :
1251 1021 : 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 766 : 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 766 : 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 778 : zi->ci.totalCompressedData = 0;
1267 778 : zi->ci.totalUncompressedData = 0;
1268 778 : zi->ci.pos_zip64extrainfo = 0;
1269 :
1270 : // For now default is to generate zip64 extra fields
1271 778 : err = Write_LocalFileHeader(zi, filename, size_extrafield_local,
1272 : extrafield_local, bZip64 ? 1 : 0);
1273 :
1274 778 : zi->ci.stream.avail_in = 0;
1275 778 : zi->ci.stream.avail_out = Z_BUFSIZE;
1276 778 : zi->ci.stream.next_out = zi->ci.buffered_data;
1277 778 : zi->ci.stream.total_in = 0;
1278 778 : zi->ci.stream.total_out = 0;
1279 778 : zi->ci.stream.data_type = Z_UNKNOWN;
1280 :
1281 778 : if ((err == ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1282 : {
1283 670 : zi->ci.stream.zalloc = nullptr;
1284 670 : zi->ci.stream.zfree = nullptr;
1285 670 : zi->ci.stream.opaque = nullptr;
1286 :
1287 670 : if (windowBits > 0)
1288 0 : windowBits = -windowBits;
1289 :
1290 670 : if (zi->use_cpl_io)
1291 : {
1292 670 : auto fpRaw = reinterpret_cast<VSIVirtualHandle *>(zi->filestream);
1293 670 : zi->vsi_raw_length_before = fpRaw->Tell();
1294 670 : zi->vsi_deflate_handle = VSICreateGZipWritable(
1295 : fpRaw, CPL_DEFLATE_TYPE_RAW_DEFLATE, false, zi->nThreads,
1296 : zi->nChunkSize, zi->nOffsetSize, zi->sozip_index);
1297 670 : err = Z_OK;
1298 : }
1299 : else
1300 : {
1301 0 : err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits,
1302 : memLevel, strategy);
1303 : }
1304 :
1305 670 : if (err == Z_OK)
1306 670 : 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 778 : if (err == Z_OK)
1329 721 : 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 778 : 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 211125 : extern int ZEXPORT cpl_zipWriteInFileInZip(zipFile file, const void *buf,
1390 : unsigned len)
1391 : {
1392 211125 : if (file == nullptr)
1393 0 : return ZIP_PARAMERROR;
1394 :
1395 211125 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(file);
1396 :
1397 211125 : if (zi->in_opened_file_inzip == 0)
1398 0 : return ZIP_PARAMERROR;
1399 :
1400 211125 : zi->ci.stream.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(buf));
1401 211125 : zi->ci.stream.avail_in = len;
1402 211125 : zi->ci.crc32 =
1403 211125 : crc32(zi->ci.crc32, reinterpret_cast<const Bytef *>(buf), len);
1404 :
1405 211125 : int err = ZIP_OK;
1406 422250 : while ((err == ZIP_OK) && (zi->ci.stream.avail_in > 0))
1407 : {
1408 211125 : 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 211125 : if (err != ZIP_OK)
1417 0 : break;
1418 :
1419 211125 : if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1420 : {
1421 211074 : if (zi->vsi_deflate_handle)
1422 : {
1423 211074 : zi->ci.totalUncompressedData += len;
1424 211074 : if (zi->vsi_deflate_handle->Write(buf, 1, len) < len)
1425 34464 : err = ZIP_INTERNALERROR;
1426 211074 : 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 211074 : }
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 211125 : return err;
1460 : }
1461 :
1462 721 : extern int ZEXPORT cpl_zipCloseFileInZipRaw(zipFile file,
1463 : ZPOS64_T uncompressed_size,
1464 : uLong crc32)
1465 : {
1466 721 : if (file == nullptr)
1467 0 : return ZIP_PARAMERROR;
1468 :
1469 721 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(file);
1470 :
1471 721 : if (zi->in_opened_file_inzip == 0)
1472 0 : return ZIP_PARAMERROR;
1473 721 : zi->ci.stream.avail_in = 0;
1474 :
1475 721 : int err = ZIP_OK;
1476 721 : if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
1477 : {
1478 670 : if (zi->vsi_deflate_handle)
1479 : {
1480 670 : auto fpRaw = reinterpret_cast<VSIVirtualHandle *>(zi->filestream);
1481 670 : delete zi->vsi_deflate_handle;
1482 670 : zi->vsi_deflate_handle = nullptr;
1483 670 : zi->ci.totalCompressedData =
1484 670 : fpRaw->Tell() - zi->vsi_raw_length_before;
1485 :
1486 670 : if (zi->sozip_index)
1487 : {
1488 13 : uint64_t nVal = CPL_AS_LSB(
1489 13 : static_cast<uint64_t>(zi->ci.totalCompressedData));
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 721 : if (err == Z_STREAM_END)
1516 0 : err = ZIP_OK; /* this is normal */
1517 :
1518 721 : 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 721 : 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 721 : if (!zi->ci.raw)
1529 : {
1530 721 : crc32 = static_cast<uLong>(zi->ci.crc32);
1531 721 : uncompressed_size = zi->ci.totalUncompressedData;
1532 : }
1533 721 : 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 721 : const bool bInCentralHeader = zi->ci.central_header != nullptr;
1601 721 : if (zi->ci.central_header)
1602 : {
1603 : // update Current Item crc and sizes,
1604 709 : 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 249 : zip64local_putValue_inmemory(zi->ci.central_header + 4, 45, 2);
1610 : /*version needed*/
1611 249 : zip64local_putValue_inmemory(zi->ci.central_header + 6, 45, 2);
1612 : }
1613 :
1614 709 : zip64local_putValue_inmemory(zi->ci.central_header + 16, crc32,
1615 : 4); /*crc*/
1616 :
1617 709 : const uLong invalidValue = 0xffffffff;
1618 709 : if (compressed_size >= 0xffffffff)
1619 0 : zip64local_putValue_inmemory(zi->ci.central_header + 20,
1620 : invalidValue, 4); /*compr size*/
1621 : else
1622 709 : zip64local_putValue_inmemory(zi->ci.central_header + 20,
1623 : compressed_size, 4); /*compr size*/
1624 :
1625 : /// set internal file attributes field
1626 709 : if (zi->ci.stream.data_type == Z_ASCII)
1627 0 : zip64local_putValue_inmemory(zi->ci.central_header + 36, Z_ASCII,
1628 : 2);
1629 :
1630 709 : if (uncompressed_size >= 0xffffffff)
1631 0 : zip64local_putValue_inmemory(zi->ci.central_header + 24,
1632 : invalidValue, 4); /*uncompr size*/
1633 : else
1634 709 : zip64local_putValue_inmemory(zi->ci.central_header + 24,
1635 : uncompressed_size, 4); /*uncompr size*/
1636 :
1637 709 : short datasize = 0;
1638 : // Add ZIP64 extra info field for uncompressed size
1639 709 : if (uncompressed_size >= 0xffffffff)
1640 0 : datasize += 8;
1641 :
1642 : // Add ZIP64 extra info field for compressed size
1643 709 : 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 709 : if (zi->ci.pos_local_header >= 0xffffffff)
1649 0 : datasize += 8;
1650 :
1651 709 : 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 709 : if (err == ZIP_OK)
1702 709 : err = add_data_in_datablock(
1703 709 : &zi->central_dir, zi->ci.central_header,
1704 709 : static_cast<uLong>(zi->ci.size_centralheader));
1705 709 : free(zi->ci.central_header);
1706 709 : zi->ci.central_header = nullptr;
1707 : }
1708 :
1709 721 : free(zi->ci.local_header);
1710 721 : zi->ci.local_header = nullptr;
1711 :
1712 721 : if (err == ZIP_OK)
1713 : {
1714 : // Update the LocalFileHeader with the new values.
1715 :
1716 721 : ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
1717 :
1718 721 : if (ZSEEK64(zi->z_filefunc, zi->filestream,
1719 721 : zi->ci.pos_local_header + 14, ZLIB_FILEFUNC_SEEK_SET) != 0)
1720 0 : err = ZIP_ERRNO;
1721 :
1722 721 : if (err == ZIP_OK)
1723 721 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, crc32,
1724 : 4); /* crc 32, unknown */
1725 :
1726 721 : 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 721 : if (err == ZIP_OK) /* compressed size, unknown */
1751 721 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1752 : compressed_size, 4);
1753 :
1754 721 : if (err == ZIP_OK) /* uncompressed size, unknown */
1755 721 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1756 : uncompressed_size, 4);
1757 : }
1758 721 : if (ZSEEK64(zi->z_filefunc, zi->filestream, cur_pos_inzip,
1759 721 : ZLIB_FILEFUNC_SEEK_SET) != 0)
1760 0 : err = ZIP_ERRNO;
1761 : }
1762 :
1763 721 : if (bInCentralHeader)
1764 709 : zi->number_entry++;
1765 721 : zi->in_opened_file_inzip = 0;
1766 :
1767 721 : return err;
1768 : }
1769 :
1770 721 : extern int ZEXPORT cpl_zipCloseFileInZip(zipFile file)
1771 : {
1772 721 : 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 434 : static int Write_EndOfCentralDirectoryRecord(zip64_internal *zi,
1853 : uLong size_centraldir,
1854 : ZPOS64_T centraldir_pos_inzip)
1855 : {
1856 434 : int err = ZIP_OK;
1857 :
1858 : /*signature*/
1859 : err =
1860 434 : zip64local_putValue(&zi->z_filefunc, zi->filestream, ENDHEADERMAGIC, 4);
1861 :
1862 434 : if (err == ZIP_OK) /* number of this disk */
1863 426 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 2);
1864 :
1865 434 : if (err ==
1866 : ZIP_OK) /* number of the disk with the start of the central directory */
1867 422 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream, 0, 2);
1868 :
1869 434 : if (err ==
1870 : ZIP_OK) /* total number of entries in the central dir on this disk */
1871 : {
1872 : {
1873 418 : 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 418 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1879 : zi->number_entry, 2);
1880 : }
1881 : }
1882 :
1883 434 : if (err == ZIP_OK) /* total number of entries in the central dir */
1884 : {
1885 414 : 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 414 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1890 : zi->number_entry, 2);
1891 : }
1892 :
1893 434 : if (err == ZIP_OK) /* size of the central directory */
1894 410 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1895 : size_centraldir, 4);
1896 :
1897 434 : if (err == ZIP_OK) /* offset of start of central directory with respect to
1898 : the starting disk number */
1899 : {
1900 402 : ZPOS64_T pos =
1901 402 : centraldir_pos_inzip - zi->add_position_when_writing_offset;
1902 402 : if (pos >= 0xffffffff)
1903 : {
1904 0 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1905 : 0xffffffff, 4);
1906 : }
1907 : else
1908 402 : err = zip64local_putValue(
1909 402 : &zi->z_filefunc, zi->filestream,
1910 402 : (centraldir_pos_inzip - zi->add_position_when_writing_offset),
1911 : 4);
1912 : }
1913 :
1914 434 : return err;
1915 : }
1916 :
1917 398 : static int Write_GlobalComment(zip64_internal *zi, const char *global_comment)
1918 : {
1919 398 : int err = ZIP_OK;
1920 398 : uInt size_global_comment = 0;
1921 :
1922 398 : if (global_comment != nullptr)
1923 0 : size_global_comment = static_cast<uInt>(strlen(global_comment));
1924 :
1925 398 : err = zip64local_putValue(&zi->z_filefunc, zi->filestream,
1926 : size_global_comment, 2);
1927 :
1928 398 : 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 398 : return err;
1935 : }
1936 :
1937 490 : extern int ZEXPORT cpl_zipClose(zipFile file, const char *global_comment)
1938 : {
1939 490 : int err = 0;
1940 490 : uLong size_centraldir = 0;
1941 : ZPOS64_T centraldir_pos_inzip;
1942 : ZPOS64_T pos;
1943 :
1944 490 : if (file == nullptr)
1945 0 : return ZIP_PARAMERROR;
1946 :
1947 490 : zip64_internal *zi = reinterpret_cast<zip64_internal *>(file);
1948 :
1949 490 : if (zi->in_opened_file_inzip == 1)
1950 : {
1951 0 : err = cpl_zipCloseFileInZip(file);
1952 : }
1953 :
1954 : #ifndef NO_ADDFILEINEXISTINGZIP
1955 490 : if (global_comment == nullptr)
1956 490 : global_comment = zi->globalcomment;
1957 : #endif
1958 :
1959 490 : centraldir_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
1960 490 : if (err == ZIP_OK)
1961 : {
1962 490 : linkedlist_datablock_internal *ldi = zi->central_dir.first_block;
1963 918 : while (ldi != nullptr)
1964 : {
1965 428 : if ((err == ZIP_OK) && (ldi->filled_in_this_block > 0))
1966 428 : if (ZWRITE64(zi->z_filefunc, zi->filestream, ldi->data,
1967 428 : ldi->filled_in_this_block) !=
1968 428 : ldi->filled_in_this_block)
1969 56 : err = ZIP_ERRNO;
1970 :
1971 428 : size_centraldir += ldi->filled_in_this_block;
1972 428 : ldi = ldi->next_datablock;
1973 : }
1974 : }
1975 490 : free_linkedlist(&(zi->central_dir));
1976 :
1977 490 : pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
1978 490 : 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 490 : if (err == ZIP_OK)
1988 434 : err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir,
1989 : centraldir_pos_inzip);
1990 :
1991 490 : if (err == ZIP_OK)
1992 398 : err = Write_GlobalComment(zi, global_comment);
1993 :
1994 490 : 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 490 : TRYFREE(zi->globalcomment);
2000 : #endif
2001 490 : TRYFREE(zi);
2002 :
2003 490 : 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 494 : void *CPLCreateZip(const char *pszZipFilename, char **papszOptions)
2027 :
2028 : {
2029 : const bool bAppend =
2030 494 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "APPEND", "FALSE"));
2031 494 : char **papszFilenames = nullptr;
2032 :
2033 494 : 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 494 : zipFile hZip = cpl_zipOpen(pszZipFilename, bAppend ? APPEND_STATUS_ADDINZIP
2056 : : APPEND_STATUS_CREATE);
2057 494 : if (hZip == nullptr)
2058 : {
2059 4 : CSLDestroy(papszFilenames);
2060 4 : return nullptr;
2061 : }
2062 :
2063 490 : CPLZip *psZip = static_cast<CPLZip *>(CPLMalloc(sizeof(CPLZip)));
2064 490 : psZip->hZip = hZip;
2065 490 : psZip->papszFilenames = papszFilenames;
2066 490 : return psZip;
2067 : }
2068 :
2069 : /************************************************************************/
2070 : /* CPLCreateFileInZip() */
2071 : /************************************************************************/
2072 :
2073 : /** Create a file in a ZIP file */
2074 781 : CPLErr CPLCreateFileInZip(void *hZip, const char *pszFilename,
2075 : char **papszOptions)
2076 :
2077 : {
2078 781 : if (hZip == nullptr)
2079 0 : return CE_Failure;
2080 :
2081 781 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2082 :
2083 781 : 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 778 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "COMPRESSED", "TRUE"));
2092 :
2093 778 : char *pszCPFilename = nullptr;
2094 1556 : std::vector<GByte> abyExtra;
2095 : // If the filename is not ASCII only, we need an extended field
2096 778 : 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 776 : pszCPFilename = CPLStrdup(pszFilename);
2175 : }
2176 :
2177 : const char *pszContentType =
2178 778 : CSLFetchNameValue(papszOptions, "CONTENT_TYPE");
2179 778 : 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 : const uint16_t nDataLengthLE =
2195 4 : CPL_AS_LSB(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 : const uint16_t nKeyLenLSB =
2205 4 : CPL_AS_LSB(static_cast<uint16_t>(strlen("Content-Type")));
2206 0 : abyExtra.insert(abyExtra.end(),
2207 : reinterpret_cast<const GByte *>(&nKeyLenLSB),
2208 4 : reinterpret_cast<const GByte *>(&nKeyLenLSB) + 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 : const uint16_t nValLenLSB =
2214 4 : CPL_AS_LSB(static_cast<uint16_t>(strlen(pszContentType)));
2215 0 : abyExtra.insert(abyExtra.end(),
2216 : reinterpret_cast<const GByte *>(&nValLenLSB),
2217 4 : reinterpret_cast<const GByte *>(&nValLenLSB) + 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 778 : const bool bIncludeInCentralDirectory = CPLTestBool(CSLFetchNameValueDef(
2226 : papszOptions, "INCLUDE_IN_CENTRAL_DIRECTORY", "YES"));
2227 778 : const bool bZip64 = CPLTestBool(CSLFetchNameValueDef(
2228 : papszOptions, "ZIP64", CPLGetConfigOption("CPL_CREATE_ZIP64", "ON")));
2229 :
2230 : // Set datetime to write
2231 : zip_fileinfo fileinfo;
2232 778 : memset(&fileinfo, 0, sizeof(fileinfo));
2233 : const char *pszTimeStamp =
2234 778 : CSLFetchNameValueDef(papszOptions, "TIMESTAMP", "NOW");
2235 : GIntBig unixTime =
2236 778 : EQUAL(pszTimeStamp, "NOW")
2237 778 : ? time(nullptr)
2238 110 : : static_cast<GIntBig>(std::strtoll(pszTimeStamp, nullptr, 10));
2239 : struct tm brokenDown;
2240 778 : CPLUnixTimeToYMDHMS(unixTime, &brokenDown);
2241 778 : fileinfo.tmz_date.tm_year = brokenDown.tm_year;
2242 778 : fileinfo.tmz_date.tm_mon = brokenDown.tm_mon;
2243 778 : fileinfo.tmz_date.tm_mday = brokenDown.tm_mday;
2244 778 : fileinfo.tmz_date.tm_hour = brokenDown.tm_hour;
2245 778 : fileinfo.tmz_date.tm_min = brokenDown.tm_min;
2246 778 : fileinfo.tmz_date.tm_sec = brokenDown.tm_sec;
2247 :
2248 2340 : const int nErr = cpl_zipOpenNewFileInZip3(
2249 : psZip->hZip, pszCPFilename, &fileinfo,
2250 784 : abyExtra.empty() ? nullptr : abyExtra.data(),
2251 778 : static_cast<uInt>(abyExtra.size()),
2252 784 : abyExtra.empty() ? nullptr : abyExtra.data(),
2253 778 : 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 778 : CPLFree(pszCPFilename);
2260 :
2261 778 : if (nErr != ZIP_OK)
2262 57 : return CE_Failure;
2263 :
2264 721 : if (bIncludeInCentralDirectory)
2265 709 : psZip->papszFilenames =
2266 709 : CSLAddString(psZip->papszFilenames, pszFilename);
2267 :
2268 721 : return CE_None;
2269 : }
2270 :
2271 : /************************************************************************/
2272 : /* CPLWriteFileInZip() */
2273 : /************************************************************************/
2274 :
2275 : /** Write in current file inside a ZIP file */
2276 211125 : CPLErr CPLWriteFileInZip(void *hZip, const void *pBuffer, int nBufferSize)
2277 :
2278 : {
2279 211125 : if (hZip == nullptr)
2280 0 : return CE_Failure;
2281 :
2282 211125 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2283 :
2284 211125 : int nErr = cpl_zipWriteInFileInZip(psZip->hZip, pBuffer,
2285 : static_cast<unsigned int>(nBufferSize));
2286 :
2287 211125 : if (nErr != ZIP_OK)
2288 34464 : return CE_Failure;
2289 :
2290 176661 : return CE_None;
2291 : }
2292 :
2293 : /************************************************************************/
2294 : /* CPLCloseFileInZip() */
2295 : /************************************************************************/
2296 :
2297 : /** Close current file inside ZIP file */
2298 715 : CPLErr CPLCloseFileInZip(void *hZip)
2299 :
2300 : {
2301 715 : if (hZip == nullptr)
2302 0 : return CE_Failure;
2303 :
2304 715 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2305 :
2306 715 : int nErr = cpl_zipCloseFileInZip(psZip->hZip);
2307 :
2308 715 : if (nErr != ZIP_OK)
2309 0 : return CE_Failure;
2310 :
2311 715 : 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 uint64_t 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("UNCOMPRESSED_SIZE",
2432 13 : CPLSPrintf("%" PRIu64, 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_AS_LSB<uint32_t>(1);
2458 13 : memcpy(sozip_index.data(), &nVal32, sizeof(nVal32));
2459 : // Extra reserved space after 32 bytes of header
2460 13 : nVal32 = CPL_AS_LSB<uint32_t>(0);
2461 13 : memcpy(sozip_index.data() + 4, &nVal32, sizeof(nVal32));
2462 : // Chunksize
2463 13 : nVal32 = CPL_AS_LSB<uint32_t>(nChunkSize);
2464 13 : memcpy(sozip_index.data() + 8, &nVal32, sizeof(nVal32));
2465 : // SOZIPIndexEltSize
2466 13 : nVal32 = CPL_AS_LSB(static_cast<uint32_t>(nOffsetSize));
2467 13 : memcpy(sozip_index.data() + 12, &nVal32, sizeof(nVal32));
2468 : // Uncompressed size
2469 13 : uint64_t nVal64 = CPL_AS_LSB(nUncompressedSize);
2470 13 : memcpy(sozip_index.data() + 16, &nVal64, sizeof(nVal64));
2471 13 : zi->sozip_index = &sozip_index;
2472 :
2473 13 : zi->nChunkSize = nChunkSize;
2474 :
2475 13 : const char *pszThreads = CSLFetchNameValue(papszOptions, "NUM_THREADS");
2476 13 : if (pszThreads == nullptr || EQUAL(pszThreads, "ALL_CPUS"))
2477 13 : zi->nThreads = CPLGetNumCPUs();
2478 : else
2479 0 : zi->nThreads = atoi(pszThreads);
2480 13 : zi->nThreads = std::max(1, std::min(128, zi->nThreads));
2481 : }
2482 :
2483 : aosNewsOptions.SetNameValue("ZIP64",
2484 101 : nUncompressedSize > 0xFFFFFFFFU ? "YES" : "NO");
2485 :
2486 201 : if (pszInputFilename != nullptr &&
2487 100 : aosNewsOptions.FetchNameValue("TIMESTAMP") == nullptr)
2488 : {
2489 : VSIStatBufL sStat;
2490 100 : if (VSIStatL(pszInputFilename, &sStat) == 0 && sStat.st_mtime != 0)
2491 : {
2492 : aosNewsOptions.SetNameValue(
2493 : "TIMESTAMP",
2494 100 : CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(sStat.st_mtime)));
2495 : }
2496 : }
2497 :
2498 101 : if (CPLCreateFileInZip(hZip, pszArchiveFilename, aosNewsOptions.List()) !=
2499 : CE_None)
2500 : {
2501 1 : zi->sozip_index = nullptr;
2502 1 : zi->nChunkSize = 0;
2503 1 : zi->nThreads = 0;
2504 1 : return CE_Failure;
2505 : }
2506 100 : zi->nChunkSize = 0;
2507 100 : zi->nThreads = 0;
2508 :
2509 100 : constexpr int CHUNK_READ_MAX_SIZE = 1024 * 1024;
2510 200 : std::vector<GByte> abyChunk(CHUNK_READ_MAX_SIZE);
2511 100 : vsi_l_offset nOffset = 0;
2512 : while (true)
2513 : {
2514 : const int nRead = static_cast<int>(
2515 112 : VSIFReadL(abyChunk.data(), 1, abyChunk.size(), fpInput));
2516 224 : if (nRead > 0 &&
2517 112 : CPLWriteFileInZip(hZip, abyChunk.data(), nRead) != CE_None)
2518 : {
2519 0 : CPLCloseFileInZip(hZip);
2520 0 : zi->sozip_index = nullptr;
2521 0 : return CE_Failure;
2522 : }
2523 112 : nOffset += nRead;
2524 176 : if (pProgressFunc &&
2525 128 : !pProgressFunc(nUncompressedSize == 0
2526 : ? 1.0
2527 64 : : double(nOffset) / nUncompressedSize,
2528 : nullptr, pProgressData))
2529 : {
2530 1 : CPLCloseFileInZip(hZip);
2531 1 : zi->sozip_index = nullptr;
2532 1 : return CE_Failure;
2533 : }
2534 111 : if (nRead < CHUNK_READ_MAX_SIZE)
2535 99 : break;
2536 12 : }
2537 :
2538 99 : if (CPLCloseFileInZip(hZip) != CE_None)
2539 : {
2540 0 : zi->sozip_index = nullptr;
2541 0 : return CE_Failure;
2542 : }
2543 :
2544 99 : if (bSeekOptimized && sozip_index.size() != nExpectedIndexSize)
2545 : {
2546 : // shouldn't happen
2547 0 : CPLError(CE_Failure, CPLE_AppDefined,
2548 : "sozip_index.size() (=%u) != nExpectedIndexSize (=%u)",
2549 0 : static_cast<unsigned>(sozip_index.size()),
2550 : static_cast<unsigned>(nExpectedIndexSize));
2551 : }
2552 99 : else if (bSeekOptimized)
2553 : {
2554 12 : std::string osIdxName;
2555 12 : const char *pszLastSlash = strchr(pszArchiveFilename, '/');
2556 12 : if (pszLastSlash)
2557 : {
2558 : osIdxName.assign(pszArchiveFilename,
2559 4 : pszLastSlash - pszArchiveFilename + 1);
2560 4 : osIdxName += '.';
2561 4 : osIdxName += pszLastSlash + 1;
2562 : }
2563 : else
2564 : {
2565 8 : osIdxName = '.';
2566 8 : osIdxName += pszArchiveFilename;
2567 : }
2568 12 : osIdxName += ".sozip.idx";
2569 :
2570 12 : CPLStringList aosIndexOptions;
2571 12 : aosIndexOptions.SetNameValue("COMPRESSED", "NO");
2572 12 : aosIndexOptions.SetNameValue("ZIP64", "NO");
2573 12 : aosIndexOptions.SetNameValue("INCLUDE_IN_CENTRAL_DIRECTORY", "NO");
2574 : aosIndexOptions.SetNameValue(
2575 12 : "TIMESTAMP", aosNewsOptions.FetchNameValue("TIMESTAMP"));
2576 12 : if (CPLCreateFileInZip(hZip, osIdxName.c_str(),
2577 12 : aosIndexOptions.List()) != CE_None)
2578 : {
2579 0 : zi->sozip_index = nullptr;
2580 0 : return CE_Failure;
2581 : }
2582 :
2583 12 : if (CPLWriteFileInZip(hZip, sozip_index.data(),
2584 24 : static_cast<int>(sozip_index.size())) != CE_None)
2585 : {
2586 0 : zi->sozip_index = nullptr;
2587 0 : CPLCloseFileInZip(hZip);
2588 0 : return CE_Failure;
2589 : }
2590 :
2591 12 : zi->sozip_index = nullptr;
2592 12 : if (CPLCloseFileInZip(hZip) != CE_None)
2593 : {
2594 0 : return CE_Failure;
2595 : }
2596 : }
2597 :
2598 99 : zi->sozip_index = nullptr;
2599 :
2600 99 : return CE_None;
2601 : }
2602 :
2603 : /************************************************************************/
2604 : /* CPLCloseZip() */
2605 : /************************************************************************/
2606 :
2607 : /** Close ZIP file */
2608 490 : CPLErr CPLCloseZip(void *hZip)
2609 : {
2610 490 : if (hZip == nullptr)
2611 0 : return CE_Failure;
2612 :
2613 490 : CPLZip *psZip = static_cast<CPLZip *>(hZip);
2614 :
2615 490 : int nErr = cpl_zipClose(psZip->hZip, nullptr);
2616 :
2617 490 : psZip->hZip = nullptr;
2618 490 : CSLDestroy(psZip->papszFilenames);
2619 490 : psZip->papszFilenames = nullptr;
2620 490 : CPLFree(psZip);
2621 :
2622 490 : if (nErr != ZIP_OK)
2623 94 : return CE_Failure;
2624 :
2625 396 : return CE_None;
2626 : }
|