LCOV - code coverage report
Current view: top level - port - cpl_vsil_gzip.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1601 2027 79.0 %
Date: 2025-08-19 18:03:11 Functions: 126 155 81.3 %

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

Generated by: LCOV version 1.14