LCOV - code coverage report
Current view: top level - port - cpl_vsil_gzip.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1608 2040 78.8 %
Date: 2024-11-21 22:18:42 Functions: 124 157 79.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for gz/zip files (.gz and .zip).
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : //! @cond Doxygen_Suppress
      14             : 
      15             : /* gzio.c -- IO on .gz files
      16             :   Copyright (C) 1995-2005 Jean-loup Gailly.
      17             : 
      18             :   This software is provided 'as-is', without any express or implied
      19             :   warranty.  In no event will the authors be held liable for any damages
      20             :   arising from the use of this software.
      21             : 
      22             :   Permission is granted to anyone to use this software for any purpose,
      23             :   including commercial applications, and to alter it and redistribute it
      24             :   freely, subject to the following restrictions:
      25             : 
      26             :   1. The origin of this software must not be misrepresented; you must not
      27             :      claim that you wrote the original software. If you use this software
      28             :      in a product, an acknowledgment in the product documentation would be
      29             :      appreciated but is not required.
      30             :   2. Altered source versions must be plainly marked as such, and must not be
      31             :      misrepresented as being the original software.
      32             :   3. This notice may not be removed or altered from any source distribution.
      33             : 
      34             :   Jean-loup Gailly        Mark Adler
      35             :   jloup@gzip.org          madler@alumni.caltech.edu
      36             : 
      37             :   The data format used by the zlib library is described by RFCs (Request for
      38             :   Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
      39             :   (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
      40             : */
      41             : 
      42             : /* This file contains a refactoring of gzio.c from zlib project.
      43             : 
      44             :    It replaces classical calls operating on FILE* by calls to the VSI large file
      45             :    API. It also adds the capability to seek at the end of the file, which is not
      46             :    implemented in original gzSeek. It also implements a concept of in-memory
      47             :    "snapshots", that are a way of improving efficiency while seeking GZip
      48             :    files. Snapshots are created regularly when decompressing the data a snapshot
      49             :    of the gzip state.  Later we can seek directly in the compressed data to the
      50             :    closest snapshot in order to reduce the amount of data to uncompress again.
      51             : 
      52             :    For .gz files, an effort is done to cache the size of the uncompressed data
      53             :    in a .gz.properties file, so that we don't need to seek at the end of the
      54             :    file each time a Stat() is done.
      55             : 
      56             :    For .zip and .gz, both reading and writing are supported, but just one mode
      57             :    at a time (read-only or write-only).
      58             : */
      59             : 
      60             : #include "cpl_port.h"
      61             : #include "cpl_conv.h"
      62             : #include "cpl_vsi.h"
      63             : 
      64             : #include <cerrno>
      65             : #include <climits>
      66             : #include <cstddef>
      67             : #include <cstdio>
      68             : #include <cstdlib>
      69             : #include <cstring>
      70             : #include <ctime>
      71             : 
      72             : #if HAVE_FCNTL_H
      73             : #include <fcntl.h>
      74             : #endif
      75             : #if HAVE_SYS_STAT_H
      76             : #include <sys/stat.h>
      77             : #endif
      78             : #include "cpl_zlib_header.h"  // to avoid warnings when including zlib.h
      79             : 
      80             : #ifdef HAVE_LIBDEFLATE
      81             : #include "libdeflate.h"
      82             : #endif
      83             : 
      84             : #include <algorithm>
      85             : #include <iterator>
      86             : #include <limits>
      87             : #include <list>
      88             : #include <map>
      89             : #include <memory>
      90             : #include <mutex>
      91             : #include <string>
      92             : #include <utility>
      93             : #include <vector>
      94             : 
      95             : #include "cpl_error.h"
      96             : #include "cpl_minizip_ioapi.h"
      97             : #include "cpl_minizip_unzip.h"
      98             : #include "cpl_multiproc.h"
      99             : #include "cpl_string.h"
     100             : #include "cpl_time.h"
     101             : #include "cpl_vsi_virtual.h"
     102             : #include "cpl_worker_thread_pool.h"
     103             : 
     104             : constexpr int Z_BUFSIZE = 65536;           // Original size is 16384
     105             : constexpr int gz_magic[2] = {0x1f, 0x8b};  // gzip magic header
     106             : 
     107             : // gzip flag byte.
     108             : #define ASCII_FLAG 0x01   // bit 0 set: file probably ascii text
     109             : #define HEAD_CRC 0x02     // bit 1 set: header CRC present
     110             : #define EXTRA_FIELD 0x04  // bit 2 set: extra field present
     111             : #define ORIG_NAME 0x08    // bit 3 set: original file name present
     112             : #define COMMENT 0x10      // bit 4 set: file comment present
     113             : #define RESERVED 0xE0     // bits 5..7: reserved
     114             : 
     115             : #define ALLOC(size) malloc(size)
     116             : #define TRYFREE(p)                                                             \
     117             :     {                                                                          \
     118             :         if (p)                                                                 \
     119             :             free(p);                                                           \
     120             :     }
     121             : 
     122             : #define CPL_VSIL_GZ_RETURN(ret)                                                \
     123             :     CPLError(CE_Failure, CPLE_AppDefined, "In file %s, at line %d, return %d", \
     124             :              __FILE__, __LINE__, ret)
     125             : 
     126             : // To avoid aliasing to CopyFile to CopyFileA on Windows
     127             : #ifdef CopyFile
     128             : #undef CopyFile
     129             : #endif
     130             : 
     131             : // #define ENABLE_DEBUG 1
     132             : 
     133             : /************************************************************************/
     134             : /* ==================================================================== */
     135             : /*                       VSIGZipHandle                                  */
     136             : /* ==================================================================== */
     137             : /************************************************************************/
     138             : 
     139             : typedef struct
     140             : {
     141             :     vsi_l_offset posInBaseHandle;
     142             :     z_stream stream;
     143             :     uLong crc;
     144             :     int transparent;
     145             :     vsi_l_offset in;
     146             :     vsi_l_offset out;
     147             : } GZipSnapshot;
     148             : 
     149             : class VSIGZipHandle final : public VSIVirtualHandle
     150             : {
     151             :     VSIVirtualHandle *m_poBaseHandle = nullptr;
     152             : #ifdef DEBUG
     153             :     vsi_l_offset m_offset = 0;
     154             : #endif
     155             :     vsi_l_offset m_compressed_size = 0;
     156             :     vsi_l_offset m_uncompressed_size = 0;
     157             :     vsi_l_offset offsetEndCompressedData = 0;
     158             :     uLong m_expected_crc = 0;
     159             :     char *m_pszBaseFileName = nullptr; /* optional */
     160             :     bool m_bWriteProperties = false;
     161             :     bool m_bCanSaveInfo = false;
     162             : 
     163             :     /* Fields from gz_stream structure */
     164             :     z_stream stream;
     165             :     int z_err = Z_OK;    /* error code for last stream operation */
     166             :     int z_eof = 0;       /* set if end of input file (but not necessarily of the
     167             :                          uncompressed stream !) */
     168             :     bool m_bEOF = false; /* EOF flag for uncompressed stream */
     169             :     Byte *inbuf = nullptr;  /* input buffer */
     170             :     Byte *outbuf = nullptr; /* output buffer */
     171             :     uLong crc = 0;          /* crc32 of uncompressed data */
     172             :     int m_transparent = 0;  /* 1 if input file is not a .gz file */
     173             :     vsi_l_offset startOff =
     174             :         0; /* startOff of compressed data in file (header skipped) */
     175             :     vsi_l_offset in = 0;  /* bytes into deflate or inflate */
     176             :     vsi_l_offset out = 0; /* bytes out of deflate or inflate */
     177             :     vsi_l_offset m_nLastReadOffset = 0;
     178             : 
     179             :     GZipSnapshot *snapshots = nullptr;
     180             :     vsi_l_offset snapshot_byte_interval =
     181             :         0; /* number of compressed bytes at which we create a "snapshot" */
     182             : 
     183             :     void check_header();
     184             :     int get_byte();
     185             :     bool gzseek(vsi_l_offset nOffset, int nWhence);
     186             :     int gzrewind();
     187             :     uLong getLong();
     188             : 
     189             :     CPL_DISALLOW_COPY_ASSIGN(VSIGZipHandle)
     190             : 
     191             :   public:
     192             :     VSIGZipHandle(VSIVirtualHandle *poBaseHandle, const char *pszBaseFileName,
     193             :                   vsi_l_offset offset = 0, vsi_l_offset compressed_size = 0,
     194             :                   vsi_l_offset uncompressed_size = 0, uLong expected_crc = 0,
     195             :                   int transparent = 0);
     196             :     ~VSIGZipHandle() override;
     197             : 
     198        4604 :     bool IsInitOK() const
     199             :     {
     200        4604 :         return inbuf != nullptr;
     201             :     }
     202             : 
     203             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     204             :     vsi_l_offset Tell() override;
     205             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
     206             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
     207             :     void ClearErr() override;
     208             :     int Eof() override;
     209             :     int Error() override;
     210             :     int Flush() override;
     211             :     int Close() override;
     212             : 
     213             :     VSIGZipHandle *Duplicate();
     214             :     bool CloseBaseHandle();
     215             : 
     216         166 :     vsi_l_offset GetLastReadOffset()
     217             :     {
     218         166 :         return m_nLastReadOffset;
     219             :     }
     220             : 
     221         472 :     const char *GetBaseFileName()
     222             :     {
     223         472 :         return m_pszBaseFileName;
     224             :     }
     225             : 
     226           4 :     void SetUncompressedSize(vsi_l_offset nUncompressedSize)
     227             :     {
     228           4 :         m_uncompressed_size = nUncompressedSize;
     229           4 :     }
     230             : 
     231           2 :     vsi_l_offset GetUncompressedSize()
     232             :     {
     233           2 :         return m_uncompressed_size;
     234             :     }
     235             : 
     236             :     void SaveInfo_unlocked();
     237             : 
     238          33 :     void UnsetCanSaveInfo()
     239             :     {
     240          33 :         m_bCanSaveInfo = false;
     241          33 :     }
     242             : };
     243             : 
     244             : #ifdef ENABLE_DEFLATE64
     245             : 
     246             : /************************************************************************/
     247             : /* ==================================================================== */
     248             : /*                           VSIDeflate64Handle                         */
     249             : /* ==================================================================== */
     250             : /************************************************************************/
     251             : 
     252             : struct VSIDeflate64Snapshot
     253             : {
     254             :     vsi_l_offset posInBaseHandle = 0;
     255             :     z_stream stream{};
     256             :     uLong crc = 0;
     257             :     vsi_l_offset in = 0;
     258             :     vsi_l_offset out = 0;
     259             :     std::vector<GByte> extraOutput{};
     260             :     bool m_bStreamEndReached = false;
     261             : };
     262             : 
     263             : class VSIDeflate64Handle final : public VSIVirtualHandle
     264             : {
     265             :     VSIVirtualHandle *m_poBaseHandle = nullptr;
     266             : #ifdef DEBUG
     267             :     vsi_l_offset m_offset = 0;
     268             : #endif
     269             :     vsi_l_offset m_compressed_size = 0;
     270             :     vsi_l_offset m_uncompressed_size = 0;
     271             :     vsi_l_offset offsetEndCompressedData = 0;
     272             :     uLong m_expected_crc = 0;
     273             :     char *m_pszBaseFileName = nullptr; /* optional */
     274             : 
     275             :     /* Fields from gz_stream structure */
     276             :     z_stream stream;
     277             :     int z_err = Z_OK;    /* error code for last stream operation */
     278             :     int z_eof = 0;       /* set if end of input file (but not necessarily of the
     279             :                          uncompressed stream ! ) */
     280             :     bool m_bEOF = false; /* EOF flag for uncompressed stream */
     281             :     Byte *inbuf = nullptr;  /* input buffer */
     282             :     Byte *outbuf = nullptr; /* output buffer */
     283             :     std::vector<GByte> extraOutput{};
     284             :     bool m_bStreamEndReached = false;
     285             :     uLong crc = 0; /* crc32 of uncompressed data */
     286             :     vsi_l_offset startOff =
     287             :         0; /* startOff of compressed data in file (header skipped) */
     288             :     vsi_l_offset in = 0;  /* bytes into deflate or inflate */
     289             :     vsi_l_offset out = 0; /* bytes out of deflate or inflate */
     290             : 
     291             :     std::vector<VSIDeflate64Snapshot> snapshots{};
     292             :     vsi_l_offset snapshot_byte_interval =
     293             :         0; /* number of compressed bytes at which we create a "snapshot" */
     294             : 
     295             :     bool gzseek(vsi_l_offset nOffset, int nWhence);
     296             :     int gzrewind();
     297             : 
     298             :     CPL_DISALLOW_COPY_ASSIGN(VSIDeflate64Handle)
     299             : 
     300             :   public:
     301             :     VSIDeflate64Handle(VSIVirtualHandle *poBaseHandle,
     302             :                        const char *pszBaseFileName, vsi_l_offset offset = 0,
     303             :                        vsi_l_offset compressed_size = 0,
     304             :                        vsi_l_offset uncompressed_size = 0,
     305             :                        uLong expected_crc = 0);
     306             :     ~VSIDeflate64Handle() override;
     307             : 
     308           1 :     bool IsInitOK() const
     309             :     {
     310           1 :         return inbuf != nullptr;
     311             :     }
     312             : 
     313             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     314             :     vsi_l_offset Tell() override;
     315             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
     316             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
     317             :     void ClearErr() override;
     318             :     int Eof() override;
     319             :     int Error() override;
     320             :     int Flush() override;
     321             :     int Close() override;
     322             : 
     323             :     VSIDeflate64Handle *Duplicate();
     324             :     bool CloseBaseHandle();
     325             : 
     326             :     const char *GetBaseFileName()
     327             :     {
     328             :         return m_pszBaseFileName;
     329             :     }
     330             : 
     331             :     void SetUncompressedSize(vsi_l_offset nUncompressedSize)
     332             :     {
     333             :         m_uncompressed_size = nUncompressedSize;
     334             :     }
     335             : 
     336             :     vsi_l_offset GetUncompressedSize()
     337             :     {
     338             :         return m_uncompressed_size;
     339             :     }
     340             : };
     341             : #endif
     342             : 
     343             : class VSIGZipFilesystemHandler final : public VSIFilesystemHandler
     344             : {
     345             :     CPL_DISALLOW_COPY_ASSIGN(VSIGZipFilesystemHandler)
     346             : 
     347             :     CPLMutex *hMutex = nullptr;
     348             :     VSIGZipHandle *poHandleLastGZipFile = nullptr;
     349             :     bool m_bInSaveInfo = false;
     350             : 
     351             :   public:
     352        1304 :     VSIGZipFilesystemHandler() = default;
     353             :     ~VSIGZipFilesystemHandler() override;
     354             : 
     355             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
     356             :                            bool bSetError,
     357             :                            CSLConstList /* papszOptions */) override;
     358             :     VSIGZipHandle *OpenGZipReadOnly(const char *pszFilename,
     359             :                                     const char *pszAccess);
     360             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     361             :              int nFlags) override;
     362             :     int Unlink(const char *pszFilename) override;
     363             :     int Rename(const char *oldpath, const char *newpath) override;
     364             :     int Mkdir(const char *pszDirname, long nMode) override;
     365             :     int Rmdir(const char *pszDirname) override;
     366             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
     367             : 
     368             :     const char *GetOptions() override;
     369             : 
     370             :     virtual bool SupportsSequentialWrite(const char *pszPath,
     371             :                                          bool bAllowLocalTempFile) override;
     372             : 
     373           3 :     virtual bool SupportsRandomWrite(const char * /* pszPath */,
     374             :                                      bool /* bAllowLocalTempFile */) override
     375             :     {
     376           3 :         return false;
     377             :     }
     378             : 
     379             :     void SaveInfo(VSIGZipHandle *poHandle);
     380             :     void SaveInfo_unlocked(VSIGZipHandle *poHandle);
     381             : };
     382             : 
     383             : /************************************************************************/
     384             : /*                            Duplicate()                               */
     385             : /************************************************************************/
     386             : 
     387         110 : VSIGZipHandle *VSIGZipHandle::Duplicate()
     388             : {
     389         110 :     CPLAssert(m_offset == 0);
     390         110 :     CPLAssert(m_compressed_size != 0);
     391         110 :     CPLAssert(m_pszBaseFileName != nullptr);
     392             : 
     393             :     VSIFilesystemHandler *poFSHandler =
     394         110 :         VSIFileManager::GetHandler(m_pszBaseFileName);
     395             : 
     396             :     VSIVirtualHandle *poNewBaseHandle =
     397         110 :         poFSHandler->Open(m_pszBaseFileName, "rb");
     398             : 
     399         110 :     if (poNewBaseHandle == nullptr)
     400           0 :         return nullptr;
     401             : 
     402             :     VSIGZipHandle *poHandle =
     403         110 :         new VSIGZipHandle(poNewBaseHandle, m_pszBaseFileName, 0,
     404         110 :                           m_compressed_size, m_uncompressed_size);
     405         110 :     if (!(poHandle->IsInitOK()))
     406             :     {
     407           0 :         delete poHandle;
     408           0 :         return nullptr;
     409             :     }
     410             : 
     411         110 :     poHandle->m_nLastReadOffset = m_nLastReadOffset;
     412             : 
     413             :     // Most important: duplicate the snapshots!
     414             : 
     415         122 :     for (unsigned int i = 0; i < m_compressed_size / snapshot_byte_interval + 1;
     416             :          i++)
     417             :     {
     418         110 :         if (snapshots[i].posInBaseHandle == 0)
     419          98 :             break;
     420             : 
     421          12 :         poHandle->snapshots[i].posInBaseHandle = snapshots[i].posInBaseHandle;
     422          12 :         inflateCopy(&poHandle->snapshots[i].stream, &snapshots[i].stream);
     423          12 :         poHandle->snapshots[i].crc = snapshots[i].crc;
     424          12 :         poHandle->snapshots[i].transparent = snapshots[i].transparent;
     425          12 :         poHandle->snapshots[i].in = snapshots[i].in;
     426          12 :         poHandle->snapshots[i].out = snapshots[i].out;
     427             :     }
     428             : 
     429         110 :     return poHandle;
     430             : }
     431             : 
     432             : /************************************************************************/
     433             : /*                     CloseBaseHandle()                                */
     434             : /************************************************************************/
     435             : 
     436        4637 : bool VSIGZipHandle::CloseBaseHandle()
     437             : {
     438        4637 :     bool bRet = true;
     439        4637 :     if (m_poBaseHandle)
     440             :     {
     441        4604 :         bRet = m_poBaseHandle->Close() == 0;
     442        4604 :         delete m_poBaseHandle;
     443             :     }
     444        4637 :     m_poBaseHandle = nullptr;
     445        4637 :     return bRet;
     446             : }
     447             : 
     448             : /************************************************************************/
     449             : /*                       VSIGZipHandle()                                */
     450             : /************************************************************************/
     451             : 
     452        4604 : VSIGZipHandle::VSIGZipHandle(VSIVirtualHandle *poBaseHandle,
     453             :                              const char *pszBaseFileName, vsi_l_offset offset,
     454             :                              vsi_l_offset compressed_size,
     455             :                              vsi_l_offset uncompressed_size, uLong expected_crc,
     456        4604 :                              int transparent)
     457             :     : m_poBaseHandle(poBaseHandle),
     458             : #ifdef DEBUG
     459             :       m_offset(offset),
     460             : #endif
     461             :       m_uncompressed_size(uncompressed_size), m_expected_crc(expected_crc),
     462        4604 :       m_pszBaseFileName(pszBaseFileName ? CPLStrdup(pszBaseFileName) : nullptr),
     463        4604 :       m_bWriteProperties(CPLTestBool(
     464             :           CPLGetConfigOption("CPL_VSIL_GZIP_WRITE_PROPERTIES", "YES"))),
     465             :       m_bCanSaveInfo(
     466        4604 :           CPLTestBool(CPLGetConfigOption("CPL_VSIL_GZIP_SAVE_INFO", "YES"))),
     467       13812 :       stream(), crc(0), m_transparent(transparent)
     468             : {
     469        4604 :     if (compressed_size || transparent)
     470             :     {
     471        4184 :         m_compressed_size = compressed_size;
     472             :     }
     473             :     else
     474             :     {
     475         420 :         if (poBaseHandle->Seek(0, SEEK_END) != 0)
     476           0 :             CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
     477         420 :         m_compressed_size = poBaseHandle->Tell() - offset;
     478         420 :         compressed_size = m_compressed_size;
     479             :     }
     480        4604 :     offsetEndCompressedData = offset + compressed_size;
     481             : 
     482        4604 :     if (poBaseHandle->Seek(offset, SEEK_SET) != 0)
     483           0 :         CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
     484             : 
     485        4604 :     stream.zalloc = nullptr;
     486        4604 :     stream.zfree = nullptr;
     487        4604 :     stream.opaque = nullptr;
     488        4604 :     stream.next_in = inbuf = nullptr;
     489        4604 :     stream.next_out = outbuf = nullptr;
     490        4604 :     stream.avail_in = stream.avail_out = 0;
     491             : 
     492        4604 :     inbuf = static_cast<Byte *>(ALLOC(Z_BUFSIZE));
     493        4604 :     stream.next_in = inbuf;
     494             : 
     495        4604 :     int err = inflateInit2(&(stream), -MAX_WBITS);
     496             :     // windowBits is passed < 0 to tell that there is no zlib header.
     497             :     // Note that in this case inflate *requires* an extra "dummy" byte
     498             :     // after the compressed stream in order to complete decompression and
     499             :     // return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
     500             :     // present after the compressed stream.
     501        4604 :     if (err != Z_OK || inbuf == nullptr)
     502             :     {
     503           0 :         CPLError(CE_Failure, CPLE_NotSupported, "inflateInit2 init failed");
     504           0 :         TRYFREE(inbuf);
     505           0 :         inbuf = nullptr;
     506           0 :         return;
     507             :     }
     508        4604 :     stream.avail_out = static_cast<uInt>(Z_BUFSIZE);
     509             : 
     510        4604 :     if (offset == 0)
     511         530 :         check_header();  // Skip the .gz header.
     512        4604 :     startOff = poBaseHandle->Tell() - stream.avail_in;
     513             : 
     514        4604 :     if (transparent == 0)
     515             :     {
     516        9066 :         snapshot_byte_interval = std::max(static_cast<vsi_l_offset>(Z_BUFSIZE),
     517        4533 :                                           compressed_size / 100);
     518        4533 :         snapshots = static_cast<GZipSnapshot *>(CPLCalloc(
     519             :             sizeof(GZipSnapshot),
     520        4533 :             static_cast<size_t>(compressed_size / snapshot_byte_interval + 1)));
     521             :     }
     522             : }
     523             : 
     524             : /************************************************************************/
     525             : /*                      SaveInfo_unlocked()                             */
     526             : /************************************************************************/
     527             : 
     528           0 : void VSIGZipHandle::SaveInfo_unlocked()
     529             : {
     530           0 :     if (m_pszBaseFileName && m_bCanSaveInfo)
     531             :     {
     532             :         VSIFilesystemHandler *poFSHandler =
     533           0 :             VSIFileManager::GetHandler("/vsigzip/");
     534             :         cpl::down_cast<VSIGZipFilesystemHandler *>(poFSHandler)
     535           0 :             ->SaveInfo_unlocked(this);
     536           0 :         m_bCanSaveInfo = false;
     537             :     }
     538           0 : }
     539             : 
     540             : /************************************************************************/
     541             : /*                      ~VSIGZipHandle()                                */
     542             : /************************************************************************/
     543             : 
     544        9202 : VSIGZipHandle::~VSIGZipHandle()
     545             : {
     546        4601 :     if (m_pszBaseFileName && m_bCanSaveInfo)
     547             :     {
     548             :         VSIFilesystemHandler *poFSHandler =
     549         115 :             VSIFileManager::GetHandler("/vsigzip/");
     550         115 :         cpl::down_cast<VSIGZipFilesystemHandler *>(poFSHandler)->SaveInfo(this);
     551             :     }
     552             : 
     553        4601 :     if (stream.state != nullptr)
     554             :     {
     555        4601 :         inflateEnd(&(stream));
     556             :     }
     557             : 
     558        4601 :     TRYFREE(inbuf);
     559        4601 :     TRYFREE(outbuf);
     560             : 
     561        4601 :     if (snapshots != nullptr)
     562             :     {
     563        9650 :         for (size_t i = 0; i < m_compressed_size / snapshot_byte_interval + 1;
     564             :              i++)
     565             :         {
     566        5120 :             if (snapshots[i].posInBaseHandle)
     567             :             {
     568        4577 :                 inflateEnd(&(snapshots[i].stream));
     569             :             }
     570             :         }
     571        4530 :         CPLFree(snapshots);
     572             :     }
     573        4601 :     CPLFree(m_pszBaseFileName);
     574             : 
     575        4601 :     CloseBaseHandle();
     576        9202 : }
     577             : 
     578             : /************************************************************************/
     579             : /*                      check_header()                                  */
     580             : /************************************************************************/
     581             : 
     582        1412 : void VSIGZipHandle::check_header()
     583             : {
     584             :     // Assure two bytes in the buffer so we can peek ahead -- handle case
     585             :     // where first byte of header is at the end of the buffer after the last
     586             :     // gzip segment.
     587        1412 :     uInt len = stream.avail_in;
     588        1412 :     if (len < 2)
     589             :     {
     590        1412 :         if (len)
     591           0 :             inbuf[0] = stream.next_in[0];
     592        1412 :         errno = 0;
     593        1412 :         size_t nToRead = static_cast<size_t>(Z_BUFSIZE - len);
     594        1412 :         CPLAssert(m_poBaseHandle->Tell() <= offsetEndCompressedData);
     595        1412 :         if (m_poBaseHandle->Tell() + nToRead > offsetEndCompressedData)
     596        2824 :             nToRead = static_cast<size_t>(offsetEndCompressedData -
     597        1412 :                                           m_poBaseHandle->Tell());
     598             : 
     599        1412 :         len = static_cast<uInt>(m_poBaseHandle->Read(inbuf + len, 1, nToRead));
     600             : #ifdef ENABLE_DEBUG
     601             :         CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
     602             :                  m_poBaseHandle->Tell(), offsetEndCompressedData);
     603             : #endif
     604        1412 :         if (len == 0)  // && ferror(file)
     605             :         {
     606         882 :             if (m_poBaseHandle->Tell() != offsetEndCompressedData)
     607           0 :                 z_err = Z_ERRNO;
     608             :         }
     609        1412 :         stream.avail_in += len;
     610        1412 :         stream.next_in = inbuf;
     611        1412 :         if (stream.avail_in < 2)
     612             :         {
     613         882 :             m_transparent = stream.avail_in;
     614         882 :             return;
     615             :         }
     616             :     }
     617             : 
     618             :     // Peek ahead to check the gzip magic header.
     619         530 :     if (stream.next_in[0] != gz_magic[0] || stream.next_in[1] != gz_magic[1])
     620             :     {
     621           0 :         m_transparent = 1;
     622           0 :         return;
     623             :     }
     624         530 :     stream.avail_in -= 2;
     625         530 :     stream.next_in += 2;
     626             : 
     627             :     // Check the rest of the gzip header.
     628         530 :     const int method = get_byte();
     629         530 :     const int flags = get_byte();
     630         530 :     if (method != Z_DEFLATED || (flags & RESERVED) != 0)
     631             :     {
     632           0 :         z_err = Z_DATA_ERROR;
     633           0 :         return;
     634             :     }
     635             : 
     636             :     // Discard time, xflags and OS code:
     637        3710 :     for (len = 0; len < 6; len++)
     638        3180 :         CPL_IGNORE_RET_VAL(get_byte());
     639             : 
     640         530 :     if ((flags & EXTRA_FIELD) != 0)
     641             :     {
     642             :         // Skip the extra field.
     643           0 :         len = static_cast<uInt>(get_byte()) & 0xFF;
     644           0 :         len += (static_cast<uInt>(get_byte()) & 0xFF) << 8;
     645             :         // len is garbage if EOF but the loop below will quit anyway.
     646           0 :         while (len != 0 && get_byte() != EOF)
     647             :         {
     648           0 :             --len;
     649             :         }
     650             :     }
     651             : 
     652         530 :     if ((flags & ORIG_NAME) != 0)
     653             :     {
     654             :         // Skip the original file name.
     655             :         int c;
     656         104 :         while ((c = get_byte()) != 0 && c != EOF)
     657             :         {
     658             :         }
     659             :     }
     660         530 :     if ((flags & COMMENT) != 0)
     661             :     {
     662             :         // skip the .gz file comment.
     663             :         int c;
     664           0 :         while ((c = get_byte()) != 0 && c != EOF)
     665             :         {
     666             :         }
     667             :     }
     668         530 :     if ((flags & HEAD_CRC) != 0)
     669             :     {
     670             :         // Skip the header crc.
     671           0 :         for (len = 0; len < 2; len++)
     672           0 :             CPL_IGNORE_RET_VAL(get_byte());
     673             :     }
     674         530 :     z_err = z_eof ? Z_DATA_ERROR : Z_OK;
     675             : }
     676             : 
     677             : /************************************************************************/
     678             : /*                            get_byte()                                */
     679             : /************************************************************************/
     680             : 
     681       11400 : int VSIGZipHandle::get_byte()
     682             : {
     683       11400 :     if (z_eof)
     684           0 :         return EOF;
     685       11400 :     if (stream.avail_in == 0)
     686             :     {
     687           0 :         errno = 0;
     688           0 :         size_t nToRead = static_cast<size_t>(Z_BUFSIZE);
     689           0 :         CPLAssert(m_poBaseHandle->Tell() <= offsetEndCompressedData);
     690           0 :         if (m_poBaseHandle->Tell() + nToRead > offsetEndCompressedData)
     691           0 :             nToRead = static_cast<size_t>(offsetEndCompressedData -
     692           0 :                                           m_poBaseHandle->Tell());
     693           0 :         stream.avail_in =
     694           0 :             static_cast<uInt>(m_poBaseHandle->Read(inbuf, 1, nToRead));
     695             : #ifdef ENABLE_DEBUG
     696             :         CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
     697             :                  m_poBaseHandle->Tell(), offsetEndCompressedData);
     698             : #endif
     699           0 :         if (stream.avail_in == 0)
     700             :         {
     701           0 :             z_eof = 1;
     702           0 :             if (m_poBaseHandle->Tell() != offsetEndCompressedData)
     703           0 :                 z_err = Z_ERRNO;
     704             :             // if( ferror(file) ) z_err = Z_ERRNO;
     705           0 :             return EOF;
     706             :         }
     707           0 :         stream.next_in = inbuf;
     708             :     }
     709       11400 :     stream.avail_in--;
     710       11400 :     return *(stream.next_in)++;
     711             : }
     712             : 
     713             : /************************************************************************/
     714             : /*                            gzrewind()                                */
     715             : /************************************************************************/
     716             : 
     717       13717 : int VSIGZipHandle::gzrewind()
     718             : {
     719       13717 :     z_err = Z_OK;
     720       13717 :     z_eof = 0;
     721       13717 :     m_bEOF = false;
     722       13717 :     stream.avail_in = 0;
     723       13717 :     stream.next_in = inbuf;
     724       13717 :     crc = 0;
     725       13717 :     if (!m_transparent)
     726       13717 :         CPL_IGNORE_RET_VAL(inflateReset(&stream));
     727       13717 :     in = 0;
     728       13717 :     out = 0;
     729       13717 :     return m_poBaseHandle->Seek(startOff, SEEK_SET);
     730             : }
     731             : 
     732             : /************************************************************************/
     733             : /*                              Seek()                                  */
     734             : /************************************************************************/
     735             : 
     736       25439 : int VSIGZipHandle::Seek(vsi_l_offset nOffset, int nWhence)
     737             : {
     738       25439 :     m_bEOF = false;
     739             : 
     740       25439 :     return gzseek(nOffset, nWhence) ? 0 : -1;
     741             : }
     742             : 
     743             : /************************************************************************/
     744             : /*                            gzseek()                                  */
     745             : /************************************************************************/
     746             : 
     747       25439 : bool VSIGZipHandle::gzseek(vsi_l_offset offset, int whence)
     748             : {
     749       25439 :     const vsi_l_offset original_offset = offset;
     750       25439 :     const int original_nWhence = whence;
     751             : 
     752       25439 :     z_eof = 0;
     753             : #ifdef ENABLE_DEBUG
     754             :     CPLDebug("GZIP", "Seek(" CPL_FRMT_GUIB ",%d)", offset, whence);
     755             : #endif
     756             : 
     757       25439 :     if (m_transparent)
     758             :     {
     759         443 :         stream.avail_in = 0;
     760         443 :         stream.next_in = inbuf;
     761         443 :         if (whence == SEEK_CUR)
     762             :         {
     763           0 :             if (out + offset > m_compressed_size)
     764             :             {
     765           0 :                 CPL_VSIL_GZ_RETURN(FALSE);
     766           0 :                 return false;
     767             :             }
     768             : 
     769           0 :             offset = startOff + out + offset;
     770             :         }
     771         443 :         else if (whence == SEEK_SET)
     772             :         {
     773         435 :             if (offset > m_compressed_size)
     774             :             {
     775           0 :                 CPL_VSIL_GZ_RETURN(FALSE);
     776           0 :                 return false;
     777             :             }
     778             : 
     779         435 :             offset = startOff + offset;
     780             :         }
     781           8 :         else if (whence == SEEK_END)
     782             :         {
     783             :             // Commented test: because vsi_l_offset is unsigned (for the moment)
     784             :             // so no way to seek backward. See #1590 */
     785           8 :             if (offset > 0)  // || -offset > compressed_size
     786             :             {
     787           0 :                 CPL_VSIL_GZ_RETURN(FALSE);
     788           0 :                 return false;
     789             :             }
     790             : 
     791           8 :             offset = startOff + m_compressed_size - offset;
     792             :         }
     793             :         else
     794             :         {
     795           0 :             CPL_VSIL_GZ_RETURN(FALSE);
     796           0 :             return false;
     797             :         }
     798             : 
     799         443 :         if (m_poBaseHandle->Seek(offset, SEEK_SET) < 0)
     800             :         {
     801           0 :             CPL_VSIL_GZ_RETURN(FALSE);
     802           0 :             return false;
     803             :         }
     804             : 
     805         443 :         out = offset - startOff;
     806         443 :         in = out;
     807         443 :         return true;
     808             :     }
     809             : 
     810             :     // whence == SEEK_END is unsuppored in original gzseek.
     811       24996 :     if (whence == SEEK_END)
     812             :     {
     813             :         // If we known the uncompressed size, we can fake a jump to
     814             :         // the end of the stream.
     815        1443 :         if (offset == 0 && m_uncompressed_size != 0)
     816             :         {
     817        1028 :             out = m_uncompressed_size;
     818        1028 :             return true;
     819             :         }
     820             : 
     821             :         // We don't know the uncompressed size. This is unfortunate.
     822             :         // Do the slow version.
     823             :         static int firstWarning = 1;
     824         415 :         if (m_compressed_size > 10 * 1024 * 1024 && firstWarning)
     825             :         {
     826           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     827             :                      "VSIFSeekL(xxx, SEEK_END) may be really slow "
     828             :                      "on GZip streams.");
     829           0 :             firstWarning = 0;
     830             :         }
     831             : 
     832         415 :         whence = SEEK_CUR;
     833         415 :         offset = 1024 * 1024 * 1024;
     834         415 :         offset *= 1024 * 1024;
     835             :     }
     836             : 
     837             :     // Rest of function is for reading only.
     838             : 
     839             :     // Compute absolute position.
     840       23968 :     if (whence == SEEK_CUR)
     841             :     {
     842         415 :         offset += out;
     843             :     }
     844             : 
     845             :     // For a negative seek, rewind and use positive seek.
     846       23968 :     if (offset >= out)
     847             :     {
     848       10251 :         offset -= out;
     849             :     }
     850       13717 :     else if (gzrewind() < 0)
     851             :     {
     852           0 :         CPL_VSIL_GZ_RETURN(FALSE);
     853           0 :         return false;
     854             :     }
     855             : 
     856       23968 :     if (z_err != Z_OK && z_err != Z_STREAM_END)
     857             :     {
     858           1 :         CPL_VSIL_GZ_RETURN(FALSE);
     859           1 :         return false;
     860             :     }
     861             : 
     862       28217 :     for (unsigned int i = 0; i < m_compressed_size / snapshot_byte_interval + 1;
     863             :          i++)
     864             :     {
     865       28216 :         if (snapshots[i].posInBaseHandle == 0)
     866        4978 :             break;
     867       23238 :         if (snapshots[i].out <= out + offset &&
     868       23237 :             (i == m_compressed_size / snapshot_byte_interval ||
     869        7909 :              snapshots[i + 1].out == 0 || snapshots[i + 1].out > out + offset))
     870             :         {
     871       18988 :             if (out >= snapshots[i].out)
     872       15982 :                 break;
     873             : 
     874             : #ifdef ENABLE_DEBUG
     875             :             CPLDebug("SNAPSHOT",
     876             :                      "using snapshot %d : "
     877             :                      "posInBaseHandle(snapshot)=" CPL_FRMT_GUIB
     878             :                      " in(snapshot)=" CPL_FRMT_GUIB
     879             :                      " out(snapshot)=" CPL_FRMT_GUIB " out=" CPL_FRMT_GUIB
     880             :                      " offset=" CPL_FRMT_GUIB,
     881             :                      i, snapshots[i].posInBaseHandle, snapshots[i].in,
     882             :                      snapshots[i].out, out, offset);
     883             : #endif
     884        3006 :             offset = out + offset - snapshots[i].out;
     885        3006 :             if (m_poBaseHandle->Seek(snapshots[i].posInBaseHandle, SEEK_SET) !=
     886             :                 0)
     887           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
     888             : 
     889        3006 :             inflateEnd(&stream);
     890        3006 :             inflateCopy(&stream, &snapshots[i].stream);
     891        3006 :             crc = snapshots[i].crc;
     892        3006 :             m_transparent = snapshots[i].transparent;
     893        3006 :             in = snapshots[i].in;
     894        3006 :             out = snapshots[i].out;
     895        3006 :             break;
     896             :         }
     897             :     }
     898             : 
     899             :     // Offset is now the number of bytes to skip.
     900             : 
     901       23967 :     if (offset != 0 && outbuf == nullptr)
     902             :     {
     903        2016 :         outbuf = static_cast<Byte *>(ALLOC(Z_BUFSIZE));
     904        2016 :         if (outbuf == nullptr)
     905             :         {
     906           0 :             CPL_VSIL_GZ_RETURN(FALSE);
     907           0 :             return false;
     908             :         }
     909             :     }
     910             : 
     911       23967 :     if (original_nWhence == SEEK_END && z_err == Z_STREAM_END)
     912             :     {
     913           4 :         return true;
     914             :     }
     915             : 
     916       43895 :     while (offset > 0)
     917             :     {
     918       20342 :         int size = Z_BUFSIZE;
     919       20342 :         if (offset < static_cast<vsi_l_offset>(Z_BUFSIZE))
     920       18579 :             size = static_cast<int>(offset);
     921             : 
     922             :         int read_size =
     923       20342 :             static_cast<int>(Read(outbuf, 1, static_cast<uInt>(size)));
     924       20342 :         if (original_nWhence == SEEK_END)
     925             :         {
     926         411 :             if (size != read_size)
     927             :             {
     928         410 :                 z_err = Z_STREAM_END;
     929         410 :                 break;
     930             :             }
     931             :         }
     932       19931 :         else if (read_size == 0)
     933             :         {
     934             :             // CPL_VSIL_GZ_RETURN(FALSE);
     935           0 :             return false;
     936             :         }
     937       19932 :         offset -= read_size;
     938             :     }
     939             : #ifdef ENABLE_DEBUG
     940             :     CPLDebug("GZIP", "gzseek at offset " CPL_FRMT_GUIB, out);
     941             : #endif
     942             : 
     943       23963 :     if (original_offset == 0 && original_nWhence == SEEK_END)
     944             :     {
     945         410 :         m_uncompressed_size = out;
     946             : 
     947         410 :         if (m_pszBaseFileName && !STARTS_WITH(m_pszBaseFileName, "/vsicurl/") &&
     948         410 :             !STARTS_WITH(m_pszBaseFileName, "/vsitar/") &&
     949         410 :             !STARTS_WITH(m_pszBaseFileName, "/vsizip/") && m_bWriteProperties)
     950             :         {
     951          26 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     952             : 
     953          26 :             CPLString osCacheFilename(m_pszBaseFileName);
     954          13 :             osCacheFilename += ".properties";
     955             : 
     956             :             // Write a .properties file to avoid seeking next time.
     957          13 :             VSILFILE *fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "wb");
     958          13 :             if (fpCacheLength)
     959             :             {
     960          13 :                 char szBuffer[32] = {};
     961             : 
     962          13 :                 CPLPrintUIntBig(szBuffer, m_compressed_size, 31);
     963          13 :                 char *pszFirstNonSpace = szBuffer;
     964         369 :                 while (*pszFirstNonSpace == ' ')
     965         356 :                     pszFirstNonSpace++;
     966          13 :                 CPL_IGNORE_RET_VAL(VSIFPrintfL(
     967             :                     fpCacheLength, "compressed_size=%s\n", pszFirstNonSpace));
     968             : 
     969          13 :                 CPLPrintUIntBig(szBuffer, m_uncompressed_size, 31);
     970          13 :                 pszFirstNonSpace = szBuffer;
     971         354 :                 while (*pszFirstNonSpace == ' ')
     972         341 :                     pszFirstNonSpace++;
     973          13 :                 CPL_IGNORE_RET_VAL(VSIFPrintfL(
     974             :                     fpCacheLength, "uncompressed_size=%s\n", pszFirstNonSpace));
     975             : 
     976          13 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fpCacheLength));
     977             :             }
     978             :         }
     979             :     }
     980             : 
     981       23963 :     return true;
     982             : }
     983             : 
     984             : /************************************************************************/
     985             : /*                              Tell()                                  */
     986             : /************************************************************************/
     987             : 
     988        1451 : vsi_l_offset VSIGZipHandle::Tell()
     989             : {
     990             : #ifdef ENABLE_DEBUG
     991             :     CPLDebug("GZIP", "Tell() = " CPL_FRMT_GUIB, out);
     992             : #endif
     993        1451 :     return out;
     994             : }
     995             : 
     996             : /************************************************************************/
     997             : /*                              Read()                                  */
     998             : /************************************************************************/
     999             : 
    1000      242536 : size_t VSIGZipHandle::Read(void *const buf, size_t const nSize,
    1001             :                            size_t const nMemb)
    1002             : {
    1003             : #ifdef ENABLE_DEBUG
    1004             :     CPLDebug("GZIP", "Read(%p, %d, %d)", buf, static_cast<int>(nSize),
    1005             :              static_cast<int>(nMemb));
    1006             : #endif
    1007             : 
    1008      242536 :     if (m_bEOF || z_err != Z_OK)
    1009             :     {
    1010         275 :         if (z_err == Z_STREAM_END && nSize > 0 && nMemb > 0)
    1011         272 :             m_bEOF = true;
    1012         275 :         return 0;
    1013             :     }
    1014             : 
    1015      242261 :     if (nSize > 0 && nMemb > UINT32_MAX / nSize)
    1016             :     {
    1017           0 :         CPLError(CE_Failure, CPLE_FileIO, "Too many bytes to read at once");
    1018           0 :         return 0;
    1019             :     }
    1020             : 
    1021      242261 :     const unsigned len =
    1022      242261 :         static_cast<unsigned int>(nSize) * static_cast<unsigned int>(nMemb);
    1023      242261 :     Bytef *pStart =
    1024             :         static_cast<Bytef *>(buf);  // Start off point for crc computation.
    1025             :     // == stream.next_out but not forced far (for MSDOS).
    1026      242261 :     Byte *next_out = static_cast<Byte *>(buf);
    1027      242261 :     stream.next_out = static_cast<Bytef *>(buf);
    1028      242261 :     stream.avail_out = len;
    1029             : 
    1030      477589 :     while (stream.avail_out != 0)
    1031             :     {
    1032      242592 :         if (m_transparent)
    1033             :         {
    1034             :             // Copy first the lookahead bytes:
    1035         461 :             uInt nRead = 0;
    1036         461 :             uInt n = stream.avail_in;
    1037         461 :             if (n > stream.avail_out)
    1038           0 :                 n = stream.avail_out;
    1039         461 :             if (n > 0)
    1040             :             {
    1041           0 :                 memcpy(stream.next_out, stream.next_in, n);
    1042           0 :                 next_out += n;
    1043           0 :                 stream.next_out = next_out;
    1044           0 :                 stream.next_in += n;
    1045           0 :                 stream.avail_out -= n;
    1046           0 :                 stream.avail_in -= n;
    1047           0 :                 nRead += n;
    1048             :             }
    1049         461 :             if (stream.avail_out > 0)
    1050             :             {
    1051             :                 const uInt nToRead = static_cast<uInt>(
    1052         922 :                     std::min(m_compressed_size - (in + nRead),
    1053         461 :                              static_cast<vsi_l_offset>(stream.avail_out)));
    1054             :                 const uInt nReadFromFile = static_cast<uInt>(
    1055         461 :                     m_poBaseHandle->Read(next_out, 1, nToRead));
    1056         461 :                 if (nReadFromFile < nToRead && m_poBaseHandle->Error())
    1057           0 :                     z_err = Z_ERRNO;
    1058         461 :                 stream.avail_out -= nReadFromFile;
    1059         461 :                 nRead += nReadFromFile;
    1060             :             }
    1061         461 :             in += nRead;
    1062         461 :             out += nRead;
    1063         461 :             if (nRead < len)
    1064             :             {
    1065          10 :                 m_bEOF = true;
    1066          10 :                 z_eof = 1;
    1067             :             }
    1068             : #ifdef ENABLE_DEBUG
    1069             :             CPLDebug("GZIP", "Read return %d", static_cast<int>(nRead / nSize));
    1070             : #endif
    1071         461 :             return static_cast<int>(nRead) / nSize;
    1072             :         }
    1073      242131 :         if (stream.avail_in == 0 && !z_eof)
    1074             :         {
    1075       17586 :             vsi_l_offset posInBaseHandle = m_poBaseHandle->Tell();
    1076       17586 :             if (posInBaseHandle - startOff > m_compressed_size)
    1077             :             {
    1078             :                 // If we reach here, file size has changed (because at
    1079             :                 // construction time startOff + m_compressed_size marked the
    1080             :                 // end of file).
    1081             :                 // We should probably have a better fix than that, by detecting
    1082             :                 // at open time that the saved snapshot is not valid and
    1083             :                 // discarding it.
    1084           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1085             :                          "File size of underlying /vsigzip/ file has changed");
    1086           0 :                 z_err = Z_ERRNO;
    1087           0 :                 CPL_VSIL_GZ_RETURN(0);
    1088           0 :                 return 0;
    1089             :             }
    1090       17586 :             GZipSnapshot *snapshot = &snapshots[(posInBaseHandle - startOff) /
    1091       17586 :                                                 snapshot_byte_interval];
    1092       17586 :             if (snapshot->posInBaseHandle == 0)
    1093             :             {
    1094        9134 :                 snapshot->crc = crc32(
    1095        4567 :                     crc, pStart, static_cast<uInt>(stream.next_out - pStart));
    1096             : #ifdef ENABLE_DEBUG
    1097             :                 CPLDebug("SNAPSHOT",
    1098             :                          "creating snapshot %d : "
    1099             :                          "posInBaseHandle=" CPL_FRMT_GUIB " in=" CPL_FRMT_GUIB
    1100             :                          " out=" CPL_FRMT_GUIB " crc=%X",
    1101             :                          static_cast<int>((posInBaseHandle - startOff) /
    1102             :                                           snapshot_byte_interval),
    1103             :                          posInBaseHandle, in, out,
    1104             :                          static_cast<unsigned int>(snapshot->crc));
    1105             : #endif
    1106        4567 :                 snapshot->posInBaseHandle = posInBaseHandle;
    1107        4567 :                 inflateCopy(&snapshot->stream, &stream);
    1108        4567 :                 snapshot->transparent = m_transparent;
    1109        4567 :                 snapshot->in = in;
    1110        4567 :                 snapshot->out = out;
    1111             : 
    1112        4567 :                 if (out > m_nLastReadOffset)
    1113         180 :                     m_nLastReadOffset = out;
    1114             :             }
    1115             : 
    1116       17586 :             errno = 0;
    1117       17586 :             stream.avail_in =
    1118       17586 :                 static_cast<uInt>(m_poBaseHandle->Read(inbuf, 1, Z_BUFSIZE));
    1119             : #ifdef ENABLE_DEBUG
    1120             :             CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
    1121             :                      m_poBaseHandle->Tell(), offsetEndCompressedData);
    1122             : #endif
    1123       17586 :             if (m_poBaseHandle->Tell() > offsetEndCompressedData)
    1124             :             {
    1125             : #ifdef ENABLE_DEBUG
    1126             :                 CPLDebug("GZIP", "avail_in before = %d", stream.avail_in);
    1127             : #endif
    1128       26620 :                 stream.avail_in = stream.avail_in -
    1129       26620 :                                   static_cast<uInt>(m_poBaseHandle->Tell() -
    1130       13310 :                                                     offsetEndCompressedData);
    1131       13310 :                 if (m_poBaseHandle->Seek(offsetEndCompressedData, SEEK_SET) !=
    1132             :                     0)
    1133           0 :                     CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
    1134             : #ifdef ENABLE_DEBUG
    1135             :                 CPLDebug("GZIP", "avail_in after = %d", stream.avail_in);
    1136             : #endif
    1137             :             }
    1138       17586 :             if (stream.avail_in == 0)
    1139             :             {
    1140         184 :                 z_eof = 1;
    1141         368 :                 if (m_poBaseHandle->Error() ||
    1142         184 :                     m_poBaseHandle->Tell() != offsetEndCompressedData)
    1143             :                 {
    1144           0 :                     z_err = Z_ERRNO;
    1145           0 :                     break;
    1146             :                 }
    1147             :             }
    1148       17586 :             stream.next_in = inbuf;
    1149             :         }
    1150      242131 :         in += stream.avail_in;
    1151      242131 :         out += stream.avail_out;
    1152      242131 :         z_err = inflate(&(stream), Z_NO_FLUSH);
    1153      242131 :         in -= stream.avail_in;
    1154      242131 :         out -= stream.avail_out;
    1155             : 
    1156      242131 :         if (z_err == Z_STREAM_END && m_compressed_size != 2)
    1157             :         {
    1158             :             // Check CRC and original size.
    1159        6801 :             crc =
    1160        6801 :                 crc32(crc, pStart, static_cast<uInt>(stream.next_out - pStart));
    1161        6801 :             pStart = stream.next_out;
    1162        6801 :             if (m_expected_crc)
    1163             :             {
    1164             : #ifdef ENABLE_DEBUG
    1165             :                 CPLDebug("GZIP", "Computed CRC = %X. Expected CRC = %X",
    1166             :                          static_cast<unsigned int>(crc),
    1167             :                          static_cast<unsigned int>(m_expected_crc));
    1168             : #endif
    1169             :             }
    1170        6801 :             if (m_expected_crc != 0 && m_expected_crc != crc)
    1171             :             {
    1172           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    1173             :                          "CRC error. Got %X instead of %X",
    1174           0 :                          static_cast<unsigned int>(crc),
    1175           0 :                          static_cast<unsigned int>(m_expected_crc));
    1176           0 :                 z_err = Z_DATA_ERROR;
    1177             :             }
    1178        6801 :             else if (m_expected_crc == 0)
    1179             :             {
    1180         882 :                 const uLong read_crc = static_cast<unsigned long>(getLong());
    1181         882 :                 if (read_crc != crc)
    1182             :                 {
    1183           0 :                     CPLError(CE_Failure, CPLE_FileIO,
    1184             :                              "CRC error. Got %X instead of %X",
    1185           0 :                              static_cast<unsigned int>(crc),
    1186             :                              static_cast<unsigned int>(read_crc));
    1187           0 :                     z_err = Z_DATA_ERROR;
    1188             :                 }
    1189             :                 else
    1190             :                 {
    1191         882 :                     CPL_IGNORE_RET_VAL(getLong());
    1192             :                     // The uncompressed length returned by above getlong() may
    1193             :                     // be different from out in case of concatenated .gz files.
    1194             :                     // Check for such files:
    1195         882 :                     check_header();
    1196         882 :                     if (z_err == Z_OK)
    1197             :                     {
    1198           0 :                         inflateReset(&(stream));
    1199           0 :                         crc = 0;
    1200             :                     }
    1201             :                 }
    1202             :             }
    1203             :         }
    1204      242131 :         if (z_err != Z_OK || z_eof)
    1205             :             break;
    1206             :     }
    1207      241800 :     crc = crc32(crc, pStart, static_cast<uInt>(stream.next_out - pStart));
    1208             : 
    1209      241800 :     size_t ret = (len - stream.avail_out) / nSize;
    1210      241800 :     if (z_err != Z_OK && z_err != Z_STREAM_END)
    1211             :     {
    1212           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1213             :                  "In file %s, at line %d, decompression failed with "
    1214             :                  "z_err = %d, return = %d",
    1215             :                  __FILE__, __LINE__, z_err, static_cast<int>(ret));
    1216             :     }
    1217      241798 :     else if (ret < nMemb)
    1218             :     {
    1219        2419 :         m_bEOF = true;
    1220             :     }
    1221             : 
    1222             : #ifdef ENABLE_DEBUG
    1223             :     CPLDebug("GZIP", "Read return %d (z_err=%d, z_eof=%d)",
    1224             :              static_cast<int>(ret), z_err, z_eof);
    1225             : #endif
    1226      241800 :     return ret;
    1227             : }
    1228             : 
    1229             : /************************************************************************/
    1230             : /*                              getLong()                               */
    1231             : /************************************************************************/
    1232             : 
    1233        1764 : uLong VSIGZipHandle::getLong()
    1234             : {
    1235        1764 :     uLong x = static_cast<uLong>(get_byte()) & 0xFF;
    1236             : 
    1237        1764 :     x += (static_cast<uLong>(get_byte()) & 0xFF) << 8;
    1238        1764 :     x += (static_cast<uLong>(get_byte()) & 0xFF) << 16;
    1239        1764 :     const int c = get_byte();
    1240        1764 :     if (c == EOF)
    1241             :     {
    1242           0 :         z_err = Z_DATA_ERROR;
    1243           0 :         return 0;
    1244             :     }
    1245        1764 :     x += static_cast<uLong>(c) << 24;
    1246             :     // coverity[overflow_sink]
    1247        1764 :     return x;
    1248             : }
    1249             : 
    1250             : /************************************************************************/
    1251             : /*                              Write()                                 */
    1252             : /************************************************************************/
    1253             : 
    1254           0 : size_t VSIGZipHandle::Write(const void * /* pBuffer */, size_t /* nSize */,
    1255             :                             size_t /* nMemb */)
    1256             : {
    1257           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    1258             :              "VSIFWriteL is not supported on GZip streams");
    1259           0 :     return 0;
    1260             : }
    1261             : 
    1262             : /************************************************************************/
    1263             : /*                               Eof()                                  */
    1264             : /************************************************************************/
    1265             : 
    1266        2296 : int VSIGZipHandle::Eof()
    1267             : {
    1268             : #ifdef ENABLE_DEBUG
    1269             :     CPLDebug("GZIP", "Eof()");
    1270             : #endif
    1271        2296 :     return m_bEOF;
    1272             : }
    1273             : 
    1274             : /************************************************************************/
    1275             : /*                             Error()                                  */
    1276             : /************************************************************************/
    1277             : 
    1278           3 : int VSIGZipHandle::Error()
    1279             : {
    1280             : #ifdef ENABLE_DEBUG
    1281             :     CPLDebug("GZIP", "Error()");
    1282             : #endif
    1283           3 :     return z_err != Z_OK && z_err != Z_STREAM_END;
    1284             : }
    1285             : 
    1286             : /************************************************************************/
    1287             : /*                             ClearErr()                               */
    1288             : /************************************************************************/
    1289             : 
    1290          60 : void VSIGZipHandle::ClearErr()
    1291             : {
    1292          60 :     m_poBaseHandle->ClearErr();
    1293          60 :     z_eof = 0;
    1294          60 :     m_bEOF = false;
    1295          60 :     z_err = Z_OK;
    1296          60 : }
    1297             : 
    1298             : /************************************************************************/
    1299             : /*                              Flush()                                 */
    1300             : /************************************************************************/
    1301             : 
    1302           0 : int VSIGZipHandle::Flush()
    1303             : {
    1304           0 :     return 0;
    1305             : }
    1306             : 
    1307             : /************************************************************************/
    1308             : /*                              Close()                                 */
    1309             : /************************************************************************/
    1310             : 
    1311        4560 : int VSIGZipHandle::Close()
    1312             : {
    1313        4560 :     return 0;
    1314             : }
    1315             : 
    1316             : #ifdef ENABLE_DEFLATE64
    1317             : 
    1318             : /************************************************************************/
    1319             : /*                            Duplicate()                               */
    1320             : /************************************************************************/
    1321             : 
    1322           0 : VSIDeflate64Handle *VSIDeflate64Handle::Duplicate()
    1323             : {
    1324           0 :     CPLAssert(m_offset == 0);
    1325           0 :     CPLAssert(m_compressed_size != 0);
    1326           0 :     CPLAssert(m_pszBaseFileName != nullptr);
    1327             : 
    1328             :     VSIFilesystemHandler *poFSHandler =
    1329           0 :         VSIFileManager::GetHandler(m_pszBaseFileName);
    1330             : 
    1331             :     VSIVirtualHandle *poNewBaseHandle =
    1332           0 :         poFSHandler->Open(m_pszBaseFileName, "rb");
    1333             : 
    1334           0 :     if (poNewBaseHandle == nullptr)
    1335           0 :         return nullptr;
    1336             : 
    1337             :     VSIDeflate64Handle *poHandle =
    1338           0 :         new VSIDeflate64Handle(poNewBaseHandle, m_pszBaseFileName, 0,
    1339           0 :                                m_compressed_size, m_uncompressed_size);
    1340           0 :     if (!(poHandle->IsInitOK()))
    1341             :     {
    1342           0 :         delete poHandle;
    1343           0 :         return nullptr;
    1344             :     }
    1345             : 
    1346             :     // Most important: duplicate the snapshots!
    1347             : 
    1348           0 :     for (unsigned int i = 0; i < m_compressed_size / snapshot_byte_interval + 1;
    1349             :          i++)
    1350             :     {
    1351           0 :         if (snapshots[i].posInBaseHandle == 0)
    1352           0 :             break;
    1353             : 
    1354           0 :         poHandle->snapshots[i].posInBaseHandle = snapshots[i].posInBaseHandle;
    1355           0 :         if (inflateBack9Copy(&poHandle->snapshots[i].stream,
    1356           0 :                              &snapshots[i].stream) != Z_OK)
    1357           0 :             CPLError(CE_Failure, CPLE_AppDefined, "inflateBack9Copy() failed");
    1358           0 :         poHandle->snapshots[i].crc = snapshots[i].crc;
    1359           0 :         poHandle->snapshots[i].in = snapshots[i].in;
    1360           0 :         poHandle->snapshots[i].out = snapshots[i].out;
    1361           0 :         poHandle->snapshots[i].extraOutput = snapshots[i].extraOutput;
    1362           0 :         poHandle->snapshots[i].m_bStreamEndReached =
    1363           0 :             snapshots[i].m_bStreamEndReached;
    1364             :     }
    1365             : 
    1366           0 :     return poHandle;
    1367             : }
    1368             : 
    1369             : /************************************************************************/
    1370             : /*                     CloseBaseHandle()                                */
    1371             : /************************************************************************/
    1372             : 
    1373           1 : bool VSIDeflate64Handle::CloseBaseHandle()
    1374             : {
    1375           1 :     bool bRet = true;
    1376           1 :     if (m_poBaseHandle)
    1377             :     {
    1378           1 :         bRet = m_poBaseHandle->Close() == 0;
    1379           1 :         delete m_poBaseHandle;
    1380             :     }
    1381           1 :     m_poBaseHandle = nullptr;
    1382           1 :     return bRet;
    1383             : }
    1384             : 
    1385             : /************************************************************************/
    1386             : /*                       VSIDeflate64Handle()                                */
    1387             : /************************************************************************/
    1388             : 
    1389           1 : VSIDeflate64Handle::VSIDeflate64Handle(VSIVirtualHandle *poBaseHandle,
    1390             :                                        const char *pszBaseFileName,
    1391             :                                        vsi_l_offset offset,
    1392             :                                        vsi_l_offset compressed_size,
    1393             :                                        vsi_l_offset uncompressed_size,
    1394           1 :                                        uLong expected_crc)
    1395             :     : m_poBaseHandle(poBaseHandle),
    1396             : #ifdef DEBUG
    1397             :       m_offset(offset),
    1398             : #endif
    1399             :       m_uncompressed_size(uncompressed_size), m_expected_crc(expected_crc),
    1400           1 :       m_pszBaseFileName(pszBaseFileName ? CPLStrdup(pszBaseFileName) : nullptr),
    1401           2 :       stream(), crc(0)
    1402             : {
    1403           1 :     if (compressed_size)
    1404             :     {
    1405           1 :         m_compressed_size = compressed_size;
    1406             :     }
    1407             :     else
    1408             :     {
    1409           0 :         if (poBaseHandle->Seek(0, SEEK_END) != 0)
    1410           0 :             CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
    1411           0 :         m_compressed_size = poBaseHandle->Tell() - offset;
    1412           0 :         compressed_size = m_compressed_size;
    1413             :     }
    1414           1 :     offsetEndCompressedData = offset + compressed_size;
    1415             : 
    1416           1 :     if (poBaseHandle->Seek(offset, SEEK_SET) != 0)
    1417           0 :         CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
    1418             : 
    1419           1 :     stream.zalloc = nullptr;
    1420           1 :     stream.zfree = nullptr;
    1421           1 :     stream.opaque = nullptr;
    1422           1 :     stream.next_in = inbuf = nullptr;
    1423           1 :     stream.next_out = outbuf = nullptr;
    1424           1 :     stream.avail_in = stream.avail_out = 0;
    1425             : 
    1426           1 :     inbuf = static_cast<Byte *>(ALLOC(Z_BUFSIZE));
    1427           1 :     stream.next_in = inbuf;
    1428             : 
    1429           1 :     int err = inflateBack9Init(&(stream), nullptr);
    1430             :     // Note that in this case inflate *requires* an extra "dummy" byte
    1431             :     // after the compressed stream in order to complete decompression and
    1432             :     // return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
    1433             :     // present after the compressed stream.
    1434           1 :     if (err != Z_OK || inbuf == nullptr)
    1435             :     {
    1436           0 :         CPLError(CE_Failure, CPLE_NotSupported, "inflateBack9Init init failed");
    1437           0 :         TRYFREE(inbuf);
    1438           0 :         inbuf = nullptr;
    1439           0 :         return;
    1440             :     }
    1441           1 :     startOff = poBaseHandle->Tell() - stream.avail_in;
    1442             : 
    1443           1 :     snapshot_byte_interval =
    1444           1 :         std::max(static_cast<vsi_l_offset>(Z_BUFSIZE), compressed_size / 100);
    1445           1 :     snapshots.resize(
    1446           1 :         static_cast<size_t>(compressed_size / snapshot_byte_interval + 1));
    1447             : }
    1448             : 
    1449             : /************************************************************************/
    1450             : /*                      ~VSIDeflate64Handle()                       */
    1451             : /************************************************************************/
    1452             : 
    1453           2 : VSIDeflate64Handle::~VSIDeflate64Handle()
    1454             : {
    1455           1 :     if (stream.state != nullptr)
    1456             :     {
    1457           1 :         inflateBack9End(&(stream));
    1458             :     }
    1459             : 
    1460           1 :     TRYFREE(inbuf);
    1461           1 :     TRYFREE(outbuf);
    1462             : 
    1463           4 :     for (auto &snapshot : snapshots)
    1464             :     {
    1465           3 :         if (snapshot.posInBaseHandle)
    1466             :         {
    1467           3 :             inflateBack9End(&(snapshot.stream));
    1468             :         }
    1469             :     }
    1470           1 :     CPLFree(m_pszBaseFileName);
    1471             : 
    1472           1 :     CloseBaseHandle();
    1473           2 : }
    1474             : 
    1475             : /************************************************************************/
    1476             : /*                            gzrewind()                                */
    1477             : /************************************************************************/
    1478             : 
    1479           4 : int VSIDeflate64Handle::gzrewind()
    1480             : {
    1481           4 :     m_bStreamEndReached = false;
    1482           4 :     extraOutput.clear();
    1483           4 :     z_err = Z_OK;
    1484           4 :     z_eof = 0;
    1485           4 :     stream.avail_in = 0;
    1486           4 :     stream.next_in = inbuf;
    1487           4 :     crc = 0;
    1488           4 :     CPL_IGNORE_RET_VAL(inflateBack9End(&stream));
    1489           4 :     CPL_IGNORE_RET_VAL(inflateBack9Init(&stream, nullptr));
    1490           4 :     in = 0;
    1491           4 :     out = 0;
    1492           4 :     return m_poBaseHandle->Seek(startOff, SEEK_SET);
    1493             : }
    1494             : 
    1495             : /************************************************************************/
    1496             : /*                              Seek()                                  */
    1497             : /************************************************************************/
    1498             : 
    1499           6 : int VSIDeflate64Handle::Seek(vsi_l_offset nOffset, int nWhence)
    1500             : {
    1501           6 :     m_bEOF = false;
    1502           6 :     return gzseek(nOffset, nWhence) ? 0 : -1;
    1503             : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                            gzseek()                                  */
    1507             : /************************************************************************/
    1508             : 
    1509           6 : bool VSIDeflate64Handle::gzseek(vsi_l_offset offset, int whence)
    1510             : {
    1511           6 :     const vsi_l_offset original_offset = offset;
    1512           6 :     const int original_nWhence = whence;
    1513             : 
    1514           6 :     z_eof = 0;
    1515             : #ifdef ENABLE_DEBUG
    1516             :     CPLDebug("GZIP", "Seek(" CPL_FRMT_GUIB ",%d)", offset, whence);
    1517             : #endif
    1518             : 
    1519             :     // whence == SEEK_END is unsuppored in original gzseek.
    1520           6 :     if (whence == SEEK_END)
    1521             :     {
    1522             :         // If we known the uncompressed size, we can fake a jump to
    1523             :         // the end of the stream.
    1524           0 :         if (offset == 0 && m_uncompressed_size != 0)
    1525             :         {
    1526           0 :             out = m_uncompressed_size;
    1527           0 :             return true;
    1528             :         }
    1529             : 
    1530             :         // We don't know the uncompressed size. This is unfortunate.
    1531             :         // Do the slow version.
    1532             :         static int firstWarning = 1;
    1533           0 :         if (m_compressed_size > 10 * 1024 * 1024 && firstWarning)
    1534             :         {
    1535           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1536             :                      "VSIFSeekL(xxx, SEEK_END) may be really slow "
    1537             :                      "on GZip streams.");
    1538           0 :             firstWarning = 0;
    1539             :         }
    1540             : 
    1541           0 :         whence = SEEK_CUR;
    1542           0 :         offset = 1024 * 1024 * 1024;
    1543           0 :         offset *= 1024 * 1024;
    1544             :     }
    1545             : 
    1546             :     // Rest of function is for reading only.
    1547             : 
    1548             :     // Compute absolute position.
    1549           6 :     if (whence == SEEK_CUR)
    1550             :     {
    1551           0 :         offset += out;
    1552             :     }
    1553             : 
    1554             :     // For a negative seek, rewind and use positive seek.
    1555           6 :     if (offset >= out)
    1556             :     {
    1557           2 :         offset -= out;
    1558             :     }
    1559           4 :     else if (gzrewind() < 0)
    1560             :     {
    1561           0 :         CPL_VSIL_GZ_RETURN(FALSE);
    1562           0 :         return false;
    1563             :     }
    1564             : 
    1565           6 :     if (z_err != Z_OK && z_err != Z_STREAM_END)
    1566             :     {
    1567           0 :         CPL_VSIL_GZ_RETURN(FALSE);
    1568           0 :         return false;
    1569             :     }
    1570             : 
    1571           9 :     for (unsigned int i = 0; i < m_compressed_size / snapshot_byte_interval + 1;
    1572             :          i++)
    1573             :     {
    1574           9 :         if (snapshots[i].posInBaseHandle == 0)
    1575           1 :             break;
    1576          16 :         if (snapshots[i].out <= out + offset &&
    1577           8 :             (i == m_compressed_size / snapshot_byte_interval ||
    1578           7 :              snapshots[i + 1].out == 0 || snapshots[i + 1].out > out + offset))
    1579             :         {
    1580           5 :             if (out >= snapshots[i].out)
    1581           3 :                 break;
    1582             : 
    1583             : #ifdef ENABLE_DEBUG
    1584             :             CPLDebug("SNAPSHOT",
    1585             :                      "using snapshot %d : "
    1586             :                      "posInBaseHandle(snapshot)=" CPL_FRMT_GUIB
    1587             :                      " in(snapshot)=" CPL_FRMT_GUIB
    1588             :                      " out(snapshot)=" CPL_FRMT_GUIB " out=" CPL_FRMT_GUIB
    1589             :                      " offset=" CPL_FRMT_GUIB,
    1590             :                      i, snapshots[i].posInBaseHandle, snapshots[i].in,
    1591             :                      snapshots[i].out, out, offset);
    1592             : #endif
    1593           2 :             offset = out + offset - snapshots[i].out;
    1594           2 :             if (m_poBaseHandle->Seek(snapshots[i].posInBaseHandle, SEEK_SET) !=
    1595             :                 0)
    1596           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
    1597             : 
    1598           2 :             inflateBack9End(&stream);
    1599           2 :             if (inflateBack9Copy(&stream, &snapshots[i].stream) != Z_OK)
    1600           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1601             :                          "inflateBack9Copy() failed");
    1602           2 :             crc = snapshots[i].crc;
    1603           2 :             in = snapshots[i].in;
    1604           2 :             out = snapshots[i].out;
    1605           2 :             extraOutput = snapshots[i].extraOutput;
    1606           2 :             m_bStreamEndReached = snapshots[i].m_bStreamEndReached;
    1607           2 :             break;
    1608             :         }
    1609             :     }
    1610             : 
    1611             :     // Offset is now the number of bytes to skip.
    1612             : 
    1613           6 :     if (offset != 0 && outbuf == nullptr)
    1614             :     {
    1615           1 :         outbuf = static_cast<Byte *>(ALLOC(Z_BUFSIZE));
    1616           1 :         if (outbuf == nullptr)
    1617             :         {
    1618           0 :             CPL_VSIL_GZ_RETURN(FALSE);
    1619           0 :             return false;
    1620             :         }
    1621             :     }
    1622             : 
    1623           6 :     if (original_nWhence == SEEK_END && z_err == Z_STREAM_END)
    1624             :     {
    1625           0 :         return true;
    1626             :     }
    1627             : 
    1628          16 :     while (offset > 0)
    1629             :     {
    1630          10 :         int size = Z_BUFSIZE;
    1631          10 :         if (offset < static_cast<vsi_l_offset>(Z_BUFSIZE))
    1632           4 :             size = static_cast<int>(offset);
    1633             : 
    1634             :         int read_size =
    1635          10 :             static_cast<int>(Read(outbuf, 1, static_cast<uInt>(size)));
    1636          10 :         if (original_nWhence == SEEK_END)
    1637             :         {
    1638           0 :             if (size != read_size)
    1639             :             {
    1640           0 :                 z_err = Z_STREAM_END;
    1641           0 :                 break;
    1642             :             }
    1643             :         }
    1644          10 :         else if (read_size == 0)
    1645             :         {
    1646             :             // CPL_VSIL_GZ_RETURN(FALSE);
    1647           0 :             return false;
    1648             :         }
    1649          10 :         offset -= read_size;
    1650             :     }
    1651             : #ifdef ENABLE_DEBUG
    1652             :     CPLDebug("GZIP", "gzseek at offset " CPL_FRMT_GUIB, out);
    1653             : #endif
    1654             : 
    1655           6 :     if (original_offset == 0 && original_nWhence == SEEK_END)
    1656             :     {
    1657           0 :         m_uncompressed_size = out;
    1658             :     }
    1659             : 
    1660           6 :     return true;
    1661             : }
    1662             : 
    1663             : /************************************************************************/
    1664             : /*                              Tell()                                  */
    1665             : /************************************************************************/
    1666             : 
    1667           0 : vsi_l_offset VSIDeflate64Handle::Tell()
    1668             : {
    1669             : #ifdef ENABLE_DEBUG
    1670             :     CPLDebug("GZIP", "Tell() = " CPL_FRMT_GUIB, out);
    1671             : #endif
    1672           0 :     return out;
    1673             : }
    1674             : 
    1675             : /************************************************************************/
    1676             : /*                              Read()                                  */
    1677             : /************************************************************************/
    1678             : 
    1679          17 : size_t VSIDeflate64Handle::Read(void *const buf, size_t const nSize,
    1680             :                                 size_t const nMemb)
    1681             : {
    1682             : #ifdef ENABLE_DEBUG
    1683             :     CPLDebug("GZIP", "Read(%p, %d, %d)", buf, static_cast<int>(nSize),
    1684             :              static_cast<int>(nMemb));
    1685             : #endif
    1686             : 
    1687          17 :     if (m_bEOF || z_err != Z_OK)
    1688             :     {
    1689           1 :         if (z_err == Z_STREAM_END && nSize > 0 && nMemb > 0)
    1690           1 :             m_bEOF = true;
    1691           1 :         return 0;
    1692             :     }
    1693             : 
    1694          16 :     if (nSize > 0 && nMemb > UINT32_MAX / nSize)
    1695             :     {
    1696           0 :         CPLError(CE_Failure, CPLE_FileIO, "Too many bytes to read at once");
    1697           0 :         return 0;
    1698             :     }
    1699             : 
    1700          16 :     const unsigned len =
    1701          16 :         static_cast<unsigned int>(nSize) * static_cast<unsigned int>(nMemb);
    1702          16 :     Bytef *pStart =
    1703             :         static_cast<Bytef *>(buf);  // Start off point for crc computation.
    1704             :     // == stream.next_out but not forced far (for MSDOS).
    1705          16 :     stream.next_out = static_cast<Bytef *>(buf);
    1706          16 :     stream.avail_out = len;
    1707             : 
    1708          28 :     while (stream.avail_out != 0)
    1709             :     {
    1710          20 :         if (!extraOutput.empty())
    1711             :         {
    1712          10 :             if (extraOutput.size() >= stream.avail_out)
    1713             :             {
    1714           9 :                 memcpy(stream.next_out, extraOutput.data(), stream.avail_out);
    1715           9 :                 extraOutput.erase(extraOutput.begin(),
    1716          18 :                                   extraOutput.begin() + stream.avail_out);
    1717           9 :                 out += stream.avail_out;
    1718           9 :                 stream.next_out += stream.avail_out;
    1719           9 :                 stream.avail_out = 0;
    1720             :             }
    1721             :             else
    1722             :             {
    1723           1 :                 memcpy(stream.next_out, extraOutput.data(), extraOutput.size());
    1724           1 :                 stream.next_out += extraOutput.size();
    1725           1 :                 out += static_cast<uInt>(extraOutput.size());
    1726           1 :                 stream.avail_out -= static_cast<uInt>(extraOutput.size());
    1727           1 :                 CPLAssert(stream.avail_out > 0);
    1728           1 :                 extraOutput.clear();
    1729             :             }
    1730          10 :             z_err = Z_OK;
    1731             :         }
    1732             : 
    1733          20 :         if (stream.avail_in == 0 && !z_eof)
    1734             :         {
    1735          14 :             vsi_l_offset posInBaseHandle = m_poBaseHandle->Tell();
    1736          14 :             if (posInBaseHandle - startOff > m_compressed_size)
    1737             :             {
    1738             :                 // If we reach here, file size has changed (because at
    1739             :                 // construction time startOff + m_compressed_size marked the
    1740             :                 // end of file).
    1741             :                 // We should probably have a better fix than that, by detecting
    1742             :                 // at open time that the saved snapshot is not valid and
    1743             :                 // discarding it.
    1744           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1745             :                          "File size of underlying /vsigzip/ file has changed");
    1746           0 :                 z_err = Z_ERRNO;
    1747           0 :                 CPL_VSIL_GZ_RETURN(0);
    1748           0 :                 return 0;
    1749             :             }
    1750             :             auto snapshot = &snapshots[static_cast<size_t>(
    1751          14 :                 (posInBaseHandle - startOff) / snapshot_byte_interval)];
    1752          14 :             if (snapshot->posInBaseHandle == 0)
    1753             :             {
    1754           6 :                 snapshot->crc = crc32(
    1755           3 :                     crc, pStart, static_cast<uInt>(stream.next_out - pStart));
    1756             : #ifdef ENABLE_DEBUG
    1757             :                 CPLDebug("SNAPSHOT",
    1758             :                          "creating snapshot %d : "
    1759             :                          "posInBaseHandle=" CPL_FRMT_GUIB " in=" CPL_FRMT_GUIB
    1760             :                          " out=" CPL_FRMT_GUIB " crc=%X",
    1761             :                          static_cast<int>((posInBaseHandle - startOff) /
    1762             :                                           snapshot_byte_interval),
    1763             :                          posInBaseHandle, in, out,
    1764             :                          static_cast<unsigned int>(snapshot->crc));
    1765             : #endif
    1766           3 :                 snapshot->posInBaseHandle = posInBaseHandle;
    1767           3 :                 if (inflateBack9Copy(&snapshot->stream, &stream) != Z_OK)
    1768           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1769             :                              "inflateBack9Copy() failed");
    1770           3 :                 snapshot->in = in;
    1771           3 :                 snapshot->out = out;
    1772           3 :                 snapshot->extraOutput = extraOutput;
    1773           3 :                 snapshot->m_bStreamEndReached = m_bStreamEndReached;
    1774             :             }
    1775             : 
    1776          14 :             errno = 0;
    1777          14 :             stream.avail_in =
    1778          14 :                 static_cast<uInt>(m_poBaseHandle->Read(inbuf, 1, Z_BUFSIZE));
    1779             : #ifdef ENABLE_DEBUG
    1780             :             CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
    1781             :                      m_poBaseHandle->Tell(), offsetEndCompressedData);
    1782             : #endif
    1783          14 :             if (m_poBaseHandle->Tell() > offsetEndCompressedData)
    1784             :             {
    1785             : #ifdef ENABLE_DEBUG
    1786             :                 CPLDebug("GZIP", "avail_in before = %d", stream.avail_in);
    1787             : #endif
    1788          10 :                 stream.avail_in = stream.avail_in -
    1789          10 :                                   static_cast<uInt>(m_poBaseHandle->Tell() -
    1790           5 :                                                     offsetEndCompressedData);
    1791           5 :                 if (m_poBaseHandle->Seek(offsetEndCompressedData, SEEK_SET) !=
    1792             :                     0)
    1793           0 :                     CPLError(CE_Failure, CPLE_FileIO, "Seek() failed");
    1794             : #ifdef ENABLE_DEBUG
    1795             :                 CPLDebug("GZIP", "avail_in after = %d", stream.avail_in);
    1796             : #endif
    1797             :             }
    1798          14 :             if (stream.avail_in == 0)
    1799             :             {
    1800           1 :                 z_eof = 1;
    1801           2 :                 if (m_poBaseHandle->Error() ||
    1802           1 :                     m_poBaseHandle->Tell() != offsetEndCompressedData)
    1803             :                 {
    1804           0 :                     z_err = Z_ERRNO;
    1805           8 :                     break;
    1806             :                 }
    1807             :             }
    1808          14 :             stream.next_in = inbuf;
    1809             :         }
    1810             : 
    1811             :         struct InOutCallback
    1812             :         {
    1813             :             vsi_l_offset *pOut = nullptr;
    1814             :             std::vector<GByte> *pExtraOutput = nullptr;
    1815             :             z_stream *pStream = nullptr;
    1816             : 
    1817           7 :             static unsigned inCbk(void FAR *, z_const unsigned char FAR *FAR *)
    1818             :             {
    1819           7 :                 return 0;
    1820             :             }
    1821             : 
    1822         121 :             static int outCbk(void FAR *user_data, unsigned char FAR *data,
    1823             :                               unsigned len)
    1824             :             {
    1825         121 :                 auto self = static_cast<InOutCallback *>(user_data);
    1826         121 :                 if (self->pStream->avail_out >= len)
    1827             :                 {
    1828          75 :                     memcpy(self->pStream->next_out, data, len);
    1829          75 :                     *(self->pOut) += len;
    1830          75 :                     self->pStream->next_out += len;
    1831          75 :                     self->pStream->avail_out -= len;
    1832             :                 }
    1833             :                 else
    1834             :                 {
    1835          46 :                     if (self->pStream->avail_out != 0)
    1836             :                     {
    1837           2 :                         memcpy(self->pStream->next_out, data,
    1838           2 :                                self->pStream->avail_out);
    1839           2 :                         *(self->pOut) += self->pStream->avail_out;
    1840           2 :                         data += self->pStream->avail_out;
    1841           2 :                         len -= self->pStream->avail_out;
    1842           2 :                         self->pStream->next_out += self->pStream->avail_out;
    1843           2 :                         self->pStream->avail_out = 0;
    1844             :                     }
    1845          46 :                     if (len > 0)
    1846             :                     {
    1847          92 :                         self->pExtraOutput->insert(self->pExtraOutput->end(),
    1848          46 :                                                    data, data + len);
    1849             :                     }
    1850             :                 }
    1851         121 :                 return 0;
    1852             :             }
    1853             :         };
    1854             : 
    1855          20 :         InOutCallback cbkData;
    1856          20 :         cbkData.pOut = &out;
    1857          20 :         cbkData.pExtraOutput = &extraOutput;
    1858          20 :         cbkData.pStream = &stream;
    1859             : 
    1860          20 :         if (stream.avail_out)
    1861             :         {
    1862          11 :             if (m_bStreamEndReached)
    1863           0 :                 z_err = Z_STREAM_END;
    1864             :             else
    1865             :             {
    1866          11 :                 in += stream.avail_in;
    1867          11 :                 z_err = inflateBack9(&(stream), InOutCallback::inCbk, &cbkData,
    1868             :                                      InOutCallback::outCbk, &cbkData);
    1869          11 :                 in -= stream.avail_in;
    1870             :             }
    1871             :         }
    1872          20 :         if (z_err == Z_BUF_ERROR && stream.next_in == Z_NULL)
    1873           7 :             z_err = Z_OK;
    1874          13 :         else if (!extraOutput.empty() && z_err == Z_STREAM_END)
    1875             :         {
    1876           1 :             m_bStreamEndReached = true;
    1877           1 :             z_err = Z_OK;
    1878             :         }
    1879             : 
    1880          20 :         if (z_err == Z_STREAM_END /*&& m_compressed_size != 2*/)
    1881             :         {
    1882             :             // Check CRC and original size.
    1883           3 :             crc =
    1884           3 :                 crc32(crc, pStart, static_cast<uInt>(stream.next_out - pStart));
    1885           3 :             pStart = stream.next_out;
    1886           3 :             if (m_expected_crc)
    1887             :             {
    1888             : #ifdef ENABLE_DEBUG
    1889             :                 CPLDebug("GZIP", "Computed CRC = %X. Expected CRC = %X",
    1890             :                          static_cast<unsigned int>(crc),
    1891             :                          static_cast<unsigned int>(m_expected_crc));
    1892             : #endif
    1893             :             }
    1894           3 :             if (m_expected_crc != 0 && m_expected_crc != crc)
    1895             :             {
    1896           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    1897             :                          "CRC error. Got %X instead of %X",
    1898           0 :                          static_cast<unsigned int>(crc),
    1899           0 :                          static_cast<unsigned int>(m_expected_crc));
    1900           0 :                 z_err = Z_DATA_ERROR;
    1901             :             }
    1902             :         }
    1903          20 :         if (z_err != Z_OK || z_eof)
    1904             :             break;
    1905             :     }
    1906          16 :     crc = crc32(crc, pStart, static_cast<uInt>(stream.next_out - pStart));
    1907             : 
    1908          16 :     size_t ret = (len - stream.avail_out) / nSize;
    1909          16 :     if (z_err != Z_OK && z_err != Z_STREAM_END)
    1910             :     {
    1911           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1912             :                  "In file %s, at line %d, decompression failed with "
    1913             :                  "z_err = %d, return = %d",
    1914             :                  __FILE__, __LINE__, z_err, static_cast<int>(ret));
    1915             :     }
    1916          16 :     else if (ret < nMemb)
    1917             :     {
    1918           1 :         m_bEOF = true;
    1919             :     }
    1920             : 
    1921             : #ifdef ENABLE_DEBUG
    1922             :     CPLDebug("GZIP", "Read return %d (z_err=%d, z_eof=%d)",
    1923             :              static_cast<int>(ret), z_err, z_eof);
    1924             : #endif
    1925          16 :     return ret;
    1926             : }
    1927             : 
    1928             : /************************************************************************/
    1929             : /*                              Write()                                 */
    1930             : /************************************************************************/
    1931             : 
    1932           0 : size_t VSIDeflate64Handle::Write(const void * /* pBuffer */, size_t /* nSize */,
    1933             :                                  size_t /* nMemb */)
    1934             : {
    1935           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    1936             :              "VSIFWriteL is not supported on GZip streams");
    1937           0 :     return 0;
    1938             : }
    1939             : 
    1940             : /************************************************************************/
    1941             : /*                               Eof()                                  */
    1942             : /************************************************************************/
    1943             : 
    1944           2 : int VSIDeflate64Handle::Eof()
    1945             : {
    1946             : #ifdef ENABLE_DEBUG
    1947             :     CPLDebug("GZIP", "Eof()");
    1948             : #endif
    1949           2 :     return m_bEOF;
    1950             : }
    1951             : 
    1952             : /************************************************************************/
    1953             : /*                             Error()                                  */
    1954             : /************************************************************************/
    1955             : 
    1956           0 : int VSIDeflate64Handle::Error()
    1957             : {
    1958             : #ifdef ENABLE_DEBUG
    1959             :     CPLDebug("GZIP", "Error()");
    1960             : #endif
    1961           0 :     return z_err != Z_OK && z_err != Z_STREAM_END;
    1962             : }
    1963             : 
    1964             : /************************************************************************/
    1965             : /*                             ClearErr()                               */
    1966             : /************************************************************************/
    1967             : 
    1968           0 : void VSIDeflate64Handle::ClearErr()
    1969             : {
    1970           0 :     m_poBaseHandle->ClearErr();
    1971           0 :     z_eof = 0;
    1972           0 :     m_bEOF = false;
    1973           0 :     z_err = Z_OK;
    1974           0 : }
    1975             : 
    1976             : /************************************************************************/
    1977             : /*                              Flush()                                 */
    1978             : /************************************************************************/
    1979             : 
    1980           0 : int VSIDeflate64Handle::Flush()
    1981             : {
    1982           0 :     return 0;
    1983             : }
    1984             : 
    1985             : /************************************************************************/
    1986             : /*                              Close()                                 */
    1987             : /************************************************************************/
    1988             : 
    1989           1 : int VSIDeflate64Handle::Close()
    1990             : {
    1991           1 :     return 0;
    1992             : }
    1993             : #endif
    1994             : 
    1995             : /************************************************************************/
    1996             : /* ==================================================================== */
    1997             : /*                       VSIGZipWriteHandleMT                           */
    1998             : /* ==================================================================== */
    1999             : /************************************************************************/
    2000             : 
    2001             : class VSIGZipWriteHandleMT final : public VSIVirtualHandle
    2002             : {
    2003             :     CPL_DISALLOW_COPY_ASSIGN(VSIGZipWriteHandleMT)
    2004             : 
    2005             :     VSIVirtualHandle *poBaseHandle_ = nullptr;
    2006             :     vsi_l_offset nCurOffset_ = 0;
    2007             :     uLong nCRC_ = 0;
    2008             :     int nDeflateType_ = CPL_DEFLATE_TYPE_GZIP;
    2009             :     bool bAutoCloseBaseHandle_ = false;
    2010             :     int nThreads_ = 0;
    2011             :     std::unique_ptr<CPLWorkerThreadPool> poPool_{};
    2012             :     std::list<std::string *> aposBuffers_{};
    2013             :     std::string *pCurBuffer_ = nullptr;
    2014             :     std::mutex sMutex_{};
    2015             :     int nSeqNumberGenerated_ = 0;
    2016             :     int nSeqNumberExpected_ = 0;
    2017             :     int nSeqNumberExpectedCRC_ = 0;
    2018             :     size_t nChunkSize_ = 0;
    2019             :     bool bHasErrored_ = false;
    2020             : 
    2021             :     struct Job
    2022             :     {
    2023             :         VSIGZipWriteHandleMT *pParent_ = nullptr;
    2024             :         std::string *pBuffer_ = nullptr;
    2025             :         int nSeqNumber_ = 0;
    2026             :         bool bFinish_ = false;
    2027             :         bool bInCRCComputation_ = false;
    2028             : 
    2029             :         std::string sCompressedData_{};
    2030             :         uLong nCRC_ = 0;
    2031             :     };
    2032             : 
    2033             :     std::list<Job *> apoFinishedJobs_{};
    2034             :     std::list<Job *> apoCRCFinishedJobs_{};
    2035             :     std::list<Job *> apoFreeJobs_{};
    2036             :     vsi_l_offset nStartOffset_ = 0;
    2037             :     size_t nSOZIPIndexEltSize_ = 0;
    2038             :     std::vector<uint8_t> *panSOZIPIndex_ = nullptr;
    2039             : 
    2040             :     static void DeflateCompress(void *inData);
    2041             :     static void CRCCompute(void *inData);
    2042             :     bool ProcessCompletedJobs();
    2043             :     Job *GetJobObject();
    2044             : #ifdef DEBUG_VERBOSE
    2045             :     void DumpState();
    2046             : #endif
    2047             : 
    2048             :   public:
    2049             :     VSIGZipWriteHandleMT(VSIVirtualHandle *poBaseHandle, int nDeflateType,
    2050             :                          bool bAutoCloseBaseHandleIn, int nThreads,
    2051             :                          size_t nChunkSize, size_t nSOZIPIndexEltSize,
    2052             :                          std::vector<uint8_t> *panSOZIPIndex);
    2053             : 
    2054             :     ~VSIGZipWriteHandleMT() override;
    2055             : 
    2056             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
    2057             :     vsi_l_offset Tell() override;
    2058             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
    2059             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
    2060             : 
    2061           0 :     int Eof() override
    2062             :     {
    2063           0 :         return 0;
    2064             :     }
    2065             : 
    2066           0 :     int Error() override
    2067             :     {
    2068           0 :         return 0;
    2069             :     }
    2070             : 
    2071           0 :     void ClearErr() override
    2072             :     {
    2073           0 :     }
    2074             : 
    2075             :     int Flush() override;
    2076             :     int Close() override;
    2077             : };
    2078             : 
    2079             : /************************************************************************/
    2080             : /*                        VSIGZipWriteHandleMT()                        */
    2081             : /************************************************************************/
    2082             : 
    2083          13 : VSIGZipWriteHandleMT::VSIGZipWriteHandleMT(VSIVirtualHandle *poBaseHandle,
    2084             :                                            int nDeflateType,
    2085             :                                            bool bAutoCloseBaseHandleIn,
    2086             :                                            int nThreads, size_t nChunkSize,
    2087             :                                            size_t nSOZIPIndexEltSize,
    2088          13 :                                            std::vector<uint8_t> *panSOZIPIndex)
    2089             :     : poBaseHandle_(poBaseHandle), nDeflateType_(nDeflateType),
    2090             :       bAutoCloseBaseHandle_(bAutoCloseBaseHandleIn), nThreads_(nThreads),
    2091             :       nChunkSize_(nChunkSize), nSOZIPIndexEltSize_(nSOZIPIndexEltSize),
    2092          13 :       panSOZIPIndex_(panSOZIPIndex)
    2093             : {
    2094          13 :     if (nChunkSize_ == 0)
    2095             :     {
    2096             :         const char *pszChunkSize =
    2097           4 :             CPLGetConfigOption("CPL_VSIL_DEFLATE_CHUNK_SIZE", "1024K");
    2098           4 :         nChunkSize_ = static_cast<size_t>(atoi(pszChunkSize));
    2099           4 :         if (strchr(pszChunkSize, 'K'))
    2100           4 :             nChunkSize_ *= 1024;
    2101           0 :         else if (strchr(pszChunkSize, 'M'))
    2102           0 :             nChunkSize_ *= 1024 * 1024;
    2103           4 :         nChunkSize_ =
    2104           8 :             std::max(static_cast<size_t>(4 * 1024),
    2105           4 :                      std::min(static_cast<size_t>(UINT_MAX), nChunkSize_));
    2106             :     }
    2107             : 
    2108          78 :     for (int i = 0; i < 1 + nThreads_; i++)
    2109          65 :         aposBuffers_.emplace_back(new std::string());
    2110             : 
    2111          13 :     nStartOffset_ = poBaseHandle_->Tell();
    2112          13 :     if (nDeflateType == CPL_DEFLATE_TYPE_GZIP)
    2113             :     {
    2114           1 :         char header[11] = {};
    2115             : 
    2116             :         // Write a very simple .gz header:
    2117           1 :         snprintf(header, sizeof(header), "%c%c%c%c%c%c%c%c%c%c", gz_magic[0],
    2118           1 :                  gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0, 0, 0, 0 /*time*/,
    2119             :                  0 /*xflags*/, 0x03);
    2120           1 :         poBaseHandle_->Write(header, 1, 10);
    2121             :     }
    2122          13 : }
    2123             : 
    2124             : /************************************************************************/
    2125             : /*                       ~VSIGZipWriteHandleMT()                        */
    2126             : /************************************************************************/
    2127             : 
    2128          26 : VSIGZipWriteHandleMT::~VSIGZipWriteHandleMT()
    2129             : 
    2130             : {
    2131          13 :     VSIGZipWriteHandleMT::Close();
    2132          16 :     for (auto &psJob : apoFinishedJobs_)
    2133             :     {
    2134           3 :         delete psJob->pBuffer_;
    2135           3 :         delete psJob;
    2136             :     }
    2137          13 :     for (auto &psJob : apoCRCFinishedJobs_)
    2138             :     {
    2139           0 :         delete psJob->pBuffer_;
    2140           0 :         delete psJob;
    2141             :     }
    2142          73 :     for (auto &psJob : apoFreeJobs_)
    2143             :     {
    2144          60 :         delete psJob->pBuffer_;
    2145          60 :         delete psJob;
    2146             :     }
    2147          78 :     for (auto &pstr : aposBuffers_)
    2148             :     {
    2149          65 :         delete pstr;
    2150             :     }
    2151          13 :     delete pCurBuffer_;
    2152          26 : }
    2153             : 
    2154             : /************************************************************************/
    2155             : /*                               Close()                                */
    2156             : /************************************************************************/
    2157             : 
    2158          14 : int VSIGZipWriteHandleMT::Close()
    2159             : 
    2160             : {
    2161          14 :     if (!poBaseHandle_)
    2162           1 :         return 0;
    2163             : 
    2164          13 :     int nRet = 0;
    2165             : 
    2166          13 :     if (!pCurBuffer_)
    2167           3 :         pCurBuffer_ = new std::string();
    2168             : 
    2169             :     {
    2170          13 :         auto psJob = GetJobObject();
    2171          13 :         psJob->bFinish_ = true;
    2172          13 :         psJob->pParent_ = this;
    2173          13 :         psJob->pBuffer_ = pCurBuffer_;
    2174          13 :         pCurBuffer_ = nullptr;
    2175          13 :         psJob->nSeqNumber_ = nSeqNumberGenerated_;
    2176          13 :         VSIGZipWriteHandleMT::DeflateCompress(psJob);
    2177             :     }
    2178             : 
    2179          13 :     if (poPool_)
    2180             :     {
    2181          12 :         poPool_->WaitCompletion(0);
    2182             :     }
    2183          13 :     if (!ProcessCompletedJobs())
    2184             :     {
    2185           1 :         nRet = -1;
    2186             :     }
    2187             :     else
    2188             :     {
    2189          12 :         CPLAssert(apoFinishedJobs_.empty());
    2190          12 :         if (nDeflateType_ == CPL_DEFLATE_TYPE_GZIP)
    2191             :         {
    2192           1 :             if (poPool_)
    2193             :             {
    2194           1 :                 poPool_->WaitCompletion(0);
    2195             :             }
    2196           1 :             ProcessCompletedJobs();
    2197             :         }
    2198          12 :         CPLAssert(apoCRCFinishedJobs_.empty());
    2199             :     }
    2200             : 
    2201          13 :     if (nDeflateType_ == CPL_DEFLATE_TYPE_GZIP)
    2202             :     {
    2203             :         const GUInt32 anTrailer[2] = {
    2204           1 :             CPL_LSBWORD32(static_cast<GUInt32>(nCRC_)),
    2205           1 :             CPL_LSBWORD32(static_cast<GUInt32>(nCurOffset_))};
    2206             : 
    2207           1 :         if (poBaseHandle_->Write(anTrailer, 1, 8) < 8)
    2208             :         {
    2209           0 :             nRet = -1;
    2210             :         }
    2211             :     }
    2212             : 
    2213          13 :     if (bAutoCloseBaseHandle_)
    2214             :     {
    2215           1 :         int nRetClose = poBaseHandle_->Close();
    2216           1 :         if (nRet == 0)
    2217           1 :             nRet = nRetClose;
    2218             : 
    2219           1 :         delete poBaseHandle_;
    2220             :     }
    2221          13 :     poBaseHandle_ = nullptr;
    2222             : 
    2223          13 :     return nRet;
    2224             : }
    2225             : 
    2226             : /************************************************************************/
    2227             : /*                                Read()                                */
    2228             : /************************************************************************/
    2229             : 
    2230           0 : size_t VSIGZipWriteHandleMT::Read(void * /* pBuffer */, size_t /* nSize */,
    2231             :                                   size_t /* nMemb */)
    2232             : {
    2233           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2234             :              "VSIFReadL is not supported on GZip write streams");
    2235           0 :     return 0;
    2236             : }
    2237             : 
    2238             : /************************************************************************/
    2239             : /*                        DeflateCompress()                             */
    2240             : /************************************************************************/
    2241             : 
    2242        1263 : void VSIGZipWriteHandleMT::DeflateCompress(void *inData)
    2243             : {
    2244        1263 :     Job *psJob = static_cast<Job *>(inData);
    2245             : 
    2246        1263 :     CPLAssert(psJob->pBuffer_);
    2247             : 
    2248             :     z_stream sStream;
    2249        1263 :     memset(&sStream, 0, sizeof(sStream));
    2250        1263 :     sStream.zalloc = nullptr;
    2251        1263 :     sStream.zfree = nullptr;
    2252        1263 :     sStream.opaque = nullptr;
    2253             : 
    2254        1263 :     sStream.avail_in = static_cast<uInt>(psJob->pBuffer_->size());
    2255        1264 :     sStream.next_in = reinterpret_cast<Bytef *>(&(*psJob->pBuffer_)[0]);
    2256             : 
    2257        1263 :     int ret = deflateInit2(
    2258             :         &sStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
    2259             :         (psJob->pParent_->nDeflateType_ == CPL_DEFLATE_TYPE_ZLIB) ? MAX_WBITS
    2260             :                                                                   : -MAX_WBITS,
    2261             :         8, Z_DEFAULT_STRATEGY);
    2262        1263 :     CPLAssertAlwaysEval(ret == Z_OK);
    2263             : 
    2264        1263 :     size_t nRealSize = 0;
    2265             : 
    2266        2524 :     while (sStream.avail_in > 0)
    2267             :     {
    2268        1261 :         psJob->sCompressedData_.resize(nRealSize + Z_BUFSIZE);
    2269        1261 :         sStream.avail_out = static_cast<uInt>(Z_BUFSIZE);
    2270        1261 :         sStream.next_out =
    2271        1261 :             reinterpret_cast<Bytef *>(&psJob->sCompressedData_[0]) + nRealSize;
    2272             : 
    2273        1261 :         const int zlibRet = deflate(&sStream, Z_NO_FLUSH);
    2274        1260 :         CPLAssertAlwaysEval(zlibRet == Z_OK);
    2275             : 
    2276        1261 :         nRealSize += static_cast<uInt>(Z_BUFSIZE) - sStream.avail_out;
    2277             :     }
    2278             : 
    2279        1263 :     psJob->sCompressedData_.resize(nRealSize + Z_BUFSIZE);
    2280        1264 :     sStream.avail_out = static_cast<uInt>(Z_BUFSIZE);
    2281        1263 :     sStream.next_out =
    2282        1264 :         reinterpret_cast<Bytef *>(&psJob->sCompressedData_[0]) + nRealSize;
    2283             : 
    2284        1263 :     if (psJob->bFinish_)
    2285             :     {
    2286          13 :         const int zlibRet = deflate(&sStream, Z_FINISH);
    2287          13 :         CPLAssertAlwaysEval(zlibRet == Z_STREAM_END);
    2288             :     }
    2289             :     else
    2290             :     {
    2291             :         // Do a Z_SYNC_FLUSH and Z_FULL_FLUSH, so as to have two markers when
    2292             :         // independent as pigz 2.3.4 or later. The following 9 byte sequence
    2293             :         // will be found: 0x00 0x00 0xff 0xff 0x00 0x00 0x00 0xff 0xff
    2294             :         // Z_FULL_FLUSH only is sufficient, but it is not obvious if a
    2295             :         // 0x00 0x00 0xff 0xff marker in the codestream is just a SYNC_FLUSH (
    2296             :         // without dictionary reset) or a FULL_FLUSH (with dictionary reset)
    2297             :         {
    2298        1250 :             const int zlibRet = deflate(&sStream, Z_SYNC_FLUSH);
    2299        1248 :             CPLAssertAlwaysEval(zlibRet == Z_OK);
    2300             :         }
    2301             : 
    2302             :         {
    2303        1248 :             const int zlibRet = deflate(&sStream, Z_FULL_FLUSH);
    2304        1248 :             CPLAssertAlwaysEval(zlibRet == Z_OK);
    2305             :         }
    2306             :     }
    2307             : 
    2308        1261 :     nRealSize += static_cast<uInt>(Z_BUFSIZE) - sStream.avail_out;
    2309        1261 :     psJob->sCompressedData_.resize(nRealSize);
    2310             : 
    2311        1262 :     deflateEnd(&sStream);
    2312             : 
    2313             :     {
    2314        2525 :         std::lock_guard<std::mutex> oLock(psJob->pParent_->sMutex_);
    2315        1264 :         psJob->pParent_->apoFinishedJobs_.push_back(psJob);
    2316             :     }
    2317        1264 : }
    2318             : 
    2319             : /************************************************************************/
    2320             : /*                          CRCCompute()                                */
    2321             : /************************************************************************/
    2322             : 
    2323          16 : void VSIGZipWriteHandleMT::CRCCompute(void *inData)
    2324             : {
    2325          16 :     Job *psJob = static_cast<Job *>(inData);
    2326          16 :     psJob->bInCRCComputation_ = true;
    2327          32 :     psJob->nCRC_ =
    2328          16 :         crc32(0U, reinterpret_cast<const Bytef *>(psJob->pBuffer_->data()),
    2329          16 :               static_cast<uInt>(psJob->pBuffer_->size()));
    2330             : 
    2331             :     {
    2332          32 :         std::lock_guard<std::mutex> oLock(psJob->pParent_->sMutex_);
    2333          16 :         psJob->pParent_->apoCRCFinishedJobs_.push_back(psJob);
    2334             :     }
    2335          16 : }
    2336             : 
    2337             : /************************************************************************/
    2338             : /*                                DumpState()                           */
    2339             : /************************************************************************/
    2340             : 
    2341             : #ifdef DEBUG_VERBOSE
    2342             : void VSIGZipWriteHandleMT::DumpState()
    2343             : {
    2344             :     fprintf(stderr, "Finished jobs (expected = %d):\n",  // ok
    2345             :             nSeqNumberExpected_);
    2346             :     for (const auto *psJob : apoFinishedJobs_)
    2347             :     {
    2348             :         fprintf(stderr, "seq number=%d, bInCRCComputation = %d\n",  // ok
    2349             :                 psJob->nSeqNumber_, psJob->bInCRCComputation_ ? 1 : 0);
    2350             :     }
    2351             :     fprintf(stderr, "Finished CRC jobs (expected = %d):\n",  // ok
    2352             :             nSeqNumberExpectedCRC_);
    2353             :     for (const auto *psJob : apoFinishedJobs_)
    2354             :     {
    2355             :         fprintf(stderr, "seq number=%d\n",  // ok
    2356             :                 psJob->nSeqNumber_);
    2357             :     }
    2358             :     fprintf(stderr, "apoFreeJobs_.size() = %d\n",  // ok
    2359             :             static_cast<int>(apoFreeJobs_.size()));
    2360             :     fprintf(stderr, "aposBuffers_.size() = %d\n",  // ok
    2361             :             static_cast<int>(aposBuffers_.size()));
    2362             : }
    2363             : #endif
    2364             : 
    2365             : /************************************************************************/
    2366             : /*                         ProcessCompletedJobs()                       */
    2367             : /************************************************************************/
    2368             : 
    2369         795 : bool VSIGZipWriteHandleMT::ProcessCompletedJobs()
    2370             : {
    2371        1590 :     std::lock_guard<std::mutex> oLock(sMutex_);
    2372         795 :     bool do_it_again = true;
    2373        2856 :     while (do_it_again)
    2374             :     {
    2375        2063 :         do_it_again = false;
    2376        2063 :         if (nDeflateType_ == CPL_DEFLATE_TYPE_GZIP)
    2377             :         {
    2378          74 :             for (auto iter = apoFinishedJobs_.begin();
    2379         117 :                  iter != apoFinishedJobs_.end(); ++iter)
    2380             :             {
    2381          43 :                 auto psJob = *iter;
    2382             : 
    2383          43 :                 if (!psJob->bInCRCComputation_)
    2384             :                 {
    2385          16 :                     psJob->bInCRCComputation_ = true;
    2386          16 :                     sMutex_.unlock();
    2387          16 :                     if (poPool_)
    2388             :                     {
    2389          16 :                         poPool_->SubmitJob(VSIGZipWriteHandleMT::CRCCompute,
    2390             :                                            psJob);
    2391             :                     }
    2392             :                     else
    2393             :                     {
    2394           0 :                         CRCCompute(psJob);
    2395             :                     }
    2396          16 :                     sMutex_.lock();
    2397             :                 }
    2398             :             }
    2399             :         }
    2400             : 
    2401        2674 :         for (auto iter = apoFinishedJobs_.begin();
    2402        3285 :              iter != apoFinishedJobs_.end(); ++iter)
    2403             :         {
    2404        1872 :             auto psJob = *iter;
    2405        1872 :             if (psJob->nSeqNumber_ == nSeqNumberExpected_)
    2406             :             {
    2407        1261 :                 apoFinishedJobs_.erase(iter);
    2408             : 
    2409        1261 :                 const bool bIsSeqNumberExpectedZero =
    2410        1261 :                     (nSeqNumberExpected_ == 0);
    2411        1261 :                 sMutex_.unlock();
    2412             : 
    2413        1261 :                 const size_t nToWrite = psJob->sCompressedData_.size();
    2414        2462 :                 if (panSOZIPIndex_ && !bIsSeqNumberExpectedZero &&
    2415        1201 :                     !psJob->pBuffer_->empty())
    2416             :                 {
    2417        1199 :                     uint64_t nOffset = poBaseHandle_->Tell() - nStartOffset_;
    2418        1199 :                     if (nSOZIPIndexEltSize_ == 8)
    2419             :                     {
    2420        1199 :                         CPL_LSBPTR64(&nOffset);
    2421             :                         std::copy(reinterpret_cast<const uint8_t *>(&nOffset),
    2422             :                                   reinterpret_cast<const uint8_t *>(&nOffset) +
    2423             :                                       sizeof(nOffset),
    2424        1199 :                                   std::back_inserter(*panSOZIPIndex_));
    2425             :                     }
    2426             :                     else
    2427             :                     {
    2428           0 :                         if (nOffset > std::numeric_limits<uint32_t>::max())
    2429             :                         {
    2430             :                             // shouldn't happen normally...
    2431           0 :                             CPLError(
    2432             :                                 CE_Failure, CPLE_AppDefined,
    2433             :                                 "Too big offset for SOZIP_OFFSET_SIZE = 4");
    2434           0 :                             panSOZIPIndex_->clear();
    2435           0 :                             panSOZIPIndex_ = nullptr;
    2436             :                         }
    2437             :                         else
    2438             :                         {
    2439           0 :                             uint32_t nOffset32 = static_cast<uint32_t>(nOffset);
    2440           0 :                             CPL_LSBPTR32(&nOffset32);
    2441             :                             std::copy(
    2442             :                                 reinterpret_cast<const uint8_t *>(&nOffset32),
    2443             :                                 reinterpret_cast<const uint8_t *>(&nOffset32) +
    2444             :                                     sizeof(nOffset32),
    2445           0 :                                 std::back_inserter(*panSOZIPIndex_));
    2446             :                         }
    2447             :                     }
    2448             :                 }
    2449             :                 bool bError =
    2450        1261 :                     poBaseHandle_->Write(psJob->sCompressedData_.data(), 1,
    2451        1261 :                                          nToWrite) < nToWrite;
    2452        1261 :                 sMutex_.lock();
    2453        1261 :                 nSeqNumberExpected_++;
    2454             : 
    2455        1261 :                 if (nDeflateType_ != CPL_DEFLATE_TYPE_GZIP)
    2456             :                 {
    2457        1245 :                     aposBuffers_.push_back(psJob->pBuffer_);
    2458        1245 :                     psJob->pBuffer_ = nullptr;
    2459             : 
    2460        1245 :                     apoFreeJobs_.push_back(psJob);
    2461             :                 }
    2462             : 
    2463        1261 :                 if (bError)
    2464             :                 {
    2465           2 :                     return false;
    2466             :                 }
    2467             : 
    2468        1259 :                 do_it_again = true;
    2469        1259 :                 break;
    2470             :             }
    2471             :         }
    2472             : 
    2473        2061 :         if (nDeflateType_ == CPL_DEFLATE_TYPE_GZIP)
    2474             :         {
    2475          38 :             for (auto iter = apoCRCFinishedJobs_.begin();
    2476          45 :                  iter != apoCRCFinishedJobs_.end(); ++iter)
    2477             :             {
    2478          23 :                 auto psJob = *iter;
    2479          23 :                 if (psJob->nSeqNumber_ == nSeqNumberExpectedCRC_)
    2480             :                 {
    2481          16 :                     apoCRCFinishedJobs_.erase(iter);
    2482             : 
    2483          16 :                     nCRC_ = crc32_combine(
    2484             :                         nCRC_, psJob->nCRC_,
    2485          16 :                         static_cast<uLong>(psJob->pBuffer_->size()));
    2486             : 
    2487          16 :                     nSeqNumberExpectedCRC_++;
    2488             : 
    2489          16 :                     aposBuffers_.push_back(psJob->pBuffer_);
    2490          16 :                     psJob->pBuffer_ = nullptr;
    2491             : 
    2492          16 :                     apoFreeJobs_.push_back(psJob);
    2493          16 :                     do_it_again = true;
    2494          16 :                     break;
    2495             :                 }
    2496             :             }
    2497             :         }
    2498             :     }
    2499         793 :     return true;
    2500             : }
    2501             : 
    2502             : /************************************************************************/
    2503             : /*                           GetJobObject()                             */
    2504             : /************************************************************************/
    2505             : 
    2506        1264 : VSIGZipWriteHandleMT::Job *VSIGZipWriteHandleMT::GetJobObject()
    2507             : {
    2508             :     {
    2509        1264 :         std::lock_guard<std::mutex> oLock(sMutex_);
    2510        1264 :         if (!apoFreeJobs_.empty())
    2511             :         {
    2512        1201 :             auto job = apoFreeJobs_.back();
    2513        1201 :             apoFreeJobs_.pop_back();
    2514        1201 :             job->sCompressedData_.clear();
    2515        1201 :             job->bInCRCComputation_ = false;
    2516        1201 :             return job;
    2517             :         }
    2518             :     }
    2519          63 :     return new Job();
    2520             : }
    2521             : 
    2522             : /************************************************************************/
    2523             : /*                               Write()                                */
    2524             : /************************************************************************/
    2525             : 
    2526      300020 : size_t VSIGZipWriteHandleMT::Write(const void *const pBuffer,
    2527             :                                    size_t const nSize, size_t const nMemb)
    2528             : 
    2529             : {
    2530      300020 :     if (bHasErrored_)
    2531       34463 :         return 0;
    2532             : 
    2533      265557 :     const char *pszBuffer = static_cast<const char *>(pBuffer);
    2534      265557 :     size_t nBytesToWrite = nSize * nMemb;
    2535      532342 :     while (nBytesToWrite > 0)
    2536             :     {
    2537      266786 :         if (pCurBuffer_ == nullptr)
    2538             :         {
    2539             :             while (true)
    2540             :             {
    2541             :                 // We store in a local variable instead of pCurBuffer_ directly
    2542             :                 // to avoid Coverity Scan to be confused by the fact that we
    2543             :                 // have used above pCurBuffer_ outside of the mutex. But what
    2544             :                 // is protected by the mutex is aposBuffers_, not pCurBuffer_.
    2545        2042 :                 std::string *l_pCurBuffer = nullptr;
    2546             :                 {
    2547        4084 :                     std::lock_guard<std::mutex> oLock(sMutex_);
    2548        2042 :                     if (!aposBuffers_.empty())
    2549             :                     {
    2550        1261 :                         l_pCurBuffer = aposBuffers_.back();
    2551        1261 :                         aposBuffers_.pop_back();
    2552             :                     }
    2553             :                 }
    2554        2042 :                 pCurBuffer_ = l_pCurBuffer;
    2555        2042 :                 if (pCurBuffer_)
    2556        1261 :                     break;
    2557             : 
    2558         781 :                 if (poPool_)
    2559             :                 {
    2560         781 :                     poPool_->WaitEvent();
    2561             :                 }
    2562         781 :                 if (!ProcessCompletedJobs())
    2563             :                 {
    2564           1 :                     bHasErrored_ = true;
    2565           1 :                     return 0;
    2566             :                 }
    2567         780 :             }
    2568        1261 :             pCurBuffer_->clear();
    2569             :         }
    2570             :         size_t nConsumed =
    2571      266785 :             std::min(nBytesToWrite, nChunkSize_ - pCurBuffer_->size());
    2572      266785 :         pCurBuffer_->append(pszBuffer, nConsumed);
    2573      266785 :         nCurOffset_ += nConsumed;
    2574      266785 :         pszBuffer += nConsumed;
    2575      266785 :         nBytesToWrite -= nConsumed;
    2576      266785 :         if (pCurBuffer_->size() == nChunkSize_)
    2577             :         {
    2578        1251 :             if (poPool_ == nullptr)
    2579             :             {
    2580          12 :                 poPool_.reset(new CPLWorkerThreadPool());
    2581          12 :                 if (!poPool_->Setup(nThreads_, nullptr, nullptr, false))
    2582             :                 {
    2583           0 :                     bHasErrored_ = true;
    2584           0 :                     poPool_.reset();
    2585           0 :                     return 0;
    2586             :                 }
    2587             :             }
    2588             : 
    2589        1251 :             auto psJob = GetJobObject();
    2590        1251 :             psJob->pParent_ = this;
    2591        1251 :             psJob->pBuffer_ = pCurBuffer_;
    2592        1251 :             psJob->nSeqNumber_ = nSeqNumberGenerated_;
    2593        1251 :             nSeqNumberGenerated_++;
    2594        1251 :             pCurBuffer_ = nullptr;
    2595        1251 :             poPool_->SubmitJob(VSIGZipWriteHandleMT::DeflateCompress, psJob);
    2596             :         }
    2597             :     }
    2598             : 
    2599      265556 :     return nMemb;
    2600             : }
    2601             : 
    2602             : /************************************************************************/
    2603             : /*                               Flush()                                */
    2604             : /************************************************************************/
    2605             : 
    2606           0 : int VSIGZipWriteHandleMT::Flush()
    2607             : 
    2608             : {
    2609             :     // we *could* do something for this but for now we choose not to.
    2610             : 
    2611           0 :     return 0;
    2612             : }
    2613             : 
    2614             : /************************************************************************/
    2615             : /*                                Seek()                                */
    2616             : /************************************************************************/
    2617             : 
    2618           0 : int VSIGZipWriteHandleMT::Seek(vsi_l_offset nOffset, int nWhence)
    2619             : 
    2620             : {
    2621           0 :     if (nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR))
    2622           0 :         return 0;
    2623           0 :     else if (nWhence == SEEK_SET && nOffset == nCurOffset_)
    2624           0 :         return 0;
    2625             :     else
    2626             :     {
    2627           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2628             :                  "Seeking on writable compressed data streams not supported.");
    2629             : 
    2630           0 :         return -1;
    2631             :     }
    2632             : }
    2633             : 
    2634             : /************************************************************************/
    2635             : /*                                Tell()                                */
    2636             : /************************************************************************/
    2637             : 
    2638           0 : vsi_l_offset VSIGZipWriteHandleMT::Tell()
    2639             : 
    2640             : {
    2641           0 :     return nCurOffset_;
    2642             : }
    2643             : 
    2644             : /************************************************************************/
    2645             : /* ==================================================================== */
    2646             : /*                       VSIGZipWriteHandle                             */
    2647             : /* ==================================================================== */
    2648             : /************************************************************************/
    2649             : 
    2650             : class VSIGZipWriteHandle final : public VSIVirtualHandle
    2651             : {
    2652             :     CPL_DISALLOW_COPY_ASSIGN(VSIGZipWriteHandle)
    2653             : 
    2654             :     VSIVirtualHandle *m_poBaseHandle = nullptr;
    2655             :     z_stream sStream;
    2656             :     Byte *pabyInBuf = nullptr;
    2657             :     Byte *pabyOutBuf = nullptr;
    2658             :     bool bCompressActive = false;
    2659             :     vsi_l_offset nCurOffset = 0;
    2660             :     uLong nCRC = 0;
    2661             :     int nDeflateType = CPL_DEFLATE_TYPE_GZIP;
    2662             :     bool bAutoCloseBaseHandle = false;
    2663             : 
    2664             :   public:
    2665             :     VSIGZipWriteHandle(VSIVirtualHandle *poBaseHandle, int nDeflateType,
    2666             :                        bool bAutoCloseBaseHandleIn);
    2667             : 
    2668             :     ~VSIGZipWriteHandle() override;
    2669             : 
    2670             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
    2671             :     vsi_l_offset Tell() override;
    2672             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
    2673             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
    2674             : 
    2675           0 :     int Eof() override
    2676             :     {
    2677           0 :         return 0;
    2678             :     }
    2679             : 
    2680           0 :     int Error() override
    2681             :     {
    2682           0 :         return 0;
    2683             :     }
    2684             : 
    2685           0 :     void ClearErr() override
    2686             :     {
    2687           0 :     }
    2688             : 
    2689             :     int Flush() override;
    2690             :     int Close() override;
    2691             : };
    2692             : 
    2693             : /************************************************************************/
    2694             : /*                         VSIGZipWriteHandle()                         */
    2695             : /************************************************************************/
    2696             : 
    2697        1988 : VSIGZipWriteHandle::VSIGZipWriteHandle(VSIVirtualHandle *poBaseHandle,
    2698             :                                        int nDeflateTypeIn,
    2699        1988 :                                        bool bAutoCloseBaseHandleIn)
    2700             :     : m_poBaseHandle(poBaseHandle), sStream(),
    2701        3976 :       pabyInBuf(static_cast<Byte *>(CPLMalloc(Z_BUFSIZE))),
    2702        3976 :       pabyOutBuf(static_cast<Byte *>(CPLMalloc(Z_BUFSIZE))),
    2703        3976 :       nCRC(crc32(0L, nullptr, 0)), nDeflateType(nDeflateTypeIn),
    2704        1988 :       bAutoCloseBaseHandle(bAutoCloseBaseHandleIn)
    2705             : {
    2706        1988 :     sStream.zalloc = nullptr;
    2707        1988 :     sStream.zfree = nullptr;
    2708        1988 :     sStream.opaque = nullptr;
    2709        1988 :     sStream.next_in = nullptr;
    2710        1988 :     sStream.next_out = nullptr;
    2711        1988 :     sStream.avail_in = sStream.avail_out = 0;
    2712             : 
    2713        1988 :     sStream.next_in = pabyInBuf;
    2714             : 
    2715        1988 :     if (deflateInit2(&sStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
    2716             :                      (nDeflateType == CPL_DEFLATE_TYPE_ZLIB) ? MAX_WBITS
    2717             :                                                              : -MAX_WBITS,
    2718        1988 :                      8, Z_DEFAULT_STRATEGY) != Z_OK)
    2719             :     {
    2720           0 :         bCompressActive = false;
    2721             :     }
    2722             :     else
    2723             :     {
    2724        1988 :         if (nDeflateType == CPL_DEFLATE_TYPE_GZIP)
    2725             :         {
    2726         932 :             char header[11] = {};
    2727             : 
    2728             :             // Write a very simple .gz header:
    2729         932 :             snprintf(header, sizeof(header), "%c%c%c%c%c%c%c%c%c%c",
    2730         932 :                      gz_magic[0], gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0, 0, 0,
    2731             :                      0 /*time*/, 0 /*xflags*/, 0x03);
    2732         932 :             m_poBaseHandle->Write(header, 1, 10);
    2733             :         }
    2734             : 
    2735        1988 :         bCompressActive = true;
    2736             :     }
    2737        1988 : }
    2738             : 
    2739             : /************************************************************************/
    2740             : /*                       VSICreateGZipWritable()                        */
    2741             : /************************************************************************/
    2742             : 
    2743        1382 : VSIVirtualHandle *VSICreateGZipWritable(VSIVirtualHandle *poBaseHandle,
    2744             :                                         int nDeflateTypeIn,
    2745             :                                         int bAutoCloseBaseHandle)
    2746             : {
    2747        1382 :     return VSICreateGZipWritable(poBaseHandle, nDeflateTypeIn,
    2748        1382 :                                  CPL_TO_BOOL(bAutoCloseBaseHandle), 0, 0, 0,
    2749        1382 :                                  nullptr);
    2750             : }
    2751             : 
    2752        2001 : VSIVirtualHandle *VSICreateGZipWritable(VSIVirtualHandle *poBaseHandle,
    2753             :                                         int nDeflateTypeIn,
    2754             :                                         bool bAutoCloseBaseHandle, int nThreads,
    2755             :                                         size_t nChunkSize,
    2756             :                                         size_t nSOZIPIndexEltSize,
    2757             :                                         std::vector<uint8_t> *panSOZIPIndex)
    2758             : {
    2759        2001 :     const char *pszThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    2760        2001 :     if (pszThreads || nThreads > 0 || nChunkSize > 0)
    2761             :     {
    2762          13 :         if (nThreads == 0)
    2763             :         {
    2764           4 :             if (!pszThreads || EQUAL(pszThreads, "ALL_CPUS"))
    2765           4 :                 nThreads = CPLGetNumCPUs();
    2766             :             else
    2767           0 :                 nThreads = atoi(pszThreads);
    2768           4 :             nThreads = std::max(1, std::min(128, nThreads));
    2769             :         }
    2770          13 :         if (nThreads > 1 || nChunkSize > 0)
    2771             :         {
    2772             :             // coverity[tainted_data]
    2773             :             return new VSIGZipWriteHandleMT(
    2774             :                 poBaseHandle, nDeflateTypeIn, bAutoCloseBaseHandle, nThreads,
    2775          13 :                 nChunkSize, nSOZIPIndexEltSize, panSOZIPIndex);
    2776             :         }
    2777             :     }
    2778             :     return new VSIGZipWriteHandle(poBaseHandle, nDeflateTypeIn,
    2779        1988 :                                   bAutoCloseBaseHandle);
    2780             : }
    2781             : 
    2782             : /************************************************************************/
    2783             : /*                        ~VSIGZipWriteHandle()                         */
    2784             : /************************************************************************/
    2785             : 
    2786        3976 : VSIGZipWriteHandle::~VSIGZipWriteHandle()
    2787             : 
    2788             : {
    2789        1988 :     if (bCompressActive)
    2790         607 :         VSIGZipWriteHandle::Close();
    2791             : 
    2792        1988 :     CPLFree(pabyInBuf);
    2793        1988 :     CPLFree(pabyOutBuf);
    2794        3976 : }
    2795             : 
    2796             : /************************************************************************/
    2797             : /*                               Close()                                */
    2798             : /************************************************************************/
    2799             : 
    2800        1988 : int VSIGZipWriteHandle::Close()
    2801             : 
    2802             : {
    2803        1988 :     int nRet = 0;
    2804        1988 :     if (bCompressActive)
    2805             :     {
    2806        1988 :         sStream.next_out = pabyOutBuf;
    2807        1988 :         sStream.avail_out = static_cast<uInt>(Z_BUFSIZE);
    2808             : 
    2809        1988 :         const int zlibRet = deflate(&sStream, Z_FINISH);
    2810        1988 :         CPLAssertAlwaysEval(zlibRet == Z_STREAM_END);
    2811             : 
    2812        1988 :         const size_t nOutBytes =
    2813        1988 :             static_cast<uInt>(Z_BUFSIZE) - sStream.avail_out;
    2814             : 
    2815        1988 :         deflateEnd(&sStream);
    2816             : 
    2817        1988 :         if (m_poBaseHandle->Write(pabyOutBuf, 1, nOutBytes) < nOutBytes)
    2818             :         {
    2819          11 :             nRet = -1;
    2820             :         }
    2821             : 
    2822        1988 :         if (nRet == 0 && nDeflateType == CPL_DEFLATE_TYPE_GZIP)
    2823             :         {
    2824             :             const GUInt32 anTrailer[2] = {
    2825         932 :                 CPL_LSBWORD32(static_cast<GUInt32>(nCRC)),
    2826         932 :                 CPL_LSBWORD32(static_cast<GUInt32>(nCurOffset))};
    2827             : 
    2828         932 :             if (m_poBaseHandle->Write(anTrailer, 1, 8) < 8)
    2829             :             {
    2830           0 :                 nRet = -1;
    2831             :             }
    2832             :         }
    2833             : 
    2834        1988 :         if (bAutoCloseBaseHandle)
    2835             :         {
    2836         932 :             if (nRet == 0)
    2837         932 :                 nRet = m_poBaseHandle->Close();
    2838             : 
    2839         932 :             delete m_poBaseHandle;
    2840             :         }
    2841             : 
    2842        1988 :         bCompressActive = false;
    2843             :     }
    2844             : 
    2845        1988 :     return nRet;
    2846             : }
    2847             : 
    2848             : /************************************************************************/
    2849             : /*                                Read()                                */
    2850             : /************************************************************************/
    2851             : 
    2852           0 : size_t VSIGZipWriteHandle::Read(void * /* pBuffer */, size_t /* nSize */,
    2853             :                                 size_t /* nMemb */)
    2854             : {
    2855           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2856             :              "VSIFReadL is not supported on GZip write streams");
    2857           0 :     return 0;
    2858             : }
    2859             : 
    2860             : /************************************************************************/
    2861             : /*                               Write()                                */
    2862             : /************************************************************************/
    2863             : 
    2864      107520 : size_t VSIGZipWriteHandle::Write(const void *const pBuffer, size_t const nSize,
    2865             :                                  size_t const nMemb)
    2866             : 
    2867             : {
    2868      107520 :     size_t nBytesToWrite = nSize * nMemb;
    2869             : 
    2870             :     {
    2871      107520 :         size_t nOffset = 0;
    2872      215040 :         while (nOffset < nBytesToWrite)
    2873             :         {
    2874      107520 :             uInt nChunk = static_cast<uInt>(std::min(
    2875      107520 :                 static_cast<size_t>(UINT_MAX), nBytesToWrite - nOffset));
    2876      107520 :             nCRC =
    2877      107520 :                 crc32(nCRC, reinterpret_cast<const Bytef *>(pBuffer) + nOffset,
    2878             :                       nChunk);
    2879      107520 :             nOffset += nChunk;
    2880             :         }
    2881             :     }
    2882             : 
    2883      107520 :     if (!bCompressActive)
    2884           0 :         return 0;
    2885             : 
    2886      107520 :     size_t nNextByte = 0;
    2887      215286 :     while (nNextByte < nBytesToWrite)
    2888             :     {
    2889      107771 :         sStream.next_out = pabyOutBuf;
    2890      107771 :         sStream.avail_out = static_cast<uInt>(Z_BUFSIZE);
    2891             : 
    2892      107771 :         if (sStream.avail_in > 0)
    2893           0 :             memmove(pabyInBuf, sStream.next_in, sStream.avail_in);
    2894             : 
    2895             :         const uInt nNewBytesToWrite = static_cast<uInt>(
    2896      215542 :             std::min(static_cast<size_t>(Z_BUFSIZE - sStream.avail_in),
    2897      107771 :                      nBytesToWrite - nNextByte));
    2898      107771 :         memcpy(pabyInBuf + sStream.avail_in,
    2899             :                reinterpret_cast<const Byte *>(pBuffer) + nNextByte,
    2900             :                nNewBytesToWrite);
    2901             : 
    2902      107771 :         sStream.next_in = pabyInBuf;
    2903      107771 :         sStream.avail_in += nNewBytesToWrite;
    2904             : 
    2905      107771 :         const int zlibRet = deflate(&sStream, Z_NO_FLUSH);
    2906      107771 :         CPLAssertAlwaysEval(zlibRet == Z_OK);
    2907             : 
    2908      107771 :         const size_t nOutBytes =
    2909      107771 :             static_cast<uInt>(Z_BUFSIZE) - sStream.avail_out;
    2910             : 
    2911      107771 :         if (nOutBytes > 0)
    2912             :         {
    2913         508 :             if (m_poBaseHandle->Write(pabyOutBuf, 1, nOutBytes) < nOutBytes)
    2914           5 :                 return 0;
    2915             :         }
    2916             : 
    2917      107766 :         nNextByte += nNewBytesToWrite;
    2918      107766 :         nCurOffset += nNewBytesToWrite;
    2919             :     }
    2920             : 
    2921      107515 :     return nMemb;
    2922             : }
    2923             : 
    2924             : /************************************************************************/
    2925             : /*                               Flush()                                */
    2926             : /************************************************************************/
    2927             : 
    2928           6 : int VSIGZipWriteHandle::Flush()
    2929             : 
    2930             : {
    2931             :     // we *could* do something for this but for now we choose not to.
    2932             : 
    2933           6 :     return 0;
    2934             : }
    2935             : 
    2936             : /************************************************************************/
    2937             : /*                                Seek()                                */
    2938             : /************************************************************************/
    2939             : 
    2940           0 : int VSIGZipWriteHandle::Seek(vsi_l_offset nOffset, int nWhence)
    2941             : 
    2942             : {
    2943           0 :     if (nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR))
    2944           0 :         return 0;
    2945           0 :     else if (nWhence == SEEK_SET && nOffset == nCurOffset)
    2946           0 :         return 0;
    2947             :     else
    2948             :     {
    2949           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2950             :                  "Seeking on writable compressed data streams not supported.");
    2951             : 
    2952           0 :         return -1;
    2953             :     }
    2954             : }
    2955             : 
    2956             : /************************************************************************/
    2957             : /*                                Tell()                                */
    2958             : /************************************************************************/
    2959             : 
    2960           6 : vsi_l_offset VSIGZipWriteHandle::Tell()
    2961             : 
    2962             : {
    2963           6 :     return nCurOffset;
    2964             : }
    2965             : 
    2966             : /************************************************************************/
    2967             : /* ==================================================================== */
    2968             : /*                       VSIGZipFilesystemHandler                       */
    2969             : /* ==================================================================== */
    2970             : /************************************************************************/
    2971             : 
    2972             : /************************************************************************/
    2973             : /*                  ~VSIGZipFilesystemHandler()                         */
    2974             : /************************************************************************/
    2975             : 
    2976        3732 : VSIGZipFilesystemHandler::~VSIGZipFilesystemHandler()
    2977             : {
    2978         933 :     if (poHandleLastGZipFile)
    2979             :     {
    2980           1 :         poHandleLastGZipFile->UnsetCanSaveInfo();
    2981           1 :         delete poHandleLastGZipFile;
    2982             :     }
    2983             : 
    2984         933 :     if (hMutex != nullptr)
    2985           5 :         CPLDestroyMutex(hMutex);
    2986         933 :     hMutex = nullptr;
    2987        1866 : }
    2988             : 
    2989             : /************************************************************************/
    2990             : /*                            SaveInfo()                                */
    2991             : /************************************************************************/
    2992             : 
    2993         115 : void VSIGZipFilesystemHandler::SaveInfo(VSIGZipHandle *poHandle)
    2994             : {
    2995         230 :     CPLMutexHolder oHolder(&hMutex);
    2996         115 :     SaveInfo_unlocked(poHandle);
    2997         115 : }
    2998             : 
    2999         119 : void VSIGZipFilesystemHandler::SaveInfo_unlocked(VSIGZipHandle *poHandle)
    3000             : {
    3001         119 :     if (m_bInSaveInfo)
    3002           0 :         return;
    3003         119 :     m_bInSaveInfo = true;
    3004             : 
    3005         119 :     CPLAssert(poHandle != poHandleLastGZipFile);
    3006         119 :     CPLAssert(poHandle->GetBaseFileName() != nullptr);
    3007             : 
    3008         321 :     if (poHandleLastGZipFile == nullptr ||
    3009          83 :         strcmp(poHandleLastGZipFile->GetBaseFileName(),
    3010         202 :                poHandle->GetBaseFileName()) != 0 ||
    3011          83 :         poHandle->GetLastReadOffset() >
    3012          83 :             poHandleLastGZipFile->GetLastReadOffset())
    3013             :     {
    3014          36 :         VSIGZipHandle *poTmp = poHandleLastGZipFile;
    3015          36 :         poHandleLastGZipFile = nullptr;
    3016          36 :         if (poTmp)
    3017             :         {
    3018           0 :             poTmp->UnsetCanSaveInfo();
    3019           0 :             delete poTmp;
    3020             :         }
    3021          36 :         CPLAssert(poHandleLastGZipFile == nullptr);
    3022          36 :         poHandleLastGZipFile = poHandle->Duplicate();
    3023          36 :         if (poHandleLastGZipFile)
    3024          36 :             poHandleLastGZipFile->CloseBaseHandle();
    3025             :     }
    3026         119 :     m_bInSaveInfo = false;
    3027             : }
    3028             : 
    3029             : /************************************************************************/
    3030             : /*                                Open()                                */
    3031             : /************************************************************************/
    3032             : 
    3033             : VSIVirtualHandle *
    3034        1455 : VSIGZipFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
    3035             :                                bool /* bSetError */,
    3036             :                                CSLConstList /* papszOptions */)
    3037             : {
    3038        1455 :     if (!STARTS_WITH_CI(pszFilename, "/vsigzip/"))
    3039           1 :         return nullptr;
    3040             : 
    3041             :     VSIFilesystemHandler *poFSHandler =
    3042        1454 :         VSIFileManager::GetHandler(pszFilename + strlen("/vsigzip/"));
    3043             : 
    3044             :     /* -------------------------------------------------------------------- */
    3045             :     /*      Is this an attempt to write a new file without update (w+)      */
    3046             :     /*      access?  If so, create a writable handle for the underlying     */
    3047             :     /*      filename.                                                       */
    3048             :     /* -------------------------------------------------------------------- */
    3049        1454 :     if (strchr(pszAccess, 'w') != nullptr)
    3050             :     {
    3051         935 :         if (strchr(pszAccess, '+') != nullptr)
    3052             :         {
    3053           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3054             :                      "Write+update (w+) not supported for /vsigzip, "
    3055             :                      "only read-only or write-only.");
    3056           0 :             return nullptr;
    3057             :         }
    3058             : 
    3059             :         VSIVirtualHandle *poVirtualHandle =
    3060         935 :             poFSHandler->Open(pszFilename + strlen("/vsigzip/"), "wb");
    3061             : 
    3062         935 :         if (poVirtualHandle == nullptr)
    3063           2 :             return nullptr;
    3064             : 
    3065        1866 :         return VSICreateGZipWritable(poVirtualHandle,
    3066         933 :                                      strchr(pszAccess, 'z') != nullptr, TRUE);
    3067             :     }
    3068             : 
    3069             :     /* -------------------------------------------------------------------- */
    3070             :     /*      Otherwise we are in the read access case.                       */
    3071             :     /* -------------------------------------------------------------------- */
    3072             : 
    3073         519 :     VSIGZipHandle *poGZIPHandle = OpenGZipReadOnly(pszFilename, pszAccess);
    3074         519 :     if (poGZIPHandle)
    3075             :         // Wrap the VSIGZipHandle inside a buffered reader that will
    3076             :         // improve dramatically performance when doing small backward
    3077             :         // seeks.
    3078         489 :         return VSICreateBufferedReaderHandle(poGZIPHandle);
    3079             : 
    3080          30 :     return nullptr;
    3081             : }
    3082             : 
    3083             : /************************************************************************/
    3084             : /*                      SupportsSequentialWrite()                       */
    3085             : /************************************************************************/
    3086             : 
    3087           2 : bool VSIGZipFilesystemHandler::SupportsSequentialWrite(const char *pszPath,
    3088             :                                                        bool bAllowLocalTempFile)
    3089             : {
    3090           2 :     if (!STARTS_WITH_CI(pszPath, "/vsigzip/"))
    3091           0 :         return false;
    3092           2 :     const char *pszBaseFileName = pszPath + strlen("/vsigzip/");
    3093             :     VSIFilesystemHandler *poFSHandler =
    3094           2 :         VSIFileManager::GetHandler(pszBaseFileName);
    3095           2 :     return poFSHandler->SupportsSequentialWrite(pszPath, bAllowLocalTempFile);
    3096             : }
    3097             : 
    3098             : /************************************************************************/
    3099             : /*                          OpenGZipReadOnly()                          */
    3100             : /************************************************************************/
    3101             : 
    3102             : VSIGZipHandle *
    3103         524 : VSIGZipFilesystemHandler::OpenGZipReadOnly(const char *pszFilename,
    3104             :                                            const char *pszAccess)
    3105             : {
    3106             :     VSIFilesystemHandler *poFSHandler =
    3107         524 :         VSIFileManager::GetHandler(pszFilename + strlen("/vsigzip/"));
    3108             : 
    3109        1048 :     CPLMutexHolder oHolder(&hMutex);
    3110             : 
    3111             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    3112             :     // Disable caching in fuzzing mode as the /vsigzip/ file is likely to
    3113             :     // change very often
    3114             :     // TODO: filename-based logic isn't enough. We should probably check
    3115             :     // timestamp and/or file size.
    3116        1163 :     if (poHandleLastGZipFile != nullptr &&
    3117         115 :         strcmp(pszFilename + strlen("/vsigzip/"),
    3118         639 :                poHandleLastGZipFile->GetBaseFileName()) == 0 &&
    3119          76 :         EQUAL(pszAccess, "rb"))
    3120             :     {
    3121          74 :         VSIGZipHandle *poHandle = poHandleLastGZipFile->Duplicate();
    3122          74 :         if (poHandle)
    3123          74 :             return poHandle;
    3124             :     }
    3125             : #else
    3126             :     CPL_IGNORE_RET_VAL(pszAccess);
    3127             : #endif
    3128             : 
    3129             :     VSIVirtualHandle *poVirtualHandle =
    3130         450 :         poFSHandler->Open(pszFilename + strlen("/vsigzip/"), "rb");
    3131             : 
    3132         450 :     if (poVirtualHandle == nullptr)
    3133          30 :         return nullptr;
    3134             : 
    3135         420 :     unsigned char signature[2] = {'\0', '\0'};
    3136         420 :     if (poVirtualHandle->Read(signature, 1, 2) != 2 ||
    3137         420 :         signature[0] != gz_magic[0] || signature[1] != gz_magic[1])
    3138             :     {
    3139           0 :         poVirtualHandle->Close();
    3140           0 :         delete poVirtualHandle;
    3141           0 :         return nullptr;
    3142             :     }
    3143             : 
    3144         420 :     if (poHandleLastGZipFile)
    3145             :     {
    3146          32 :         poHandleLastGZipFile->UnsetCanSaveInfo();
    3147          32 :         delete poHandleLastGZipFile;
    3148          32 :         poHandleLastGZipFile = nullptr;
    3149             :     }
    3150             : 
    3151             :     VSIGZipHandle *poHandle =
    3152         420 :         new VSIGZipHandle(poVirtualHandle, pszFilename + strlen("/vsigzip/"));
    3153         420 :     if (!(poHandle->IsInitOK()))
    3154             :     {
    3155           0 :         delete poHandle;
    3156           0 :         return nullptr;
    3157             :     }
    3158         420 :     return poHandle;
    3159             : }
    3160             : 
    3161             : /************************************************************************/
    3162             : /*                                Stat()                                */
    3163             : /************************************************************************/
    3164             : 
    3165         142 : int VSIGZipFilesystemHandler::Stat(const char *pszFilename,
    3166             :                                    VSIStatBufL *pStatBuf, int nFlags)
    3167             : {
    3168         142 :     if (!STARTS_WITH_CI(pszFilename, "/vsigzip/"))
    3169           1 :         return -1;
    3170             : 
    3171         282 :     CPLMutexHolder oHolder(&hMutex);
    3172             : 
    3173         141 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
    3174             : 
    3175         213 :     if (poHandleLastGZipFile != nullptr &&
    3176          72 :         strcmp(pszFilename + strlen("/vsigzip/"),
    3177          72 :                poHandleLastGZipFile->GetBaseFileName()) == 0)
    3178             :     {
    3179           2 :         if (poHandleLastGZipFile->GetUncompressedSize() != 0)
    3180             :         {
    3181           0 :             pStatBuf->st_mode = S_IFREG;
    3182           0 :             pStatBuf->st_size = poHandleLastGZipFile->GetUncompressedSize();
    3183           0 :             return 0;
    3184             :         }
    3185             :     }
    3186             : 
    3187             :     // Begin by doing a stat on the real file.
    3188         141 :     int ret = VSIStatExL(pszFilename + strlen("/vsigzip/"), pStatBuf, nFlags);
    3189             : 
    3190         141 :     if (ret == 0 && (nFlags & VSI_STAT_SIZE_FLAG))
    3191             :     {
    3192           5 :         CPLString osCacheFilename(pszFilename + strlen("/vsigzip/"));
    3193           5 :         osCacheFilename += ".properties";
    3194             : 
    3195             :         // Can we save a bit of seeking by using a .properties file?
    3196           5 :         VSILFILE *fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "rb");
    3197           5 :         if (fpCacheLength)
    3198             :         {
    3199             :             const char *pszLine;
    3200           4 :             GUIntBig nCompressedSize = 0;
    3201           4 :             GUIntBig nUncompressedSize = 0;
    3202          12 :             while ((pszLine = CPLReadLineL(fpCacheLength)) != nullptr)
    3203             :             {
    3204           8 :                 if (STARTS_WITH_CI(pszLine, "compressed_size="))
    3205             :                 {
    3206           4 :                     const char *pszBuffer =
    3207             :                         pszLine + strlen("compressed_size=");
    3208           4 :                     nCompressedSize = CPLScanUIntBig(
    3209           4 :                         pszBuffer, static_cast<int>(strlen(pszBuffer)));
    3210             :                 }
    3211           4 :                 else if (STARTS_WITH_CI(pszLine, "uncompressed_size="))
    3212             :                 {
    3213           4 :                     const char *pszBuffer =
    3214             :                         pszLine + strlen("uncompressed_size=");
    3215           4 :                     nUncompressedSize = CPLScanUIntBig(
    3216           4 :                         pszBuffer, static_cast<int>(strlen(pszBuffer)));
    3217             :                 }
    3218             :             }
    3219             : 
    3220           4 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpCacheLength));
    3221             : 
    3222           4 :             if (nCompressedSize == static_cast<GUIntBig>(pStatBuf->st_size))
    3223             :             {
    3224             :                 // Patch with the uncompressed size.
    3225           4 :                 pStatBuf->st_size = nUncompressedSize;
    3226             : 
    3227             :                 VSIGZipHandle *poHandle =
    3228           4 :                     VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename,
    3229             :                                                                "rb");
    3230           4 :                 if (poHandle)
    3231             :                 {
    3232           4 :                     poHandle->SetUncompressedSize(nUncompressedSize);
    3233           4 :                     SaveInfo_unlocked(poHandle);
    3234           4 :                     delete poHandle;
    3235             :                 }
    3236             : 
    3237           4 :                 return ret;
    3238             :             }
    3239             :         }
    3240             : 
    3241             :         // No, then seek at the end of the data (slow).
    3242             :         VSIGZipHandle *poHandle =
    3243           1 :             VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb");
    3244           1 :         if (poHandle)
    3245             :         {
    3246           1 :             poHandle->Seek(0, SEEK_END);
    3247             :             const GUIntBig uncompressed_size =
    3248           1 :                 static_cast<GUIntBig>(poHandle->Tell());
    3249           1 :             poHandle->Seek(0, SEEK_SET);
    3250             : 
    3251             :             // Patch with the uncompressed size.
    3252           1 :             pStatBuf->st_size = uncompressed_size;
    3253             : 
    3254           1 :             delete poHandle;
    3255             :         }
    3256             :         else
    3257             :         {
    3258           0 :             ret = -1;
    3259             :         }
    3260             :     }
    3261             : 
    3262         137 :     return ret;
    3263             : }
    3264             : 
    3265             : /************************************************************************/
    3266             : /*                               Unlink()                               */
    3267             : /************************************************************************/
    3268             : 
    3269           0 : int VSIGZipFilesystemHandler::Unlink(const char * /* pszFilename */)
    3270             : {
    3271           0 :     return -1;
    3272             : }
    3273             : 
    3274             : /************************************************************************/
    3275             : /*                               Rename()                               */
    3276             : /************************************************************************/
    3277             : 
    3278           0 : int VSIGZipFilesystemHandler::Rename(const char * /* oldpath */,
    3279             :                                      const char * /* newpath */)
    3280             : {
    3281           0 :     return -1;
    3282             : }
    3283             : 
    3284             : /************************************************************************/
    3285             : /*                               Mkdir()                                */
    3286             : /************************************************************************/
    3287             : 
    3288           0 : int VSIGZipFilesystemHandler::Mkdir(const char * /* pszDirname */,
    3289             :                                     long /* nMode */)
    3290             : {
    3291           0 :     return -1;
    3292             : }
    3293             : 
    3294             : /************************************************************************/
    3295             : /*                               Rmdir()                                */
    3296             : /************************************************************************/
    3297             : 
    3298           0 : int VSIGZipFilesystemHandler::Rmdir(const char * /* pszDirname */)
    3299             : {
    3300           0 :     return -1;
    3301             : }
    3302             : 
    3303             : /************************************************************************/
    3304             : /*                             ReadDirEx()                                */
    3305             : /************************************************************************/
    3306             : 
    3307           2 : char **VSIGZipFilesystemHandler::ReadDirEx(const char * /*pszDirname*/,
    3308             :                                            int /* nMaxFiles */)
    3309             : {
    3310           2 :     return nullptr;
    3311             : }
    3312             : 
    3313             : /************************************************************************/
    3314             : /*                           GetOptions()                               */
    3315             : /************************************************************************/
    3316             : 
    3317           1 : const char *VSIGZipFilesystemHandler::GetOptions()
    3318             : {
    3319             :     return "<Options>"
    3320             :            "  <Option name='GDAL_NUM_THREADS' type='string' "
    3321             :            "description='Number of threads for compression. Either a integer "
    3322             :            "or ALL_CPUS'/>"
    3323             :            "  <Option name='CPL_VSIL_DEFLATE_CHUNK_SIZE' type='string' "
    3324             :            "description='Chunk of uncompressed data for parallelization. "
    3325             :            "Use K(ilobytes) or M(egabytes) suffix' default='1M'/>"
    3326           1 :            "</Options>";
    3327             : }
    3328             : 
    3329             : //! @endcond
    3330             : /************************************************************************/
    3331             : /*                   VSIInstallGZipFileHandler()                        */
    3332             : /************************************************************************/
    3333             : 
    3334             : /*!
    3335             :  \brief Install GZip file system handler.
    3336             : 
    3337             :  A special file handler is installed that allows reading on-the-fly and
    3338             :  writing in GZip (.gz) files.
    3339             : 
    3340             :  All portions of the file system underneath the base
    3341             :  path "/vsigzip/" will be handled by this driver.
    3342             : 
    3343             :  \verbatim embed:rst
    3344             :  See :ref:`/vsigzip/ documentation <vsigzip>`
    3345             :  \endverbatim
    3346             : 
    3347             :  @since GDAL 1.6.0
    3348             :  */
    3349             : 
    3350        1304 : void VSIInstallGZipFileHandler()
    3351             : {
    3352        1304 :     VSIFileManager::InstallHandler("/vsigzip/", new VSIGZipFilesystemHandler);
    3353        1304 : }
    3354             : 
    3355             : //! @cond Doxygen_Suppress
    3356             : 
    3357             : /************************************************************************/
    3358             : /* ==================================================================== */
    3359             : /*                         VSIZipEntryFileOffset                        */
    3360             : /* ==================================================================== */
    3361             : /************************************************************************/
    3362             : 
    3363             : class VSIZipEntryFileOffset final : public VSIArchiveEntryFileOffset
    3364             : {
    3365             :   public:
    3366             :     unz_file_pos m_file_pos;
    3367             : 
    3368        4790 :     explicit VSIZipEntryFileOffset(unz_file_pos file_pos) : m_file_pos()
    3369             :     {
    3370        4790 :         m_file_pos.pos_in_zip_directory = file_pos.pos_in_zip_directory;
    3371        4790 :         m_file_pos.num_of_file = file_pos.num_of_file;
    3372        4790 :     }
    3373             : };
    3374             : 
    3375             : /************************************************************************/
    3376             : /* ==================================================================== */
    3377             : /*                             VSIZipReader                             */
    3378             : /* ==================================================================== */
    3379             : /************************************************************************/
    3380             : 
    3381             : class VSIZipReader final : public VSIArchiveReader
    3382             : {
    3383             :     CPL_DISALLOW_COPY_ASSIGN(VSIZipReader)
    3384             : 
    3385             :   private:
    3386             :     unzFile unzF = nullptr;
    3387             :     unz_file_pos file_pos;
    3388             :     GUIntBig nNextFileSize = 0;
    3389             :     CPLString osNextFileName{};
    3390             :     GIntBig nModifiedTime = 0;
    3391             : 
    3392             :     bool SetInfo();
    3393             : 
    3394             :   public:
    3395             :     explicit VSIZipReader(const char *pszZipFileName);
    3396             :     ~VSIZipReader() override;
    3397             : 
    3398        4970 :     int IsValid()
    3399             :     {
    3400        4970 :         return unzF != nullptr;
    3401             :     }
    3402             : 
    3403        4151 :     unzFile GetUnzFileHandle()
    3404             :     {
    3405        4151 :         return unzF;
    3406             :     }
    3407             : 
    3408             :     int GotoFirstFile() override;
    3409             :     int GotoNextFile() override;
    3410             : 
    3411        4790 :     VSIArchiveEntryFileOffset *GetFileOffset() override
    3412             :     {
    3413        4790 :         return new VSIZipEntryFileOffset(file_pos);
    3414             :     }
    3415             : 
    3416        4814 :     GUIntBig GetFileSize() override
    3417             :     {
    3418        4814 :         return nNextFileSize;
    3419             :     }
    3420             : 
    3421        5468 :     CPLString GetFileName() override
    3422             :     {
    3423        5468 :         return osNextFileName;
    3424             :     }
    3425             : 
    3426        6056 :     GIntBig GetModifiedTime() override
    3427             :     {
    3428        6056 :         return nModifiedTime;
    3429             :     }
    3430             : 
    3431             :     int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
    3432             : };
    3433             : 
    3434             : /************************************************************************/
    3435             : /*                           VSIZipReader()                             */
    3436             : /************************************************************************/
    3437             : 
    3438        4970 : VSIZipReader::VSIZipReader(const char *pszZipFileName)
    3439        4970 :     : unzF(cpl_unzOpen(pszZipFileName)), file_pos()
    3440             : {
    3441        4970 :     file_pos.pos_in_zip_directory = 0;
    3442        4970 :     file_pos.num_of_file = 0;
    3443        4970 : }
    3444             : 
    3445             : /************************************************************************/
    3446             : /*                          ~VSIZipReader()                             */
    3447             : /************************************************************************/
    3448             : 
    3449        9940 : VSIZipReader::~VSIZipReader()
    3450             : {
    3451        4970 :     if (unzF)
    3452        4968 :         cpl_unzClose(unzF);
    3453        9940 : }
    3454             : 
    3455             : /************************************************************************/
    3456             : /*                              SetInfo()                               */
    3457             : /************************************************************************/
    3458             : 
    3459       14941 : bool VSIZipReader::SetInfo()
    3460             : {
    3461       14941 :     char fileName[8193] = {};
    3462             :     unz_file_info file_info;
    3463       14941 :     if (UNZ_OK != cpl_unzGetCurrentFileInfo(unzF, &file_info, fileName,
    3464             :                                             sizeof(fileName) - 1, nullptr, 0,
    3465             :                                             nullptr, 0))
    3466             :     {
    3467           0 :         CPLError(CE_Failure, CPLE_FileIO, "cpl_unzGetCurrentFileInfo failed");
    3468           0 :         cpl_unzGetFilePos(unzF, &file_pos);
    3469           0 :         return false;
    3470             :     }
    3471       14941 :     fileName[sizeof(fileName) - 1] = '\0';
    3472       14941 :     osNextFileName = fileName;
    3473       14941 :     nNextFileSize = file_info.uncompressed_size;
    3474             :     struct tm brokendowntime;
    3475       14941 :     brokendowntime.tm_sec = file_info.tmu_date.tm_sec;
    3476       14941 :     brokendowntime.tm_min = file_info.tmu_date.tm_min;
    3477       14941 :     brokendowntime.tm_hour = file_info.tmu_date.tm_hour;
    3478       14941 :     brokendowntime.tm_mday = file_info.tmu_date.tm_mday;
    3479       14941 :     brokendowntime.tm_mon = file_info.tmu_date.tm_mon;
    3480             :     // The minizip conventions differs from the Unix one.
    3481       14941 :     brokendowntime.tm_year = file_info.tmu_date.tm_year - 1900;
    3482       14941 :     nModifiedTime = CPLYMDHMSToUnixTime(&brokendowntime);
    3483             : 
    3484       14941 :     cpl_unzGetFilePos(unzF, &file_pos);
    3485       14941 :     return true;
    3486             : }
    3487             : 
    3488             : /************************************************************************/
    3489             : /*                           GotoNextFile()                             */
    3490             : /************************************************************************/
    3491             : 
    3492        5353 : int VSIZipReader::GotoNextFile()
    3493             : {
    3494        5353 :     if (cpl_unzGoToNextFile(unzF) != UNZ_OK)
    3495         283 :         return FALSE;
    3496             : 
    3497        5070 :     if (!SetInfo())
    3498           0 :         return FALSE;
    3499             : 
    3500        5070 :     return TRUE;
    3501             : }
    3502             : 
    3503             : /************************************************************************/
    3504             : /*                          GotoFirstFile()                             */
    3505             : /************************************************************************/
    3506             : 
    3507        5837 : int VSIZipReader::GotoFirstFile()
    3508             : {
    3509        5837 :     if (cpl_unzGoToFirstFile(unzF) != UNZ_OK)
    3510           0 :         return FALSE;
    3511             : 
    3512        5837 :     if (!SetInfo())
    3513           0 :         return FALSE;
    3514             : 
    3515        5837 :     return TRUE;
    3516             : }
    3517             : 
    3518             : /************************************************************************/
    3519             : /*                         GotoFileOffset()                             */
    3520             : /************************************************************************/
    3521             : 
    3522        4034 : int VSIZipReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
    3523             : {
    3524        4034 :     VSIZipEntryFileOffset *pZipEntryOffset =
    3525             :         reinterpret_cast<VSIZipEntryFileOffset *>(pOffset);
    3526        4034 :     if (cpl_unzGoToFilePos(unzF, &(pZipEntryOffset->m_file_pos)) != UNZ_OK)
    3527             :     {
    3528           0 :         CPLError(CE_Failure, CPLE_AppDefined, "GotoFileOffset failed");
    3529           0 :         return FALSE;
    3530             :     }
    3531             : 
    3532        4034 :     if (!SetInfo())
    3533           0 :         return FALSE;
    3534             : 
    3535        4034 :     return TRUE;
    3536             : }
    3537             : 
    3538             : /************************************************************************/
    3539             : /* ==================================================================== */
    3540             : /*                       VSIZipFilesystemHandler                  */
    3541             : /* ==================================================================== */
    3542             : /************************************************************************/
    3543             : 
    3544             : class VSIZipWriteHandle;
    3545             : 
    3546             : class VSIZipFilesystemHandler final : public VSIArchiveFilesystemHandler
    3547             : {
    3548             :     CPL_DISALLOW_COPY_ASSIGN(VSIZipFilesystemHandler)
    3549             : 
    3550             :     std::map<CPLString, VSIZipWriteHandle *> oMapZipWriteHandles{};
    3551             :     VSIVirtualHandle *OpenForWrite_unlocked(const char *pszFilename,
    3552             :                                             const char *pszAccess);
    3553             : 
    3554             :     struct VSIFileInZipInfo
    3555             :     {
    3556             :         VSIVirtualHandleUniquePtr poVirtualHandle{};
    3557             :         std::map<std::string, std::string> oMapProperties{};
    3558             :         int nCompressionMethod = 0;
    3559             :         uint64_t nUncompressedSize = 0;
    3560             :         uint64_t nCompressedSize = 0;
    3561             :         uint64_t nStartDataStream = 0;
    3562             :         uLong nCRC = 0;
    3563             :         bool bSOZipIndexFound = false;
    3564             :         bool bSOZipIndexValid = false;
    3565             :         uint32_t nSOZIPVersion = 0;
    3566             :         uint32_t nSOZIPToSkip = 0;
    3567             :         uint32_t nSOZIPChunkSize = 0;
    3568             :         uint32_t nSOZIPOffsetSize = 0;
    3569             :         uint64_t nSOZIPStartData = 0;
    3570             :     };
    3571             : 
    3572             :     bool GetFileInfo(const char *pszFilename, VSIFileInZipInfo &info);
    3573             : 
    3574             :   public:
    3575        1304 :     VSIZipFilesystemHandler() = default;
    3576             :     ~VSIZipFilesystemHandler() override;
    3577             : 
    3578       56204 :     const char *GetPrefix() override
    3579             :     {
    3580       56204 :         return "/vsizip";
    3581             :     }
    3582             : 
    3583             :     std::vector<CPLString> GetExtensions() override;
    3584             :     VSIArchiveReader *CreateReader(const char *pszZipFileName) override;
    3585             : 
    3586             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
    3587             :                            bool bSetError,
    3588             :                            CSLConstList /* papszOptions */) override;
    3589             : 
    3590             :     char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
    3591             :                            CSLConstList papszOptions) override;
    3592             : 
    3593             :     VSIVirtualHandle *OpenForWrite(const char *pszFilename,
    3594             :                                    const char *pszAccess);
    3595             : 
    3596             :     int CopyFile(const char *pszSource, const char *pszTarget,
    3597             :                  VSILFILE *fpSource, vsi_l_offset nSourceSize,
    3598             :                  const char *const *papszOptions,
    3599             :                  GDALProgressFunc pProgressFunc, void *pProgressData) override;
    3600             : 
    3601             :     int Mkdir(const char *pszDirname, long nMode) override;
    3602             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
    3603             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
    3604             :              int nFlags) override;
    3605             : 
    3606             :     const char *GetOptions() override;
    3607             : 
    3608             :     void RemoveFromMap(VSIZipWriteHandle *poHandle);
    3609             : };
    3610             : 
    3611             : /************************************************************************/
    3612             : /* ==================================================================== */
    3613             : /*                       VSIZipWriteHandle                              */
    3614             : /* ==================================================================== */
    3615             : /************************************************************************/
    3616             : 
    3617             : class VSIZipWriteHandle final : public VSIVirtualHandle
    3618             : {
    3619             :     CPL_DISALLOW_COPY_ASSIGN(VSIZipWriteHandle)
    3620             : 
    3621             :     VSIZipFilesystemHandler *m_poFS = nullptr;
    3622             :     void *m_hZIP = nullptr;
    3623             :     VSIZipWriteHandle *poChildInWriting = nullptr;
    3624             :     VSIZipWriteHandle *m_poParent = nullptr;
    3625             :     bool bAutoDeleteParent = false;
    3626             :     vsi_l_offset nCurOffset = 0;
    3627             : 
    3628             :   public:
    3629             :     VSIZipWriteHandle(VSIZipFilesystemHandler *poFS, void *hZIP,
    3630             :                       VSIZipWriteHandle *poParent);
    3631             : 
    3632             :     ~VSIZipWriteHandle() override;
    3633             : 
    3634             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
    3635             :     vsi_l_offset Tell() override;
    3636             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
    3637             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
    3638             : 
    3639           0 :     int Eof() override
    3640             :     {
    3641           0 :         return 0;
    3642             :     }
    3643             : 
    3644           0 :     int Error() override
    3645             :     {
    3646           0 :         return 0;
    3647             :     }
    3648             : 
    3649           0 :     void ClearErr() override
    3650             :     {
    3651           0 :     }
    3652             : 
    3653             :     int Flush() override;
    3654             :     int Close() override;
    3655             : 
    3656             :     void StartNewFile(VSIZipWriteHandle *poSubFile);
    3657             :     void StopCurrentFile();
    3658             : 
    3659         596 :     void *GetHandle()
    3660             :     {
    3661         596 :         return m_hZIP;
    3662             :     }
    3663             : 
    3664         597 :     VSIZipWriteHandle *GetChildInWriting()
    3665             :     {
    3666         597 :         return poChildInWriting;
    3667             :     }
    3668             : 
    3669         277 :     void SetAutoDeleteParent()
    3670             :     {
    3671         277 :         bAutoDeleteParent = true;
    3672         277 :     }
    3673             : };
    3674             : 
    3675             : /************************************************************************/
    3676             : /*                      ~VSIZipFilesystemHandler()                      */
    3677             : /************************************************************************/
    3678             : 
    3679        1866 : VSIZipFilesystemHandler::~VSIZipFilesystemHandler()
    3680             : {
    3681           0 :     for (std::map<CPLString, VSIZipWriteHandle *>::const_iterator iter =
    3682         933 :              oMapZipWriteHandles.begin();
    3683         933 :          iter != oMapZipWriteHandles.end(); ++iter)
    3684             :     {
    3685           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s has not been closed",
    3686           0 :                  iter->first.c_str());
    3687             :     }
    3688        1866 : }
    3689             : 
    3690             : /************************************************************************/
    3691             : /*                          GetExtensions()                             */
    3692             : /************************************************************************/
    3693             : 
    3694       13063 : std::vector<CPLString> VSIZipFilesystemHandler::GetExtensions()
    3695             : {
    3696       13063 :     std::vector<CPLString> oList;
    3697       13063 :     oList.push_back(".zip");
    3698       13063 :     oList.push_back(".kmz");
    3699       13063 :     oList.push_back(".dwf");
    3700       13063 :     oList.push_back(".ods");
    3701       13063 :     oList.push_back(".xlsx");
    3702       13063 :     oList.push_back(".xlsm");
    3703             : 
    3704             :     // Add to zip FS handler extensions array additional extensions
    3705             :     // listed in CPL_VSIL_ZIP_ALLOWED_EXTENSIONS config option.
    3706             :     // The extensions are divided by commas.
    3707             :     const char *pszAllowedExtensions =
    3708       13063 :         CPLGetConfigOption("CPL_VSIL_ZIP_ALLOWED_EXTENSIONS", nullptr);
    3709       13063 :     if (pszAllowedExtensions)
    3710             :     {
    3711             :         char **papszExtensions =
    3712           0 :             CSLTokenizeString2(pszAllowedExtensions, ", ", 0);
    3713           0 :         for (int i = 0; papszExtensions[i] != nullptr; i++)
    3714             :         {
    3715           0 :             oList.push_back(papszExtensions[i]);
    3716             :         }
    3717           0 :         CSLDestroy(papszExtensions);
    3718             :     }
    3719             : 
    3720       13063 :     return oList;
    3721             : }
    3722             : 
    3723             : /************************************************************************/
    3724             : /*                           CreateReader()                             */
    3725             : /************************************************************************/
    3726             : 
    3727             : VSIArchiveReader *
    3728        4970 : VSIZipFilesystemHandler::CreateReader(const char *pszZipFileName)
    3729             : {
    3730        4970 :     VSIZipReader *poReader = new VSIZipReader(pszZipFileName);
    3731             : 
    3732        4970 :     if (!poReader->IsValid())
    3733             :     {
    3734           2 :         delete poReader;
    3735           2 :         return nullptr;
    3736             :     }
    3737             : 
    3738        4968 :     if (!poReader->GotoFirstFile())
    3739             :     {
    3740           0 :         delete poReader;
    3741           0 :         return nullptr;
    3742             :     }
    3743             : 
    3744        4968 :     return poReader;
    3745             : }
    3746             : 
    3747             : /************************************************************************/
    3748             : /*                         VSISOZipHandle                               */
    3749             : /************************************************************************/
    3750             : 
    3751             : class VSISOZipHandle final : public VSIVirtualHandle
    3752             : {
    3753             :     VSIVirtualHandle *poBaseHandle_;
    3754             :     vsi_l_offset nPosCompressedStream_;
    3755             :     uint64_t compressed_size_;
    3756             :     uint64_t uncompressed_size_;
    3757             :     vsi_l_offset indexPos_;
    3758             :     uint32_t nToSkip_;
    3759             :     uint32_t nChunkSize_;
    3760             :     bool bEOF_ = false;
    3761             :     bool bError_ = false;
    3762             :     vsi_l_offset nCurPos_ = 0;
    3763             :     bool bOK_ = true;
    3764             : #ifdef HAVE_LIBDEFLATE
    3765             :     struct libdeflate_decompressor *pDecompressor_ = nullptr;
    3766             : #else
    3767             :     z_stream sStream_{};
    3768             : #endif
    3769             : 
    3770             :     VSISOZipHandle(const VSISOZipHandle &) = delete;
    3771             :     VSISOZipHandle &operator=(const VSISOZipHandle &) = delete;
    3772             : 
    3773             :   public:
    3774             :     VSISOZipHandle(VSIVirtualHandle *poVirtualHandle,
    3775             :                    vsi_l_offset nPosCompressedStream, uint64_t compressed_size,
    3776             :                    uint64_t uncompressed_size, vsi_l_offset indexPos,
    3777             :                    uint32_t nToSkip, uint32_t nChunkSize);
    3778             :     ~VSISOZipHandle() override;
    3779             : 
    3780             :     virtual int Seek(vsi_l_offset nOffset, int nWhence) override;
    3781             : 
    3782          17 :     virtual vsi_l_offset Tell() override
    3783             :     {
    3784          17 :         return nCurPos_;
    3785             :     }
    3786             : 
    3787             :     virtual size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
    3788             : 
    3789           0 :     virtual size_t Write(const void *, size_t, size_t) override
    3790             :     {
    3791           0 :         return 0;
    3792             :     }
    3793             : 
    3794           0 :     virtual int Eof() override
    3795             :     {
    3796           0 :         return bEOF_;
    3797             :     }
    3798             : 
    3799          12 :     virtual int Error() override
    3800             :     {
    3801          12 :         return bError_;
    3802             :     }
    3803             : 
    3804           1 :     virtual void ClearErr() override
    3805             :     {
    3806           1 :         bEOF_ = false;
    3807           1 :         bError_ = false;
    3808           1 :     }
    3809             : 
    3810             :     virtual int Close() override;
    3811             : 
    3812          17 :     bool IsOK() const
    3813             :     {
    3814          17 :         return bOK_;
    3815             :     }
    3816             : };
    3817             : 
    3818             : /************************************************************************/
    3819             : /*                         VSISOZipHandle()                             */
    3820             : /************************************************************************/
    3821             : 
    3822          17 : VSISOZipHandle::VSISOZipHandle(VSIVirtualHandle *poVirtualHandle,
    3823             :                                vsi_l_offset nPosCompressedStream,
    3824             :                                uint64_t compressed_size,
    3825             :                                uint64_t uncompressed_size,
    3826             :                                vsi_l_offset indexPos, uint32_t nToSkip,
    3827          17 :                                uint32_t nChunkSize)
    3828             :     : poBaseHandle_(poVirtualHandle),
    3829             :       nPosCompressedStream_(nPosCompressedStream),
    3830             :       compressed_size_(compressed_size), uncompressed_size_(uncompressed_size),
    3831          17 :       indexPos_(indexPos), nToSkip_(nToSkip), nChunkSize_(nChunkSize)
    3832             : {
    3833             : #ifdef HAVE_LIBDEFLATE
    3834          17 :     pDecompressor_ = libdeflate_alloc_decompressor();
    3835          17 :     if (!pDecompressor_)
    3836           0 :         bOK_ = false;
    3837             : #else
    3838             :     memset(&sStream_, 0, sizeof(sStream_));
    3839             :     int err = inflateInit2(&sStream_, -MAX_WBITS);
    3840             :     if (err != Z_OK)
    3841             :         bOK_ = false;
    3842             : #endif
    3843          17 : }
    3844             : 
    3845             : /************************************************************************/
    3846             : /*                        ~VSISOZipHandle()                             */
    3847             : /************************************************************************/
    3848             : 
    3849          34 : VSISOZipHandle::~VSISOZipHandle()
    3850             : {
    3851          17 :     VSISOZipHandle::Close();
    3852          17 :     if (bOK_)
    3853             :     {
    3854             : #ifdef HAVE_LIBDEFLATE
    3855          17 :         libdeflate_free_decompressor(pDecompressor_);
    3856             : #else
    3857             :         inflateEnd(&sStream_);
    3858             : #endif
    3859             :     }
    3860          34 : }
    3861             : 
    3862             : /************************************************************************/
    3863             : /*                              Close()                                 */
    3864             : /************************************************************************/
    3865             : 
    3866          34 : int VSISOZipHandle::Close()
    3867             : {
    3868          34 :     delete poBaseHandle_;
    3869          34 :     poBaseHandle_ = nullptr;
    3870          34 :     return 0;
    3871             : }
    3872             : 
    3873             : /************************************************************************/
    3874             : /*                              Seek()                                  */
    3875             : /************************************************************************/
    3876             : 
    3877         388 : int VSISOZipHandle::Seek(vsi_l_offset nOffset, int nWhence)
    3878             : {
    3879         388 :     bEOF_ = false;
    3880         388 :     if (nWhence == SEEK_SET)
    3881         371 :         nCurPos_ = nOffset;
    3882          17 :     else if (nWhence == SEEK_END)
    3883          17 :         nCurPos_ = uncompressed_size_;
    3884             :     else
    3885           0 :         nCurPos_ += nOffset;
    3886         388 :     return 0;
    3887             : }
    3888             : 
    3889             : /************************************************************************/
    3890             : /*                              Read()                                  */
    3891             : /************************************************************************/
    3892             : 
    3893         371 : size_t VSISOZipHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
    3894             : {
    3895         371 :     size_t nToRead = nSize * nCount;
    3896         371 :     if (nCurPos_ >= uncompressed_size_ && nToRead > 0)
    3897             :     {
    3898           1 :         bEOF_ = true;
    3899           1 :         return 0;
    3900             :     }
    3901             : 
    3902         370 :     if (nSize != 1)
    3903             :     {
    3904           0 :         bError_ = true;
    3905           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported nSize");
    3906           0 :         return 0;
    3907             :     }
    3908         370 :     if ((nCurPos_ % nChunkSize_) != 0)
    3909             :     {
    3910           0 :         bError_ = true;
    3911           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3912             :                  "nCurPos is not a multiple of nChunkSize");
    3913           0 :         return 0;
    3914             :     }
    3915         370 :     if (nCurPos_ + nToRead > uncompressed_size_)
    3916             :     {
    3917          12 :         nToRead = static_cast<size_t>(uncompressed_size_ - nCurPos_);
    3918          12 :         nCount = nToRead;
    3919             :     }
    3920         358 :     else if ((nToRead % nChunkSize_) != 0)
    3921             :     {
    3922           0 :         bError_ = true;
    3923           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3924             :                  "nToRead is not a multiple of nChunkSize");
    3925           0 :         return 0;
    3926             :     }
    3927             : 
    3928             :     const auto ReadOffsetInCompressedStream =
    3929        5726 :         [this](uint64_t nChunkIdx) -> uint64_t
    3930             :     {
    3931        1446 :         if (nChunkIdx == 0)
    3932          15 :             return 0;
    3933        1431 :         if (nChunkIdx == 1 + (uncompressed_size_ - 1) / nChunkSize_)
    3934          13 :             return compressed_size_;
    3935        1418 :         constexpr size_t nOffsetSize = 8;
    3936        2836 :         if (poBaseHandle_->Seek(indexPos_ + 32 + nToSkip_ +
    3937        1418 :                                     (nChunkIdx - 1) * nOffsetSize,
    3938        1418 :                                 SEEK_SET) != 0)
    3939           0 :             return static_cast<uint64_t>(-1);
    3940             : 
    3941             :         uint64_t nOffset;
    3942        1418 :         if (poBaseHandle_->Read(&nOffset, sizeof(nOffset), 1) != 1)
    3943           0 :             return static_cast<uint64_t>(-1);
    3944        1418 :         CPL_LSBPTR64(&nOffset);
    3945        1418 :         return nOffset;
    3946         370 :     };
    3947             : 
    3948         370 :     size_t nOffsetInOutputBuffer = 0;
    3949             :     while (true)
    3950             :     {
    3951             :         uint64_t nOffsetInCompressedStream =
    3952         723 :             ReadOffsetInCompressedStream(nCurPos_ / nChunkSize_);
    3953         723 :         if (nOffsetInCompressedStream == static_cast<uint64_t>(-1))
    3954             :         {
    3955           0 :             bError_ = true;
    3956           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3957             :                      "Cannot read nOffsetInCompressedStream");
    3958           0 :             return 0;
    3959             :         }
    3960             :         uint64_t nNextOffsetInCompressedStream =
    3961         723 :             ReadOffsetInCompressedStream(1 + nCurPos_ / nChunkSize_);
    3962         723 :         if (nNextOffsetInCompressedStream == static_cast<uint64_t>(-1))
    3963             :         {
    3964           0 :             bError_ = true;
    3965           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3966             :                      "Cannot read nNextOffsetInCompressedStream");
    3967           0 :             return 0;
    3968             :         }
    3969             : 
    3970         723 :         if (nNextOffsetInCompressedStream <= nOffsetInCompressedStream ||
    3971         723 :             nNextOffsetInCompressedStream - nOffsetInCompressedStream >
    3972         723 :                 13 + 2 * nChunkSize_ ||
    3973         723 :             nNextOffsetInCompressedStream > compressed_size_)
    3974             :         {
    3975           0 :             bError_ = true;
    3976           0 :             CPLError(
    3977             :                 CE_Failure, CPLE_AppDefined,
    3978             :                 "Invalid values for nOffsetInCompressedStream (" CPL_FRMT_GUIB
    3979             :                 ") / "
    3980             :                 "nNextOffsetInCompressedStream(" CPL_FRMT_GUIB ")",
    3981             :                 static_cast<GUIntBig>(nOffsetInCompressedStream),
    3982             :                 static_cast<GUIntBig>(nNextOffsetInCompressedStream));
    3983           0 :             return 0;
    3984             :         }
    3985             : 
    3986             :         // CPLDebug("VSIZIP", "Seek to compressed data at offset "
    3987             :         // CPL_FRMT_GUIB, static_cast<GUIntBig>(nPosCompressedStream_ +
    3988             :         // nOffsetInCompressedStream));
    3989        1446 :         if (poBaseHandle_->Seek(
    3990         723 :                 nPosCompressedStream_ + nOffsetInCompressedStream, SEEK_SET) !=
    3991             :             0)
    3992             :         {
    3993           0 :             bError_ = true;
    3994           0 :             return 0;
    3995             :         }
    3996             : 
    3997         723 :         const int nCompressedToRead = static_cast<int>(
    3998         723 :             nNextOffsetInCompressedStream - nOffsetInCompressedStream);
    3999             :         // CPLDebug("VSIZIP", "nCompressedToRead = %d", nCompressedToRead);
    4000         723 :         std::vector<GByte> abyCompressedData(nCompressedToRead);
    4001         723 :         if (poBaseHandle_->Read(&abyCompressedData[0], nCompressedToRead, 1) !=
    4002             :             1)
    4003             :         {
    4004           0 :             bError_ = true;
    4005           0 :             return 0;
    4006             :         }
    4007             : 
    4008             :         size_t nToReadThisIter =
    4009         723 :             std::min(nToRead, static_cast<size_t>(nChunkSize_));
    4010             : 
    4011        1446 :         if (nCompressedToRead >= 5 &&
    4012        1436 :             abyCompressedData[nCompressedToRead - 5] == 0x00 &&
    4013         713 :             memcmp(&abyCompressedData[nCompressedToRead - 4],
    4014             :                    "\x00\x00\xFF\xFF", 4) == 0)
    4015             :         {
    4016             :             // Tag this flush block as the last one.
    4017         710 :             abyCompressedData[nCompressedToRead - 5] = 0x01;
    4018             :         }
    4019             : 
    4020             : #ifdef HAVE_LIBDEFLATE
    4021         723 :         size_t nOut = 0;
    4022         723 :         if (libdeflate_deflate_decompress(
    4023         723 :                 pDecompressor_, &abyCompressedData[0], nCompressedToRead,
    4024             :                 static_cast<Bytef *>(pBuffer) + nOffsetInOutputBuffer,
    4025         723 :                 nToReadThisIter, &nOut) != LIBDEFLATE_SUCCESS)
    4026             :         {
    4027           0 :             bError_ = true;
    4028           0 :             CPLError(
    4029             :                 CE_Failure, CPLE_AppDefined,
    4030             :                 "libdeflate_deflate_decompress() failed at pos " CPL_FRMT_GUIB,
    4031           0 :                 static_cast<GUIntBig>(nCurPos_));
    4032           0 :             return 0;
    4033             :         }
    4034         723 :         if (nOut != nToReadThisIter)
    4035             :         {
    4036           0 :             bError_ = true;
    4037           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4038             :                      "Only %u bytes decompressed at pos " CPL_FRMT_GUIB
    4039             :                      " whereas %u where expected",
    4040             :                      static_cast<unsigned>(nOut),
    4041           0 :                      static_cast<GUIntBig>(nCurPos_),
    4042             :                      static_cast<unsigned>(nToReadThisIter));
    4043           0 :             return 0;
    4044             :         }
    4045             : #else
    4046             :         sStream_.avail_in = nCompressedToRead;
    4047             :         sStream_.next_in = &abyCompressedData[0];
    4048             :         sStream_.avail_out = static_cast<int>(nToReadThisIter);
    4049             :         sStream_.next_out =
    4050             :             static_cast<Bytef *>(pBuffer) + nOffsetInOutputBuffer;
    4051             : 
    4052             :         int err = inflate(&sStream_, Z_FINISH);
    4053             :         if ((err != Z_OK && err != Z_STREAM_END))
    4054             :         {
    4055             :             bError_ = true;
    4056             :             CPLError(CE_Failure, CPLE_AppDefined,
    4057             :                      "inflate() failed at pos " CPL_FRMT_GUIB,
    4058             :                      static_cast<GUIntBig>(nCurPos_));
    4059             :             inflateReset(&sStream_);
    4060             :             return 0;
    4061             :         }
    4062             :         if (sStream_.avail_in != 0)
    4063             :             CPLDebug("VSIZIP", "avail_in = %d", sStream_.avail_in);
    4064             :         if (sStream_.avail_out != 0)
    4065             :         {
    4066             :             bError_ = true;
    4067             :             CPLError(
    4068             :                 CE_Failure, CPLE_AppDefined,
    4069             :                 "Only %u bytes decompressed at pos " CPL_FRMT_GUIB
    4070             :                 " whereas %u where expected",
    4071             :                 static_cast<unsigned>(nToReadThisIter - sStream_.avail_out),
    4072             :                 static_cast<GUIntBig>(nCurPos_),
    4073             :                 static_cast<unsigned>(nToReadThisIter));
    4074             :             inflateReset(&sStream_);
    4075             :             return 0;
    4076             :         }
    4077             :         inflateReset(&sStream_);
    4078             : #endif
    4079         723 :         nOffsetInOutputBuffer += nToReadThisIter;
    4080         723 :         nCurPos_ += nToReadThisIter;
    4081         723 :         nToRead -= nToReadThisIter;
    4082         723 :         if (nToRead == 0)
    4083         370 :             break;
    4084         353 :     }
    4085             : 
    4086         370 :     return nCount;
    4087             : }
    4088             : 
    4089             : /************************************************************************/
    4090             : /*                          GetFileInfo()                               */
    4091             : /************************************************************************/
    4092             : 
    4093        4355 : bool VSIZipFilesystemHandler::GetFileInfo(const char *pszFilename,
    4094             :                                           VSIFileInZipInfo &info)
    4095             : {
    4096             : 
    4097        8710 :     CPLString osZipInFileName;
    4098        4355 :     char *zipFilename = SplitFilename(pszFilename, osZipInFileName, TRUE);
    4099        4355 :     if (zipFilename == nullptr)
    4100          52 :         return false;
    4101             : 
    4102             :     {
    4103        4303 :         CPLMutexHolder oHolder(&hMutex);
    4104        4303 :         if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end())
    4105             :         {
    4106           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    4107             :                      "Cannot read a zip file being written");
    4108           1 :             CPLFree(zipFilename);
    4109           1 :             return false;
    4110             :         }
    4111             :     }
    4112             : 
    4113        4302 :     VSIArchiveReader *poReader = OpenArchiveFile(zipFilename, osZipInFileName);
    4114        4302 :     if (poReader == nullptr)
    4115             :     {
    4116         151 :         CPLFree(zipFilename);
    4117         151 :         return false;
    4118             :     }
    4119             : 
    4120        4151 :     VSIFilesystemHandler *poFSHandler = VSIFileManager::GetHandler(zipFilename);
    4121             : 
    4122        4151 :     VSIVirtualHandle *poVirtualHandle = poFSHandler->Open(zipFilename, "rb");
    4123             : 
    4124        4151 :     CPLFree(zipFilename);
    4125        4151 :     zipFilename = nullptr;
    4126             : 
    4127        4151 :     if (poVirtualHandle == nullptr)
    4128             :     {
    4129           0 :         delete poReader;
    4130           0 :         return false;
    4131             :     }
    4132             : 
    4133             :     unzFile unzF =
    4134        4151 :         reinterpret_cast<VSIZipReader *>(poReader)->GetUnzFileHandle();
    4135             : 
    4136        4151 :     if (cpl_unzOpenCurrentFile(unzF) != UNZ_OK)
    4137             :     {
    4138           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4139             :                  "cpl_unzOpenCurrentFile() failed");
    4140           0 :         delete poReader;
    4141           0 :         delete poVirtualHandle;
    4142           0 :         return false;
    4143             :     }
    4144             : 
    4145        4151 :     info.nStartDataStream = cpl_unzGetCurrentFileZStreamPos(unzF);
    4146             : 
    4147             :     unz_file_info file_info;
    4148        4151 :     if (cpl_unzGetCurrentFileInfo(unzF, &file_info, nullptr, 0, nullptr, 0,
    4149        4151 :                                   nullptr, 0) != UNZ_OK)
    4150             :     {
    4151           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4152             :                  "cpl_unzGetCurrentFileInfo() failed");
    4153           0 :         cpl_unzCloseCurrentFile(unzF);
    4154           0 :         delete poReader;
    4155           0 :         delete poVirtualHandle;
    4156           0 :         return false;
    4157             :     }
    4158             : 
    4159        4151 :     if (file_info.size_file_extra)
    4160             :     {
    4161        7052 :         std::vector<GByte> abyExtra(file_info.size_file_extra);
    4162        3526 :         poVirtualHandle->Seek(file_info.file_extra_abs_offset, SEEK_SET);
    4163        3526 :         if (poVirtualHandle->Read(&abyExtra[0], abyExtra.size(), 1) == 1)
    4164             :         {
    4165        3526 :             size_t nPos = 0;
    4166       10531 :             while (nPos + 2 * sizeof(uint16_t) <= abyExtra.size())
    4167             :             {
    4168             :                 uint16_t nId;
    4169        7005 :                 memcpy(&nId, &abyExtra[nPos], sizeof(uint16_t));
    4170        7005 :                 nPos += sizeof(uint16_t);
    4171        7005 :                 CPL_LSBPTR16(&nId);
    4172             :                 uint16_t nSize;
    4173        7005 :                 memcpy(&nSize, &abyExtra[nPos], sizeof(uint16_t));
    4174        7005 :                 nPos += sizeof(uint16_t);
    4175        7005 :                 CPL_LSBPTR16(&nSize);
    4176        7005 :                 if (nId == 0x564b && nPos + nSize <= abyExtra.size())  // "KV"
    4177             :                 {
    4178           4 :                     if (nSize >= strlen("KeyValuePairs") + 1 &&
    4179           2 :                         memcmp(&abyExtra[nPos], "KeyValuePairs",
    4180             :                                strlen("KeyValuePairs")) == 0)
    4181             :                     {
    4182           2 :                         int nPos2 = static_cast<int>(strlen("KeyValuePairs"));
    4183           2 :                         int nKVPairs = abyExtra[nPos + nPos2];
    4184           2 :                         nPos2++;
    4185           4 :                         for (int iKV = 0; iKV < nKVPairs; ++iKV)
    4186             :                         {
    4187           2 :                             if (nPos2 + sizeof(uint16_t) > nSize)
    4188           0 :                                 break;
    4189             :                             uint16_t nKeyLen;
    4190           2 :                             memcpy(&nKeyLen, &abyExtra[nPos + nPos2],
    4191             :                                    sizeof(uint16_t));
    4192           2 :                             nPos2 += sizeof(uint16_t);
    4193           2 :                             CPL_LSBPTR16(&nKeyLen);
    4194           2 :                             if (nPos2 + nKeyLen > nSize)
    4195           0 :                                 break;
    4196           2 :                             std::string osKey;
    4197           2 :                             osKey.resize(nKeyLen);
    4198           2 :                             memcpy(&osKey[0], &abyExtra[nPos + nPos2], nKeyLen);
    4199           2 :                             nPos2 += nKeyLen;
    4200             : 
    4201           2 :                             if (nPos2 + sizeof(uint16_t) > nSize)
    4202           0 :                                 break;
    4203             :                             uint16_t nValLen;
    4204           2 :                             memcpy(&nValLen, &abyExtra[nPos + nPos2],
    4205             :                                    sizeof(uint16_t));
    4206           2 :                             nPos2 += sizeof(uint16_t);
    4207           2 :                             CPL_LSBPTR16(&nValLen);
    4208           2 :                             if (nPos2 + nValLen > nSize)
    4209           0 :                                 break;
    4210           4 :                             std::string osVal;
    4211           2 :                             osVal.resize(nValLen);
    4212           2 :                             memcpy(&osVal[0], &abyExtra[nPos + nPos2], nValLen);
    4213           2 :                             nPos2 += nValLen;
    4214             : 
    4215           2 :                             info.oMapProperties[osKey] = std::move(osVal);
    4216             :                         }
    4217             :                     }
    4218             :                 }
    4219        7005 :                 nPos += nSize;
    4220             :             }
    4221             :         }
    4222             :     }
    4223             : 
    4224        4151 :     info.nCRC = file_info.crc;
    4225        4151 :     info.nCompressionMethod = static_cast<int>(file_info.compression_method);
    4226        4151 :     info.nUncompressedSize = static_cast<uint64_t>(file_info.uncompressed_size);
    4227        4151 :     info.nCompressedSize = static_cast<uint64_t>(file_info.compressed_size);
    4228             : 
    4229             :     // Try to locate .sozip.idx file
    4230             :     uLong64 local_header_pos;
    4231        4151 :     cpl_unzGetLocalHeaderPos(unzF, &local_header_pos);
    4232        4151 :     local_header_pos = info.nStartDataStream + file_info.compressed_size;
    4233             :     unz_file_info file_info2;
    4234        4151 :     std::string osAuxName;
    4235        4151 :     osAuxName.resize(1024);
    4236             :     uLong64 indexPos;
    4237        8230 :     if (file_info.compression_method == 8 &&
    4238        8158 :         cpl_unzCurrentFileInfoFromLocalHeader(
    4239        4079 :             unzF, local_header_pos, &file_info2, &osAuxName[0],
    4240             :             osAuxName.size(), &indexPos) == UNZ_OK)
    4241             :     {
    4242        3571 :         osAuxName.resize(strlen(osAuxName.c_str()));
    4243        3571 :         if (osAuxName.find(".sozip.idx") != std::string::npos)
    4244             :         {
    4245          30 :             info.bSOZipIndexFound = true;
    4246          30 :             info.nSOZIPStartData = indexPos;
    4247          30 :             poVirtualHandle->Seek(indexPos, SEEK_SET);
    4248          30 :             uint32_t nVersion = 0;
    4249          30 :             poVirtualHandle->Read(&nVersion, sizeof(nVersion), 1);
    4250          30 :             CPL_LSBPTR32(&nVersion);
    4251          30 :             uint32_t nToSkip = 0;
    4252          30 :             poVirtualHandle->Read(&nToSkip, sizeof(nToSkip), 1);
    4253          30 :             CPL_LSBPTR32(&nToSkip);
    4254          30 :             uint32_t nChunkSize = 0;
    4255          30 :             poVirtualHandle->Read(&nChunkSize, sizeof(nChunkSize), 1);
    4256          30 :             CPL_LSBPTR32(&nChunkSize);
    4257          30 :             uint32_t nOffsetSize = 0;
    4258          30 :             poVirtualHandle->Read(&nOffsetSize, sizeof(nOffsetSize), 1);
    4259          30 :             CPL_LSBPTR32(&nOffsetSize);
    4260          30 :             uint64_t nUncompressedSize = 0;
    4261          30 :             poVirtualHandle->Read(&nUncompressedSize, sizeof(nUncompressedSize),
    4262          30 :                                   1);
    4263          30 :             CPL_LSBPTR64(&nUncompressedSize);
    4264          30 :             uint64_t nCompressedSize = 0;
    4265          30 :             poVirtualHandle->Read(&nCompressedSize, sizeof(nCompressedSize), 1);
    4266          30 :             CPL_LSBPTR64(&nCompressedSize);
    4267             : 
    4268          30 :             info.nSOZIPVersion = nVersion;
    4269          30 :             info.nSOZIPToSkip = nToSkip;
    4270          30 :             info.nSOZIPChunkSize = nChunkSize;
    4271          30 :             info.nSOZIPOffsetSize = nOffsetSize;
    4272             : 
    4273          30 :             bool bValid = true;
    4274          30 :             if (nVersion != 1)
    4275             :             {
    4276           0 :                 CPLDebug("SOZIP", "version = %u, expected 1", nVersion);
    4277           0 :                 bValid = false;
    4278             :             }
    4279          30 :             if (nCompressedSize != file_info.compressed_size)
    4280             :             {
    4281           0 :                 CPLDebug("SOZIP",
    4282             :                          "compressedSize field inconsistent with file");
    4283           0 :                 bValid = false;
    4284             :             }
    4285          30 :             if (nUncompressedSize != file_info.uncompressed_size)
    4286             :             {
    4287           0 :                 CPLDebug("SOZIP",
    4288             :                          "uncompressedSize field inconsistent with file");
    4289           0 :                 bValid = false;
    4290             :             }
    4291          30 :             if (!(nChunkSize > 0 && nChunkSize < 100 * 1024 * 1024))
    4292             :             {
    4293           0 :                 CPLDebug("SOZIP", "invalid chunkSize = %u", nChunkSize);
    4294           0 :                 bValid = false;
    4295             :             }
    4296          30 :             if (nOffsetSize != 8)
    4297             :             {
    4298           0 :                 CPLDebug("SOZIP", "invalid offsetSize = %u", nOffsetSize);
    4299           0 :                 bValid = false;
    4300             :             }
    4301          30 :             if (file_info2.compression_method != 0)
    4302             :             {
    4303           0 :                 CPLDebug("SOZIP", "unexpected compression_method = %u",
    4304           0 :                          static_cast<unsigned>(file_info2.compression_method));
    4305           0 :                 bValid = false;
    4306             :             }
    4307          30 :             if (bValid)
    4308             :             {
    4309          30 :                 const auto nExpectedIndexSize =
    4310          30 :                     32 + static_cast<uint64_t>(nToSkip) +
    4311          30 :                     ((nUncompressedSize - 1) / nChunkSize) * nOffsetSize;
    4312          30 :                 if (nExpectedIndexSize != file_info2.uncompressed_size)
    4313             :                 {
    4314           0 :                     CPLDebug("SOZIP", "invalid file size for index");
    4315           0 :                     bValid = false;
    4316             :                 }
    4317             :             }
    4318          30 :             if (bValid)
    4319             :             {
    4320          30 :                 info.bSOZipIndexValid = true;
    4321          30 :                 CPLDebug("SOZIP", "Found valid SOZIP index: %s",
    4322             :                          osAuxName.c_str());
    4323             :             }
    4324             :             else
    4325             :             {
    4326           0 :                 CPLDebug("SOZIP", "Found *invalid* SOZIP index: %s",
    4327             :                          osAuxName.c_str());
    4328             :             }
    4329             :         }
    4330             :     }
    4331             : 
    4332        4151 :     cpl_unzCloseCurrentFile(unzF);
    4333             : 
    4334        4151 :     delete poReader;
    4335             : 
    4336        4151 :     info.poVirtualHandle.reset(poVirtualHandle);
    4337             : 
    4338        4151 :     return true;
    4339             : }
    4340             : 
    4341             : /************************************************************************/
    4342             : /*                                 Open()                               */
    4343             : /************************************************************************/
    4344             : 
    4345        4884 : VSIVirtualHandle *VSIZipFilesystemHandler::Open(const char *pszFilename,
    4346             :                                                 const char *pszAccess,
    4347             :                                                 bool /* bSetError */,
    4348             :                                                 CSLConstList /* papszOptions */)
    4349             : {
    4350             : 
    4351        4884 :     if (strchr(pszAccess, 'w') != nullptr)
    4352             :     {
    4353         588 :         return OpenForWrite(pszFilename, pszAccess);
    4354             :     }
    4355             : 
    4356        4296 :     if (strchr(pszAccess, '+') != nullptr)
    4357             :     {
    4358           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4359             :                  "Read-write random access not supported for /vsizip");
    4360           0 :         return nullptr;
    4361             :     }
    4362             : 
    4363        8592 :     VSIFileInZipInfo info;
    4364        4296 :     if (!GetFileInfo(pszFilename, info))
    4365         204 :         return nullptr;
    4366             : 
    4367             : #ifdef ENABLE_DEFLATE64
    4368        4092 :     if (info.nCompressionMethod == 9)
    4369             :     {
    4370             :         auto poGZIPHandle = new VSIDeflate64Handle(
    4371           1 :             info.poVirtualHandle.release(), nullptr, info.nStartDataStream,
    4372           1 :             info.nCompressedSize, info.nUncompressedSize, info.nCRC);
    4373           1 :         if (!(poGZIPHandle->IsInitOK()))
    4374             :         {
    4375           0 :             delete poGZIPHandle;
    4376           0 :             return nullptr;
    4377             :         }
    4378             : 
    4379             :         // Wrap the VSIGZipHandle inside a buffered reader that will
    4380             :         // improve dramatically performance when doing small backward
    4381             :         // seeks.
    4382           1 :         return VSICreateBufferedReaderHandle(poGZIPHandle);
    4383             :     }
    4384             :     else
    4385             : #endif
    4386             :     {
    4387        4091 :         if (info.bSOZipIndexValid)
    4388             :         {
    4389             :             auto poSOZIPHandle = new VSISOZipHandle(
    4390          17 :                 info.poVirtualHandle.release(), info.nStartDataStream,
    4391             :                 info.nCompressedSize, info.nUncompressedSize,
    4392          17 :                 info.nSOZIPStartData, info.nSOZIPToSkip, info.nSOZIPChunkSize);
    4393          17 :             if (!poSOZIPHandle->IsOK())
    4394             :             {
    4395           0 :                 delete poSOZIPHandle;
    4396           0 :                 return nullptr;
    4397             :             }
    4398          17 :             return VSICreateCachedFile(poSOZIPHandle, info.nSOZIPChunkSize, 0);
    4399             :         }
    4400             : 
    4401             :         VSIGZipHandle *poGZIPHandle = new VSIGZipHandle(
    4402        4074 :             info.poVirtualHandle.release(), nullptr, info.nStartDataStream,
    4403        4074 :             info.nCompressedSize, info.nUncompressedSize, info.nCRC,
    4404        4074 :             info.nCompressionMethod == 0);
    4405        4074 :         if (!(poGZIPHandle->IsInitOK()))
    4406             :         {
    4407           0 :             delete poGZIPHandle;
    4408           0 :             return nullptr;
    4409             :         }
    4410             : 
    4411             :         // Wrap the VSIGZipHandle inside a buffered reader that will
    4412             :         // improve dramatically performance when doing small backward
    4413             :         // seeks.
    4414        4074 :         return VSICreateBufferedReaderHandle(poGZIPHandle);
    4415             :     }
    4416             : }
    4417             : 
    4418             : /************************************************************************/
    4419             : /*                          GetFileMetadata()                           */
    4420             : /************************************************************************/
    4421             : 
    4422          59 : char **VSIZipFilesystemHandler::GetFileMetadata(const char *pszFilename,
    4423             :                                                 const char *pszDomain,
    4424             :                                                 CSLConstList /*papszOptions*/)
    4425             : {
    4426         118 :     VSIFileInZipInfo info;
    4427          59 :     if (!GetFileInfo(pszFilename, info))
    4428           0 :         return nullptr;
    4429             : 
    4430          59 :     if (!pszDomain)
    4431             :     {
    4432           4 :         CPLStringList aosMetadata;
    4433           3 :         for (const auto &kv : info.oMapProperties)
    4434             :         {
    4435           1 :             aosMetadata.AddNameValue(kv.first.c_str(), kv.second.c_str());
    4436             :         }
    4437           2 :         return aosMetadata.StealList();
    4438             :     }
    4439          57 :     else if (EQUAL(pszDomain, "ZIP"))
    4440             :     {
    4441         114 :         CPLStringList aosMetadata;
    4442             :         aosMetadata.SetNameValue(
    4443             :             "START_DATA_OFFSET",
    4444             :             CPLSPrintf(CPL_FRMT_GUIB,
    4445          57 :                        static_cast<GUIntBig>(info.nStartDataStream)));
    4446             : 
    4447          57 :         if (info.nCompressionMethod == 0)
    4448           0 :             aosMetadata.SetNameValue("COMPRESSION_METHOD", "0 (STORED)");
    4449          57 :         else if (info.nCompressionMethod == 8)
    4450          57 :             aosMetadata.SetNameValue("COMPRESSION_METHOD", "8 (DEFLATE)");
    4451             :         else
    4452             :         {
    4453             :             aosMetadata.SetNameValue("COMPRESSION_METHOD",
    4454           0 :                                      CPLSPrintf("%d", info.nCompressionMethod));
    4455             :         }
    4456             :         aosMetadata.SetNameValue(
    4457             :             "COMPRESSED_SIZE",
    4458             :             CPLSPrintf(CPL_FRMT_GUIB,
    4459          57 :                        static_cast<GUIntBig>(info.nCompressedSize)));
    4460             :         aosMetadata.SetNameValue(
    4461             :             "UNCOMPRESSED_SIZE",
    4462             :             CPLSPrintf(CPL_FRMT_GUIB,
    4463          57 :                        static_cast<GUIntBig>(info.nUncompressedSize)));
    4464             : 
    4465          57 :         if (info.bSOZipIndexFound)
    4466             :         {
    4467          11 :             aosMetadata.SetNameValue("SOZIP_FOUND", "YES");
    4468             : 
    4469             :             aosMetadata.SetNameValue("SOZIP_VERSION",
    4470          11 :                                      CPLSPrintf("%u", info.nSOZIPVersion));
    4471             : 
    4472             :             aosMetadata.SetNameValue("SOZIP_OFFSET_SIZE",
    4473          11 :                                      CPLSPrintf("%u", info.nSOZIPOffsetSize));
    4474             : 
    4475             :             aosMetadata.SetNameValue("SOZIP_CHUNK_SIZE",
    4476          11 :                                      CPLSPrintf("%u", info.nSOZIPChunkSize));
    4477             : 
    4478             :             aosMetadata.SetNameValue(
    4479             :                 "SOZIP_START_DATA_OFFSET",
    4480             :                 CPLSPrintf(CPL_FRMT_GUIB,
    4481          11 :                            static_cast<GUIntBig>(info.nSOZIPStartData)));
    4482             : 
    4483          11 :             if (info.bSOZipIndexValid)
    4484             :             {
    4485          11 :                 aosMetadata.SetNameValue("SOZIP_VALID", "YES");
    4486             :             }
    4487             :         }
    4488             : 
    4489          57 :         return aosMetadata.StealList();
    4490             :     }
    4491           0 :     return nullptr;
    4492             : }
    4493             : 
    4494             : /************************************************************************/
    4495             : /*                                Mkdir()                               */
    4496             : /************************************************************************/
    4497             : 
    4498          34 : int VSIZipFilesystemHandler::Mkdir(const char *pszDirname, long /* nMode */)
    4499             : {
    4500          68 :     CPLString osDirname = pszDirname;
    4501          34 :     if (!osDirname.empty() && osDirname.back() != '/')
    4502          34 :         osDirname += "/";
    4503          34 :     VSIVirtualHandle *poZIPHandle = OpenForWrite(osDirname, "wb");
    4504          34 :     if (poZIPHandle == nullptr)
    4505           0 :         return -1;
    4506          34 :     delete poZIPHandle;
    4507          34 :     return 0;
    4508             : }
    4509             : 
    4510             : /************************************************************************/
    4511             : /*                               ReadDirEx()                            */
    4512             : /************************************************************************/
    4513             : 
    4514        1441 : char **VSIZipFilesystemHandler::ReadDirEx(const char *pszDirname, int nMaxFiles)
    4515             : {
    4516        2882 :     CPLString osInArchiveSubDir;
    4517        1441 :     char *zipFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE);
    4518        1441 :     if (zipFilename == nullptr)
    4519          68 :         return nullptr;
    4520             : 
    4521             :     {
    4522        1373 :         CPLMutexHolder oHolder(&hMutex);
    4523             : 
    4524        1373 :         if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end())
    4525             :         {
    4526           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    4527             :                      "Cannot read a zip file being written");
    4528           1 :             CPLFree(zipFilename);
    4529           1 :             return nullptr;
    4530             :         }
    4531             :     }
    4532        1372 :     CPLFree(zipFilename);
    4533             : 
    4534        1372 :     return VSIArchiveFilesystemHandler::ReadDirEx(pszDirname, nMaxFiles);
    4535             : }
    4536             : 
    4537             : /************************************************************************/
    4538             : /*                                 Stat()                               */
    4539             : /************************************************************************/
    4540             : 
    4541        3147 : int VSIZipFilesystemHandler::Stat(const char *pszFilename,
    4542             :                                   VSIStatBufL *pStatBuf, int nFlags)
    4543             : {
    4544        6294 :     CPLString osInArchiveSubDir;
    4545             : 
    4546        3147 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
    4547             : 
    4548        3147 :     char *zipFilename = SplitFilename(pszFilename, osInArchiveSubDir, TRUE);
    4549        3147 :     if (zipFilename == nullptr)
    4550          71 :         return -1;
    4551             : 
    4552             :     {
    4553        3076 :         CPLMutexHolder oHolder(&hMutex);
    4554             : 
    4555        3076 :         if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end())
    4556             :         {
    4557           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4558             :                      "Cannot read a zip file being written");
    4559           0 :             CPLFree(zipFilename);
    4560           0 :             return -1;
    4561             :         }
    4562             :     }
    4563        3076 :     CPLFree(zipFilename);
    4564             : 
    4565        3076 :     return VSIArchiveFilesystemHandler::Stat(pszFilename, pStatBuf, nFlags);
    4566             : }
    4567             : 
    4568             : /************************************************************************/
    4569             : /*                             RemoveFromMap()                           */
    4570             : /************************************************************************/
    4571             : 
    4572         380 : void VSIZipFilesystemHandler::RemoveFromMap(VSIZipWriteHandle *poHandle)
    4573             : {
    4574         760 :     CPLMutexHolder oHolder(&hMutex);
    4575             : 
    4576           0 :     for (std::map<CPLString, VSIZipWriteHandle *>::iterator iter =
    4577         380 :              oMapZipWriteHandles.begin();
    4578         380 :          iter != oMapZipWriteHandles.end(); ++iter)
    4579             :     {
    4580         380 :         if (iter->second == poHandle)
    4581             :         {
    4582         380 :             oMapZipWriteHandles.erase(iter);
    4583         380 :             break;
    4584             :         }
    4585             :     }
    4586         380 : }
    4587             : 
    4588             : /************************************************************************/
    4589             : /*                             OpenForWrite()                           */
    4590             : /************************************************************************/
    4591             : 
    4592         622 : VSIVirtualHandle *VSIZipFilesystemHandler::OpenForWrite(const char *pszFilename,
    4593             :                                                         const char *pszAccess)
    4594             : {
    4595        1244 :     CPLMutexHolder oHolder(&hMutex);
    4596        1244 :     return OpenForWrite_unlocked(pszFilename, pszAccess);
    4597             : }
    4598             : 
    4599             : VSIVirtualHandle *
    4600         957 : VSIZipFilesystemHandler::OpenForWrite_unlocked(const char *pszFilename,
    4601             :                                                const char *pszAccess)
    4602             : {
    4603        1914 :     CPLString osZipInFileName;
    4604             : 
    4605         957 :     char *zipFilename = SplitFilename(pszFilename, osZipInFileName, FALSE);
    4606         957 :     if (zipFilename == nullptr)
    4607           0 :         return nullptr;
    4608        1914 :     CPLString osZipFilename = zipFilename;
    4609         957 :     CPLFree(zipFilename);
    4610         957 :     zipFilename = nullptr;
    4611             : 
    4612             :     // Invalidate cached file list.
    4613             :     std::map<CPLString, VSIArchiveContent *>::iterator iter =
    4614         957 :         oFileList.find(osZipFilename);
    4615         957 :     if (iter != oFileList.end())
    4616             :     {
    4617           8 :         delete iter->second;
    4618             : 
    4619           8 :         oFileList.erase(iter);
    4620             :     }
    4621             : 
    4622         957 :     if (oMapZipWriteHandles.find(osZipFilename) != oMapZipWriteHandles.end())
    4623             :     {
    4624         575 :         if (strchr(pszAccess, '+') != nullptr)
    4625             :         {
    4626           0 :             CPLError(
    4627             :                 CE_Failure, CPLE_AppDefined,
    4628             :                 "Random access not supported for writable file in /vsizip");
    4629           0 :             return nullptr;
    4630             :         }
    4631             : 
    4632         575 :         VSIZipWriteHandle *poZIPHandle = oMapZipWriteHandles[osZipFilename];
    4633             : 
    4634         575 :         if (poZIPHandle->GetChildInWriting() != nullptr)
    4635             :         {
    4636           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    4637             :                      "Cannot create %s while another file is being "
    4638             :                      "written in the .zip",
    4639             :                      osZipInFileName.c_str());
    4640           1 :             return nullptr;
    4641             :         }
    4642             : 
    4643         574 :         poZIPHandle->StopCurrentFile();
    4644             : 
    4645             :         // Re-add path separator when creating directories.
    4646         574 :         char chLastChar = pszFilename[strlen(pszFilename) - 1];
    4647         574 :         if (chLastChar == '/' || chLastChar == '\\')
    4648          35 :             osZipInFileName += chLastChar;
    4649             : 
    4650         574 :         if (CPLCreateFileInZip(poZIPHandle->GetHandle(), osZipInFileName,
    4651         574 :                                nullptr) != CE_None)
    4652          59 :             return nullptr;
    4653             : 
    4654             :         VSIZipWriteHandle *poChildHandle =
    4655         515 :             new VSIZipWriteHandle(this, nullptr, poZIPHandle);
    4656             : 
    4657         515 :         poZIPHandle->StartNewFile(poChildHandle);
    4658             : 
    4659         515 :         return poChildHandle;
    4660             :     }
    4661             :     else
    4662             :     {
    4663         382 :         char **papszOptions = nullptr;
    4664         764 :         if ((strchr(pszAccess, '+') && osZipInFileName.empty()) ||
    4665         382 :             !osZipInFileName.empty())
    4666             :         {
    4667             :             VSIStatBufL sBuf;
    4668         335 :             if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0)
    4669         199 :                 papszOptions = CSLAddNameValue(papszOptions, "APPEND", "TRUE");
    4670             :         }
    4671             : 
    4672         382 :         void *hZIP = CPLCreateZip(osZipFilename, papszOptions);
    4673         382 :         CSLDestroy(papszOptions);
    4674             : 
    4675         382 :         if (hZIP == nullptr)
    4676           2 :             return nullptr;
    4677             : 
    4678         380 :         auto poHandle = new VSIZipWriteHandle(this, hZIP, nullptr);
    4679         380 :         oMapZipWriteHandles[osZipFilename] = poHandle;
    4680             : 
    4681         380 :         if (!osZipInFileName.empty())
    4682             :         {
    4683             :             VSIZipWriteHandle *poRes = reinterpret_cast<VSIZipWriteHandle *>(
    4684         335 :                 OpenForWrite_unlocked(pszFilename, pszAccess));
    4685         335 :             if (poRes == nullptr)
    4686             :             {
    4687          58 :                 delete poHandle;
    4688          58 :                 oMapZipWriteHandles.erase(osZipFilename);
    4689          58 :                 return nullptr;
    4690             :             }
    4691             : 
    4692         277 :             poRes->SetAutoDeleteParent();
    4693             : 
    4694         277 :             return poRes;
    4695             :         }
    4696             : 
    4697          45 :         return poHandle;
    4698             :     }
    4699             : }
    4700             : 
    4701             : /************************************************************************/
    4702             : /*                           GetOptions()                               */
    4703             : /************************************************************************/
    4704             : 
    4705           1 : const char *VSIZipFilesystemHandler::GetOptions()
    4706             : {
    4707             :     return "<Options>"
    4708             :            "  <Option name='GDAL_NUM_THREADS' type='string' "
    4709             :            "description='Number of threads for compression. Either a integer "
    4710             :            "or ALL_CPUS'/>"
    4711             :            "  <Option name='CPL_VSIL_DEFLATE_CHUNK_SIZE' type='string' "
    4712             :            "description='Chunk of uncompressed data for parallelization. "
    4713             :            "Use K(ilobytes) or M(egabytes) suffix' default='1M'/>"
    4714           1 :            "</Options>";
    4715             : }
    4716             : 
    4717             : /************************************************************************/
    4718             : /*                           CopyFile()                                 */
    4719             : /************************************************************************/
    4720             : 
    4721          32 : int VSIZipFilesystemHandler::CopyFile(const char *pszSource,
    4722             :                                       const char *pszTarget, VSILFILE *fpSource,
    4723             :                                       vsi_l_offset /* nSourceSize */,
    4724             :                                       CSLConstList papszOptions,
    4725             :                                       GDALProgressFunc pProgressFunc,
    4726             :                                       void *pProgressData)
    4727             : {
    4728          64 :     CPLString osZipInFileName;
    4729             : 
    4730          32 :     char *zipFilename = SplitFilename(pszTarget, osZipInFileName, FALSE);
    4731          32 :     if (zipFilename == nullptr)
    4732           0 :         return -1;
    4733          64 :     CPLString osZipFilename = zipFilename;
    4734          32 :     CPLFree(zipFilename);
    4735          32 :     zipFilename = nullptr;
    4736          32 :     if (osZipInFileName.empty())
    4737             :     {
    4738           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4739             :                  "Target filename should be of the form "
    4740             :                  "/vsizip/path_to.zip/filename_within_zip");
    4741           0 :         return -1;
    4742             :     }
    4743             : 
    4744             :     // Invalidate cached file list.
    4745          32 :     auto oIterFileList = oFileList.find(osZipFilename);
    4746          32 :     if (oIterFileList != oFileList.end())
    4747             :     {
    4748           2 :         delete oIterFileList->second;
    4749             : 
    4750           2 :         oFileList.erase(oIterFileList);
    4751             :     }
    4752             : 
    4753          32 :     const auto oIter = oMapZipWriteHandles.find(osZipFilename);
    4754          32 :     if (oIter != oMapZipWriteHandles.end())
    4755             :     {
    4756          22 :         VSIZipWriteHandle *poZIPHandle = oIter->second;
    4757             : 
    4758          22 :         if (poZIPHandle->GetChildInWriting() != nullptr)
    4759             :         {
    4760           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4761             :                      "Cannot create %s while another file is being "
    4762             :                      "written in the .zip",
    4763             :                      osZipInFileName.c_str());
    4764           0 :             return -1;
    4765             :         }
    4766             : 
    4767          22 :         if (CPLAddFileInZip(poZIPHandle->GetHandle(), osZipInFileName.c_str(),
    4768             :                             pszSource, fpSource, papszOptions, pProgressFunc,
    4769          22 :                             pProgressData) != CE_None)
    4770             :         {
    4771           0 :             return -1;
    4772             :         }
    4773          22 :         return 0;
    4774             :     }
    4775             :     else
    4776             :     {
    4777          20 :         CPLStringList aosOptionsCreateZip;
    4778             :         VSIStatBufL sBuf;
    4779          10 :         if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0)
    4780           3 :             aosOptionsCreateZip.SetNameValue("APPEND", "TRUE");
    4781             : 
    4782          10 :         void *hZIP = CPLCreateZip(osZipFilename, aosOptionsCreateZip.List());
    4783             : 
    4784          10 :         if (hZIP == nullptr)
    4785           0 :             return -1;
    4786             : 
    4787          10 :         if (CPLAddFileInZip(hZIP, osZipInFileName.c_str(), pszSource, fpSource,
    4788             :                             papszOptions, pProgressFunc,
    4789          10 :                             pProgressData) != CE_None)
    4790             :         {
    4791           2 :             CPLCloseZip(hZIP);
    4792           2 :             return -1;
    4793             :         }
    4794           8 :         CPLCloseZip(hZIP);
    4795           8 :         return 0;
    4796             :     }
    4797             : }
    4798             : 
    4799             : /************************************************************************/
    4800             : /*                          VSIZipWriteHandle()                         */
    4801             : /************************************************************************/
    4802             : 
    4803         895 : VSIZipWriteHandle::VSIZipWriteHandle(VSIZipFilesystemHandler *poFS, void *hZIP,
    4804         895 :                                      VSIZipWriteHandle *poParent)
    4805         895 :     : m_poFS(poFS), m_hZIP(hZIP), m_poParent(poParent)
    4806             : {
    4807         895 : }
    4808             : 
    4809             : /************************************************************************/
    4810             : /*                         ~VSIZipWriteHandle()                         */
    4811             : /************************************************************************/
    4812             : 
    4813        1790 : VSIZipWriteHandle::~VSIZipWriteHandle()
    4814             : {
    4815         895 :     VSIZipWriteHandle::Close();
    4816        1790 : }
    4817             : 
    4818             : /************************************************************************/
    4819             : /*                               Seek()                                 */
    4820             : /************************************************************************/
    4821             : 
    4822           0 : int VSIZipWriteHandle::Seek(vsi_l_offset nOffset, int nWhence)
    4823             : {
    4824           0 :     if (nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR))
    4825           0 :         return 0;
    4826           0 :     if (nOffset == nCurOffset && nWhence == SEEK_SET)
    4827           0 :         return 0;
    4828             : 
    4829           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    4830             :              "VSIFSeekL() is not supported on writable Zip files");
    4831           0 :     return -1;
    4832             : }
    4833             : 
    4834             : /************************************************************************/
    4835             : /*                               Tell()                                 */
    4836             : /************************************************************************/
    4837             : 
    4838           0 : vsi_l_offset VSIZipWriteHandle::Tell()
    4839             : {
    4840           0 :     return nCurOffset;
    4841             : }
    4842             : 
    4843             : /************************************************************************/
    4844             : /*                               Read()                                 */
    4845             : /************************************************************************/
    4846             : 
    4847           0 : size_t VSIZipWriteHandle::Read(void * /* pBuffer */, size_t /* nSize */,
    4848             :                                size_t /* nMemb */)
    4849             : {
    4850           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    4851             :              "VSIFReadL() is not supported on writable Zip files");
    4852           0 :     return 0;
    4853             : }
    4854             : 
    4855             : /************************************************************************/
    4856             : /*                               Write()                                 */
    4857             : /************************************************************************/
    4858             : 
    4859      210902 : size_t VSIZipWriteHandle::Write(const void *pBuffer, size_t nSize, size_t nMemb)
    4860             : {
    4861      210902 :     if (m_poParent == nullptr)
    4862             :     {
    4863           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4864             :                  "VSIFWriteL() is not supported on "
    4865             :                  "main Zip file or closed subfiles");
    4866           0 :         return 0;
    4867             :     }
    4868             : 
    4869      210902 :     const GByte *pabyBuffer = static_cast<const GByte *>(pBuffer);
    4870      210902 :     size_t nBytesToWrite = nSize * nMemb;
    4871      210902 :     size_t nWritten = 0;
    4872      387340 :     while (nWritten < nBytesToWrite)
    4873             :     {
    4874             :         int nToWrite = static_cast<int>(
    4875      210902 :             std::min(static_cast<size_t>(INT_MAX), nBytesToWrite));
    4876      210902 :         if (CPLWriteFileInZip(m_poParent->m_hZIP, pabyBuffer, nToWrite) !=
    4877             :             CE_None)
    4878       34464 :             return 0;
    4879      176438 :         nWritten += nToWrite;
    4880      176438 :         pabyBuffer += nToWrite;
    4881             :     }
    4882             : 
    4883      176438 :     nCurOffset += nSize * nMemb;
    4884             : 
    4885      176438 :     return nMemb;
    4886             : }
    4887             : 
    4888             : /************************************************************************/
    4889             : /*                               Flush()                                */
    4890             : /************************************************************************/
    4891             : 
    4892          13 : int VSIZipWriteHandle::Flush()
    4893             : {
    4894             :     /*CPLError(CE_Failure, CPLE_NotSupported,
    4895             :              "VSIFFlushL() is not supported on writable Zip files");*/
    4896          13 :     return 0;
    4897             : }
    4898             : 
    4899             : /************************************************************************/
    4900             : /*                               Close()                                */
    4901             : /************************************************************************/
    4902             : 
    4903        1698 : int VSIZipWriteHandle::Close()
    4904             : {
    4905        1698 :     int nRet = 0;
    4906        1698 :     if (m_poParent)
    4907             :     {
    4908         515 :         CPLCloseFileInZip(m_poParent->m_hZIP);
    4909         515 :         m_poParent->poChildInWriting = nullptr;
    4910         515 :         if (bAutoDeleteParent)
    4911             :         {
    4912         277 :             if (m_poParent->Close() != 0)
    4913          71 :                 nRet = -1;
    4914         277 :             delete m_poParent;
    4915             :         }
    4916         515 :         m_poParent = nullptr;
    4917             :     }
    4918        1698 :     if (poChildInWriting)
    4919             :     {
    4920           0 :         if (poChildInWriting->Close() != 0)
    4921           0 :             nRet = -1;
    4922           0 :         poChildInWriting = nullptr;
    4923             :     }
    4924        1698 :     if (m_hZIP)
    4925             :     {
    4926         380 :         if (CPLCloseZip(m_hZIP) != CE_None)
    4927          94 :             nRet = -1;
    4928         380 :         m_hZIP = nullptr;
    4929             : 
    4930         380 :         m_poFS->RemoveFromMap(this);
    4931             :     }
    4932             : 
    4933        1698 :     return nRet;
    4934             : }
    4935             : 
    4936             : /************************************************************************/
    4937             : /*                           StopCurrentFile()                          */
    4938             : /************************************************************************/
    4939             : 
    4940         574 : void VSIZipWriteHandle::StopCurrentFile()
    4941             : {
    4942         574 :     if (poChildInWriting)
    4943           0 :         poChildInWriting->Close();
    4944         574 :     poChildInWriting = nullptr;
    4945         574 : }
    4946             : 
    4947             : /************************************************************************/
    4948             : /*                           StartNewFile()                             */
    4949             : /************************************************************************/
    4950             : 
    4951         515 : void VSIZipWriteHandle::StartNewFile(VSIZipWriteHandle *poSubFile)
    4952             : {
    4953         515 :     poChildInWriting = poSubFile;
    4954         515 : }
    4955             : 
    4956             : //! @endcond
    4957             : 
    4958             : /************************************************************************/
    4959             : /*                    VSIInstallZipFileHandler()                        */
    4960             : /************************************************************************/
    4961             : 
    4962             : /*!
    4963             :  \brief Install ZIP file system handler.
    4964             : 
    4965             :  A special file handler is installed that allows reading on-the-fly in ZIP
    4966             :  (.zip) archives.
    4967             : 
    4968             :  All portions of the file system underneath the base path "/vsizip/" will be
    4969             :  handled by this driver.
    4970             : 
    4971             :  \verbatim embed:rst
    4972             :  See :ref:`/vsizip/ documentation <vsizip>`
    4973             :  \endverbatim
    4974             : 
    4975             :  @since GDAL 1.6.0
    4976             :  */
    4977             : 
    4978        1304 : void VSIInstallZipFileHandler()
    4979             : {
    4980        1304 :     VSIFileManager::InstallHandler("/vsizip/", new VSIZipFilesystemHandler());
    4981        1304 : }
    4982             : 
    4983             : /************************************************************************/
    4984             : /*                         CPLZLibDeflate()                             */
    4985             : /************************************************************************/
    4986             : 
    4987             : /**
    4988             :  * \brief Compress a buffer with ZLib compression.
    4989             :  *
    4990             :  * @param ptr input buffer.
    4991             :  * @param nBytes size of input buffer in bytes.
    4992             :  * @param nLevel ZLib compression level (-1 for default).
    4993             :  * @param outptr output buffer, or NULL to let the function allocate it.
    4994             :  * @param nOutAvailableBytes size of output buffer if provided, or ignored.
    4995             :  * @param pnOutBytes pointer to a size_t, where to store the size of the
    4996             :  *                   output buffer.
    4997             :  *
    4998             :  * @return the output buffer (to be freed with VSIFree() if not provided)
    4999             :  *         or NULL in case of error.
    5000             :  *
    5001             :  * @since GDAL 1.10.0
    5002             :  */
    5003             : 
    5004        1646 : void *CPLZLibDeflate(const void *ptr, size_t nBytes, int nLevel, void *outptr,
    5005             :                      size_t nOutAvailableBytes, size_t *pnOutBytes)
    5006             : {
    5007        1646 :     if (pnOutBytes != nullptr)
    5008        1645 :         *pnOutBytes = 0;
    5009             : 
    5010        1646 :     size_t nTmpSize = 0;
    5011             :     void *pTmp;
    5012             : #ifdef HAVE_LIBDEFLATE
    5013             :     struct libdeflate_compressor *enc =
    5014        1646 :         libdeflate_alloc_compressor(nLevel < 0 ? 7 : nLevel);
    5015        1640 :     if (enc == nullptr)
    5016             :     {
    5017           0 :         return nullptr;
    5018             :     }
    5019             : #endif
    5020        1640 :     if (outptr == nullptr)
    5021             :     {
    5022             : #ifdef HAVE_LIBDEFLATE
    5023        1530 :         nTmpSize = libdeflate_zlib_compress_bound(enc, nBytes);
    5024             : #else
    5025             :         nTmpSize = 32 + nBytes * 2;
    5026             : #endif
    5027        1528 :         pTmp = VSIMalloc(nTmpSize);
    5028        1535 :         if (pTmp == nullptr)
    5029             :         {
    5030             : #ifdef HAVE_LIBDEFLATE
    5031           0 :             libdeflate_free_compressor(enc);
    5032             : #endif
    5033           0 :             return nullptr;
    5034             :         }
    5035             :     }
    5036             :     else
    5037             :     {
    5038         110 :         pTmp = outptr;
    5039         110 :         nTmpSize = nOutAvailableBytes;
    5040             :     }
    5041             : 
    5042             : #ifdef HAVE_LIBDEFLATE
    5043             :     size_t nCompressedBytes =
    5044        1645 :         libdeflate_zlib_compress(enc, ptr, nBytes, pTmp, nTmpSize);
    5045        1641 :     libdeflate_free_compressor(enc);
    5046        1640 :     if (nCompressedBytes == 0)
    5047             :     {
    5048           1 :         if (pTmp != outptr)
    5049           0 :             VSIFree(pTmp);
    5050           1 :         return nullptr;
    5051             :     }
    5052        1639 :     if (pnOutBytes != nullptr)
    5053        1641 :         *pnOutBytes = nCompressedBytes;
    5054             : #else
    5055             :     z_stream strm;
    5056             :     strm.zalloc = nullptr;
    5057             :     strm.zfree = nullptr;
    5058             :     strm.opaque = nullptr;
    5059             :     int ret = deflateInit(&strm, nLevel < 0 ? Z_DEFAULT_COMPRESSION : nLevel);
    5060             :     if (ret != Z_OK)
    5061             :     {
    5062             :         if (pTmp != outptr)
    5063             :             VSIFree(pTmp);
    5064             :         return nullptr;
    5065             :     }
    5066             : 
    5067             :     strm.avail_in = static_cast<uInt>(nBytes);
    5068             :     strm.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(ptr));
    5069             :     strm.avail_out = static_cast<uInt>(nTmpSize);
    5070             :     strm.next_out = reinterpret_cast<Bytef *>(pTmp);
    5071             :     ret = deflate(&strm, Z_FINISH);
    5072             :     if (ret != Z_STREAM_END)
    5073             :     {
    5074             :         if (pTmp != outptr)
    5075             :             VSIFree(pTmp);
    5076             :         return nullptr;
    5077             :     }
    5078             :     if (pnOutBytes != nullptr)
    5079             :         *pnOutBytes = nTmpSize - strm.avail_out;
    5080             :     deflateEnd(&strm);
    5081             : #endif
    5082             : 
    5083        1639 :     return pTmp;
    5084             : }
    5085             : 
    5086             : /************************************************************************/
    5087             : /*                         CPLZLibInflate()                             */
    5088             : /************************************************************************/
    5089             : 
    5090             : /**
    5091             :  * \brief Uncompress a buffer compressed with ZLib compression.
    5092             :  *
    5093             :  * @param ptr input buffer.
    5094             :  * @param nBytes size of input buffer in bytes.
    5095             :  * @param outptr output buffer, or NULL to let the function allocate it.
    5096             :  * @param nOutAvailableBytes size of output buffer if provided, or ignored.
    5097             :  * @param pnOutBytes pointer to a size_t, where to store the size of the
    5098             :  *                   output buffer.
    5099             :  *
    5100             :  * @return the output buffer (to be freed with VSIFree() if not provided)
    5101             :  *         or NULL in case of error.
    5102             :  *
    5103             :  * @since GDAL 1.10.0
    5104             :  */
    5105             : 
    5106       16051 : void *CPLZLibInflate(const void *ptr, size_t nBytes, void *outptr,
    5107             :                      size_t nOutAvailableBytes, size_t *pnOutBytes)
    5108             : {
    5109       16051 :     return CPLZLibInflateEx(ptr, nBytes, outptr, nOutAvailableBytes, false,
    5110       15738 :                             pnOutBytes);
    5111             : }
    5112             : 
    5113             : /************************************************************************/
    5114             : /*                         CPLZLibInflateEx()                           */
    5115             : /************************************************************************/
    5116             : 
    5117             : /**
    5118             :  * \brief Uncompress a buffer compressed with ZLib compression.
    5119             :  *
    5120             :  * @param ptr input buffer.
    5121             :  * @param nBytes size of input buffer in bytes.
    5122             :  * @param outptr output buffer, or NULL to let the function allocate it.
    5123             :  * @param nOutAvailableBytes size of output buffer if provided, or ignored.
    5124             :  * @param bAllowResizeOutptr whether the function is allowed to grow outptr
    5125             :  *                           (using VSIRealloc) if its initial capacity
    5126             :  *                           provided by nOutAvailableBytes is not
    5127             :  *                           large enough. Ignored if outptr is NULL.
    5128             :  * @param pnOutBytes pointer to a size_t, where to store the size of the
    5129             :  *                   output buffer.
    5130             :  *
    5131             :  * @return the output buffer (to be freed with VSIFree() if not provided)
    5132             :  *         or NULL in case of error. If bAllowResizeOutptr is set to true,
    5133             :  *         only the returned pointer should be freed by the caller, as outptr
    5134             :  *         might have been reallocated or freed.
    5135             :  *
    5136             :  * @since GDAL 3.9.0
    5137             :  */
    5138             : 
    5139       15794 : void *CPLZLibInflateEx(const void *ptr, size_t nBytes, void *outptr,
    5140             :                        size_t nOutAvailableBytes, bool bAllowResizeOutptr,
    5141             :                        size_t *pnOutBytes)
    5142             : {
    5143       15794 :     if (pnOutBytes != nullptr)
    5144       15619 :         *pnOutBytes = 0;
    5145       15794 :     char *pszReallocatableBuf = nullptr;
    5146             : 
    5147             : #ifdef HAVE_LIBDEFLATE
    5148       15794 :     if (outptr)
    5149             :     {
    5150       13966 :         struct libdeflate_decompressor *dec = libdeflate_alloc_decompressor();
    5151       14384 :         if (dec == nullptr)
    5152             :         {
    5153           0 :             if (bAllowResizeOutptr)
    5154           0 :                 VSIFree(outptr);
    5155       14292 :             return nullptr;
    5156             :         }
    5157             :         enum libdeflate_result res;
    5158       14384 :         size_t nOutBytes = 0;
    5159       14384 :         if (nBytes > 2 && static_cast<const GByte *>(ptr)[0] == 0x1F &&
    5160       11256 :             static_cast<const GByte *>(ptr)[1] == 0x8B)
    5161             :         {
    5162       11221 :             res = libdeflate_gzip_decompress(dec, ptr, nBytes, outptr,
    5163             :                                              nOutAvailableBytes, &nOutBytes);
    5164             :         }
    5165             :         else
    5166             :         {
    5167        3163 :             res = libdeflate_zlib_decompress(dec, ptr, nBytes, outptr,
    5168             :                                              nOutAvailableBytes, &nOutBytes);
    5169             :         }
    5170       14334 :         if (pnOutBytes)
    5171       14259 :             *pnOutBytes = nOutBytes;
    5172       14334 :         libdeflate_free_decompressor(dec);
    5173       14293 :         if (res == LIBDEFLATE_INSUFFICIENT_SPACE && bAllowResizeOutptr)
    5174             :         {
    5175           1 :             if (nOutAvailableBytes >
    5176           1 :                 (std::numeric_limits<size_t>::max() - 1) / 2)
    5177             :             {
    5178           0 :                 VSIFree(outptr);
    5179           0 :                 return nullptr;
    5180             :             }
    5181           1 :             size_t nOutBufSize = nOutAvailableBytes * 2;
    5182             :             pszReallocatableBuf = static_cast<char *>(
    5183           1 :                 VSI_REALLOC_VERBOSE(outptr, nOutBufSize + 1));
    5184           1 :             if (!pszReallocatableBuf)
    5185             :             {
    5186           0 :                 VSIFree(outptr);
    5187           0 :                 return nullptr;
    5188             :             }
    5189           1 :             outptr = nullptr;
    5190           1 :             nOutAvailableBytes = nOutBufSize;
    5191             :         }
    5192       14292 :         else if (res != LIBDEFLATE_SUCCESS)
    5193             :         {
    5194           0 :             if (bAllowResizeOutptr)
    5195           0 :                 VSIFree(outptr);
    5196           0 :             return nullptr;
    5197             :         }
    5198             :         else
    5199             :         {
    5200             :             // Nul-terminate if possible.
    5201       14292 :             if (nOutBytes < nOutAvailableBytes)
    5202             :             {
    5203        5921 :                 static_cast<char *>(outptr)[nOutBytes] = '\0';
    5204             :             }
    5205       14292 :             return outptr;
    5206             :         }
    5207             :     }
    5208             : #endif
    5209             : 
    5210             :     z_stream strm;
    5211        1829 :     memset(&strm, 0, sizeof(strm));
    5212        1829 :     strm.zalloc = nullptr;
    5213        1829 :     strm.zfree = nullptr;
    5214        1829 :     strm.opaque = nullptr;
    5215             :     int ret;
    5216             :     // MAX_WBITS + 32 mode which detects automatically gzip vs zlib
    5217             :     // encapsulation seems to be broken with
    5218             :     // /opt/intel/oneapi/intelpython/latest/lib/libz.so.1 from
    5219             :     // intel/oneapi-basekit Docker image
    5220        1829 :     if (nBytes > 2 && static_cast<const GByte *>(ptr)[0] == 0x1F &&
    5221           5 :         static_cast<const GByte *>(ptr)[1] == 0x8B)
    5222             :     {
    5223           5 :         ret = inflateInit2(&strm, MAX_WBITS + 16);  // gzip
    5224             :     }
    5225             :     else
    5226             :     {
    5227        1824 :         ret = inflateInit2(&strm, MAX_WBITS);  // zlib
    5228             :     }
    5229        1641 :     if (ret != Z_OK)
    5230             :     {
    5231           0 :         if (bAllowResizeOutptr)
    5232           0 :             VSIFree(outptr);
    5233           0 :         VSIFree(pszReallocatableBuf);
    5234           0 :         return nullptr;
    5235             :     }
    5236             : 
    5237        1641 :     size_t nOutBufSize = 0;
    5238        1641 :     char *pszOutBuf = nullptr;
    5239             : 
    5240             : #ifdef HAVE_LIBDEFLATE
    5241        1641 :     if (pszReallocatableBuf)
    5242             :     {
    5243           1 :         pszOutBuf = pszReallocatableBuf;
    5244           1 :         nOutBufSize = nOutAvailableBytes;
    5245             :     }
    5246             :     else
    5247             : #endif
    5248        1640 :         if (!outptr)
    5249             :     {
    5250        1640 :         if (nBytes > (std::numeric_limits<size_t>::max() - 1) / 2)
    5251             :         {
    5252           0 :             inflateEnd(&strm);
    5253           0 :             return nullptr;
    5254             :         }
    5255        1640 :         nOutBufSize = 2 * nBytes + 1;
    5256        1640 :         pszOutBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nOutBufSize));
    5257        1640 :         if (pszOutBuf == nullptr)
    5258             :         {
    5259           0 :             inflateEnd(&strm);
    5260           0 :             return nullptr;
    5261             :         }
    5262        1640 :         pszReallocatableBuf = pszOutBuf;
    5263        1640 :         bAllowResizeOutptr = true;
    5264             :     }
    5265             : #ifndef HAVE_LIBDEFLATE
    5266             :     else
    5267             :     {
    5268             :         pszOutBuf = static_cast<char *>(outptr);
    5269             :         nOutBufSize = nOutAvailableBytes;
    5270             :         if (bAllowResizeOutptr)
    5271             :             pszReallocatableBuf = pszOutBuf;
    5272             :     }
    5273             : #endif
    5274             : 
    5275        1641 :     strm.next_in = static_cast<Bytef *>(const_cast<void *>(ptr));
    5276        1641 :     strm.next_out = reinterpret_cast<Bytef *>(pszOutBuf);
    5277        1641 :     size_t nInBytesRemaining = nBytes;
    5278        1641 :     size_t nOutBytesRemaining = nOutBufSize;
    5279             : 
    5280             :     while (true)
    5281             :     {
    5282        1676 :         strm.avail_in = static_cast<uInt>(std::min<size_t>(
    5283        1676 :             nInBytesRemaining, std::numeric_limits<uInt>::max()));
    5284        1676 :         const auto avail_in_before = strm.avail_in;
    5285        1676 :         strm.avail_out = static_cast<uInt>(std::min<size_t>(
    5286        1676 :             nOutBytesRemaining, std::numeric_limits<uInt>::max()));
    5287        1676 :         const auto avail_out_before = strm.avail_out;
    5288        1676 :         ret = inflate(&strm, Z_FINISH);
    5289        1676 :         nInBytesRemaining -= (avail_in_before - strm.avail_in);
    5290        1676 :         nOutBytesRemaining -= (avail_out_before - strm.avail_out);
    5291             : 
    5292        1676 :         if (ret == Z_BUF_ERROR && strm.avail_out == 0)
    5293             :         {
    5294             : #ifdef HAVE_LIBDEFLATE
    5295          35 :             CPLAssert(bAllowResizeOutptr);
    5296             : #else
    5297             :             if (!bAllowResizeOutptr)
    5298             :             {
    5299             :                 VSIFree(pszReallocatableBuf);
    5300             :                 inflateEnd(&strm);
    5301             :                 return nullptr;
    5302             :             }
    5303             : #endif
    5304             : 
    5305          35 :             const size_t nAlreadyWritten = nOutBufSize - nOutBytesRemaining;
    5306          35 :             if (nOutBufSize > (std::numeric_limits<size_t>::max() - 1) / 2)
    5307             :             {
    5308           0 :                 VSIFree(pszReallocatableBuf);
    5309           0 :                 inflateEnd(&strm);
    5310           0 :                 return nullptr;
    5311             :             }
    5312          35 :             nOutBufSize = nOutBufSize * 2 + 1;
    5313             :             char *pszNew = static_cast<char *>(
    5314          35 :                 VSI_REALLOC_VERBOSE(pszReallocatableBuf, nOutBufSize));
    5315          35 :             if (!pszNew)
    5316             :             {
    5317           0 :                 VSIFree(pszReallocatableBuf);
    5318           0 :                 inflateEnd(&strm);
    5319           0 :                 return nullptr;
    5320             :             }
    5321          35 :             pszOutBuf = pszNew;
    5322          35 :             pszReallocatableBuf = pszOutBuf;
    5323          35 :             nOutBytesRemaining = nOutBufSize - nAlreadyWritten;
    5324          35 :             strm.next_out =
    5325          35 :                 reinterpret_cast<Bytef *>(pszOutBuf + nAlreadyWritten);
    5326             :         }
    5327        1641 :         else if (ret != Z_OK || nInBytesRemaining == 0)
    5328             :             break;
    5329          35 :     }
    5330             : 
    5331        1641 :     if (ret == Z_OK || ret == Z_STREAM_END)
    5332             :     {
    5333        1640 :         size_t nOutBytes = nOutBufSize - nOutBytesRemaining;
    5334             :         // Nul-terminate if possible.
    5335        1640 :         if (nOutBytes < nOutBufSize)
    5336             :         {
    5337        1640 :             pszOutBuf[nOutBytes] = '\0';
    5338             :         }
    5339        1640 :         inflateEnd(&strm);
    5340        1640 :         if (pnOutBytes != nullptr)
    5341        1601 :             *pnOutBytes = nOutBytes;
    5342        1640 :         return pszOutBuf;
    5343             :     }
    5344             :     else
    5345             :     {
    5346           1 :         VSIFree(pszReallocatableBuf);
    5347           1 :         inflateEnd(&strm);
    5348           1 :         return nullptr;
    5349             :     }
    5350             : }

Generated by: LCOV version 1.14