LCOV - code coverage report
Current view: top level - port - cpl_vsil_gzip.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1583 1985 79.7 %
Date: 2024-04-28 18:08:58 Functions: 120 145 82.8 %

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

Generated by: LCOV version 1.14