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