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

Generated by: LCOV version 1.14