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