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