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

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

Generated by: LCOV version 1.14