LCOV - code coverage report
Current view: top level - port - cpl_vsil_gzip.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1572 1994 78.8 %
Date: 2026-03-25 02:32:38 Functions: 125 155 80.6 %

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

Generated by: LCOV version 1.14