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