Coverage Report

Created: 2025-08-28 06:57

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