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