Coverage Report

Created: 2025-11-16 06:25

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