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