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 1754 : 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 94 : for (auto &psJob : apoFreeJobs_)
2131 : {
2132 77 : delete psJob->pBuffer_;
2133 77 : 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 1411 : void VSIGZipWriteHandleMT::DeflateCompress(void *inData)
2231 : {
2232 1411 : Job *psJob = static_cast<Job *>(inData);
2233 :
2234 1411 : CPLAssert(psJob->pBuffer_);
2235 :
2236 : z_stream sStream;
2237 1411 : memset(&sStream, 0, sizeof(sStream));
2238 1411 : sStream.zalloc = nullptr;
2239 1411 : sStream.zfree = nullptr;
2240 1411 : sStream.opaque = nullptr;
2241 :
2242 1411 : sStream.avail_in = static_cast<uInt>(psJob->pBuffer_->size());
2243 1412 : sStream.next_in = reinterpret_cast<Bytef *>(&(*psJob->pBuffer_)[0]);
2244 :
2245 1412 : 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 1410 : CPLAssertAlwaysEval(ret == Z_OK);
2251 :
2252 1410 : 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 1409 : nRealSize += static_cast<uInt>(Z_BUFSIZE) - sStream.avail_out;
2265 : }
2266 :
2267 1411 : psJob->sCompressedData_.resize(nRealSize + Z_BUFSIZE);
2268 1411 : sStream.avail_out = static_cast<uInt>(Z_BUFSIZE);
2269 1411 : sStream.next_out =
2270 1411 : 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 1390 : CPLAssertAlwaysEval(zlibRet == Z_OK);
2288 : }
2289 :
2290 : {
2291 1390 : const int zlibRet = deflate(&sStream, Z_FULL_FLUSH);
2292 1392 : CPLAssertAlwaysEval(zlibRet == Z_OK);
2293 : }
2294 : }
2295 :
2296 1409 : nRealSize += static_cast<uInt>(Z_BUFSIZE) - sStream.avail_out;
2297 1409 : psJob->sCompressedData_.resize(nRealSize);
2298 :
2299 1410 : deflateEnd(&sStream);
2300 :
2301 : {
2302 2818 : 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 409 : bool VSIGZipWriteHandleMT::ProcessCompletedJobs()
2358 : {
2359 818 : std::lock_guard<std::mutex> oLock(sMutex_);
2360 409 : bool do_it_again = true;
2361 2229 : while (do_it_again)
2362 : {
2363 1822 : do_it_again = false;
2364 1822 : if (nDeflateType_ == CPL_DEFLATE_TYPE_GZIP)
2365 : {
2366 71 : for (auto iter = apoFinishedJobs_.begin();
2367 113 : iter != apoFinishedJobs_.end(); ++iter)
2368 : {
2369 42 : auto psJob = *iter;
2370 :
2371 42 : 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 2111 : for (auto iter = apoFinishedJobs_.begin();
2390 2400 : iter != apoFinishedJobs_.end(); ++iter)
2391 : {
2392 1698 : auto psJob = *iter;
2393 1698 : 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 1820 : if (nDeflateType_ == CPL_DEFLATE_TYPE_GZIP)
2462 : {
2463 31 : for (auto iter = apoCRCFinishedJobs_.begin();
2464 33 : iter != apoCRCFinishedJobs_.end(); ++iter)
2465 : {
2466 18 : auto psJob = *iter;
2467 18 : 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 407 : 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 1332 : auto job = apoFreeJobs_.back();
2501 1332 : apoFreeJobs_.pop_back();
2502 1332 : job->sCompressedData_.clear();
2503 1332 : job->bInCRCComputation_ = false;
2504 1332 : return job;
2505 : }
2506 : }
2507 80 : 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 1800 : std::string *l_pCurBuffer = nullptr;
2534 : {
2535 3600 : std::lock_guard<std::mutex> oLock(sMutex_);
2536 1800 : if (!aposBuffers_.empty())
2537 : {
2538 1409 : l_pCurBuffer = aposBuffers_.back();
2539 1409 : aposBuffers_.pop_back();
2540 : }
2541 : }
2542 1800 : pCurBuffer_ = l_pCurBuffer;
2543 1800 : if (pCurBuffer_)
2544 1409 : break;
2545 :
2546 391 : if (poPool_)
2547 : {
2548 391 : poPool_->WaitEvent();
2549 : }
2550 391 : if (!ProcessCompletedJobs())
2551 : {
2552 1 : bHasErrored_ = true;
2553 1 : return 0;
2554 : }
2555 390 : }
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 2242 : VSIGZipFilesystemHandler::~VSIGZipFilesystemHandler()
2965 : {
2966 1121 : if (poHandleLastGZipFile)
2967 : {
2968 1 : poHandleLastGZipFile->UnsetCanSaveInfo();
2969 1 : poHandleLastGZipFile.reset();
2970 : }
2971 2242 : }
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 : @since GDAL 1.6.0
3291 : */
3292 :
3293 1754 : void VSIInstallGZipFileHandler()
3294 : {
3295 1754 : VSIFileManager::InstallHandler("/vsigzip/", new VSIGZipFilesystemHandler);
3296 1754 : }
3297 :
3298 : //! @cond Doxygen_Suppress
3299 :
3300 : /************************************************************************/
3301 : /* ==================================================================== */
3302 : /* VSIZipEntryFileOffset */
3303 : /* ==================================================================== */
3304 : /************************************************************************/
3305 :
3306 3032 : class VSIZipEntryFileOffset final : public VSIArchiveEntryFileOffset
3307 : {
3308 : public:
3309 : unz_file_pos m_file_pos;
3310 :
3311 5081 : explicit VSIZipEntryFileOffset(unz_file_pos file_pos) : m_file_pos()
3312 : {
3313 5081 : m_file_pos.pos_in_zip_directory = file_pos.pos_in_zip_directory;
3314 5081 : m_file_pos.num_of_file = file_pos.num_of_file;
3315 5081 : }
3316 :
3317 : ~VSIZipEntryFileOffset() override;
3318 : };
3319 :
3320 : VSIZipEntryFileOffset::~VSIZipEntryFileOffset() = default;
3321 :
3322 : /************************************************************************/
3323 : /* ==================================================================== */
3324 : /* VSIZipReader */
3325 : /* ==================================================================== */
3326 : /************************************************************************/
3327 :
3328 : class VSIZipReader final : public VSIArchiveReader
3329 : {
3330 : CPL_DISALLOW_COPY_ASSIGN(VSIZipReader)
3331 :
3332 : private:
3333 : unzFile unzF = nullptr;
3334 : unz_file_pos file_pos;
3335 : GUIntBig nNextFileSize = 0;
3336 : CPLString osNextFileName{};
3337 : GIntBig nModifiedTime = 0;
3338 :
3339 : bool SetInfo();
3340 :
3341 : public:
3342 : explicit VSIZipReader(const char *pszZipFileName);
3343 : ~VSIZipReader() override;
3344 :
3345 5060 : int IsValid()
3346 : {
3347 5060 : return unzF != nullptr;
3348 : }
3349 :
3350 4200 : unzFile GetUnzFileHandle()
3351 : {
3352 4200 : return unzF;
3353 : }
3354 :
3355 : int GotoFirstFile() override;
3356 : int GotoNextFile() override;
3357 :
3358 5081 : VSIArchiveEntryFileOffset *GetFileOffset() override
3359 : {
3360 5081 : return new VSIZipEntryFileOffset(file_pos);
3361 : }
3362 :
3363 5105 : GUIntBig GetFileSize() override
3364 : {
3365 5105 : return nNextFileSize;
3366 : }
3367 :
3368 5760 : CPLString GetFileName() override
3369 : {
3370 5760 : return osNextFileName;
3371 : }
3372 :
3373 6357 : GIntBig GetModifiedTime() override
3374 : {
3375 6357 : return nModifiedTime;
3376 : }
3377 :
3378 : int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
3379 : };
3380 :
3381 : /************************************************************************/
3382 : /* VSIZipReader() */
3383 : /************************************************************************/
3384 :
3385 5060 : VSIZipReader::VSIZipReader(const char *pszZipFileName)
3386 5060 : : unzF(cpl_unzOpen(pszZipFileName)), file_pos()
3387 : {
3388 5060 : file_pos.pos_in_zip_directory = 0;
3389 5060 : file_pos.num_of_file = 0;
3390 5060 : }
3391 :
3392 : /************************************************************************/
3393 : /* ~VSIZipReader() */
3394 : /************************************************************************/
3395 :
3396 10120 : VSIZipReader::~VSIZipReader()
3397 : {
3398 5060 : if (unzF)
3399 5057 : cpl_unzClose(unzF);
3400 10120 : }
3401 :
3402 : /************************************************************************/
3403 : /* SetInfo() */
3404 : /************************************************************************/
3405 :
3406 15363 : bool VSIZipReader::SetInfo()
3407 : {
3408 15363 : char fileName[8193] = {};
3409 : unz_file_info file_info;
3410 15363 : if (UNZ_OK != cpl_unzGetCurrentFileInfo(unzF, &file_info, fileName,
3411 : sizeof(fileName) - 1, nullptr, 0,
3412 : nullptr, 0))
3413 : {
3414 0 : CPLError(CE_Failure, CPLE_FileIO, "cpl_unzGetCurrentFileInfo failed");
3415 0 : cpl_unzGetFilePos(unzF, &file_pos);
3416 0 : return false;
3417 : }
3418 15363 : fileName[sizeof(fileName) - 1] = '\0';
3419 15363 : osNextFileName = fileName;
3420 15363 : nNextFileSize = file_info.uncompressed_size;
3421 : struct tm brokendowntime;
3422 15363 : brokendowntime.tm_sec = file_info.tmu_date.tm_sec;
3423 15363 : brokendowntime.tm_min = file_info.tmu_date.tm_min;
3424 15363 : brokendowntime.tm_hour = file_info.tmu_date.tm_hour;
3425 15363 : brokendowntime.tm_mday = file_info.tmu_date.tm_mday;
3426 15363 : brokendowntime.tm_mon = file_info.tmu_date.tm_mon;
3427 : // The minizip conventions differs from the Unix one.
3428 15363 : brokendowntime.tm_year = file_info.tmu_date.tm_year - 1900;
3429 15363 : nModifiedTime = CPLYMDHMSToUnixTime(&brokendowntime);
3430 :
3431 15363 : cpl_unzGetFilePos(unzF, &file_pos);
3432 15363 : return true;
3433 : }
3434 :
3435 : /************************************************************************/
3436 : /* GotoNextFile() */
3437 : /************************************************************************/
3438 :
3439 5637 : int VSIZipReader::GotoNextFile()
3440 : {
3441 5637 : if (cpl_unzGoToNextFile(unzF) != UNZ_OK)
3442 306 : return FALSE;
3443 :
3444 5331 : if (!SetInfo())
3445 0 : return FALSE;
3446 :
3447 5331 : return TRUE;
3448 : }
3449 :
3450 : /************************************************************************/
3451 : /* GotoFirstFile() */
3452 : /************************************************************************/
3453 :
3454 5950 : int VSIZipReader::GotoFirstFile()
3455 : {
3456 5950 : if (cpl_unzGoToFirstFile(unzF) != UNZ_OK)
3457 0 : return FALSE;
3458 :
3459 5950 : if (!SetInfo())
3460 0 : return FALSE;
3461 :
3462 5950 : return TRUE;
3463 : }
3464 :
3465 : /************************************************************************/
3466 : /* GotoFileOffset() */
3467 : /************************************************************************/
3468 :
3469 4082 : int VSIZipReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
3470 : {
3471 4082 : VSIZipEntryFileOffset *pZipEntryOffset =
3472 : reinterpret_cast<VSIZipEntryFileOffset *>(pOffset);
3473 4082 : if (cpl_unzGoToFilePos(unzF, &(pZipEntryOffset->m_file_pos)) != UNZ_OK)
3474 : {
3475 0 : CPLError(CE_Failure, CPLE_AppDefined, "GotoFileOffset failed");
3476 0 : return FALSE;
3477 : }
3478 :
3479 4082 : if (!SetInfo())
3480 0 : return FALSE;
3481 :
3482 4082 : return TRUE;
3483 : }
3484 :
3485 : /************************************************************************/
3486 : /* ==================================================================== */
3487 : /* VSIZipFilesystemHandler */
3488 : /* ==================================================================== */
3489 : /************************************************************************/
3490 :
3491 : class VSIZipWriteHandle;
3492 :
3493 : class VSIZipFilesystemHandler final : public VSIArchiveFilesystemHandler
3494 : {
3495 : CPL_DISALLOW_COPY_ASSIGN(VSIZipFilesystemHandler)
3496 :
3497 : std::map<CPLString, VSIZipWriteHandle *> oMapZipWriteHandles{};
3498 : VSIVirtualHandleUniquePtr OpenForWrite_unlocked(const char *pszFilename,
3499 : const char *pszAccess);
3500 :
3501 : struct VSIFileInZipInfo
3502 : {
3503 : VSIVirtualHandleUniquePtr poVirtualHandle{};
3504 : std::map<std::string, std::string> oMapProperties{};
3505 : int nCompressionMethod = 0;
3506 : uint64_t nUncompressedSize = 0;
3507 : uint64_t nCompressedSize = 0;
3508 : uint64_t nStartDataStream = 0;
3509 : uLong nCRC = 0;
3510 : bool bSOZipIndexFound = false;
3511 : bool bSOZipIndexValid = false;
3512 : uint32_t nSOZIPVersion = 0;
3513 : uint32_t nSOZIPToSkip = 0;
3514 : uint32_t nSOZIPChunkSize = 0;
3515 : uint32_t nSOZIPOffsetSize = 0;
3516 : uint64_t nSOZIPStartData = 0;
3517 : };
3518 :
3519 : bool GetFileInfo(const char *pszFilename, VSIFileInZipInfo &info,
3520 : bool bSetError);
3521 :
3522 : public:
3523 1754 : VSIZipFilesystemHandler() = default;
3524 : ~VSIZipFilesystemHandler() override;
3525 :
3526 59653 : const char *GetPrefix() const override
3527 : {
3528 59653 : return "/vsizip";
3529 : }
3530 :
3531 : std::vector<CPLString> GetExtensions() const override;
3532 : std::unique_ptr<VSIArchiveReader>
3533 : CreateReader(const char *pszZipFileName) override;
3534 :
3535 : VSIVirtualHandleUniquePtr Open(const char *pszFilename,
3536 : const char *pszAccess, bool bSetError,
3537 : CSLConstList /* papszOptions */) override;
3538 :
3539 : char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
3540 : CSLConstList papszOptions) override;
3541 :
3542 : VSIVirtualHandleUniquePtr OpenForWrite(const char *pszFilename,
3543 : const char *pszAccess);
3544 :
3545 : int CopyFile(const char *pszSource, const char *pszTarget,
3546 : VSILFILE *fpSource, vsi_l_offset nSourceSize,
3547 : const char *const *papszOptions,
3548 : GDALProgressFunc pProgressFunc, void *pProgressData) override;
3549 :
3550 : int Mkdir(const char *pszDirname, long nMode) override;
3551 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
3552 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
3553 : int nFlags) override;
3554 :
3555 : const char *GetOptions() override;
3556 :
3557 : void RemoveFromMap(VSIZipWriteHandle *poHandle);
3558 : };
3559 :
3560 : /************************************************************************/
3561 : /* ==================================================================== */
3562 : /* VSIZipWriteHandle */
3563 : /* ==================================================================== */
3564 : /************************************************************************/
3565 :
3566 : class VSIZipWriteHandle final : public VSIVirtualHandle
3567 : {
3568 : CPL_DISALLOW_COPY_ASSIGN(VSIZipWriteHandle)
3569 :
3570 : VSIZipFilesystemHandler *m_poFS = nullptr;
3571 : void *m_hZIP = nullptr;
3572 : VSIZipWriteHandle *poChildInWriting = nullptr;
3573 : VSIZipWriteHandle *m_poParent = nullptr;
3574 : bool bAutoDeleteParent = false;
3575 : vsi_l_offset nCurOffset = 0;
3576 :
3577 : public:
3578 : VSIZipWriteHandle(VSIZipFilesystemHandler *poFS, void *hZIP,
3579 : VSIZipWriteHandle *poParent);
3580 :
3581 : ~VSIZipWriteHandle() override;
3582 :
3583 : int Seek(vsi_l_offset nOffset, int nWhence) override;
3584 : vsi_l_offset Tell() override;
3585 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
3586 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
3587 :
3588 0 : int Eof() override
3589 : {
3590 0 : return 0;
3591 : }
3592 :
3593 0 : int Error() override
3594 : {
3595 0 : return 0;
3596 : }
3597 :
3598 0 : void ClearErr() override
3599 : {
3600 0 : }
3601 :
3602 : int Flush() override;
3603 : int Close() override;
3604 :
3605 : void StartNewFile(VSIZipWriteHandle *poSubFile);
3606 : void StopCurrentFile();
3607 :
3608 631 : void *GetHandle()
3609 : {
3610 631 : return m_hZIP;
3611 : }
3612 :
3613 632 : VSIZipWriteHandle *GetChildInWriting()
3614 : {
3615 632 : return poChildInWriting;
3616 : }
3617 :
3618 308 : void SetAutoDeleteParent()
3619 : {
3620 308 : bAutoDeleteParent = true;
3621 308 : }
3622 : };
3623 :
3624 : /************************************************************************/
3625 : /* ~VSIZipFilesystemHandler() */
3626 : /************************************************************************/
3627 :
3628 2242 : VSIZipFilesystemHandler::~VSIZipFilesystemHandler()
3629 : {
3630 0 : for (std::map<CPLString, VSIZipWriteHandle *>::const_iterator iter =
3631 1121 : oMapZipWriteHandles.begin();
3632 1121 : iter != oMapZipWriteHandles.end(); ++iter)
3633 : {
3634 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s has not been closed",
3635 0 : iter->first.c_str());
3636 : }
3637 2242 : }
3638 :
3639 : /************************************************************************/
3640 : /* GetExtensions() */
3641 : /************************************************************************/
3642 :
3643 13887 : std::vector<CPLString> VSIZipFilesystemHandler::GetExtensions() const
3644 : {
3645 13887 : std::vector<CPLString> oList;
3646 13887 : oList.push_back(".zip");
3647 13887 : oList.push_back(".kmz");
3648 13887 : oList.push_back(".dwf");
3649 13887 : oList.push_back(".ods");
3650 13887 : oList.push_back(".xlsx");
3651 13887 : oList.push_back(".xlsm");
3652 :
3653 : // Add to zip FS handler extensions array additional extensions
3654 : // listed in CPL_VSIL_ZIP_ALLOWED_EXTENSIONS config option.
3655 : // The extensions are divided by commas.
3656 : const char *pszAllowedExtensions =
3657 13887 : CPLGetConfigOption("CPL_VSIL_ZIP_ALLOWED_EXTENSIONS", nullptr);
3658 13887 : if (pszAllowedExtensions)
3659 : {
3660 : char **papszExtensions =
3661 0 : CSLTokenizeString2(pszAllowedExtensions, ", ", 0);
3662 0 : for (int i = 0; papszExtensions[i] != nullptr; i++)
3663 : {
3664 0 : oList.push_back(papszExtensions[i]);
3665 : }
3666 0 : CSLDestroy(papszExtensions);
3667 : }
3668 :
3669 13887 : return oList;
3670 : }
3671 :
3672 : /************************************************************************/
3673 : /* CreateReader() */
3674 : /************************************************************************/
3675 :
3676 : std::unique_ptr<VSIArchiveReader>
3677 5060 : VSIZipFilesystemHandler::CreateReader(const char *pszZipFileName)
3678 : {
3679 10120 : auto poReader = std::make_unique<VSIZipReader>(pszZipFileName);
3680 :
3681 5060 : if (!poReader->IsValid() || !poReader->GotoFirstFile())
3682 : {
3683 3 : return nullptr;
3684 : }
3685 :
3686 5057 : return poReader;
3687 : }
3688 :
3689 : /************************************************************************/
3690 : /* VSISOZipHandle */
3691 : /************************************************************************/
3692 :
3693 : class VSISOZipHandle final : public VSIVirtualHandle
3694 : {
3695 : VSIVirtualHandleUniquePtr poBaseHandle_{};
3696 : vsi_l_offset nPosCompressedStream_;
3697 : uint64_t compressed_size_;
3698 : uint64_t uncompressed_size_;
3699 : vsi_l_offset indexPos_;
3700 : uint32_t nToSkip_;
3701 : uint32_t nChunkSize_;
3702 : bool bEOF_ = false;
3703 : bool bError_ = false;
3704 : vsi_l_offset nCurPos_ = 0;
3705 : bool bOK_ = true;
3706 : #ifdef HAVE_LIBDEFLATE
3707 : struct libdeflate_decompressor *pDecompressor_ = nullptr;
3708 : #else
3709 : z_stream sStream_{};
3710 : #endif
3711 :
3712 : VSISOZipHandle(const VSISOZipHandle &) = delete;
3713 : VSISOZipHandle &operator=(const VSISOZipHandle &) = delete;
3714 :
3715 : public:
3716 : VSISOZipHandle(VSIVirtualHandleUniquePtr poVirtualHandleIn,
3717 : vsi_l_offset nPosCompressedStream, uint64_t compressed_size,
3718 : uint64_t uncompressed_size, vsi_l_offset indexPos,
3719 : uint32_t nToSkip, uint32_t nChunkSize);
3720 : ~VSISOZipHandle() override;
3721 :
3722 : int Seek(vsi_l_offset nOffset, int nWhence) override;
3723 :
3724 21 : vsi_l_offset Tell() override
3725 : {
3726 21 : return nCurPos_;
3727 : }
3728 :
3729 : size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
3730 :
3731 0 : size_t Write(const void *, size_t, size_t) override
3732 : {
3733 0 : return 0;
3734 : }
3735 :
3736 0 : int Eof() override
3737 : {
3738 0 : return bEOF_;
3739 : }
3740 :
3741 16 : int Error() override
3742 : {
3743 16 : return bError_;
3744 : }
3745 :
3746 1 : void ClearErr() override
3747 : {
3748 1 : bEOF_ = false;
3749 1 : bError_ = false;
3750 1 : }
3751 :
3752 : int Close() override;
3753 :
3754 21 : bool IsOK() const
3755 : {
3756 21 : return bOK_;
3757 : }
3758 : };
3759 :
3760 : /************************************************************************/
3761 : /* VSISOZipHandle() */
3762 : /************************************************************************/
3763 :
3764 21 : VSISOZipHandle::VSISOZipHandle(VSIVirtualHandleUniquePtr poVirtualHandleIn,
3765 : vsi_l_offset nPosCompressedStream,
3766 : uint64_t compressed_size,
3767 : uint64_t uncompressed_size,
3768 : vsi_l_offset indexPos, uint32_t nToSkip,
3769 21 : uint32_t nChunkSize)
3770 21 : : poBaseHandle_(std::move(poVirtualHandleIn)),
3771 : nPosCompressedStream_(nPosCompressedStream),
3772 : compressed_size_(compressed_size), uncompressed_size_(uncompressed_size),
3773 21 : indexPos_(indexPos), nToSkip_(nToSkip), nChunkSize_(nChunkSize)
3774 : {
3775 : #ifdef HAVE_LIBDEFLATE
3776 21 : pDecompressor_ = libdeflate_alloc_decompressor();
3777 21 : if (!pDecompressor_)
3778 0 : bOK_ = false;
3779 : #else
3780 : memset(&sStream_, 0, sizeof(sStream_));
3781 : int err = inflateInit2(&sStream_, -MAX_WBITS);
3782 : if (err != Z_OK)
3783 : bOK_ = false;
3784 : #endif
3785 21 : }
3786 :
3787 : /************************************************************************/
3788 : /* ~VSISOZipHandle() */
3789 : /************************************************************************/
3790 :
3791 42 : VSISOZipHandle::~VSISOZipHandle()
3792 : {
3793 21 : VSISOZipHandle::Close();
3794 21 : if (bOK_)
3795 : {
3796 : #ifdef HAVE_LIBDEFLATE
3797 21 : libdeflate_free_decompressor(pDecompressor_);
3798 : #else
3799 : inflateEnd(&sStream_);
3800 : #endif
3801 : }
3802 42 : }
3803 :
3804 : /************************************************************************/
3805 : /* Close() */
3806 : /************************************************************************/
3807 :
3808 42 : int VSISOZipHandle::Close()
3809 : {
3810 42 : int ret = 0;
3811 42 : if (poBaseHandle_)
3812 : {
3813 21 : ret = poBaseHandle_->Close();
3814 21 : poBaseHandle_.reset();
3815 : }
3816 42 : return ret;
3817 : }
3818 :
3819 : /************************************************************************/
3820 : /* Seek() */
3821 : /************************************************************************/
3822 :
3823 436 : int VSISOZipHandle::Seek(vsi_l_offset nOffset, int nWhence)
3824 : {
3825 436 : bEOF_ = false;
3826 436 : if (nWhence == SEEK_SET)
3827 415 : nCurPos_ = nOffset;
3828 21 : else if (nWhence == SEEK_END)
3829 21 : nCurPos_ = uncompressed_size_;
3830 : else
3831 0 : nCurPos_ += nOffset;
3832 436 : return 0;
3833 : }
3834 :
3835 : /************************************************************************/
3836 : /* Read() */
3837 : /************************************************************************/
3838 :
3839 415 : size_t VSISOZipHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
3840 : {
3841 415 : size_t nToRead = nSize * nCount;
3842 415 : if (nCurPos_ >= uncompressed_size_ && nToRead > 0)
3843 : {
3844 1 : bEOF_ = true;
3845 1 : return 0;
3846 : }
3847 :
3848 414 : if (nSize != 1)
3849 : {
3850 0 : bError_ = true;
3851 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported nSize");
3852 0 : return 0;
3853 : }
3854 414 : if ((nCurPos_ % nChunkSize_) != 0)
3855 : {
3856 0 : bError_ = true;
3857 0 : CPLError(CE_Failure, CPLE_NotSupported,
3858 : "nCurPos is not a multiple of nChunkSize");
3859 0 : return 0;
3860 : }
3861 414 : if (nCurPos_ + nToRead > uncompressed_size_)
3862 : {
3863 16 : nToRead = static_cast<size_t>(uncompressed_size_ - nCurPos_);
3864 16 : nCount = nToRead;
3865 : }
3866 398 : else if ((nToRead % nChunkSize_) != 0)
3867 : {
3868 0 : bError_ = true;
3869 0 : CPLError(CE_Failure, CPLE_NotSupported,
3870 : "nToRead is not a multiple of nChunkSize");
3871 0 : return 0;
3872 : }
3873 :
3874 : const auto ReadOffsetInCompressedStream =
3875 7870 : [this](uint64_t nChunkIdx) -> uint64_t
3876 : {
3877 1596 : if (nChunkIdx == 0)
3878 19 : return 0;
3879 1577 : if (nChunkIdx == 1 + (uncompressed_size_ - 1) / nChunkSize_)
3880 17 : return compressed_size_;
3881 1560 : constexpr size_t nOffsetSize = 8;
3882 1560 : if (poBaseHandle_->Seek(indexPos_ + 32 + nToSkip_ +
3883 1560 : (nChunkIdx - 1) * nOffsetSize,
3884 1560 : SEEK_SET) != 0)
3885 0 : return static_cast<uint64_t>(-1);
3886 :
3887 : uint64_t nOffset;
3888 1560 : if (poBaseHandle_->Read(&nOffset, sizeof(nOffset), 1) != 1)
3889 0 : return static_cast<uint64_t>(-1);
3890 1560 : CPL_LSBPTR64(&nOffset);
3891 1560 : return nOffset;
3892 414 : };
3893 :
3894 414 : size_t nOffsetInOutputBuffer = 0;
3895 : while (true)
3896 : {
3897 : uint64_t nOffsetInCompressedStream =
3898 798 : ReadOffsetInCompressedStream(nCurPos_ / nChunkSize_);
3899 798 : if (nOffsetInCompressedStream == static_cast<uint64_t>(-1))
3900 : {
3901 0 : bError_ = true;
3902 0 : CPLError(CE_Failure, CPLE_AppDefined,
3903 : "Cannot read nOffsetInCompressedStream");
3904 0 : return 0;
3905 : }
3906 : uint64_t nNextOffsetInCompressedStream =
3907 798 : ReadOffsetInCompressedStream(1 + nCurPos_ / nChunkSize_);
3908 798 : if (nNextOffsetInCompressedStream == static_cast<uint64_t>(-1))
3909 : {
3910 0 : bError_ = true;
3911 0 : CPLError(CE_Failure, CPLE_AppDefined,
3912 : "Cannot read nNextOffsetInCompressedStream");
3913 0 : return 0;
3914 : }
3915 :
3916 798 : if (nNextOffsetInCompressedStream <= nOffsetInCompressedStream ||
3917 798 : nNextOffsetInCompressedStream - nOffsetInCompressedStream >
3918 798 : 13 + 2 * nChunkSize_ ||
3919 798 : nNextOffsetInCompressedStream > compressed_size_)
3920 : {
3921 0 : bError_ = true;
3922 0 : CPLError(
3923 : CE_Failure, CPLE_AppDefined,
3924 : "Invalid values for nOffsetInCompressedStream (" CPL_FRMT_GUIB
3925 : ") / "
3926 : "nNextOffsetInCompressedStream(" CPL_FRMT_GUIB ")",
3927 : static_cast<GUIntBig>(nOffsetInCompressedStream),
3928 : static_cast<GUIntBig>(nNextOffsetInCompressedStream));
3929 0 : return 0;
3930 : }
3931 :
3932 : // CPLDebug("VSIZIP", "Seek to compressed data at offset "
3933 : // CPL_FRMT_GUIB, static_cast<GUIntBig>(nPosCompressedStream_ +
3934 : // nOffsetInCompressedStream));
3935 798 : if (poBaseHandle_->Seek(
3936 798 : nPosCompressedStream_ + nOffsetInCompressedStream, SEEK_SET) !=
3937 : 0)
3938 : {
3939 0 : bError_ = true;
3940 0 : return 0;
3941 : }
3942 :
3943 798 : const int nCompressedToRead = static_cast<int>(
3944 798 : nNextOffsetInCompressedStream - nOffsetInCompressedStream);
3945 : // CPLDebug("VSIZIP", "nCompressedToRead = %d", nCompressedToRead);
3946 798 : std::vector<GByte> abyCompressedData(nCompressedToRead);
3947 798 : if (poBaseHandle_->Read(&abyCompressedData[0], nCompressedToRead, 1) !=
3948 : 1)
3949 : {
3950 0 : bError_ = true;
3951 0 : return 0;
3952 : }
3953 :
3954 : size_t nToReadThisIter =
3955 798 : std::min(nToRead, static_cast<size_t>(nChunkSize_));
3956 :
3957 1594 : if (nCompressedToRead >= 5 &&
3958 1583 : abyCompressedData[nCompressedToRead - 5] == 0x00 &&
3959 785 : memcmp(&abyCompressedData[nCompressedToRead - 4],
3960 : "\x00\x00\xFF\xFF", 4) == 0)
3961 : {
3962 : // Tag this flush block as the last one.
3963 781 : abyCompressedData[nCompressedToRead - 5] = 0x01;
3964 : }
3965 :
3966 : #ifdef HAVE_LIBDEFLATE
3967 798 : size_t nOut = 0;
3968 798 : if (libdeflate_deflate_decompress(
3969 798 : pDecompressor_, &abyCompressedData[0], nCompressedToRead,
3970 : static_cast<Bytef *>(pBuffer) + nOffsetInOutputBuffer,
3971 798 : nToReadThisIter, &nOut) != LIBDEFLATE_SUCCESS)
3972 : {
3973 0 : bError_ = true;
3974 0 : CPLError(
3975 : CE_Failure, CPLE_AppDefined,
3976 : "libdeflate_deflate_decompress() failed at pos " CPL_FRMT_GUIB,
3977 0 : static_cast<GUIntBig>(nCurPos_));
3978 0 : return 0;
3979 : }
3980 798 : if (nOut != nToReadThisIter)
3981 : {
3982 0 : bError_ = true;
3983 0 : CPLError(CE_Failure, CPLE_AppDefined,
3984 : "Only %u bytes decompressed at pos " CPL_FRMT_GUIB
3985 : " whereas %u where expected",
3986 : static_cast<unsigned>(nOut),
3987 0 : static_cast<GUIntBig>(nCurPos_),
3988 : static_cast<unsigned>(nToReadThisIter));
3989 0 : return 0;
3990 : }
3991 : #else
3992 : sStream_.avail_in = nCompressedToRead;
3993 : sStream_.next_in = &abyCompressedData[0];
3994 : sStream_.avail_out = static_cast<int>(nToReadThisIter);
3995 : sStream_.next_out =
3996 : static_cast<Bytef *>(pBuffer) + nOffsetInOutputBuffer;
3997 :
3998 : int err = inflate(&sStream_, Z_FINISH);
3999 : if ((err != Z_OK && err != Z_STREAM_END))
4000 : {
4001 : bError_ = true;
4002 : CPLError(CE_Failure, CPLE_AppDefined,
4003 : "inflate() failed at pos " CPL_FRMT_GUIB,
4004 : static_cast<GUIntBig>(nCurPos_));
4005 : inflateReset(&sStream_);
4006 : return 0;
4007 : }
4008 : if (sStream_.avail_in != 0)
4009 : CPLDebug("VSIZIP", "avail_in = %d", sStream_.avail_in);
4010 : if (sStream_.avail_out != 0)
4011 : {
4012 : bError_ = true;
4013 : CPLError(
4014 : CE_Failure, CPLE_AppDefined,
4015 : "Only %u bytes decompressed at pos " CPL_FRMT_GUIB
4016 : " whereas %u where expected",
4017 : static_cast<unsigned>(nToReadThisIter - sStream_.avail_out),
4018 : static_cast<GUIntBig>(nCurPos_),
4019 : static_cast<unsigned>(nToReadThisIter));
4020 : inflateReset(&sStream_);
4021 : return 0;
4022 : }
4023 : inflateReset(&sStream_);
4024 : #endif
4025 798 : nOffsetInOutputBuffer += nToReadThisIter;
4026 798 : nCurPos_ += nToReadThisIter;
4027 798 : nToRead -= nToReadThisIter;
4028 798 : if (nToRead == 0)
4029 414 : break;
4030 384 : }
4031 :
4032 414 : return nCount;
4033 : }
4034 :
4035 : /************************************************************************/
4036 : /* GetFileInfo() */
4037 : /************************************************************************/
4038 :
4039 4429 : bool VSIZipFilesystemHandler::GetFileInfo(const char *pszFilename,
4040 : VSIFileInZipInfo &info,
4041 : bool bSetError)
4042 : {
4043 :
4044 8858 : CPLString osZipInFileName;
4045 : std::unique_ptr<char, VSIFreeReleaser> zipFilename(
4046 8858 : SplitFilename(pszFilename, osZipInFileName, true, bSetError));
4047 4429 : if (zipFilename == nullptr)
4048 58 : return false;
4049 :
4050 : {
4051 4371 : std::unique_lock oLock(oMutex);
4052 4371 : if (oMapZipWriteHandles.find(zipFilename.get()) !=
4053 8742 : oMapZipWriteHandles.end())
4054 : {
4055 1 : CPLError(CE_Failure, CPLE_AppDefined,
4056 : "Cannot read a zip file being written");
4057 1 : return false;
4058 : }
4059 : }
4060 :
4061 8740 : auto poReader = OpenArchiveFile(zipFilename.get(), osZipInFileName);
4062 4370 : if (poReader == nullptr)
4063 : {
4064 170 : return false;
4065 : }
4066 :
4067 : VSIFilesystemHandler *poFSHandler =
4068 4200 : VSIFileManager::GetHandler(zipFilename.get());
4069 :
4070 : VSIVirtualHandleUniquePtr poVirtualHandle(
4071 8400 : poFSHandler->Open(zipFilename.get(), "rb"));
4072 :
4073 4200 : if (poVirtualHandle == nullptr)
4074 : {
4075 0 : return false;
4076 : }
4077 :
4078 : unzFile unzF =
4079 4200 : cpl::down_cast<VSIZipReader *>(poReader.get())->GetUnzFileHandle();
4080 :
4081 4200 : if (cpl_unzOpenCurrentFile(unzF) != UNZ_OK)
4082 : {
4083 0 : CPLError(CE_Failure, CPLE_AppDefined,
4084 : "cpl_unzOpenCurrentFile() failed");
4085 0 : return false;
4086 : }
4087 :
4088 4200 : info.nStartDataStream = cpl_unzGetCurrentFileZStreamPos(unzF);
4089 :
4090 : unz_file_info file_info;
4091 4200 : if (cpl_unzGetCurrentFileInfo(unzF, &file_info, nullptr, 0, nullptr, 0,
4092 4200 : nullptr, 0) != UNZ_OK)
4093 : {
4094 0 : CPLError(CE_Failure, CPLE_AppDefined,
4095 : "cpl_unzGetCurrentFileInfo() failed");
4096 0 : cpl_unzCloseCurrentFile(unzF);
4097 0 : return false;
4098 : }
4099 :
4100 4200 : if (file_info.size_file_extra)
4101 : {
4102 7078 : std::vector<GByte> abyExtra(file_info.size_file_extra);
4103 3539 : poVirtualHandle->Seek(file_info.file_extra_abs_offset, SEEK_SET);
4104 3539 : if (poVirtualHandle->Read(&abyExtra[0], abyExtra.size(), 1) == 1)
4105 : {
4106 3539 : size_t nPos = 0;
4107 10567 : while (nPos + 2 * sizeof(uint16_t) <= abyExtra.size())
4108 : {
4109 : uint16_t nId;
4110 7028 : memcpy(&nId, &abyExtra[nPos], sizeof(uint16_t));
4111 7028 : nPos += sizeof(uint16_t);
4112 7028 : CPL_LSBPTR16(&nId);
4113 : uint16_t nSize;
4114 7028 : memcpy(&nSize, &abyExtra[nPos], sizeof(uint16_t));
4115 7028 : nPos += sizeof(uint16_t);
4116 7028 : CPL_LSBPTR16(&nSize);
4117 7028 : if (nId == 0x564b && nPos + nSize <= abyExtra.size()) // "KV"
4118 : {
4119 10 : if (nSize >= strlen("KeyValuePairs") + 1 &&
4120 5 : memcmp(&abyExtra[nPos], "KeyValuePairs",
4121 : strlen("KeyValuePairs")) == 0)
4122 : {
4123 5 : int nPos2 = static_cast<int>(strlen("KeyValuePairs"));
4124 5 : int nKVPairs = abyExtra[nPos + nPos2];
4125 5 : nPos2++;
4126 10 : for (int iKV = 0; iKV < nKVPairs; ++iKV)
4127 : {
4128 5 : if (nPos2 + sizeof(uint16_t) > nSize)
4129 0 : break;
4130 : uint16_t nKeyLen;
4131 5 : memcpy(&nKeyLen, &abyExtra[nPos + nPos2],
4132 : sizeof(uint16_t));
4133 5 : nPos2 += sizeof(uint16_t);
4134 5 : CPL_LSBPTR16(&nKeyLen);
4135 5 : if (nPos2 + nKeyLen > nSize)
4136 0 : break;
4137 5 : std::string osKey;
4138 5 : osKey.resize(nKeyLen);
4139 5 : memcpy(&osKey[0], &abyExtra[nPos + nPos2], nKeyLen);
4140 5 : nPos2 += nKeyLen;
4141 :
4142 5 : if (nPos2 + sizeof(uint16_t) > nSize)
4143 0 : break;
4144 : uint16_t nValLen;
4145 5 : memcpy(&nValLen, &abyExtra[nPos + nPos2],
4146 : sizeof(uint16_t));
4147 5 : nPos2 += sizeof(uint16_t);
4148 5 : CPL_LSBPTR16(&nValLen);
4149 5 : if (nPos2 + nValLen > nSize)
4150 0 : break;
4151 10 : std::string osVal;
4152 5 : osVal.resize(nValLen);
4153 5 : memcpy(&osVal[0], &abyExtra[nPos + nPos2], nValLen);
4154 5 : nPos2 += nValLen;
4155 :
4156 5 : info.oMapProperties[osKey] = std::move(osVal);
4157 : }
4158 : }
4159 : }
4160 7028 : nPos += nSize;
4161 : }
4162 : }
4163 : }
4164 :
4165 4200 : info.nCRC = file_info.crc;
4166 4200 : info.nCompressionMethod = static_cast<int>(file_info.compression_method);
4167 4200 : info.nUncompressedSize = static_cast<uint64_t>(file_info.uncompressed_size);
4168 4200 : info.nCompressedSize = static_cast<uint64_t>(file_info.compressed_size);
4169 :
4170 : // Try to locate .sozip.idx file
4171 : uLong64 local_header_pos;
4172 4200 : cpl_unzGetLocalHeaderPos(unzF, &local_header_pos);
4173 4200 : local_header_pos = info.nStartDataStream + file_info.compressed_size;
4174 : unz_file_info file_info2;
4175 4200 : std::string osAuxName;
4176 4200 : osAuxName.resize(1024);
4177 : uLong64 indexPos;
4178 8328 : if (file_info.compression_method == 8 &&
4179 8256 : cpl_unzCurrentFileInfoFromLocalHeader(
4180 4128 : unzF, local_header_pos, &file_info2, &osAuxName[0],
4181 : osAuxName.size(), &indexPos) == UNZ_OK)
4182 : {
4183 3605 : osAuxName.resize(strlen(osAuxName.c_str()));
4184 3605 : if (osAuxName.find(".sozip.idx") != std::string::npos)
4185 : {
4186 39 : info.bSOZipIndexFound = true;
4187 39 : info.nSOZIPStartData = indexPos;
4188 39 : poVirtualHandle->Seek(indexPos, SEEK_SET);
4189 39 : uint32_t nVersion = 0;
4190 39 : poVirtualHandle->Read(&nVersion, sizeof(nVersion), 1);
4191 39 : CPL_LSBPTR32(&nVersion);
4192 39 : uint32_t nToSkip = 0;
4193 39 : poVirtualHandle->Read(&nToSkip, sizeof(nToSkip), 1);
4194 39 : CPL_LSBPTR32(&nToSkip);
4195 39 : uint32_t nChunkSize = 0;
4196 39 : poVirtualHandle->Read(&nChunkSize, sizeof(nChunkSize), 1);
4197 39 : CPL_LSBPTR32(&nChunkSize);
4198 39 : uint32_t nOffsetSize = 0;
4199 39 : poVirtualHandle->Read(&nOffsetSize, sizeof(nOffsetSize), 1);
4200 39 : CPL_LSBPTR32(&nOffsetSize);
4201 39 : uint64_t nUncompressedSize = 0;
4202 39 : poVirtualHandle->Read(&nUncompressedSize, sizeof(nUncompressedSize),
4203 39 : 1);
4204 39 : CPL_LSBPTR64(&nUncompressedSize);
4205 39 : uint64_t nCompressedSize = 0;
4206 39 : poVirtualHandle->Read(&nCompressedSize, sizeof(nCompressedSize), 1);
4207 39 : CPL_LSBPTR64(&nCompressedSize);
4208 :
4209 39 : info.nSOZIPVersion = nVersion;
4210 39 : info.nSOZIPToSkip = nToSkip;
4211 39 : info.nSOZIPChunkSize = nChunkSize;
4212 39 : info.nSOZIPOffsetSize = nOffsetSize;
4213 :
4214 39 : bool bValid = true;
4215 39 : if (nVersion != 1)
4216 : {
4217 0 : CPLDebug("SOZIP", "version = %u, expected 1", nVersion);
4218 0 : bValid = false;
4219 : }
4220 39 : if (nCompressedSize != file_info.compressed_size)
4221 : {
4222 0 : CPLDebug("SOZIP",
4223 : "compressedSize field inconsistent with file");
4224 0 : bValid = false;
4225 : }
4226 39 : if (nUncompressedSize != file_info.uncompressed_size)
4227 : {
4228 0 : CPLDebug("SOZIP",
4229 : "uncompressedSize field inconsistent with file");
4230 0 : bValid = false;
4231 : }
4232 39 : if (!(nChunkSize > 0 && nChunkSize < 100 * 1024 * 1024))
4233 : {
4234 0 : CPLDebug("SOZIP", "invalid chunkSize = %u", nChunkSize);
4235 0 : bValid = false;
4236 : }
4237 39 : if (nOffsetSize != 8)
4238 : {
4239 0 : CPLDebug("SOZIP", "invalid offsetSize = %u", nOffsetSize);
4240 0 : bValid = false;
4241 : }
4242 39 : if (file_info2.compression_method != 0)
4243 : {
4244 0 : CPLDebug("SOZIP", "unexpected compression_method = %u",
4245 0 : static_cast<unsigned>(file_info2.compression_method));
4246 0 : bValid = false;
4247 : }
4248 39 : if (bValid)
4249 : {
4250 39 : const auto nExpectedIndexSize =
4251 39 : 32 + static_cast<uint64_t>(nToSkip) +
4252 39 : ((nUncompressedSize - 1) / nChunkSize) * nOffsetSize;
4253 39 : if (nExpectedIndexSize != file_info2.uncompressed_size)
4254 : {
4255 0 : CPLDebug("SOZIP", "invalid file size for index");
4256 0 : bValid = false;
4257 : }
4258 : }
4259 39 : if (bValid)
4260 : {
4261 39 : info.bSOZipIndexValid = true;
4262 39 : CPLDebug("SOZIP", "Found valid SOZIP index: %s",
4263 : osAuxName.c_str());
4264 : }
4265 : else
4266 : {
4267 0 : CPLDebug("SOZIP", "Found *invalid* SOZIP index: %s",
4268 : osAuxName.c_str());
4269 : }
4270 : }
4271 : }
4272 :
4273 4200 : cpl_unzCloseCurrentFile(unzF);
4274 :
4275 4200 : info.poVirtualHandle = std::move(poVirtualHandle);
4276 :
4277 4200 : return true;
4278 : }
4279 :
4280 : /************************************************************************/
4281 : /* Open() */
4282 : /************************************************************************/
4283 :
4284 : VSIVirtualHandleUniquePtr
4285 4964 : VSIZipFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
4286 : bool bSetError, CSLConstList /* papszOptions */)
4287 : {
4288 :
4289 4964 : if (strchr(pszAccess, 'w') != nullptr)
4290 : {
4291 608 : return OpenForWrite(pszFilename, pszAccess);
4292 : }
4293 :
4294 4356 : if (strchr(pszAccess, '+') != nullptr)
4295 : {
4296 0 : CPLError(CE_Failure, CPLE_AppDefined,
4297 : "Read-write random access not supported for /vsizip");
4298 0 : return nullptr;
4299 : }
4300 :
4301 8712 : VSIFileInZipInfo info;
4302 4356 : if (!GetFileInfo(pszFilename, info, bSetError))
4303 229 : return nullptr;
4304 :
4305 : #ifdef ENABLE_DEFLATE64
4306 4127 : if (info.nCompressionMethod == 9)
4307 : {
4308 : auto poGZIPHandle = std::make_unique<VSIDeflate64Handle>(
4309 1 : std::move(info.poVirtualHandle), nullptr, info.nStartDataStream,
4310 2 : info.nCompressedSize, info.nUncompressedSize, info.nCRC);
4311 1 : if (!(poGZIPHandle->IsInitOK()))
4312 : {
4313 0 : return nullptr;
4314 : }
4315 :
4316 : // Wrap the VSIGZipHandle inside a buffered reader that will
4317 : // improve dramatically performance when doing small backward
4318 : // seeks.
4319 : return VSIVirtualHandleUniquePtr(
4320 1 : VSICreateBufferedReaderHandle(poGZIPHandle.release()));
4321 : }
4322 : else
4323 : #endif
4324 : {
4325 4126 : if (info.bSOZipIndexValid)
4326 : {
4327 : auto poSOZIPHandle = std::make_unique<VSISOZipHandle>(
4328 21 : std::move(info.poVirtualHandle), info.nStartDataStream,
4329 : info.nCompressedSize, info.nUncompressedSize,
4330 42 : info.nSOZIPStartData, info.nSOZIPToSkip, info.nSOZIPChunkSize);
4331 21 : if (!poSOZIPHandle->IsOK())
4332 : {
4333 0 : return nullptr;
4334 : }
4335 : return VSIVirtualHandleUniquePtr(VSICreateCachedFile(
4336 21 : poSOZIPHandle.release(), info.nSOZIPChunkSize, 0));
4337 : }
4338 :
4339 : auto poGZIPHandle = std::make_unique<VSIGZipHandle>(
4340 0 : std::move(info.poVirtualHandle), nullptr, info.nStartDataStream,
4341 : info.nCompressedSize, info.nUncompressedSize, info.nCRC,
4342 8210 : info.nCompressionMethod == 0);
4343 4105 : if (!(poGZIPHandle->IsInitOK()))
4344 : {
4345 0 : return nullptr;
4346 : }
4347 :
4348 : // Wrap the VSIGZipHandle inside a buffered reader that will
4349 : // improve dramatically performance when doing small backward
4350 : // seeks.
4351 : return VSIVirtualHandleUniquePtr(
4352 4105 : VSICreateBufferedReaderHandle(poGZIPHandle.release()));
4353 : }
4354 : }
4355 :
4356 : /************************************************************************/
4357 : /* GetFileMetadata() */
4358 : /************************************************************************/
4359 :
4360 73 : char **VSIZipFilesystemHandler::GetFileMetadata(const char *pszFilename,
4361 : const char *pszDomain,
4362 : CSLConstList /*papszOptions*/)
4363 : {
4364 146 : VSIFileInZipInfo info;
4365 73 : if (!GetFileInfo(pszFilename, info, true))
4366 0 : return nullptr;
4367 :
4368 73 : if (!pszDomain)
4369 : {
4370 10 : CPLStringList aosMetadata;
4371 9 : for (const auto &kv : info.oMapProperties)
4372 : {
4373 4 : aosMetadata.AddNameValue(kv.first.c_str(), kv.second.c_str());
4374 : }
4375 5 : return aosMetadata.StealList();
4376 : }
4377 68 : else if (EQUAL(pszDomain, "ZIP"))
4378 : {
4379 136 : CPLStringList aosMetadata;
4380 : aosMetadata.SetNameValue(
4381 : "START_DATA_OFFSET",
4382 : CPLSPrintf(CPL_FRMT_GUIB,
4383 68 : static_cast<GUIntBig>(info.nStartDataStream)));
4384 :
4385 68 : if (info.nCompressionMethod == 0)
4386 0 : aosMetadata.SetNameValue("COMPRESSION_METHOD", "0 (STORED)");
4387 68 : else if (info.nCompressionMethod == 8)
4388 68 : aosMetadata.SetNameValue("COMPRESSION_METHOD", "8 (DEFLATE)");
4389 : else
4390 : {
4391 : aosMetadata.SetNameValue("COMPRESSION_METHOD",
4392 0 : CPLSPrintf("%d", info.nCompressionMethod));
4393 : }
4394 : aosMetadata.SetNameValue(
4395 : "COMPRESSED_SIZE",
4396 : CPLSPrintf(CPL_FRMT_GUIB,
4397 68 : static_cast<GUIntBig>(info.nCompressedSize)));
4398 : aosMetadata.SetNameValue(
4399 : "UNCOMPRESSED_SIZE",
4400 : CPLSPrintf(CPL_FRMT_GUIB,
4401 68 : static_cast<GUIntBig>(info.nUncompressedSize)));
4402 :
4403 68 : if (info.bSOZipIndexFound)
4404 : {
4405 16 : aosMetadata.SetNameValue("SOZIP_FOUND", "YES");
4406 :
4407 : aosMetadata.SetNameValue("SOZIP_VERSION",
4408 16 : CPLSPrintf("%u", info.nSOZIPVersion));
4409 :
4410 : aosMetadata.SetNameValue("SOZIP_OFFSET_SIZE",
4411 16 : CPLSPrintf("%u", info.nSOZIPOffsetSize));
4412 :
4413 : aosMetadata.SetNameValue("SOZIP_CHUNK_SIZE",
4414 16 : CPLSPrintf("%u", info.nSOZIPChunkSize));
4415 :
4416 : aosMetadata.SetNameValue(
4417 : "SOZIP_START_DATA_OFFSET",
4418 : CPLSPrintf(CPL_FRMT_GUIB,
4419 16 : static_cast<GUIntBig>(info.nSOZIPStartData)));
4420 :
4421 16 : if (info.bSOZipIndexValid)
4422 : {
4423 16 : aosMetadata.SetNameValue("SOZIP_VALID", "YES");
4424 : }
4425 : }
4426 :
4427 68 : return aosMetadata.StealList();
4428 : }
4429 0 : return nullptr;
4430 : }
4431 :
4432 : /************************************************************************/
4433 : /* Mkdir() */
4434 : /************************************************************************/
4435 :
4436 46 : int VSIZipFilesystemHandler::Mkdir(const char *pszDirname, long /* nMode */)
4437 : {
4438 46 : CPLString osDirname = pszDirname;
4439 46 : if (!osDirname.empty() && osDirname.back() != '/')
4440 46 : osDirname += "/";
4441 92 : return OpenForWrite(osDirname, "wb") != nullptr ? 0 : -1;
4442 : }
4443 :
4444 : /************************************************************************/
4445 : /* ReadDirEx() */
4446 : /************************************************************************/
4447 :
4448 1465 : char **VSIZipFilesystemHandler::ReadDirEx(const char *pszDirname, int nMaxFiles)
4449 : {
4450 2930 : CPLString osInArchiveSubDir;
4451 : char *zipFilename =
4452 1465 : SplitFilename(pszDirname, osInArchiveSubDir, true, true);
4453 1465 : if (zipFilename == nullptr)
4454 73 : return nullptr;
4455 :
4456 : {
4457 1392 : std::unique_lock oLock(oMutex);
4458 :
4459 1392 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end())
4460 : {
4461 1 : CPLError(CE_Failure, CPLE_AppDefined,
4462 : "Cannot read a zip file being written");
4463 1 : CPLFree(zipFilename);
4464 1 : return nullptr;
4465 : }
4466 : }
4467 1391 : CPLFree(zipFilename);
4468 :
4469 1391 : return VSIArchiveFilesystemHandler::ReadDirEx(pszDirname, nMaxFiles);
4470 : }
4471 :
4472 : /************************************************************************/
4473 : /* Stat() */
4474 : /************************************************************************/
4475 :
4476 3502 : int VSIZipFilesystemHandler::Stat(const char *pszFilename,
4477 : VSIStatBufL *pStatBuf, int nFlags)
4478 : {
4479 7004 : CPLString osInArchiveSubDir;
4480 :
4481 3502 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
4482 :
4483 7004 : char *zipFilename = SplitFilename(pszFilename, osInArchiveSubDir, true,
4484 3502 : (nFlags & VSI_STAT_SET_ERROR_FLAG) != 0);
4485 3502 : if (zipFilename == nullptr)
4486 90 : return -1;
4487 :
4488 : {
4489 3412 : std::unique_lock oLock(oMutex);
4490 :
4491 3412 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end())
4492 : {
4493 0 : CPLError(CE_Failure, CPLE_AppDefined,
4494 : "Cannot read a zip file being written");
4495 0 : CPLFree(zipFilename);
4496 0 : return -1;
4497 : }
4498 : }
4499 3412 : CPLFree(zipFilename);
4500 :
4501 3412 : return VSIArchiveFilesystemHandler::Stat(pszFilename, pStatBuf, nFlags);
4502 : }
4503 :
4504 : /************************************************************************/
4505 : /* RemoveFromMap() */
4506 : /************************************************************************/
4507 :
4508 412 : void VSIZipFilesystemHandler::RemoveFromMap(VSIZipWriteHandle *poHandle)
4509 : {
4510 824 : std::unique_lock oLock(oMutex);
4511 :
4512 0 : for (std::map<CPLString, VSIZipWriteHandle *>::iterator iter =
4513 412 : oMapZipWriteHandles.begin();
4514 412 : iter != oMapZipWriteHandles.end(); ++iter)
4515 : {
4516 412 : if (iter->second == poHandle)
4517 : {
4518 412 : oMapZipWriteHandles.erase(iter);
4519 412 : break;
4520 : }
4521 : }
4522 412 : }
4523 :
4524 : /************************************************************************/
4525 : /* OpenForWrite() */
4526 : /************************************************************************/
4527 :
4528 : VSIVirtualHandleUniquePtr
4529 654 : VSIZipFilesystemHandler::OpenForWrite(const char *pszFilename,
4530 : const char *pszAccess)
4531 : {
4532 1308 : std::unique_lock oLock(oMutex);
4533 1308 : return OpenForWrite_unlocked(pszFilename, pszAccess);
4534 : }
4535 :
4536 : VSIVirtualHandleUniquePtr
4537 1020 : VSIZipFilesystemHandler::OpenForWrite_unlocked(const char *pszFilename,
4538 : const char *pszAccess)
4539 : {
4540 2040 : CPLString osZipInFileName;
4541 :
4542 : char *zipFilename =
4543 1020 : SplitFilename(pszFilename, osZipInFileName, false, false);
4544 1020 : if (zipFilename == nullptr)
4545 0 : return nullptr;
4546 2040 : CPLString osZipFilename = zipFilename;
4547 1020 : CPLFree(zipFilename);
4548 1020 : zipFilename = nullptr;
4549 :
4550 : // Invalidate cached file list.
4551 1020 : auto iter = oFileList.find(osZipFilename);
4552 1020 : if (iter != oFileList.end())
4553 : {
4554 15 : oFileList.erase(iter);
4555 : }
4556 :
4557 1020 : auto oIter = oMapZipWriteHandles.find(osZipFilename);
4558 1020 : if (oIter != oMapZipWriteHandles.end())
4559 : {
4560 606 : if (strchr(pszAccess, '+') != nullptr)
4561 : {
4562 0 : CPLError(
4563 : CE_Failure, CPLE_AppDefined,
4564 : "Random access not supported for writable file in /vsizip");
4565 0 : return nullptr;
4566 : }
4567 :
4568 606 : VSIZipWriteHandle *poZIPHandle = oIter->second;
4569 :
4570 606 : if (poZIPHandle->GetChildInWriting() != nullptr)
4571 : {
4572 1 : CPLError(CE_Failure, CPLE_AppDefined,
4573 : "Cannot create %s while another file is being "
4574 : "written in the .zip",
4575 : osZipInFileName.c_str());
4576 1 : return nullptr;
4577 : }
4578 :
4579 605 : poZIPHandle->StopCurrentFile();
4580 :
4581 : // Re-add path separator when creating directories.
4582 605 : char chLastChar = pszFilename[strlen(pszFilename) - 1];
4583 605 : if (chLastChar == '/' || chLastChar == '\\')
4584 47 : osZipInFileName += chLastChar;
4585 :
4586 605 : if (CPLCreateFileInZip(poZIPHandle->GetHandle(), osZipInFileName,
4587 605 : nullptr) != CE_None)
4588 59 : return nullptr;
4589 :
4590 : auto poChildHandle =
4591 1092 : std::make_unique<VSIZipWriteHandle>(this, nullptr, poZIPHandle);
4592 :
4593 546 : poZIPHandle->StartNewFile(poChildHandle.get());
4594 :
4595 546 : return VSIVirtualHandleUniquePtr(poChildHandle.release());
4596 : }
4597 : else
4598 : {
4599 414 : char **papszOptions = nullptr;
4600 828 : if ((strchr(pszAccess, '+') && osZipInFileName.empty()) ||
4601 414 : !osZipInFileName.empty())
4602 : {
4603 : VSIStatBufL sBuf;
4604 366 : if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0)
4605 227 : papszOptions = CSLAddNameValue(papszOptions, "APPEND", "TRUE");
4606 : }
4607 :
4608 414 : void *hZIP = CPLCreateZip(osZipFilename, papszOptions);
4609 414 : CSLDestroy(papszOptions);
4610 :
4611 414 : if (hZIP == nullptr)
4612 2 : return nullptr;
4613 :
4614 412 : auto poHandle = new VSIZipWriteHandle(this, hZIP, nullptr);
4615 412 : oMapZipWriteHandles[osZipFilename] = poHandle;
4616 :
4617 412 : if (!osZipInFileName.empty())
4618 : {
4619 : auto poRes = std::unique_ptr<VSIZipWriteHandle>(
4620 : cpl::down_cast<VSIZipWriteHandle *>(
4621 732 : OpenForWrite_unlocked(pszFilename, pszAccess).release()));
4622 366 : if (poRes == nullptr)
4623 : {
4624 58 : delete poHandle;
4625 58 : oMapZipWriteHandles.erase(osZipFilename);
4626 58 : return nullptr;
4627 : }
4628 :
4629 308 : poRes->SetAutoDeleteParent();
4630 :
4631 308 : return VSIVirtualHandleUniquePtr(poRes.release());
4632 : }
4633 :
4634 46 : return VSIVirtualHandleUniquePtr(poHandle);
4635 : }
4636 : }
4637 :
4638 : /************************************************************************/
4639 : /* GetOptions() */
4640 : /************************************************************************/
4641 :
4642 1 : const char *VSIZipFilesystemHandler::GetOptions()
4643 : {
4644 : return "<Options>"
4645 : " <Option name='GDAL_NUM_THREADS' type='string' "
4646 : "description='Number of threads for compression. Either a integer "
4647 : "or ALL_CPUS'/>"
4648 : " <Option name='CPL_VSIL_DEFLATE_CHUNK_SIZE' type='string' "
4649 : "description='Chunk of uncompressed data for parallelization. "
4650 : "Use K(ilobytes) or M(egabytes) suffix' default='1M'/>"
4651 1 : "</Options>";
4652 : }
4653 :
4654 : /************************************************************************/
4655 : /* CopyFile() */
4656 : /************************************************************************/
4657 :
4658 36 : int VSIZipFilesystemHandler::CopyFile(const char *pszSource,
4659 : const char *pszTarget, VSILFILE *fpSource,
4660 : vsi_l_offset /* nSourceSize */,
4661 : CSLConstList papszOptions,
4662 : GDALProgressFunc pProgressFunc,
4663 : void *pProgressData)
4664 : {
4665 72 : CPLString osZipInFileName;
4666 :
4667 36 : char *zipFilename = SplitFilename(pszTarget, osZipInFileName, false, false);
4668 36 : if (zipFilename == nullptr)
4669 0 : return -1;
4670 72 : CPLString osZipFilename = zipFilename;
4671 36 : CPLFree(zipFilename);
4672 36 : zipFilename = nullptr;
4673 36 : if (osZipInFileName.empty())
4674 : {
4675 0 : CPLError(CE_Failure, CPLE_AppDefined,
4676 : "Target filename should be of the form "
4677 : "/vsizip/path_to.zip/filename_within_zip");
4678 0 : return -1;
4679 : }
4680 :
4681 : // Invalidate cached file list.
4682 36 : auto oIterFileList = oFileList.find(osZipFilename);
4683 36 : if (oIterFileList != oFileList.end())
4684 : {
4685 2 : oFileList.erase(oIterFileList);
4686 : }
4687 :
4688 36 : const auto oIter = oMapZipWriteHandles.find(osZipFilename);
4689 36 : if (oIter != oMapZipWriteHandles.end())
4690 : {
4691 26 : VSIZipWriteHandle *poZIPHandle = oIter->second;
4692 :
4693 26 : if (poZIPHandle->GetChildInWriting() != nullptr)
4694 : {
4695 0 : CPLError(CE_Failure, CPLE_AppDefined,
4696 : "Cannot create %s while another file is being "
4697 : "written in the .zip",
4698 : osZipInFileName.c_str());
4699 0 : return -1;
4700 : }
4701 :
4702 26 : if (CPLAddFileInZip(poZIPHandle->GetHandle(), osZipInFileName.c_str(),
4703 : pszSource, fpSource, papszOptions, pProgressFunc,
4704 26 : pProgressData) != CE_None)
4705 : {
4706 0 : return -1;
4707 : }
4708 26 : return 0;
4709 : }
4710 : else
4711 : {
4712 20 : CPLStringList aosOptionsCreateZip;
4713 : VSIStatBufL sBuf;
4714 10 : if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0)
4715 3 : aosOptionsCreateZip.SetNameValue("APPEND", "TRUE");
4716 :
4717 10 : void *hZIP = CPLCreateZip(osZipFilename, aosOptionsCreateZip.List());
4718 :
4719 10 : if (hZIP == nullptr)
4720 0 : return -1;
4721 :
4722 10 : if (CPLAddFileInZip(hZIP, osZipInFileName.c_str(), pszSource, fpSource,
4723 : papszOptions, pProgressFunc,
4724 10 : pProgressData) != CE_None)
4725 : {
4726 2 : CPLCloseZip(hZIP);
4727 2 : return -1;
4728 : }
4729 8 : CPLCloseZip(hZIP);
4730 8 : return 0;
4731 : }
4732 : }
4733 :
4734 : /************************************************************************/
4735 : /* VSIZipWriteHandle() */
4736 : /************************************************************************/
4737 :
4738 958 : VSIZipWriteHandle::VSIZipWriteHandle(VSIZipFilesystemHandler *poFS, void *hZIP,
4739 958 : VSIZipWriteHandle *poParent)
4740 958 : : m_poFS(poFS), m_hZIP(hZIP), m_poParent(poParent)
4741 : {
4742 958 : }
4743 :
4744 : /************************************************************************/
4745 : /* ~VSIZipWriteHandle() */
4746 : /************************************************************************/
4747 :
4748 1916 : VSIZipWriteHandle::~VSIZipWriteHandle()
4749 : {
4750 958 : VSIZipWriteHandle::Close();
4751 1916 : }
4752 :
4753 : /************************************************************************/
4754 : /* Seek() */
4755 : /************************************************************************/
4756 :
4757 0 : int VSIZipWriteHandle::Seek(vsi_l_offset nOffset, int nWhence)
4758 : {
4759 0 : if (nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR))
4760 0 : return 0;
4761 0 : if (nOffset == nCurOffset && nWhence == SEEK_SET)
4762 0 : return 0;
4763 :
4764 0 : CPLError(CE_Failure, CPLE_NotSupported,
4765 : "VSIFSeekL() is not supported on writable Zip files");
4766 0 : return -1;
4767 : }
4768 :
4769 : /************************************************************************/
4770 : /* Tell() */
4771 : /************************************************************************/
4772 :
4773 0 : vsi_l_offset VSIZipWriteHandle::Tell()
4774 : {
4775 0 : return nCurOffset;
4776 : }
4777 :
4778 : /************************************************************************/
4779 : /* Read() */
4780 : /************************************************************************/
4781 :
4782 0 : size_t VSIZipWriteHandle::Read(void * /* pBuffer */, size_t /* nSize */,
4783 : size_t /* nMemb */)
4784 : {
4785 0 : CPLError(CE_Failure, CPLE_NotSupported,
4786 : "VSIFReadL() is not supported on writable Zip files");
4787 0 : return 0;
4788 : }
4789 :
4790 : /************************************************************************/
4791 : /* Write() */
4792 : /************************************************************************/
4793 :
4794 210920 : size_t VSIZipWriteHandle::Write(const void *pBuffer, size_t nSize, size_t nMemb)
4795 : {
4796 210920 : if (m_poParent == nullptr)
4797 : {
4798 0 : CPLError(CE_Failure, CPLE_NotSupported,
4799 : "VSIFWriteL() is not supported on "
4800 : "main Zip file or closed subfiles");
4801 0 : return 0;
4802 : }
4803 :
4804 210920 : const GByte *pabyBuffer = static_cast<const GByte *>(pBuffer);
4805 210920 : size_t nBytesToWrite = nSize * nMemb;
4806 210920 : size_t nWritten = 0;
4807 387376 : while (nWritten < nBytesToWrite)
4808 : {
4809 : int nToWrite = static_cast<int>(
4810 210920 : std::min(static_cast<size_t>(INT_MAX), nBytesToWrite));
4811 210920 : if (CPLWriteFileInZip(m_poParent->m_hZIP, pabyBuffer, nToWrite) !=
4812 : CE_None)
4813 34464 : return 0;
4814 176456 : nWritten += nToWrite;
4815 176456 : pabyBuffer += nToWrite;
4816 : }
4817 :
4818 176456 : nCurOffset += nSize * nMemb;
4819 :
4820 176456 : return nMemb;
4821 : }
4822 :
4823 : /************************************************************************/
4824 : /* Flush() */
4825 : /************************************************************************/
4826 :
4827 13 : int VSIZipWriteHandle::Flush()
4828 : {
4829 : /*CPLError(CE_Failure, CPLE_NotSupported,
4830 : "VSIFFlushL() is not supported on writable Zip files");*/
4831 13 : return 0;
4832 : }
4833 :
4834 : /************************************************************************/
4835 : /* Close() */
4836 : /************************************************************************/
4837 :
4838 1877 : int VSIZipWriteHandle::Close()
4839 : {
4840 1877 : int nRet = 0;
4841 1877 : if (m_poParent)
4842 : {
4843 546 : CPLCloseFileInZip(m_poParent->m_hZIP);
4844 546 : m_poParent->poChildInWriting = nullptr;
4845 546 : if (bAutoDeleteParent)
4846 : {
4847 308 : if (m_poParent->Close() != 0)
4848 71 : nRet = -1;
4849 308 : delete m_poParent;
4850 : }
4851 546 : m_poParent = nullptr;
4852 : }
4853 1877 : if (poChildInWriting)
4854 : {
4855 0 : if (poChildInWriting->Close() != 0)
4856 0 : nRet = -1;
4857 0 : poChildInWriting = nullptr;
4858 : }
4859 1877 : if (m_hZIP)
4860 : {
4861 412 : if (CPLCloseZip(m_hZIP) != CE_None)
4862 94 : nRet = -1;
4863 412 : m_hZIP = nullptr;
4864 :
4865 412 : m_poFS->RemoveFromMap(this);
4866 : }
4867 :
4868 1877 : return nRet;
4869 : }
4870 :
4871 : /************************************************************************/
4872 : /* StopCurrentFile() */
4873 : /************************************************************************/
4874 :
4875 605 : void VSIZipWriteHandle::StopCurrentFile()
4876 : {
4877 605 : if (poChildInWriting)
4878 0 : poChildInWriting->Close();
4879 605 : poChildInWriting = nullptr;
4880 605 : }
4881 :
4882 : /************************************************************************/
4883 : /* StartNewFile() */
4884 : /************************************************************************/
4885 :
4886 546 : void VSIZipWriteHandle::StartNewFile(VSIZipWriteHandle *poSubFile)
4887 : {
4888 546 : poChildInWriting = poSubFile;
4889 546 : }
4890 :
4891 : //! @endcond
4892 :
4893 : /************************************************************************/
4894 : /* VSIInstallZipFileHandler() */
4895 : /************************************************************************/
4896 :
4897 : /*!
4898 : \brief Install ZIP file system handler.
4899 :
4900 : A special file handler is installed that allows reading on-the-fly in ZIP
4901 : (.zip) archives.
4902 :
4903 : All portions of the file system underneath the base path "/vsizip/" will be
4904 : handled by this driver.
4905 :
4906 : \verbatim embed:rst
4907 : See :ref:`/vsizip/ documentation <vsizip>`
4908 : \endverbatim
4909 :
4910 : @since GDAL 1.6.0
4911 : */
4912 :
4913 1754 : void VSIInstallZipFileHandler()
4914 : {
4915 1754 : VSIFileManager::InstallHandler("/vsizip/", new VSIZipFilesystemHandler());
4916 1754 : }
4917 :
4918 : /************************************************************************/
4919 : /* CPLZLibDeflate() */
4920 : /************************************************************************/
4921 :
4922 : /**
4923 : * \brief Compress a buffer with ZLib compression.
4924 : *
4925 : * @param ptr input buffer.
4926 : * @param nBytes size of input buffer in bytes.
4927 : * @param nLevel ZLib compression level (-1 for default).
4928 : * @param outptr output buffer, or NULL to let the function allocate it.
4929 : * @param nOutAvailableBytes size of output buffer if provided, or ignored.
4930 : * @param pnOutBytes pointer to a size_t, where to store the size of the
4931 : * output buffer.
4932 : *
4933 : * @return the output buffer (to be freed with VSIFree() if not provided)
4934 : * or NULL in case of error.
4935 : *
4936 : * @since GDAL 1.10.0
4937 : */
4938 :
4939 2247 : void *CPLZLibDeflate(const void *ptr, size_t nBytes, int nLevel, void *outptr,
4940 : size_t nOutAvailableBytes, size_t *pnOutBytes)
4941 : {
4942 2247 : if (pnOutBytes != nullptr)
4943 2245 : *pnOutBytes = 0;
4944 :
4945 2247 : size_t nTmpSize = 0;
4946 : void *pTmp;
4947 : #ifdef HAVE_LIBDEFLATE
4948 : struct libdeflate_compressor *enc =
4949 2247 : libdeflate_alloc_compressor(nLevel < 0 ? 7 : nLevel);
4950 2230 : if (enc == nullptr)
4951 : {
4952 0 : return nullptr;
4953 : }
4954 : #endif
4955 2230 : if (outptr == nullptr)
4956 : {
4957 : #ifdef HAVE_LIBDEFLATE
4958 1545 : nTmpSize = libdeflate_zlib_compress_bound(enc, nBytes);
4959 : #else
4960 : nTmpSize = 32 + nBytes * 2;
4961 : #endif
4962 1554 : pTmp = VSIMalloc(nTmpSize);
4963 1557 : if (pTmp == nullptr)
4964 : {
4965 : #ifdef HAVE_LIBDEFLATE
4966 0 : libdeflate_free_compressor(enc);
4967 : #endif
4968 0 : return nullptr;
4969 : }
4970 : }
4971 : else
4972 : {
4973 685 : pTmp = outptr;
4974 685 : nTmpSize = nOutAvailableBytes;
4975 : }
4976 :
4977 : #ifdef HAVE_LIBDEFLATE
4978 : size_t nCompressedBytes =
4979 2242 : libdeflate_zlib_compress(enc, ptr, nBytes, pTmp, nTmpSize);
4980 2224 : libdeflate_free_compressor(enc);
4981 2228 : if (nCompressedBytes == 0)
4982 : {
4983 1 : if (pTmp != outptr)
4984 0 : VSIFree(pTmp);
4985 1 : return nullptr;
4986 : }
4987 2227 : if (pnOutBytes != nullptr)
4988 2233 : *pnOutBytes = nCompressedBytes;
4989 : #else
4990 : z_stream strm;
4991 : strm.zalloc = nullptr;
4992 : strm.zfree = nullptr;
4993 : strm.opaque = nullptr;
4994 : int ret = deflateInit(&strm, nLevel < 0 ? Z_DEFAULT_COMPRESSION : nLevel);
4995 : if (ret != Z_OK)
4996 : {
4997 : if (pTmp != outptr)
4998 : VSIFree(pTmp);
4999 : return nullptr;
5000 : }
5001 :
5002 : strm.avail_in = static_cast<uInt>(nBytes);
5003 : strm.next_in = reinterpret_cast<Bytef *>(const_cast<void *>(ptr));
5004 : strm.avail_out = static_cast<uInt>(nTmpSize);
5005 : strm.next_out = reinterpret_cast<Bytef *>(pTmp);
5006 : ret = deflate(&strm, Z_FINISH);
5007 : if (ret != Z_STREAM_END)
5008 : {
5009 : if (pTmp != outptr)
5010 : VSIFree(pTmp);
5011 : return nullptr;
5012 : }
5013 : if (pnOutBytes != nullptr)
5014 : *pnOutBytes = nTmpSize - strm.avail_out;
5015 : deflateEnd(&strm);
5016 : #endif
5017 :
5018 2227 : return pTmp;
5019 : }
5020 :
5021 : /************************************************************************/
5022 : /* CPLZLibInflate() */
5023 : /************************************************************************/
5024 :
5025 : /**
5026 : * \brief Uncompress a buffer compressed with ZLib compression.
5027 : *
5028 : * @param ptr input buffer.
5029 : * @param nBytes size of input buffer in bytes.
5030 : * @param outptr output buffer, or NULL to let the function allocate it.
5031 : * @param nOutAvailableBytes size of output buffer if provided, or ignored.
5032 : * @param pnOutBytes pointer to a size_t, where to store the size of the
5033 : * output buffer.
5034 : *
5035 : * @return the output buffer (to be freed with VSIFree() if not provided)
5036 : * or NULL in case of error.
5037 : *
5038 : * @since GDAL 1.10.0
5039 : */
5040 :
5041 22300 : void *CPLZLibInflate(const void *ptr, size_t nBytes, void *outptr,
5042 : size_t nOutAvailableBytes, size_t *pnOutBytes)
5043 : {
5044 22300 : return CPLZLibInflateEx(ptr, nBytes, outptr, nOutAvailableBytes, false,
5045 21714 : pnOutBytes);
5046 : }
5047 :
5048 : /************************************************************************/
5049 : /* CPLZLibInflateEx() */
5050 : /************************************************************************/
5051 :
5052 : /**
5053 : * \brief Uncompress a buffer compressed with ZLib compression.
5054 : *
5055 : * @param ptr input buffer.
5056 : * @param nBytes size of input buffer in bytes.
5057 : * @param outptr output buffer, or NULL to let the function allocate it.
5058 : * @param nOutAvailableBytes size of output buffer if provided, or ignored.
5059 : * @param bAllowResizeOutptr whether the function is allowed to grow outptr
5060 : * (using VSIRealloc) if its initial capacity
5061 : * provided by nOutAvailableBytes is not
5062 : * large enough. Ignored if outptr is NULL.
5063 : * @param pnOutBytes pointer to a size_t, where to store the size of the
5064 : * output buffer.
5065 : *
5066 : * @return the output buffer (to be freed with VSIFree() if not provided)
5067 : * or NULL in case of error. If bAllowResizeOutptr is set to true,
5068 : * only the returned pointer should be freed by the caller, as outptr
5069 : * might have been reallocated or freed.
5070 : *
5071 : * @since GDAL 3.9.0
5072 : */
5073 :
5074 21425 : void *CPLZLibInflateEx(const void *ptr, size_t nBytes, void *outptr,
5075 : size_t nOutAvailableBytes, bool bAllowResizeOutptr,
5076 : size_t *pnOutBytes)
5077 : {
5078 21425 : if (pnOutBytes != nullptr)
5079 21206 : *pnOutBytes = 0;
5080 21425 : char *pszReallocatableBuf = nullptr;
5081 :
5082 : #ifdef HAVE_LIBDEFLATE
5083 21425 : if (outptr)
5084 : {
5085 19533 : struct libdeflate_decompressor *dec = libdeflate_alloc_decompressor();
5086 20505 : if (dec == nullptr)
5087 : {
5088 0 : if (bAllowResizeOutptr)
5089 0 : VSIFree(outptr);
5090 20034 : return nullptr;
5091 : }
5092 : enum libdeflate_result res;
5093 20505 : size_t nOutBytes = 0;
5094 20505 : if (nBytes > 2 && static_cast<const GByte *>(ptr)[0] == 0x1F &&
5095 11273 : static_cast<const GByte *>(ptr)[1] == 0x8B)
5096 : {
5097 11265 : res = libdeflate_gzip_decompress(dec, ptr, nBytes, outptr,
5098 : nOutAvailableBytes, &nOutBytes);
5099 : }
5100 : else
5101 : {
5102 9240 : res = libdeflate_zlib_decompress(dec, ptr, nBytes, outptr,
5103 : nOutAvailableBytes, &nOutBytes);
5104 : }
5105 20377 : if (pnOutBytes)
5106 20275 : *pnOutBytes = nOutBytes;
5107 20377 : libdeflate_free_decompressor(dec);
5108 20035 : if (res == LIBDEFLATE_INSUFFICIENT_SPACE && bAllowResizeOutptr)
5109 : {
5110 1 : if (nOutAvailableBytes >
5111 1 : (std::numeric_limits<size_t>::max() - 1) / 2)
5112 : {
5113 0 : VSIFree(outptr);
5114 0 : return nullptr;
5115 : }
5116 1 : size_t nOutBufSize = nOutAvailableBytes * 2;
5117 : pszReallocatableBuf = static_cast<char *>(
5118 1 : VSI_REALLOC_VERBOSE(outptr, nOutBufSize + 1));
5119 1 : if (!pszReallocatableBuf)
5120 : {
5121 0 : VSIFree(outptr);
5122 0 : return nullptr;
5123 : }
5124 1 : outptr = nullptr;
5125 1 : nOutAvailableBytes = nOutBufSize;
5126 : }
5127 20034 : else if (res != LIBDEFLATE_SUCCESS)
5128 : {
5129 0 : if (bAllowResizeOutptr)
5130 0 : VSIFree(outptr);
5131 0 : return nullptr;
5132 : }
5133 : else
5134 : {
5135 : // Nul-terminate if possible.
5136 20034 : if (nOutBytes < nOutAvailableBytes)
5137 : {
5138 5862 : static_cast<char *>(outptr)[nOutBytes] = '\0';
5139 : }
5140 20034 : return outptr;
5141 : }
5142 : }
5143 : #endif
5144 :
5145 : z_stream strm;
5146 1893 : memset(&strm, 0, sizeof(strm));
5147 1893 : strm.zalloc = nullptr;
5148 1893 : strm.zfree = nullptr;
5149 1893 : strm.opaque = nullptr;
5150 : int ret;
5151 : // MAX_WBITS + 32 mode which detects automatically gzip vs zlib
5152 : // encapsulation seems to be broken with
5153 : // /opt/intel/oneapi/intelpython/latest/lib/libz.so.1 from
5154 : // intel/oneapi-basekit Docker image
5155 1893 : if (nBytes > 2 && static_cast<const GByte *>(ptr)[0] == 0x1F &&
5156 5 : static_cast<const GByte *>(ptr)[1] == 0x8B)
5157 : {
5158 5 : ret = inflateInit2(&strm, MAX_WBITS + 16); // gzip
5159 : }
5160 : else
5161 : {
5162 1888 : ret = inflateInit2(&strm, MAX_WBITS); // zlib
5163 : }
5164 1749 : if (ret != Z_OK)
5165 : {
5166 0 : if (bAllowResizeOutptr)
5167 0 : VSIFree(outptr);
5168 0 : VSIFree(pszReallocatableBuf);
5169 0 : return nullptr;
5170 : }
5171 :
5172 1749 : size_t nOutBufSize = 0;
5173 1749 : char *pszOutBuf = nullptr;
5174 :
5175 : #ifdef HAVE_LIBDEFLATE
5176 1749 : if (pszReallocatableBuf)
5177 : {
5178 1 : pszOutBuf = pszReallocatableBuf;
5179 1 : nOutBufSize = nOutAvailableBytes;
5180 : }
5181 : else
5182 : #endif
5183 1748 : if (!outptr)
5184 : {
5185 1748 : if (nBytes > (std::numeric_limits<size_t>::max() - 1) / 2)
5186 : {
5187 0 : inflateEnd(&strm);
5188 0 : return nullptr;
5189 : }
5190 1748 : nOutBufSize = 2 * nBytes + 1;
5191 1748 : pszOutBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nOutBufSize));
5192 1748 : if (pszOutBuf == nullptr)
5193 : {
5194 0 : inflateEnd(&strm);
5195 0 : return nullptr;
5196 : }
5197 1748 : pszReallocatableBuf = pszOutBuf;
5198 1748 : bAllowResizeOutptr = true;
5199 : }
5200 : #ifndef HAVE_LIBDEFLATE
5201 : else
5202 : {
5203 : pszOutBuf = static_cast<char *>(outptr);
5204 : nOutBufSize = nOutAvailableBytes;
5205 : if (bAllowResizeOutptr)
5206 : pszReallocatableBuf = pszOutBuf;
5207 : }
5208 : #endif
5209 :
5210 1749 : strm.next_in = static_cast<Bytef *>(const_cast<void *>(ptr));
5211 1749 : strm.next_out = reinterpret_cast<Bytef *>(pszOutBuf);
5212 1749 : size_t nInBytesRemaining = nBytes;
5213 1749 : size_t nOutBytesRemaining = nOutBufSize;
5214 :
5215 : while (true)
5216 : {
5217 1784 : strm.avail_in = static_cast<uInt>(std::min<size_t>(
5218 1784 : nInBytesRemaining, std::numeric_limits<uInt>::max()));
5219 1784 : const auto avail_in_before = strm.avail_in;
5220 1784 : strm.avail_out = static_cast<uInt>(std::min<size_t>(
5221 1784 : nOutBytesRemaining, std::numeric_limits<uInt>::max()));
5222 1784 : const auto avail_out_before = strm.avail_out;
5223 1784 : ret = inflate(&strm, Z_FINISH);
5224 1784 : nInBytesRemaining -= (avail_in_before - strm.avail_in);
5225 1784 : nOutBytesRemaining -= (avail_out_before - strm.avail_out);
5226 :
5227 1784 : if (ret == Z_BUF_ERROR && strm.avail_out == 0)
5228 : {
5229 : #ifdef HAVE_LIBDEFLATE
5230 35 : CPLAssert(bAllowResizeOutptr);
5231 : #else
5232 : if (!bAllowResizeOutptr)
5233 : {
5234 : VSIFree(pszReallocatableBuf);
5235 : inflateEnd(&strm);
5236 : return nullptr;
5237 : }
5238 : #endif
5239 :
5240 35 : const size_t nAlreadyWritten = nOutBufSize - nOutBytesRemaining;
5241 35 : if (nOutBufSize > (std::numeric_limits<size_t>::max() - 1) / 2)
5242 : {
5243 0 : VSIFree(pszReallocatableBuf);
5244 0 : inflateEnd(&strm);
5245 0 : return nullptr;
5246 : }
5247 35 : nOutBufSize = nOutBufSize * 2 + 1;
5248 : char *pszNew = static_cast<char *>(
5249 35 : VSI_REALLOC_VERBOSE(pszReallocatableBuf, nOutBufSize));
5250 35 : if (!pszNew)
5251 : {
5252 0 : VSIFree(pszReallocatableBuf);
5253 0 : inflateEnd(&strm);
5254 0 : return nullptr;
5255 : }
5256 35 : pszOutBuf = pszNew;
5257 35 : pszReallocatableBuf = pszOutBuf;
5258 35 : nOutBytesRemaining = nOutBufSize - nAlreadyWritten;
5259 35 : strm.next_out =
5260 35 : reinterpret_cast<Bytef *>(pszOutBuf + nAlreadyWritten);
5261 : }
5262 1749 : else if (ret != Z_OK || nInBytesRemaining == 0)
5263 : break;
5264 35 : }
5265 :
5266 1749 : if (ret == Z_OK || ret == Z_STREAM_END)
5267 : {
5268 1748 : size_t nOutBytes = nOutBufSize - nOutBytesRemaining;
5269 : // Nul-terminate if possible.
5270 1748 : if (nOutBytes < nOutBufSize)
5271 : {
5272 1748 : pszOutBuf[nOutBytes] = '\0';
5273 : }
5274 1748 : inflateEnd(&strm);
5275 1748 : if (pnOutBytes != nullptr)
5276 1709 : *pnOutBytes = nOutBytes;
5277 1748 : return pszOutBuf;
5278 : }
5279 : else
5280 : {
5281 1 : VSIFree(pszReallocatableBuf);
5282 1 : inflateEnd(&strm);
5283 1 : return nullptr;
5284 : }
5285 : }
|