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