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