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