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