LCOV - code coverage report
Current view: top level - port - cpl_minizip_zip.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 831 1158 71.8 %
Date: 2026-03-25 02:32:38 Functions: 32 37 86.5 %

          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             : }

Generated by: LCOV version 1.14