1 : /******************************************************************************
2 : * $Id: cpl_vsil_gzip.cpp 25393 2012-12-29 21:36:44Z rouault $
3 : *
4 : * Project: CPL - Common Portability Library
5 : * Purpose: Implement VSI large file api for gz/zip files (.gz and .zip).
6 : * Author: Even Rouault, even.rouault at mines-paris.org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Even Rouault
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : /* gzio.c -- IO on .gz files
31 : Copyright (C) 1995-2005 Jean-loup Gailly.
32 :
33 : This software is provided 'as-is', without any express or implied
34 : warranty. In no event will the authors be held liable for any damages
35 : arising from the use of this software.
36 :
37 : Permission is granted to anyone to use this software for any purpose,
38 : including commercial applications, and to alter it and redistribute it
39 : freely, subject to the following restrictions:
40 :
41 : 1. The origin of this software must not be misrepresented; you must not
42 : claim that you wrote the original software. If you use this software
43 : in a product, an acknowledgment in the product documentation would be
44 : appreciated but is not required.
45 : 2. Altered source versions must be plainly marked as such, and must not be
46 : misrepresented as being the original software.
47 : 3. This notice may not be removed or altered from any source distribution.
48 :
49 : Jean-loup Gailly Mark Adler
50 : jloup@gzip.org madler@alumni.caltech.edu
51 :
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 :
59 : /* This file contains a refactoring of gzio.c from zlib project.
60 :
61 : It replaces classical calls operating on FILE* by calls to the VSI large file
62 : API. It also adds the capability to seek at the end of the file, which is not
63 : implemented in original gzSeek. It also implements a concept of in-memory "snapshots",
64 : that are a way of improving efficiency while seeking GZip files. Snapshots are
65 : ceated regularly when decompressing the data a snapshot of the gzip state.
66 : Later we can seek directly in the compressed data to the closest snapshot in order to
67 : reduce the amount of data to uncompress again.
68 :
69 : For .gz files, an effort is done to cache the size of the uncompressed data in
70 : a .gz.properties file, so that we don't need to seek at the end of the file
71 : each time a Stat() is done.
72 :
73 : For .zip and .gz, both reading and writing are supported, but just one mode at a time
74 : (read-only or write-only)
75 : */
76 :
77 :
78 : #include "cpl_vsi_virtual.h"
79 : #include "cpl_string.h"
80 : #include "cpl_multiproc.h"
81 : #include <map>
82 :
83 : #include <zlib.h>
84 : #include "cpl_minizip_unzip.h"
85 : #include "cpl_time.h"
86 :
87 : CPL_CVSID("$Id: cpl_vsil_gzip.cpp 25393 2012-12-29 21:36:44Z rouault $");
88 :
89 : #define Z_BUFSIZE 65536 /* original size is 16384 */
90 : static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
91 :
92 : /* gzip flag byte */
93 : #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
94 : #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
95 : #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
96 : #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
97 : #define COMMENT 0x10 /* bit 4 set: file comment present */
98 : #define RESERVED 0xE0 /* bits 5..7: reserved */
99 :
100 : #define ALLOC(size) malloc(size)
101 : #define TRYFREE(p) {if (p) free(p);}
102 :
103 : #define CPL_VSIL_GZ_RETURN_MINUS_ONE() \
104 : CPLError(CE_Failure, CPLE_AppDefined, "In file %s, at line %d, return -1", __FILE__, __LINE__)
105 :
106 : #define ENABLE_DEBUG 0
107 :
108 : /************************************************************************/
109 : /* ==================================================================== */
110 : /* VSIGZipHandle */
111 : /* ==================================================================== */
112 : /************************************************************************/
113 :
114 : typedef struct
115 : {
116 : vsi_l_offset uncompressed_pos;
117 : z_stream stream;
118 : uLong crc;
119 : int transparent;
120 : vsi_l_offset in;
121 : vsi_l_offset out;
122 : } GZipSnapshot;
123 :
124 : class VSIGZipHandle : public VSIVirtualHandle
125 : {
126 : VSIVirtualHandle* poBaseHandle;
127 : vsi_l_offset offset;
128 : vsi_l_offset compressed_size;
129 : vsi_l_offset uncompressed_size;
130 : vsi_l_offset offsetEndCompressedData;
131 : unsigned int expected_crc;
132 : char *pszBaseFileName; /* optional */
133 :
134 : /* Fields from gz_stream structure */
135 : z_stream stream;
136 : int z_err; /* error code for last stream operation */
137 : int z_eof; /* set if end of input file */
138 : Byte *inbuf; /* input buffer */
139 : Byte *outbuf; /* output buffer */
140 : uLong crc; /* crc32 of uncompressed data */
141 : int transparent; /* 1 if input file is not a .gz file */
142 : vsi_l_offset startOff; /* startOff of compressed data in file (header skipped) */
143 : vsi_l_offset in; /* bytes into deflate or inflate */
144 : vsi_l_offset out; /* bytes out of deflate or inflate */
145 : vsi_l_offset nLastReadOffset;
146 :
147 : GZipSnapshot* snapshots;
148 : vsi_l_offset snapshot_byte_interval; /* number of compressed bytes at which we create a "snapshot" */
149 :
150 : void check_header();
151 : int get_byte();
152 : int gzseek( vsi_l_offset nOffset, int nWhence );
153 : int gzrewind ();
154 : uLong getLong ();
155 :
156 : public:
157 :
158 : VSIGZipHandle(VSIVirtualHandle* poBaseHandle,
159 : const char* pszBaseFileName,
160 : vsi_l_offset offset = 0,
161 : vsi_l_offset compressed_size = 0,
162 : vsi_l_offset uncompressed_size = 0,
163 : unsigned int expected_crc = 0,
164 : int transparent = 0);
165 : ~VSIGZipHandle();
166 :
167 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
168 : virtual vsi_l_offset Tell();
169 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
170 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
171 : virtual int Eof();
172 : virtual int Flush();
173 : virtual int Close();
174 :
175 : VSIGZipHandle* Duplicate();
176 : void CloseBaseHandle();
177 :
178 104 : vsi_l_offset GetLastReadOffset() { return nLastReadOffset; }
179 310 : const char* GetBaseFileName() { return pszBaseFileName; }
180 :
181 0 : void SetUncompressedSize(vsi_l_offset nUncompressedSize) { uncompressed_size = nUncompressedSize; }
182 5 : vsi_l_offset GetUncompressedSize() { return uncompressed_size; }
183 : };
184 :
185 :
186 : class VSIGZipFilesystemHandler : public VSIFilesystemHandler
187 : {
188 : void* hMutex;
189 : VSIGZipHandle* poHandleLastGZipFile;
190 : int bInSaveInfo;
191 :
192 : public:
193 : VSIGZipFilesystemHandler();
194 : ~VSIGZipFilesystemHandler();
195 :
196 : virtual VSIVirtualHandle *Open( const char *pszFilename,
197 : const char *pszAccess);
198 : VSIGZipHandle *OpenGZipReadOnly( const char *pszFilename,
199 : const char *pszAccess);
200 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags );
201 : virtual int Unlink( const char *pszFilename );
202 : virtual int Rename( const char *oldpath, const char *newpath );
203 : virtual int Mkdir( const char *pszDirname, long nMode );
204 : virtual int Rmdir( const char *pszDirname );
205 : virtual char **ReadDir( const char *pszDirname );
206 :
207 : void SaveInfo( VSIGZipHandle* poHandle );
208 : };
209 :
210 :
211 : /************************************************************************/
212 : /* Duplicate() */
213 : /************************************************************************/
214 :
215 52 : VSIGZipHandle* VSIGZipHandle::Duplicate()
216 : {
217 52 : CPLAssert (offset == 0);
218 52 : CPLAssert (compressed_size != 0);
219 52 : CPLAssert (pszBaseFileName != NULL);
220 :
221 : VSIFilesystemHandler *poFSHandler =
222 52 : VSIFileManager::GetHandler( pszBaseFileName );
223 :
224 : VSIVirtualHandle* poNewBaseHandle =
225 52 : poFSHandler->Open( pszBaseFileName, "rb" );
226 :
227 52 : if (poNewBaseHandle == NULL)
228 0 : return NULL;
229 :
230 : VSIGZipHandle* poHandle = new VSIGZipHandle(poNewBaseHandle,
231 : pszBaseFileName,
232 : 0,
233 : compressed_size,
234 52 : uncompressed_size);
235 :
236 52 : poHandle->nLastReadOffset = nLastReadOffset;
237 :
238 : /* Most important : duplicate the snapshots ! */
239 :
240 : unsigned int i;
241 55 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
242 : {
243 52 : if (snapshots[i].uncompressed_pos == 0)
244 49 : break;
245 :
246 3 : poHandle->snapshots[i].uncompressed_pos = snapshots[i].uncompressed_pos;
247 3 : inflateCopy( &poHandle->snapshots[i].stream, &snapshots[i].stream);
248 3 : poHandle->snapshots[i].crc = snapshots[i].crc;
249 3 : poHandle->snapshots[i].transparent = snapshots[i].transparent;
250 3 : poHandle->snapshots[i].in = snapshots[i].in;
251 3 : poHandle->snapshots[i].out = snapshots[i].out;
252 : }
253 :
254 52 : return poHandle;
255 : }
256 :
257 : /************************************************************************/
258 : /* CloseBaseHandle() */
259 : /************************************************************************/
260 :
261 11 : void VSIGZipHandle::CloseBaseHandle()
262 : {
263 11 : if (poBaseHandle)
264 11 : VSIFCloseL((VSILFILE*)poBaseHandle);
265 11 : poBaseHandle = NULL;
266 11 : }
267 :
268 : /************************************************************************/
269 : /* VSIGZipHandle() */
270 : /************************************************************************/
271 :
272 326 : VSIGZipHandle::VSIGZipHandle(VSIVirtualHandle* poBaseHandle,
273 : const char* pszBaseFileName,
274 : vsi_l_offset offset,
275 : vsi_l_offset compressed_size,
276 : vsi_l_offset uncompressed_size,
277 : unsigned int expected_crc,
278 326 : int transparent)
279 : {
280 326 : this->poBaseHandle = poBaseHandle;
281 326 : this->expected_crc = expected_crc;
282 326 : this->pszBaseFileName = (pszBaseFileName) ? CPLStrdup(pszBaseFileName) : NULL;
283 326 : this->offset = offset;
284 641 : if (compressed_size || transparent)
285 : {
286 315 : this->compressed_size = compressed_size;
287 : }
288 : else
289 : {
290 11 : VSIFSeekL((VSILFILE*)poBaseHandle, 0, SEEK_END);
291 11 : this->compressed_size = VSIFTellL((VSILFILE*)poBaseHandle) - offset;
292 11 : compressed_size = this->compressed_size;
293 : }
294 326 : this->uncompressed_size = uncompressed_size;
295 326 : offsetEndCompressedData = offset + compressed_size;
296 :
297 326 : VSIFSeekL((VSILFILE*)poBaseHandle, offset, SEEK_SET);
298 :
299 326 : nLastReadOffset = 0;
300 326 : stream.zalloc = (alloc_func)0;
301 326 : stream.zfree = (free_func)0;
302 326 : stream.opaque = (voidpf)0;
303 326 : stream.next_in = inbuf = Z_NULL;
304 326 : stream.next_out = outbuf = Z_NULL;
305 326 : stream.avail_in = stream.avail_out = 0;
306 326 : z_err = Z_OK;
307 326 : z_eof = 0;
308 326 : in = 0;
309 326 : out = 0;
310 326 : crc = crc32(0L, Z_NULL, 0);
311 326 : this->transparent = transparent;
312 :
313 326 : stream.next_in = inbuf = (Byte*)ALLOC(Z_BUFSIZE);
314 :
315 326 : int err = inflateInit2(&(stream), -MAX_WBITS);
316 : /* windowBits is passed < 0 to tell that there is no zlib header.
317 : * Note that in this case inflate *requires* an extra "dummy" byte
318 : * after the compressed stream in order to complete decompression and
319 : * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
320 : * present after the compressed stream.
321 : */
322 326 : if (err != Z_OK || inbuf == Z_NULL) {
323 0 : CPLError(CE_Failure, CPLE_NotSupported, "inflateInit2 init failed");
324 : }
325 326 : stream.avail_out = Z_BUFSIZE;
326 :
327 326 : if (offset == 0) check_header(); /* skip the .gz header */
328 326 : startOff = VSIFTellL((VSILFILE*)poBaseHandle) - stream.avail_in;
329 :
330 326 : if (transparent == 0)
331 : {
332 322 : snapshot_byte_interval = MAX(Z_BUFSIZE, compressed_size / 100);
333 322 : snapshots = (GZipSnapshot*)CPLCalloc(sizeof(GZipSnapshot), (size_t) (compressed_size / snapshot_byte_interval + 1));
334 : }
335 : else
336 : {
337 4 : snapshots = NULL;
338 : }
339 326 : }
340 :
341 : /************************************************************************/
342 : /* ~VSIGZipHandle() */
343 : /************************************************************************/
344 :
345 326 : VSIGZipHandle::~VSIGZipHandle()
346 : {
347 :
348 326 : if (pszBaseFileName)
349 : {
350 : VSIFilesystemHandler *poFSHandler =
351 63 : VSIFileManager::GetHandler( "/vsigzip/" );
352 63 : ((VSIGZipFilesystemHandler*)poFSHandler)->SaveInfo(this);
353 : }
354 :
355 326 : if (stream.state != NULL) {
356 326 : inflateEnd(&(stream));
357 : }
358 :
359 326 : TRYFREE(inbuf);
360 326 : TRYFREE(outbuf);
361 :
362 326 : if (snapshots != NULL)
363 : {
364 : unsigned int i;
365 671 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
366 : {
367 349 : if (snapshots[i].uncompressed_pos)
368 : {
369 279 : inflateEnd(&(snapshots[i].stream));
370 : }
371 : }
372 322 : CPLFree(snapshots);
373 : }
374 326 : CPLFree(pszBaseFileName);
375 :
376 326 : if (poBaseHandle)
377 315 : VSIFCloseL((VSILFILE*)poBaseHandle);
378 326 : }
379 :
380 : /************************************************************************/
381 : /* check_header() */
382 : /************************************************************************/
383 :
384 84 : void VSIGZipHandle::check_header()
385 : {
386 : int method; /* method byte */
387 : int flags; /* flags byte */
388 : uInt len;
389 : int c;
390 :
391 : /* Assure two bytes in the buffer so we can peek ahead -- handle case
392 : where first byte of header is at the end of the buffer after the last
393 : gzip segment */
394 84 : len = stream.avail_in;
395 84 : if (len < 2) {
396 84 : if (len) inbuf[0] = stream.next_in[0];
397 84 : errno = 0;
398 84 : len = (uInt)VSIFReadL(inbuf + len, 1, Z_BUFSIZE >> len, (VSILFILE*)poBaseHandle);
399 : if (ENABLE_DEBUG) CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
400 : VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData);
401 84 : if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData)
402 : {
403 0 : len = len + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle));
404 0 : VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
405 : }
406 84 : if (len == 0 /* && ferror(file)*/)
407 : {
408 21 : if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData)
409 0 : z_err = Z_ERRNO;
410 : }
411 84 : stream.avail_in += len;
412 84 : stream.next_in = inbuf;
413 84 : if (stream.avail_in < 2) {
414 21 : transparent = stream.avail_in;
415 21 : return;
416 : }
417 : }
418 :
419 : /* Peek ahead to check the gzip magic header */
420 189 : if (stream.next_in[0] != gz_magic[0] ||
421 126 : stream.next_in[1] != gz_magic[1]) {
422 0 : transparent = 1;
423 0 : return;
424 : }
425 63 : stream.avail_in -= 2;
426 63 : stream.next_in += 2;
427 :
428 : /* Check the rest of the gzip header */
429 63 : method = get_byte();
430 63 : flags = get_byte();
431 63 : if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
432 0 : z_err = Z_DATA_ERROR;
433 0 : return;
434 : }
435 :
436 : /* Discard time, xflags and OS code: */
437 63 : for (len = 0; len < 6; len++) (void)get_byte();
438 :
439 63 : if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
440 0 : len = (uInt)get_byte();
441 0 : len += ((uInt)get_byte())<<8;
442 : /* len is garbage if EOF but the loop below will quit anyway */
443 0 : while (len-- != 0 && get_byte() != EOF) ;
444 : }
445 63 : if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
446 0 : while ((c = get_byte()) != 0 && c != EOF) ;
447 : }
448 63 : if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
449 0 : while ((c = get_byte()) != 0 && c != EOF) ;
450 : }
451 63 : if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
452 0 : for (len = 0; len < 2; len++) (void)get_byte();
453 : }
454 63 : z_err = z_eof ? Z_DATA_ERROR : Z_OK;
455 : }
456 :
457 : /************************************************************************/
458 : /* get_byte() */
459 : /************************************************************************/
460 :
461 672 : int VSIGZipHandle::get_byte()
462 : {
463 672 : if (z_eof) return EOF;
464 672 : if (stream.avail_in == 0) {
465 0 : errno = 0;
466 0 : stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (VSILFILE*)poBaseHandle);
467 : if (ENABLE_DEBUG) CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
468 : VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData);
469 0 : if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData)
470 : {
471 0 : stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle));
472 0 : VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
473 : }
474 0 : if (stream.avail_in == 0) {
475 0 : z_eof = 1;
476 0 : if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData)
477 0 : z_err = Z_ERRNO;
478 : /*if (ferror(file)) z_err = Z_ERRNO;*/
479 0 : return EOF;
480 : }
481 0 : stream.next_in = inbuf;
482 : }
483 672 : stream.avail_in--;
484 672 : return *(stream.next_in)++;
485 : }
486 :
487 : /************************************************************************/
488 : /* gzrewind() */
489 : /************************************************************************/
490 :
491 90 : int VSIGZipHandle::gzrewind ()
492 : {
493 90 : z_err = Z_OK;
494 90 : z_eof = 0;
495 90 : stream.avail_in = 0;
496 90 : stream.next_in = inbuf;
497 90 : crc = crc32(0L, Z_NULL, 0);
498 90 : if (!transparent) (void)inflateReset(&stream);
499 90 : in = 0;
500 90 : out = 0;
501 90 : return VSIFSeekL((VSILFILE*)poBaseHandle, startOff, SEEK_SET);
502 : }
503 :
504 : /************************************************************************/
505 : /* Seek() */
506 : /************************************************************************/
507 :
508 566 : int VSIGZipHandle::Seek( vsi_l_offset nOffset, int nWhence )
509 : {
510 : /* The semantics of gzseek is different from ::Seek */
511 : /* It returns the current offset, where as ::Seek shoud return 0 */
512 : /* if successfull */
513 566 : int ret = gzseek(nOffset, nWhence);
514 566 : return (ret >= 0) ? 0 : ret;
515 : }
516 :
517 : /************************************************************************/
518 : /* gzseek() */
519 : /************************************************************************/
520 :
521 566 : int VSIGZipHandle::gzseek( vsi_l_offset offset, int whence )
522 : {
523 566 : vsi_l_offset original_offset = offset;
524 566 : int original_nWhence = whence;
525 :
526 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Seek(" CPL_FRMT_GUIB ",%d)", offset, whence);
527 :
528 566 : if (transparent)
529 : {
530 3 : stream.avail_in = 0;
531 3 : stream.next_in = inbuf;
532 3 : if (whence == SEEK_CUR)
533 : {
534 0 : if (out + offset > compressed_size)
535 : {
536 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
537 0 : return -1L;
538 : }
539 :
540 0 : offset = startOff + out + offset;
541 : }
542 3 : else if (whence == SEEK_SET)
543 : {
544 2 : if (offset > compressed_size)
545 : {
546 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
547 0 : return -1L;
548 : }
549 :
550 2 : offset = startOff + offset;
551 : }
552 1 : else if (whence == SEEK_END)
553 : {
554 : /* Commented test : because vsi_l_offset is unsigned (for the moment) */
555 : /* so no way to seek backward. See #1590 */
556 1 : if (offset > 0 /*|| -offset > compressed_size*/)
557 : {
558 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
559 0 : return -1L;
560 : }
561 :
562 1 : offset = startOff + compressed_size - offset;
563 : }
564 : else
565 : {
566 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
567 0 : return -1L;
568 : }
569 3 : if (VSIFSeekL((VSILFILE*)poBaseHandle, offset, SEEK_SET) < 0)
570 : {
571 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
572 0 : return -1L;
573 : }
574 :
575 3 : in = out = offset - startOff;
576 : if (ENABLE_DEBUG) CPLDebug("GZIP", "return " CPL_FRMT_GUIB, in);
577 3 : return (int) in;
578 : }
579 :
580 : /* whence == SEEK_END is unsuppored in original gzseek. */
581 563 : if (whence == SEEK_END)
582 : {
583 : /* If we known the uncompressed size, we can fake a jump to */
584 : /* the end of the stream */
585 63 : if (offset == 0 && uncompressed_size != 0)
586 : {
587 55 : out = uncompressed_size;
588 55 : return 1;
589 : }
590 :
591 : /* We don't know the uncompressed size. This is unfortunate. Let's do the slow version... */
592 : static int firstWarning = 1;
593 8 : if (compressed_size > 10 * 1024 * 1024 && firstWarning)
594 : {
595 : CPLError(CE_Warning, CPLE_AppDefined,
596 0 : "VSIFSeekL(xxx, SEEK_END) may be really slow on GZip streams.");
597 0 : firstWarning = 0;
598 : }
599 :
600 8 : whence = SEEK_CUR;
601 8 : offset = 1024 * 1024 * 1024;
602 8 : offset *= 1024 * 1024;
603 : }
604 :
605 508 : if (/*whence == SEEK_END ||*/
606 : z_err == Z_ERRNO || z_err == Z_DATA_ERROR) {
607 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
608 0 : return -1L;
609 : }
610 :
611 : /* Rest of function is for reading only */
612 :
613 : /* compute absolute position */
614 508 : if (whence == SEEK_CUR) {
615 8 : offset += out;
616 : }
617 :
618 : /* For a negative seek, rewind and use positive seek */
619 508 : if (offset >= out) {
620 418 : offset -= out;
621 90 : } else if (gzrewind() < 0) {
622 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
623 0 : return -1L;
624 : }
625 :
626 : unsigned int i;
627 508 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
628 : {
629 508 : if (snapshots[i].uncompressed_pos == 0)
630 442 : break;
631 69 : if (snapshots[i].out <= out + offset &&
632 3 : (i == compressed_size / snapshot_byte_interval || snapshots[i+1].out == 0 || snapshots[i+1].out > out+offset))
633 : {
634 66 : if (out >= snapshots[i].out)
635 66 : break;
636 :
637 : if (ENABLE_DEBUG)
638 : CPLDebug("SNAPSHOT", "using snapshot %d : uncompressed_pos(snapshot)=" CPL_FRMT_GUIB
639 : " in(snapshot)=" CPL_FRMT_GUIB
640 : " out(snapshot)=" CPL_FRMT_GUIB
641 : " out=" CPL_FRMT_GUIB
642 : " offset=" CPL_FRMT_GUIB,
643 : i, snapshots[i].uncompressed_pos, snapshots[i].in, snapshots[i].out, out, offset);
644 0 : offset = out + offset - snapshots[i].out;
645 0 : VSIFSeekL((VSILFILE*)poBaseHandle, snapshots[i].uncompressed_pos, SEEK_SET);
646 0 : inflateEnd(&stream);
647 0 : inflateCopy(&stream, &snapshots[i].stream);
648 0 : crc = snapshots[i].crc;
649 0 : transparent = snapshots[i].transparent;
650 0 : in = snapshots[i].in;
651 0 : out = snapshots[i].out;
652 0 : break;
653 : }
654 : }
655 :
656 : /* offset is now the number of bytes to skip. */
657 :
658 508 : if (offset != 0 && outbuf == Z_NULL) {
659 50 : outbuf = (Byte*)ALLOC(Z_BUFSIZE);
660 50 : if (outbuf == Z_NULL) {
661 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
662 0 : return -1L;
663 : }
664 : }
665 :
666 508 : if (original_nWhence == SEEK_END && z_err == Z_STREAM_END)
667 : {
668 : if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out);
669 2 : return (int) out;
670 : }
671 :
672 1213 : while (offset > 0) {
673 208 : int size = Z_BUFSIZE;
674 208 : if (offset < Z_BUFSIZE) size = (int)offset;
675 :
676 208 : int read_size = Read(outbuf, 1, (uInt)size);
677 208 : if (read_size == 0) {
678 : //CPL_VSIL_GZ_RETURN_MINUS_ONE();
679 1 : return -1L;
680 : }
681 207 : if (original_nWhence == SEEK_END)
682 : {
683 6 : if (size != read_size)
684 : {
685 6 : z_err = Z_STREAM_END;
686 6 : break;
687 : }
688 : }
689 201 : offset -= read_size;
690 : }
691 : if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out);
692 :
693 505 : if (original_offset == 0 && original_nWhence == SEEK_END)
694 : {
695 6 : uncompressed_size = out;
696 :
697 6 : if (pszBaseFileName)
698 : {
699 6 : CPLString osCacheFilename (pszBaseFileName);
700 6 : osCacheFilename += ".properties";
701 :
702 : /* Write a .properties file to avoid seeking next time */
703 6 : VSILFILE* fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "wb");
704 6 : if (fpCacheLength)
705 : {
706 : char* pszFirstNonSpace;
707 : char szBuffer[32];
708 6 : szBuffer[31] = 0;
709 :
710 6 : CPLPrintUIntBig(szBuffer, compressed_size, 31);
711 6 : pszFirstNonSpace = szBuffer;
712 6 : while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++;
713 6 : VSIFPrintfL(fpCacheLength, "compressed_size=%s\n", pszFirstNonSpace);
714 :
715 6 : CPLPrintUIntBig(szBuffer, uncompressed_size, 31);
716 6 : pszFirstNonSpace = szBuffer;
717 6 : while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++;
718 6 : VSIFPrintfL(fpCacheLength, "uncompressed_size=%s\n", pszFirstNonSpace);
719 :
720 6 : VSIFCloseL(fpCacheLength);
721 6 : }
722 : }
723 : }
724 :
725 505 : return (int) out;
726 : }
727 :
728 : /************************************************************************/
729 : /* Tell() */
730 : /************************************************************************/
731 :
732 64 : vsi_l_offset VSIGZipHandle::Tell()
733 : {
734 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Tell() = " CPL_FRMT_GUIB, out);
735 64 : return out;
736 : }
737 :
738 : /************************************************************************/
739 : /* Read() */
740 : /************************************************************************/
741 :
742 2130 : size_t VSIGZipHandle::Read( void *buf, size_t nSize, size_t nMemb )
743 : {
744 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read(%p, %d, %d)", buf, (int)nSize, (int)nMemb);
745 :
746 2130 : unsigned len = nSize * nMemb;
747 :
748 2130 : Bytef *pStart = (Bytef*)buf; /* startOffing point for crc computation */
749 : Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
750 :
751 2130 : if (z_err == Z_DATA_ERROR || z_err == Z_ERRNO)
752 : {
753 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
754 0 : return 0;
755 : }
756 2130 : if (z_eof || z_err == Z_STREAM_END)
757 : {
758 119 : z_eof = 1;
759 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read: Eof");
760 119 : return 0; /* EOF */
761 : }
762 :
763 2011 : next_out = (Byte*)buf;
764 2011 : stream.next_out = (Bytef*)buf;
765 2011 : stream.avail_out = len;
766 :
767 5777 : while (stream.avail_out != 0) {
768 :
769 2014 : if (transparent) {
770 : /* Copy first the lookahead bytes: */
771 2 : uInt nRead = 0;
772 2 : uInt n = stream.avail_in;
773 2 : if (n > stream.avail_out) n = stream.avail_out;
774 2 : if (n > 0) {
775 0 : memcpy (stream.next_out, stream.next_in, n);
776 0 : next_out += n;
777 0 : stream.next_out = next_out;
778 0 : stream.next_in += n;
779 0 : stream.avail_out -= n;
780 0 : stream.avail_in -= n;
781 0 : nRead += n;
782 : }
783 2 : if (stream.avail_out > 0) {
784 2 : uInt nToRead = (uInt) MIN(compressed_size - (in + nRead), stream.avail_out);
785 : uInt nReadFromFile =
786 2 : (uInt)VSIFReadL(next_out, 1, nToRead, (VSILFILE*)poBaseHandle);
787 2 : stream.avail_out -= nReadFromFile;
788 2 : nRead += nReadFromFile;
789 : }
790 2 : in += nRead;
791 2 : out += nRead;
792 2 : if (nRead < len) z_eof = 1;
793 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read return %d", (int)(nRead / nSize));
794 2 : return (int)nRead / nSize;
795 : }
796 2012 : if (stream.avail_in == 0 && !z_eof)
797 : {
798 314 : vsi_l_offset uncompressed_pos = VSIFTellL((VSILFILE*)poBaseHandle);
799 314 : GZipSnapshot* snapshot = &snapshots[(uncompressed_pos - startOff) / snapshot_byte_interval];
800 314 : if (snapshot->uncompressed_pos == 0)
801 : {
802 276 : snapshot->crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
803 : if (ENABLE_DEBUG)
804 : CPLDebug("SNAPSHOT",
805 : "creating snapshot %d : uncompressed_pos=" CPL_FRMT_GUIB
806 : " in=" CPL_FRMT_GUIB
807 : " out=" CPL_FRMT_GUIB
808 : " crc=%X",
809 : (int)((uncompressed_pos - startOff) / snapshot_byte_interval),
810 : uncompressed_pos, in, out, (unsigned int)snapshot->crc);
811 276 : snapshot->uncompressed_pos = uncompressed_pos;
812 276 : inflateCopy(&snapshot->stream, &stream);
813 276 : snapshot->transparent = transparent;
814 276 : snapshot->in = in;
815 276 : snapshot->out = out;
816 :
817 276 : if (out > nLastReadOffset)
818 3 : nLastReadOffset = out;
819 : }
820 :
821 314 : errno = 0;
822 314 : stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (VSILFILE*)poBaseHandle);
823 : if (ENABLE_DEBUG)
824 : CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
825 : VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData);
826 314 : if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData)
827 : {
828 : if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in before = %d", stream.avail_in);
829 283 : stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle));
830 283 : VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
831 283 : if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in after = %d", stream.avail_in);
832 : }
833 314 : if (stream.avail_in == 0) {
834 0 : z_eof = 1;
835 0 : if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData)
836 : {
837 0 : z_err = Z_ERRNO;
838 0 : break;
839 : }
840 : /*if (ferror (file)) {
841 : z_err = Z_ERRNO;
842 : break;
843 : }*/
844 : }
845 314 : stream.next_in = inbuf;
846 : }
847 2012 : in += stream.avail_in;
848 2012 : out += stream.avail_out;
849 2012 : z_err = inflate(& (stream), Z_NO_FLUSH);
850 2012 : in -= stream.avail_in;
851 2012 : out -= stream.avail_out;
852 :
853 2012 : if (z_err == Z_STREAM_END && compressed_size != 2 ) {
854 : /* Check CRC and original size */
855 257 : crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
856 257 : pStart = stream.next_out;
857 257 : if (expected_crc)
858 : {
859 236 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Computed CRC = %X. Expected CRC = %X", (unsigned int)crc, expected_crc);
860 : }
861 257 : if (expected_crc != 0 && expected_crc != crc)
862 : {
863 0 : CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, expected_crc);
864 0 : z_err = Z_DATA_ERROR;
865 : }
866 257 : else if (expected_crc == 0)
867 : {
868 21 : unsigned int read_crc = getLong();
869 21 : if (read_crc != crc)
870 : {
871 0 : CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, read_crc);
872 0 : z_err = Z_DATA_ERROR;
873 : }
874 : else
875 : {
876 21 : (void)getLong();
877 : /* The uncompressed length returned by above getlong() may be
878 : * different from out in case of concatenated .gz files.
879 : * Check for such files:
880 : */
881 21 : check_header();
882 21 : if (z_err == Z_OK) {
883 0 : inflateReset(& (stream));
884 0 : crc = crc32(0L, Z_NULL, 0);
885 : }
886 : }
887 : }
888 : }
889 2012 : if (z_err != Z_OK || z_eof) break;
890 : }
891 2009 : crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
892 :
893 2009 : if (len == stream.avail_out &&
894 : (z_err == Z_DATA_ERROR || z_err == Z_ERRNO))
895 : {
896 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
897 0 : return 0;
898 : }
899 : if (ENABLE_DEBUG)
900 : CPLDebug("GZIP", "Read return %d (z_err=%d, z_eof=%d)",
901 : (int)((len - stream.avail_out) / nSize), z_err, z_eof);
902 2009 : return (int)(len - stream.avail_out) / nSize;
903 : }
904 :
905 : /************************************************************************/
906 : /* getLong() */
907 : /************************************************************************/
908 :
909 42 : uLong VSIGZipHandle::getLong ()
910 : {
911 42 : uLong x = (uLong)get_byte();
912 : int c;
913 :
914 42 : x += ((uLong)get_byte())<<8;
915 42 : x += ((uLong)get_byte())<<16;
916 42 : c = get_byte();
917 42 : if (c == EOF) z_err = Z_DATA_ERROR;
918 42 : x += ((uLong)c)<<24;
919 42 : return x;
920 : }
921 :
922 : /************************************************************************/
923 : /* Write() */
924 : /************************************************************************/
925 :
926 0 : size_t VSIGZipHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb )
927 : {
928 0 : CPLError(CE_Failure, CPLE_NotSupported, "VSIFWriteL is not supported on GZip streams");
929 0 : return 0;
930 : }
931 :
932 : /************************************************************************/
933 : /* Eof() */
934 : /************************************************************************/
935 :
936 :
937 1922 : int VSIGZipHandle::Eof()
938 : {
939 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Eof()");
940 1922 : return z_eof;
941 : }
942 :
943 : /************************************************************************/
944 : /* Flush() */
945 : /************************************************************************/
946 :
947 0 : int VSIGZipHandle::Flush()
948 : {
949 0 : return 0;
950 : }
951 :
952 : /************************************************************************/
953 : /* Close() */
954 : /************************************************************************/
955 :
956 315 : int VSIGZipHandle::Close()
957 : {
958 315 : return 0;
959 : }
960 :
961 :
962 : /************************************************************************/
963 : /* ==================================================================== */
964 : /* VSIGZipWriteHandle */
965 : /* ==================================================================== */
966 : /************************************************************************/
967 :
968 : class VSIGZipWriteHandle : public VSIVirtualHandle
969 : {
970 : VSIVirtualHandle* poBaseHandle;
971 : z_stream sStream;
972 : Byte *pabyInBuf;
973 : Byte *pabyOutBuf;
974 : bool bCompressActive;
975 : vsi_l_offset nCurOffset;
976 : GUInt32 nCRC;
977 : int bRegularZLib;
978 : int bAutoCloseBaseHandle;
979 :
980 : public:
981 :
982 : VSIGZipWriteHandle(VSIVirtualHandle* poBaseHandle, int bRegularZLib, int bAutoCloseBaseHandleIn);
983 :
984 : ~VSIGZipWriteHandle();
985 :
986 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
987 : virtual vsi_l_offset Tell();
988 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
989 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
990 : virtual int Eof();
991 : virtual int Flush();
992 : virtual int Close();
993 : };
994 :
995 : /************************************************************************/
996 : /* VSIGZipWriteHandle() */
997 : /************************************************************************/
998 :
999 301 : VSIGZipWriteHandle::VSIGZipWriteHandle( VSIVirtualHandle *poBaseHandle,
1000 : int bRegularZLibIn,
1001 301 : int bAutoCloseBaseHandleIn )
1002 :
1003 : {
1004 301 : nCurOffset = 0;
1005 :
1006 301 : this->poBaseHandle = poBaseHandle;
1007 301 : bRegularZLib = bRegularZLibIn;
1008 301 : bAutoCloseBaseHandle = bAutoCloseBaseHandleIn;
1009 :
1010 301 : nCRC = crc32(0L, Z_NULL, 0);
1011 301 : sStream.zalloc = (alloc_func)0;
1012 301 : sStream.zfree = (free_func)0;
1013 301 : sStream.opaque = (voidpf)0;
1014 301 : sStream.next_in = Z_NULL;
1015 301 : sStream.next_out = Z_NULL;
1016 301 : sStream.avail_in = sStream.avail_out = 0;
1017 :
1018 301 : pabyInBuf = (Byte *) CPLMalloc( Z_BUFSIZE );
1019 301 : sStream.next_in = pabyInBuf;
1020 :
1021 301 : pabyOutBuf = (Byte *) CPLMalloc( Z_BUFSIZE );
1022 :
1023 301 : if( deflateInit2( &sStream, Z_DEFAULT_COMPRESSION,
1024 : Z_DEFLATED, (bRegularZLib) ? MAX_WBITS : -MAX_WBITS, 8,
1025 : Z_DEFAULT_STRATEGY ) != Z_OK )
1026 0 : bCompressActive = false;
1027 : else
1028 : {
1029 301 : if (!bRegularZLib)
1030 : {
1031 : char header[11];
1032 :
1033 : /* Write a very simple .gz header:
1034 : */
1035 24 : sprintf( header, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
1036 : Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/,
1037 24 : 0x03 );
1038 24 : poBaseHandle->Write( header, 1, 10 );
1039 : }
1040 :
1041 301 : bCompressActive = true;
1042 : }
1043 301 : }
1044 :
1045 : /************************************************************************/
1046 : /* VSICreateGZipWritable() */
1047 : /************************************************************************/
1048 :
1049 277 : VSIVirtualHandle* VSICreateGZipWritable( VSIVirtualHandle* poBaseHandle,
1050 : int bRegularZLibIn,
1051 : int bAutoCloseBaseHandle )
1052 : {
1053 277 : return new VSIGZipWriteHandle( poBaseHandle, bRegularZLibIn, bAutoCloseBaseHandle );
1054 : }
1055 :
1056 : /************************************************************************/
1057 : /* ~VSIGZipWriteHandle() */
1058 : /************************************************************************/
1059 :
1060 301 : VSIGZipWriteHandle::~VSIGZipWriteHandle()
1061 :
1062 : {
1063 301 : if( bCompressActive )
1064 0 : Close();
1065 :
1066 301 : CPLFree( pabyInBuf );
1067 301 : CPLFree( pabyOutBuf );
1068 301 : }
1069 :
1070 : /************************************************************************/
1071 : /* Close() */
1072 : /************************************************************************/
1073 :
1074 301 : int VSIGZipWriteHandle::Close()
1075 :
1076 : {
1077 301 : if( bCompressActive )
1078 : {
1079 301 : sStream.next_out = pabyOutBuf;
1080 301 : sStream.avail_out = Z_BUFSIZE;
1081 :
1082 301 : deflate( &sStream, Z_FINISH );
1083 :
1084 301 : size_t nOutBytes = Z_BUFSIZE - sStream.avail_out;
1085 :
1086 301 : if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes )
1087 0 : return EOF;
1088 :
1089 301 : deflateEnd( &sStream );
1090 :
1091 301 : if( !bRegularZLib )
1092 : {
1093 : GUInt32 anTrailer[2];
1094 :
1095 24 : anTrailer[0] = CPL_LSBWORD32( nCRC );
1096 24 : anTrailer[1] = CPL_LSBWORD32( (GUInt32) nCurOffset );
1097 :
1098 24 : poBaseHandle->Write( anTrailer, 1, 8 );
1099 : }
1100 :
1101 301 : if( bAutoCloseBaseHandle )
1102 : {
1103 24 : poBaseHandle->Close();
1104 :
1105 24 : delete poBaseHandle;
1106 : }
1107 :
1108 301 : bCompressActive = false;
1109 : }
1110 :
1111 301 : return 0;
1112 : }
1113 :
1114 : /************************************************************************/
1115 : /* Read() */
1116 : /************************************************************************/
1117 :
1118 0 : size_t VSIGZipWriteHandle::Read( void *pBuffer, size_t nSize, size_t nMemb )
1119 :
1120 : {
1121 0 : CPLError(CE_Failure, CPLE_NotSupported, "VSIFReadL is not supported on GZip write streams\n");
1122 0 : return 0;
1123 : }
1124 :
1125 : /************************************************************************/
1126 : /* Write() */
1127 : /************************************************************************/
1128 :
1129 10714 : size_t VSIGZipWriteHandle::Write( const void *pBuffer,
1130 : size_t nSize, size_t nMemb )
1131 :
1132 : {
1133 : int nBytesToWrite, nNextByte;
1134 :
1135 10714 : nBytesToWrite = (int) (nSize * nMemb);
1136 10714 : nNextByte = 0;
1137 :
1138 10714 : nCRC = crc32(nCRC, (const Bytef *)pBuffer, nBytesToWrite);
1139 :
1140 10714 : if( !bCompressActive )
1141 0 : return 0;
1142 :
1143 32142 : while( nNextByte < nBytesToWrite )
1144 : {
1145 10714 : sStream.next_out = pabyOutBuf;
1146 10714 : sStream.avail_out = Z_BUFSIZE;
1147 :
1148 10714 : if( sStream.avail_in > 0 )
1149 0 : memmove( pabyInBuf, sStream.next_in, sStream.avail_in );
1150 :
1151 10714 : int nNewBytesToWrite = MIN((int) (Z_BUFSIZE-sStream.avail_in),
1152 : nBytesToWrite - nNextByte);
1153 : memcpy( pabyInBuf + sStream.avail_in,
1154 : ((Byte *) pBuffer) + nNextByte,
1155 10714 : nNewBytesToWrite );
1156 :
1157 10714 : sStream.next_in = pabyInBuf;
1158 10714 : sStream.avail_in += nNewBytesToWrite;
1159 :
1160 10714 : deflate( &sStream, Z_NO_FLUSH );
1161 :
1162 10714 : size_t nOutBytes = Z_BUFSIZE - sStream.avail_out;
1163 :
1164 10714 : if( nOutBytes > 0 )
1165 : {
1166 294 : if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes )
1167 0 : return 0;
1168 : }
1169 :
1170 10714 : nNextByte += nNewBytesToWrite;
1171 10714 : nCurOffset += nNewBytesToWrite;
1172 : }
1173 :
1174 10714 : return nMemb;
1175 : }
1176 :
1177 : /************************************************************************/
1178 : /* Flush() */
1179 : /************************************************************************/
1180 :
1181 0 : int VSIGZipWriteHandle::Flush()
1182 :
1183 : {
1184 : // we *could* do something for this but for now we choose not to.
1185 :
1186 0 : return 0;
1187 : }
1188 :
1189 : /************************************************************************/
1190 : /* Eof() */
1191 : /************************************************************************/
1192 :
1193 0 : int VSIGZipWriteHandle::Eof()
1194 :
1195 : {
1196 0 : return 1;
1197 : }
1198 :
1199 : /************************************************************************/
1200 : /* Seek() */
1201 : /************************************************************************/
1202 :
1203 0 : int VSIGZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence )
1204 :
1205 : {
1206 0 : if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) )
1207 0 : return 0;
1208 0 : else if( nWhence == SEEK_SET && nOffset == nCurOffset )
1209 0 : return 0;
1210 : else
1211 : {
1212 : CPLError(CE_Failure, CPLE_NotSupported,
1213 0 : "Seeking on writable compressed data streams not supported." );
1214 :
1215 0 : return -1;
1216 : }
1217 : }
1218 :
1219 : /************************************************************************/
1220 : /* Tell() */
1221 : /************************************************************************/
1222 :
1223 0 : vsi_l_offset VSIGZipWriteHandle::Tell()
1224 :
1225 : {
1226 0 : return nCurOffset;
1227 : }
1228 :
1229 :
1230 : /************************************************************************/
1231 : /* ==================================================================== */
1232 : /* VSIGZipFilesystemHandler */
1233 : /* ==================================================================== */
1234 : /************************************************************************/
1235 :
1236 :
1237 : /************************************************************************/
1238 : /* VSIGZipFilesystemHandler() */
1239 : /************************************************************************/
1240 :
1241 757 : VSIGZipFilesystemHandler::VSIGZipFilesystemHandler()
1242 : {
1243 757 : hMutex = NULL;
1244 :
1245 757 : poHandleLastGZipFile = NULL;
1246 757 : bInSaveInfo = FALSE;
1247 757 : }
1248 :
1249 : /************************************************************************/
1250 : /* ~VSIGZipFilesystemHandler() */
1251 : /************************************************************************/
1252 :
1253 704 : VSIGZipFilesystemHandler::~VSIGZipFilesystemHandler()
1254 : {
1255 704 : if (poHandleLastGZipFile)
1256 1 : delete poHandleLastGZipFile;
1257 :
1258 704 : if( hMutex != NULL )
1259 1 : CPLDestroyMutex( hMutex );
1260 704 : hMutex = NULL;
1261 704 : }
1262 :
1263 : /************************************************************************/
1264 : /* SaveInfo() */
1265 : /************************************************************************/
1266 :
1267 63 : void VSIGZipFilesystemHandler::SaveInfo( VSIGZipHandle* poHandle )
1268 : {
1269 63 : CPLMutexHolder oHolder(&hMutex);
1270 :
1271 63 : if (bInSaveInfo)
1272 : return;
1273 63 : bInSaveInfo = TRUE;
1274 :
1275 63 : CPLAssert(poHandle->GetBaseFileName() != NULL);
1276 :
1277 63 : if (poHandleLastGZipFile &&
1278 : strcmp(poHandleLastGZipFile->GetBaseFileName(), poHandle->GetBaseFileName()) == 0)
1279 : {
1280 52 : if (poHandle->GetLastReadOffset() > poHandleLastGZipFile->GetLastReadOffset())
1281 : {
1282 0 : VSIGZipHandle* poTmp = poHandleLastGZipFile;
1283 0 : poHandleLastGZipFile = NULL;
1284 0 : delete poTmp;
1285 0 : poHandleLastGZipFile = poHandle->Duplicate();
1286 0 : poHandleLastGZipFile->CloseBaseHandle();
1287 : }
1288 : }
1289 : else
1290 : {
1291 11 : VSIGZipHandle* poTmp = poHandleLastGZipFile;
1292 11 : poHandleLastGZipFile = NULL;
1293 11 : delete poTmp;
1294 11 : poHandleLastGZipFile = poHandle->Duplicate();
1295 11 : poHandleLastGZipFile->CloseBaseHandle();
1296 : }
1297 :
1298 63 : bInSaveInfo = FALSE;
1299 : }
1300 :
1301 : /************************************************************************/
1302 : /* Open() */
1303 : /************************************************************************/
1304 :
1305 126 : VSIVirtualHandle* VSIGZipFilesystemHandler::Open( const char *pszFilename,
1306 : const char *pszAccess)
1307 : {
1308 : VSIFilesystemHandler *poFSHandler =
1309 126 : VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/"));
1310 :
1311 : /* -------------------------------------------------------------------- */
1312 : /* Is this an attempt to write a new file without update (w+) */
1313 : /* access? If so, create a writable handle for the underlying */
1314 : /* filename. */
1315 : /* -------------------------------------------------------------------- */
1316 126 : if (strchr(pszAccess, 'w') != NULL )
1317 : {
1318 26 : if( strchr(pszAccess, '+') != NULL )
1319 : {
1320 : CPLError(CE_Failure, CPLE_AppDefined,
1321 0 : "Write+update (w+) not supported for /vsigzip, only read-only or write-only.");
1322 0 : return NULL;
1323 : }
1324 :
1325 : VSIVirtualHandle* poVirtualHandle =
1326 26 : poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "wb" );
1327 :
1328 26 : if (poVirtualHandle == NULL)
1329 2 : return NULL;
1330 :
1331 : else
1332 24 : return new VSIGZipWriteHandle( poVirtualHandle, strchr(pszAccess, 'z') != NULL, TRUE );
1333 : }
1334 :
1335 : /* -------------------------------------------------------------------- */
1336 : /* Otherwise we are in the read access case. */
1337 : /* -------------------------------------------------------------------- */
1338 :
1339 100 : VSIGZipHandle* poGZIPHandle = OpenGZipReadOnly(pszFilename, pszAccess);
1340 100 : if (poGZIPHandle)
1341 : /* Wrap the VSIGZipHandle inside a buffered reader that will */
1342 : /* improve dramatically performance when doing small backward */
1343 : /* seeks */
1344 52 : return VSICreateBufferedReaderHandle(poGZIPHandle);
1345 : else
1346 48 : return NULL;
1347 : }
1348 :
1349 : /************************************************************************/
1350 : /* OpenGZipReadOnly() */
1351 : /************************************************************************/
1352 :
1353 100 : VSIGZipHandle* VSIGZipFilesystemHandler::OpenGZipReadOnly( const char *pszFilename,
1354 : const char *pszAccess)
1355 : {
1356 : VSIFilesystemHandler *poFSHandler =
1357 100 : VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/"));
1358 :
1359 100 : CPLMutexHolder oHolder(&hMutex);
1360 :
1361 100 : if (poHandleLastGZipFile != NULL &&
1362 : strcmp(pszFilename + strlen("/vsigzip/"), poHandleLastGZipFile->GetBaseFileName()) == 0 &&
1363 : EQUAL(pszAccess, "rb"))
1364 : {
1365 41 : VSIGZipHandle* poHandle = poHandleLastGZipFile->Duplicate();
1366 41 : if (poHandle)
1367 41 : return poHandle;
1368 : }
1369 :
1370 : unsigned char signature[2];
1371 :
1372 : VSIVirtualHandle* poVirtualHandle =
1373 59 : poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "rb" );
1374 :
1375 59 : if (poVirtualHandle == NULL)
1376 48 : return NULL;
1377 :
1378 55 : if (VSIFReadL(signature, 1, 2, (VSILFILE*)poVirtualHandle) != 2 ||
1379 44 : signature[0] != gz_magic[0] || signature[1] != gz_magic[1])
1380 : {
1381 0 : delete poVirtualHandle;
1382 0 : return NULL;
1383 : }
1384 :
1385 11 : if (poHandleLastGZipFile)
1386 10 : delete poHandleLastGZipFile;
1387 11 : poHandleLastGZipFile = NULL;
1388 :
1389 11 : return new VSIGZipHandle(poVirtualHandle, pszFilename + strlen("/vsigzip/"));
1390 : }
1391 :
1392 : /************************************************************************/
1393 : /* Stat() */
1394 : /************************************************************************/
1395 :
1396 44 : int VSIGZipFilesystemHandler::Stat( const char *pszFilename,
1397 : VSIStatBufL *pStatBuf,
1398 : int nFlags )
1399 : {
1400 44 : CPLMutexHolder oHolder(&hMutex);
1401 :
1402 44 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
1403 :
1404 44 : if (poHandleLastGZipFile != NULL &&
1405 : strcmp(pszFilename+strlen("/vsigzip/"), poHandleLastGZipFile->GetBaseFileName()) == 0)
1406 : {
1407 5 : if (poHandleLastGZipFile->GetUncompressedSize() != 0)
1408 : {
1409 0 : pStatBuf->st_mode = S_IFREG;
1410 0 : pStatBuf->st_size = poHandleLastGZipFile->GetUncompressedSize();
1411 0 : return 0;
1412 : }
1413 : }
1414 :
1415 : /* Begin by doing a stat on the real file */
1416 44 : int ret = VSIStatExL(pszFilename+strlen("/vsigzip/"), pStatBuf, nFlags);
1417 :
1418 44 : if (ret == 0 && (nFlags & VSI_STAT_SIZE_FLAG))
1419 : {
1420 0 : CPLString osCacheFilename(pszFilename+strlen("/vsigzip/"));
1421 0 : osCacheFilename += ".properties";
1422 :
1423 : /* Can we save a bit of seeking by using a .properties file ? */
1424 0 : VSILFILE* fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "rb");
1425 0 : if (fpCacheLength)
1426 : {
1427 : const char* pszLine;
1428 0 : GUIntBig nCompressedSize = 0;
1429 0 : GUIntBig nUncompressedSize = 0;
1430 0 : while ((pszLine = CPLReadLineL(fpCacheLength)) != NULL)
1431 : {
1432 0 : if (EQUALN(pszLine, "compressed_size=", strlen("compressed_size=")))
1433 : {
1434 0 : const char* pszBuffer = pszLine + strlen("compressed_size=");
1435 : nCompressedSize =
1436 0 : CPLScanUIntBig(pszBuffer, strlen(pszBuffer));
1437 : }
1438 0 : else if (EQUALN(pszLine, "uncompressed_size=", strlen("uncompressed_size=")))
1439 : {
1440 0 : const char* pszBuffer = pszLine + strlen("uncompressed_size=");
1441 : nUncompressedSize =
1442 0 : CPLScanUIntBig(pszBuffer, strlen(pszBuffer));
1443 : }
1444 : }
1445 :
1446 0 : VSIFCloseL(fpCacheLength);
1447 :
1448 0 : if (nCompressedSize == (GUIntBig) pStatBuf->st_size)
1449 : {
1450 : /* Patch with the uncompressed size */
1451 0 : pStatBuf->st_size = (long)nUncompressedSize;
1452 :
1453 : VSIGZipHandle* poHandle =
1454 0 : VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb");
1455 0 : if (poHandle)
1456 : {
1457 0 : poHandle->SetUncompressedSize(nUncompressedSize);
1458 0 : SaveInfo(poHandle);
1459 0 : delete poHandle;
1460 : }
1461 :
1462 0 : return ret;
1463 : }
1464 : }
1465 :
1466 : /* No, then seek at the end of the data (slow) */
1467 : VSIGZipHandle* poHandle =
1468 0 : VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb");
1469 0 : if (poHandle)
1470 : {
1471 : GUIntBig uncompressed_size;
1472 0 : poHandle->Seek(0, SEEK_END);
1473 0 : uncompressed_size = (GUIntBig) poHandle->Tell();
1474 0 : poHandle->Seek(0, SEEK_SET);
1475 :
1476 : /* Patch with the uncompressed size */
1477 0 : pStatBuf->st_size = (long)uncompressed_size;
1478 :
1479 0 : delete poHandle;
1480 : }
1481 : else
1482 : {
1483 0 : ret = -1;
1484 0 : }
1485 : }
1486 :
1487 44 : return ret;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* Unlink() */
1492 : /************************************************************************/
1493 :
1494 0 : int VSIGZipFilesystemHandler::Unlink( const char *pszFilename )
1495 : {
1496 0 : return -1;
1497 : }
1498 :
1499 : /************************************************************************/
1500 : /* Rename() */
1501 : /************************************************************************/
1502 :
1503 0 : int VSIGZipFilesystemHandler::Rename( const char *oldpath, const char *newpath )
1504 : {
1505 0 : return -1;
1506 : }
1507 :
1508 : /************************************************************************/
1509 : /* Mkdir() */
1510 : /************************************************************************/
1511 :
1512 0 : int VSIGZipFilesystemHandler::Mkdir( const char *pszDirname, long nMode )
1513 : {
1514 0 : return -1;
1515 : }
1516 : /************************************************************************/
1517 : /* Rmdir() */
1518 : /************************************************************************/
1519 :
1520 0 : int VSIGZipFilesystemHandler::Rmdir( const char *pszDirname )
1521 : {
1522 0 : return -1;
1523 : }
1524 :
1525 : /************************************************************************/
1526 : /* ReadDir() */
1527 : /************************************************************************/
1528 :
1529 5 : char** VSIGZipFilesystemHandler::ReadDir( const char *pszDirname )
1530 : {
1531 5 : return NULL;
1532 : }
1533 :
1534 : /************************************************************************/
1535 : /* VSIInstallGZipFileHandler() */
1536 : /************************************************************************/
1537 :
1538 :
1539 : /**
1540 : * \brief Install GZip file system handler.
1541 : *
1542 : * A special file handler is installed that allows reading on-the-fly and
1543 : * writing in GZip (.gz) files.
1544 : *
1545 : * All portions of the file system underneath the base
1546 : * path "/vsigzip/" will be handled by this driver.
1547 : *
1548 : * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
1549 : *
1550 : * @since GDAL 1.6.0
1551 : */
1552 :
1553 757 : void VSIInstallGZipFileHandler(void)
1554 : {
1555 757 : VSIFileManager::InstallHandler( "/vsigzip/", new VSIGZipFilesystemHandler );
1556 757 : }
1557 :
1558 :
1559 : /************************************************************************/
1560 : /* ==================================================================== */
1561 : /* VSIZipEntryFileOffset */
1562 : /* ==================================================================== */
1563 : /************************************************************************/
1564 :
1565 : class VSIZipEntryFileOffset : public VSIArchiveEntryFileOffset
1566 2443 : {
1567 : public:
1568 : unz_file_pos file_pos;
1569 :
1570 2443 : VSIZipEntryFileOffset(unz_file_pos file_pos)
1571 2443 : {
1572 2443 : this->file_pos.pos_in_zip_directory = file_pos.pos_in_zip_directory;
1573 2443 : this->file_pos.num_of_file = file_pos.num_of_file;
1574 2443 : }
1575 : };
1576 :
1577 : /************************************************************************/
1578 : /* ==================================================================== */
1579 : /* VSIZipReader */
1580 : /* ==================================================================== */
1581 : /************************************************************************/
1582 :
1583 : class VSIZipReader : public VSIArchiveReader
1584 : {
1585 : private:
1586 : unzFile unzF;
1587 : unz_file_pos file_pos;
1588 : GUIntBig nNextFileSize;
1589 : CPLString osNextFileName;
1590 : GIntBig nModifiedTime;
1591 :
1592 : void SetInfo();
1593 :
1594 : public:
1595 : VSIZipReader(const char* pszZipFileName);
1596 : virtual ~VSIZipReader();
1597 :
1598 351 : int IsValid() { return unzF != NULL; }
1599 :
1600 263 : unzFile GetUnzFileHandle() { return unzF; }
1601 :
1602 : virtual int GotoFirstFile();
1603 : virtual int GotoNextFile();
1604 2443 : virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSIZipEntryFileOffset(file_pos); }
1605 2446 : virtual GUIntBig GetFileSize() { return nNextFileSize; }
1606 2461 : virtual CPLString GetFileName() { return osNextFileName; }
1607 3867 : virtual GIntBig GetModifiedTime() { return nModifiedTime; }
1608 : virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset);
1609 : };
1610 :
1611 :
1612 : /************************************************************************/
1613 : /* VSIZipReader() */
1614 : /************************************************************************/
1615 :
1616 351 : VSIZipReader::VSIZipReader(const char* pszZipFileName)
1617 : {
1618 351 : unzF = cpl_unzOpen(pszZipFileName);
1619 351 : nNextFileSize = 0;
1620 351 : nModifiedTime = 0;
1621 351 : }
1622 :
1623 : /************************************************************************/
1624 : /* ~VSIZipReader() */
1625 : /************************************************************************/
1626 :
1627 351 : VSIZipReader::~VSIZipReader()
1628 : {
1629 351 : if (unzF)
1630 345 : cpl_unzClose(unzF);
1631 351 : }
1632 :
1633 : /************************************************************************/
1634 : /* SetInfo() */
1635 : /************************************************************************/
1636 :
1637 3068 : void VSIZipReader::SetInfo()
1638 : {
1639 : char fileName[8193];
1640 : unz_file_info file_info;
1641 3068 : cpl_unzGetCurrentFileInfo (unzF, &file_info, fileName, sizeof(fileName) - 1, NULL, 0, NULL, 0);
1642 3068 : fileName[sizeof(fileName) - 1] = '\0';
1643 3068 : osNextFileName = fileName;
1644 3068 : nNextFileSize = file_info.uncompressed_size;
1645 : struct tm brokendowntime;
1646 3068 : brokendowntime.tm_sec = file_info.tmu_date.tm_sec;
1647 3068 : brokendowntime.tm_min = file_info.tmu_date.tm_min;
1648 3068 : brokendowntime.tm_hour = file_info.tmu_date.tm_hour;
1649 3068 : brokendowntime.tm_mday = file_info.tmu_date.tm_mday;
1650 3068 : brokendowntime.tm_mon = file_info.tmu_date.tm_mon;
1651 3068 : brokendowntime.tm_year = file_info.tmu_date.tm_year - 1900; /* the minizip conventions differs from the Unix one */
1652 3068 : nModifiedTime = CPLYMDHMSToUnixTime(&brokendowntime);
1653 :
1654 3068 : cpl_unzGetFilePos(unzF, &this->file_pos);
1655 3068 : }
1656 :
1657 : /************************************************************************/
1658 : /* GotoNextFile() */
1659 : /************************************************************************/
1660 :
1661 2465 : int VSIZipReader::GotoNextFile()
1662 : {
1663 2465 : if (cpl_unzGoToNextFile(unzF) != UNZ_OK)
1664 63 : return FALSE;
1665 :
1666 2402 : SetInfo();
1667 :
1668 2402 : return TRUE;
1669 : }
1670 :
1671 : /************************************************************************/
1672 : /* GotoFirstFile() */
1673 : /************************************************************************/
1674 :
1675 413 : int VSIZipReader::GotoFirstFile()
1676 : {
1677 413 : if (cpl_unzGoToFirstFile(unzF) != UNZ_OK)
1678 0 : return FALSE;
1679 :
1680 413 : SetInfo();
1681 :
1682 413 : return TRUE;
1683 : }
1684 :
1685 : /************************************************************************/
1686 : /* GotoFileOffset() */
1687 : /************************************************************************/
1688 :
1689 253 : int VSIZipReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset)
1690 : {
1691 253 : VSIZipEntryFileOffset* pZipEntryOffset = (VSIZipEntryFileOffset*)pOffset;
1692 253 : cpl_unzGoToFilePos(unzF, &(pZipEntryOffset->file_pos));
1693 :
1694 253 : SetInfo();
1695 :
1696 253 : return TRUE;
1697 : }
1698 :
1699 : /************************************************************************/
1700 : /* ==================================================================== */
1701 : /* VSIZipFilesystemHandler */
1702 : /* ==================================================================== */
1703 : /************************************************************************/
1704 :
1705 : class VSIZipWriteHandle;
1706 :
1707 : class VSIZipFilesystemHandler : public VSIArchiveFilesystemHandler
1708 757 : {
1709 : std::map<CPLString, VSIZipWriteHandle*> oMapZipWriteHandles;
1710 :
1711 : public:
1712 : virtual ~VSIZipFilesystemHandler();
1713 :
1714 16977 : virtual const char* GetPrefix() { return "/vsizip"; }
1715 : virtual std::vector<CPLString> GetExtensions();
1716 : virtual VSIArchiveReader* CreateReader(const char* pszZipFileName);
1717 :
1718 : virtual VSIVirtualHandle *Open( const char *pszFilename,
1719 : const char *pszAccess);
1720 :
1721 : virtual VSIVirtualHandle *OpenForWrite( const char *pszFilename,
1722 : const char *pszAccess );
1723 :
1724 : virtual int Mkdir( const char *pszDirname, long nMode );
1725 : virtual char **ReadDir( const char *pszDirname );
1726 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags );
1727 :
1728 : void RemoveFromMap(VSIZipWriteHandle* poHandle);
1729 : };
1730 :
1731 : /************************************************************************/
1732 : /* ==================================================================== */
1733 : /* VSIZipWriteHandle */
1734 : /* ==================================================================== */
1735 : /************************************************************************/
1736 :
1737 : class VSIZipWriteHandle : public VSIVirtualHandle
1738 : {
1739 : VSIZipFilesystemHandler *poFS;
1740 : void *hZIP;
1741 : VSIZipWriteHandle *poChildInWriting;
1742 : VSIZipWriteHandle *poParent;
1743 : int bAutoDeleteParent;
1744 :
1745 : public:
1746 :
1747 : VSIZipWriteHandle(VSIZipFilesystemHandler* poFS,
1748 : void *hZIP,
1749 : VSIZipWriteHandle* poParent);
1750 :
1751 : ~VSIZipWriteHandle();
1752 :
1753 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
1754 : virtual vsi_l_offset Tell();
1755 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
1756 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
1757 : virtual int Eof();
1758 : virtual int Flush();
1759 : virtual int Close();
1760 :
1761 : void StartNewFile(VSIZipWriteHandle* poSubFile);
1762 : void StopCurrentFile();
1763 56 : void* GetHandle() { return hZIP; }
1764 57 : VSIZipWriteHandle* GetChildInWriting() { return poChildInWriting; };
1765 14 : void SetAutoDeleteParent() { bAutoDeleteParent = TRUE; }
1766 : };
1767 :
1768 : /************************************************************************/
1769 : /* ~VSIZipFilesystemHandler() */
1770 : /************************************************************************/
1771 :
1772 704 : VSIZipFilesystemHandler::~VSIZipFilesystemHandler()
1773 : {
1774 704 : std::map<CPLString,VSIZipWriteHandle*>::const_iterator iter;
1775 :
1776 704 : for( iter = oMapZipWriteHandles.begin(); iter != oMapZipWriteHandles.end(); ++iter )
1777 : {
1778 : CPLError(CE_Failure, CPLE_AppDefined, "%s has not been closed",
1779 0 : iter->first.c_str());
1780 : }
1781 704 : }
1782 :
1783 : /************************************************************************/
1784 : /* GetExtensions() */
1785 : /************************************************************************/
1786 :
1787 106732 : std::vector<CPLString> VSIZipFilesystemHandler::GetExtensions()
1788 : {
1789 106732 : std::vector<CPLString> oList;
1790 106732 : oList.push_back(".zip");
1791 106732 : oList.push_back(".kmz");
1792 106732 : oList.push_back(".dwf");
1793 106732 : oList.push_back(".ods");
1794 106732 : oList.push_back(".xlsx");
1795 0 : return oList;
1796 : }
1797 :
1798 : /************************************************************************/
1799 : /* CreateReader() */
1800 : /************************************************************************/
1801 :
1802 351 : VSIArchiveReader* VSIZipFilesystemHandler::CreateReader(const char* pszZipFileName)
1803 : {
1804 351 : VSIZipReader* poReader = new VSIZipReader(pszZipFileName);
1805 :
1806 351 : if (!poReader->IsValid())
1807 : {
1808 6 : delete poReader;
1809 6 : return NULL;
1810 : }
1811 :
1812 345 : if (!poReader->GotoFirstFile())
1813 : {
1814 0 : delete poReader;
1815 0 : return NULL;
1816 : }
1817 :
1818 345 : return poReader;
1819 : }
1820 :
1821 : /************************************************************************/
1822 : /* Open() */
1823 : /************************************************************************/
1824 :
1825 380 : VSIVirtualHandle* VSIZipFilesystemHandler::Open( const char *pszFilename,
1826 : const char *pszAccess)
1827 : {
1828 : char* zipFilename;
1829 380 : CPLString osZipInFileName;
1830 :
1831 380 : if (strchr(pszAccess, 'w') != NULL)
1832 : {
1833 62 : return OpenForWrite(pszFilename, pszAccess);
1834 : }
1835 :
1836 318 : if (strchr(pszAccess, '+') != NULL)
1837 : {
1838 : CPLError(CE_Failure, CPLE_AppDefined,
1839 0 : "Random access not supported for /vsizip");
1840 0 : return NULL;
1841 : }
1842 :
1843 318 : zipFilename = SplitFilename(pszFilename, osZipInFileName, TRUE);
1844 318 : if (zipFilename == NULL)
1845 24 : return NULL;
1846 :
1847 : {
1848 294 : CPLMutexHolder oHolder(&hMutex);
1849 294 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() )
1850 : {
1851 : CPLError(CE_Failure, CPLE_AppDefined,
1852 1 : "Cannot read a zip file being written");
1853 1 : CPLFree(zipFilename);
1854 1 : return NULL;
1855 0 : }
1856 : }
1857 :
1858 293 : VSIArchiveReader* poReader = OpenArchiveFile(zipFilename, osZipInFileName);
1859 293 : if (poReader == NULL)
1860 : {
1861 30 : CPLFree(zipFilename);
1862 30 : return NULL;
1863 : }
1864 :
1865 : VSIFilesystemHandler *poFSHandler =
1866 263 : VSIFileManager::GetHandler( zipFilename);
1867 :
1868 : VSIVirtualHandle* poVirtualHandle =
1869 263 : poFSHandler->Open( zipFilename, "rb" );
1870 :
1871 263 : CPLFree(zipFilename);
1872 263 : zipFilename = NULL;
1873 :
1874 263 : if (poVirtualHandle == NULL)
1875 : {
1876 0 : delete poReader;
1877 0 : return NULL;
1878 : }
1879 :
1880 263 : unzFile unzF = ((VSIZipReader*)poReader)->GetUnzFileHandle();
1881 :
1882 263 : cpl_unzOpenCurrentFile(unzF);
1883 :
1884 263 : uLong64 pos = cpl_unzGetCurrentFileZStreamPos(unzF);
1885 :
1886 : unz_file_info file_info;
1887 263 : cpl_unzGetCurrentFileInfo (unzF, &file_info, NULL, 0, NULL, 0, NULL, 0);
1888 :
1889 263 : cpl_unzCloseCurrentFile(unzF);
1890 :
1891 263 : delete poReader;
1892 :
1893 : VSIGZipHandle* poGZIPHandle = new VSIGZipHandle(poVirtualHandle,
1894 : NULL,
1895 : pos,
1896 : file_info.compressed_size,
1897 : file_info.uncompressed_size,
1898 : file_info.crc,
1899 263 : file_info.compression_method == 0);
1900 : /* Wrap the VSIGZipHandle inside a buffered reader that will */
1901 : /* improve dramatically performance when doing small backward */
1902 : /* seeks */
1903 526 : return VSICreateBufferedReaderHandle(poGZIPHandle);
1904 : }
1905 :
1906 : /************************************************************************/
1907 : /* Mkdir() */
1908 : /************************************************************************/
1909 :
1910 1 : int VSIZipFilesystemHandler::Mkdir( const char *pszDirname, long nMode )
1911 : {
1912 1 : CPLString osDirname = pszDirname;
1913 1 : if (osDirname.size() != 0 && osDirname[osDirname.size() - 1] != '/')
1914 1 : osDirname += "/";
1915 1 : VSIVirtualHandle* poZIPHandle = OpenForWrite(osDirname, "wb");
1916 1 : if (poZIPHandle == NULL)
1917 0 : return -1;
1918 1 : delete poZIPHandle;
1919 1 : return 0;
1920 : }
1921 :
1922 : /************************************************************************/
1923 : /* ReadDir() */
1924 : /************************************************************************/
1925 :
1926 1569 : char **VSIZipFilesystemHandler::ReadDir( const char *pszDirname )
1927 : {
1928 1569 : CPLString osInArchiveSubDir;
1929 1569 : char* zipFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE);
1930 1569 : if (zipFilename == NULL)
1931 3 : return NULL;
1932 :
1933 : {
1934 1566 : CPLMutexHolder oHolder(&hMutex);
1935 :
1936 1566 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() )
1937 : {
1938 : CPLError(CE_Failure, CPLE_AppDefined,
1939 1 : "Cannot read a zip file being written");
1940 1 : CPLFree(zipFilename);
1941 1 : return NULL;
1942 0 : }
1943 : }
1944 1565 : CPLFree(zipFilename);
1945 :
1946 1565 : return VSIArchiveFilesystemHandler::ReadDir(pszDirname);
1947 : }
1948 :
1949 :
1950 : /************************************************************************/
1951 : /* Stat() */
1952 : /************************************************************************/
1953 :
1954 1068 : int VSIZipFilesystemHandler::Stat( const char *pszFilename,
1955 : VSIStatBufL *pStatBuf, int nFlags )
1956 : {
1957 1068 : CPLString osInArchiveSubDir;
1958 :
1959 1068 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
1960 :
1961 1068 : char* zipFilename = SplitFilename(pszFilename, osInArchiveSubDir, TRUE);
1962 1068 : if (zipFilename == NULL)
1963 7 : return -1;
1964 :
1965 : {
1966 1061 : CPLMutexHolder oHolder(&hMutex);
1967 :
1968 1061 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() )
1969 : {
1970 : CPLError(CE_Failure, CPLE_AppDefined,
1971 0 : "Cannot read a zip file being written");
1972 0 : CPLFree(zipFilename);
1973 0 : return -1;
1974 0 : }
1975 : }
1976 1061 : CPLFree(zipFilename);
1977 :
1978 1061 : return VSIArchiveFilesystemHandler::Stat(pszFilename, pStatBuf, nFlags);
1979 : }
1980 :
1981 : /************************************************************************/
1982 : /* RemoveFromMap() */
1983 : /************************************************************************/
1984 :
1985 21 : void VSIZipFilesystemHandler::RemoveFromMap(VSIZipWriteHandle* poHandle)
1986 : {
1987 21 : CPLMutexHolder oHolder( &hMutex );
1988 21 : std::map<CPLString,VSIZipWriteHandle*>::iterator iter;
1989 :
1990 21 : for( iter = oMapZipWriteHandles.begin();
1991 : iter != oMapZipWriteHandles.end(); ++iter )
1992 : {
1993 21 : if (iter->second == poHandle)
1994 : {
1995 21 : oMapZipWriteHandles.erase(iter);
1996 21 : break;
1997 : }
1998 21 : }
1999 21 : }
2000 :
2001 : /************************************************************************/
2002 : /* OpenForWrite() */
2003 : /************************************************************************/
2004 :
2005 78 : VSIVirtualHandle* VSIZipFilesystemHandler::OpenForWrite( const char *pszFilename,
2006 : const char *pszAccess)
2007 : {
2008 : char* zipFilename;
2009 78 : CPLString osZipInFileName;
2010 :
2011 78 : CPLMutexHolder oHolder( &hMutex );
2012 :
2013 78 : zipFilename = SplitFilename(pszFilename, osZipInFileName, FALSE);
2014 78 : if (zipFilename == NULL)
2015 0 : return NULL;
2016 78 : CPLString osZipFilename = zipFilename;
2017 78 : CPLFree(zipFilename);
2018 78 : zipFilename = NULL;
2019 :
2020 : /* Invalidate cached file list */
2021 78 : std::map<CPLString,VSIArchiveContent*>::iterator iter = oFileList.find(osZipFilename);
2022 78 : if (iter != oFileList.end())
2023 : {
2024 4 : VSIArchiveContent* content = iter->second;
2025 : int i;
2026 53 : for(i=0;i<content->nEntries;i++)
2027 : {
2028 49 : delete content->entries[i].file_pos;
2029 49 : CPLFree(content->entries[i].fileName);
2030 : }
2031 4 : CPLFree(content->entries);
2032 4 : delete content;
2033 :
2034 4 : oFileList.erase(iter);
2035 : }
2036 :
2037 : VSIZipWriteHandle* poZIPHandle;
2038 :
2039 78 : if (oMapZipWriteHandles.find(osZipFilename) != oMapZipWriteHandles.end() )
2040 : {
2041 57 : if (strchr(pszAccess, '+') != NULL)
2042 : {
2043 : CPLError(CE_Failure, CPLE_AppDefined,
2044 0 : "Random access not supported for writable file in /vsizip");
2045 0 : return NULL;
2046 : }
2047 :
2048 57 : poZIPHandle = oMapZipWriteHandles[osZipFilename];
2049 :
2050 57 : if (poZIPHandle->GetChildInWriting() != NULL)
2051 : {
2052 : CPLError(CE_Failure, CPLE_AppDefined,
2053 : "Cannot create %s while another file is being written in the .zip",
2054 1 : osZipInFileName.c_str());
2055 1 : return NULL;
2056 : }
2057 :
2058 56 : poZIPHandle->StopCurrentFile();
2059 :
2060 : /* Re-add path separator when creating directories */
2061 56 : char chLastChar = pszFilename[strlen(pszFilename) - 1];
2062 56 : if (chLastChar == '/' || chLastChar == '\\')
2063 2 : osZipInFileName += chLastChar;
2064 :
2065 56 : if (CPLCreateFileInZip(poZIPHandle->GetHandle(),
2066 : osZipInFileName, NULL) != CE_None)
2067 2 : return NULL;
2068 :
2069 : VSIZipWriteHandle* poChildHandle =
2070 54 : new VSIZipWriteHandle(this, NULL, poZIPHandle);
2071 :
2072 54 : poZIPHandle->StartNewFile(poChildHandle);
2073 :
2074 54 : return poChildHandle;
2075 : }
2076 : else
2077 : {
2078 21 : char** papszOptions = NULL;
2079 21 : if ((strchr(pszAccess, '+') && osZipInFileName.size() == 0) ||
2080 : osZipInFileName.size() != 0)
2081 : {
2082 : VSIStatBufL sBuf;
2083 15 : if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0)
2084 13 : papszOptions = CSLAddNameValue(papszOptions, "APPEND", "TRUE");
2085 : }
2086 :
2087 21 : void* hZIP = CPLCreateZip(osZipFilename, papszOptions);
2088 21 : CSLDestroy(papszOptions);
2089 :
2090 21 : if (hZIP == NULL)
2091 0 : return NULL;
2092 :
2093 : oMapZipWriteHandles[osZipFilename] =
2094 21 : new VSIZipWriteHandle(this, hZIP, NULL);
2095 :
2096 21 : if (osZipInFileName.size() != 0)
2097 : {
2098 : VSIZipWriteHandle* poRes =
2099 15 : (VSIZipWriteHandle*)OpenForWrite(pszFilename, pszAccess);
2100 15 : if (poRes == NULL)
2101 : {
2102 1 : delete oMapZipWriteHandles[osZipFilename];
2103 1 : return NULL;
2104 : }
2105 :
2106 14 : poRes->SetAutoDeleteParent();
2107 :
2108 14 : return poRes;
2109 : }
2110 :
2111 6 : return oMapZipWriteHandles[osZipFilename];
2112 0 : }
2113 : }
2114 :
2115 :
2116 : /************************************************************************/
2117 : /* VSIZipWriteHandle() */
2118 : /************************************************************************/
2119 :
2120 75 : VSIZipWriteHandle::VSIZipWriteHandle(VSIZipFilesystemHandler* poFS,
2121 : void* hZIP,
2122 75 : VSIZipWriteHandle* poParent)
2123 : {
2124 75 : this->poFS = poFS;
2125 75 : this->hZIP = hZIP;
2126 75 : this->poParent = poParent;
2127 75 : poChildInWriting = NULL;
2128 75 : bAutoDeleteParent = FALSE;
2129 75 : }
2130 :
2131 : /************************************************************************/
2132 : /* ~VSIZipWriteHandle() */
2133 : /************************************************************************/
2134 :
2135 75 : VSIZipWriteHandle::~VSIZipWriteHandle()
2136 : {
2137 75 : Close();
2138 75 : }
2139 :
2140 : /************************************************************************/
2141 : /* Seek() */
2142 : /************************************************************************/
2143 :
2144 0 : int VSIZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence )
2145 : {
2146 : CPLError(CE_Failure, CPLE_NotSupported,
2147 0 : "VSIFSeekL() is not supported on writable Zip files");
2148 0 : return -1;
2149 : }
2150 :
2151 : /************************************************************************/
2152 : /* Tell() */
2153 : /************************************************************************/
2154 :
2155 0 : vsi_l_offset VSIZipWriteHandle::Tell()
2156 : {
2157 : CPLError(CE_Failure, CPLE_NotSupported,
2158 0 : "VSIFTellL() is not supported on writable Zip files");
2159 0 : return 0;
2160 : }
2161 :
2162 : /************************************************************************/
2163 : /* Read() */
2164 : /************************************************************************/
2165 :
2166 0 : size_t VSIZipWriteHandle::Read( void *pBuffer, size_t nSize, size_t nMemb )
2167 : {
2168 : CPLError(CE_Failure, CPLE_NotSupported,
2169 0 : "VSIFReadL() is not supported on writable Zip files");
2170 0 : return 0;
2171 : }
2172 :
2173 : /************************************************************************/
2174 : /* Write() */
2175 : /************************************************************************/
2176 :
2177 2048 : size_t VSIZipWriteHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb )
2178 : {
2179 2048 : if (poParent == NULL)
2180 : {
2181 : CPLError(CE_Failure, CPLE_NotSupported,
2182 0 : "VSIFWriteL() is not supported on main Zip file or closed subfiles");
2183 0 : return 0;
2184 : }
2185 :
2186 2048 : if (CPLWriteFileInZip( poParent->hZIP, pBuffer, (int)(nSize * nMemb) ) != CE_None)
2187 0 : return 0;
2188 :
2189 2048 : return nMemb;
2190 : }
2191 :
2192 : /************************************************************************/
2193 : /* Eof() */
2194 : /************************************************************************/
2195 :
2196 0 : int VSIZipWriteHandle::Eof()
2197 : {
2198 : CPLError(CE_Failure, CPLE_NotSupported,
2199 0 : "VSIFEofL() is not supported on writable Zip files");
2200 0 : return FALSE;
2201 : }
2202 :
2203 : /************************************************************************/
2204 : /* Flush() */
2205 : /************************************************************************/
2206 :
2207 0 : int VSIZipWriteHandle::Flush()
2208 : {
2209 : CPLError(CE_Failure, CPLE_NotSupported,
2210 0 : "VSIFFlushL() is not supported on writable Zip files");
2211 0 : return 0;
2212 : }
2213 :
2214 : /************************************************************************/
2215 : /* Close() */
2216 : /************************************************************************/
2217 :
2218 134 : int VSIZipWriteHandle::Close()
2219 : {
2220 134 : if (poParent)
2221 : {
2222 54 : CPLCloseFileInZip(poParent->hZIP);
2223 54 : poParent->poChildInWriting = NULL;
2224 54 : if (bAutoDeleteParent)
2225 14 : delete poParent;
2226 54 : poParent = NULL;
2227 : }
2228 134 : if (poChildInWriting)
2229 : {
2230 0 : poChildInWriting->Close();
2231 0 : poChildInWriting = NULL;
2232 : }
2233 134 : if (hZIP)
2234 : {
2235 21 : CPLCloseZip(hZIP);
2236 21 : hZIP = NULL;
2237 :
2238 21 : poFS->RemoveFromMap(this);
2239 : }
2240 :
2241 134 : return 0;
2242 : }
2243 :
2244 : /************************************************************************/
2245 : /* StopCurrentFile() */
2246 : /************************************************************************/
2247 :
2248 56 : void VSIZipWriteHandle::StopCurrentFile()
2249 : {
2250 56 : if (poChildInWriting)
2251 0 : poChildInWriting->Close();
2252 56 : poChildInWriting = NULL;
2253 56 : }
2254 :
2255 : /************************************************************************/
2256 : /* StartNewFile() */
2257 : /************************************************************************/
2258 :
2259 54 : void VSIZipWriteHandle::StartNewFile(VSIZipWriteHandle* poSubFile)
2260 : {
2261 54 : poChildInWriting = poSubFile;
2262 54 : }
2263 :
2264 : /************************************************************************/
2265 : /* VSIInstallZipFileHandler() */
2266 : /************************************************************************/
2267 :
2268 :
2269 : /**
2270 : * \brief Install ZIP file system handler.
2271 : *
2272 : * A special file handler is installed that allows reading on-the-fly in ZIP
2273 : * (.zip) archives.
2274 : *
2275 : * All portions of the file system underneath the base path "/vsizip/" will be
2276 : * handled by this driver.
2277 : *
2278 : * The syntax to open a file inside a zip file is /vsizip/path/to/the/file.zip/path/inside/the/zip/file
2279 : * were path/to/the/file.zip is relative or absolute and path/inside/the/zip/file
2280 : * is the relative path to the file inside the archive.
2281 : *
2282 : * If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows),
2283 : * so the line looks like /vsizip//home/gdal/...
2284 : * For example gdalinfo /vsizip/myarchive.zip/subdir1/file1.tif
2285 : *
2286 : * Syntaxic sugar : if the .zip file contains only one file located at its root,
2287 : * just mentionning "/vsizip/path/to/the/file.zip" will work
2288 : *
2289 : * VSIStatL() will return the uncompressed size in st_size member and file
2290 : * nature- file or directory - in st_mode member.
2291 : *
2292 : * Directory listing is available through VSIReadDir().
2293 : *
2294 : * Since GDAL 1.8.0, write capabilities are available. They allow creating
2295 : * a new zip file and adding new files to an already existing (or just created)
2296 : * zip file. Read and write operations cannot be interleaved : the new zip must
2297 : * be closed before being re-opened for read.
2298 : *
2299 : * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
2300 : *
2301 : * @since GDAL 1.6.0
2302 : */
2303 :
2304 757 : void VSIInstallZipFileHandler(void)
2305 : {
2306 757 : VSIFileManager::InstallHandler( "/vsizip/", new VSIZipFilesystemHandler() );
2307 757 : }
2308 :
2309 :
2310 : /************************************************************************/
2311 : /* CPLZLibDeflate() */
2312 : /************************************************************************/
2313 :
2314 : /**
2315 : * \brief Compress a buffer with ZLib DEFLATE compression.
2316 : *
2317 : * @param ptr input buffer.
2318 : * @param nBytes size of input buffer in bytes.
2319 : * @param nLevel ZLib compression level (-1 for default).
2320 : * @param outptr output buffer, or NULL to let the function allocate it.
2321 : * @param nOutAvailableBytes size of output buffer if provided, or ignored.
2322 : * @param pnOutBytes pointer to a size_t, where to store the size of the
2323 : * output buffer.
2324 : *
2325 : * @return the output buffer (to be freed with VSIFree() if not provided)
2326 : * or NULL in case of error.
2327 : *
2328 : * @since GDAL 1.10.0
2329 : */
2330 :
2331 22 : void* CPLZLibDeflate( const void* ptr, size_t nBytes, int nLevel,
2332 : void* outptr, size_t nOutAvailableBytes,
2333 : size_t* pnOutBytes )
2334 : {
2335 : z_stream strm;
2336 22 : strm.zalloc = Z_NULL;
2337 22 : strm.zfree = Z_NULL;
2338 22 : strm.opaque = Z_NULL;
2339 22 : int ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
2340 22 : if (ret != Z_OK)
2341 : {
2342 0 : if( pnOutBytes != NULL )
2343 0 : *pnOutBytes = 0;
2344 0 : return NULL;
2345 : }
2346 :
2347 : size_t nTmpSize;
2348 : void* pTmp;
2349 22 : if( outptr == NULL )
2350 : {
2351 20 : nTmpSize = 8 + nBytes * 2;
2352 20 : pTmp = VSIMalloc(nTmpSize);
2353 20 : if( pTmp == NULL )
2354 : {
2355 0 : deflateEnd(&strm);
2356 0 : if( pnOutBytes != NULL )
2357 0 : *pnOutBytes = 0;
2358 0 : return NULL;
2359 : }
2360 : }
2361 : else
2362 : {
2363 2 : pTmp = outptr;
2364 2 : nTmpSize = nOutAvailableBytes;
2365 : }
2366 :
2367 22 : strm.avail_in = nBytes;
2368 22 : strm.next_in = (Bytef*) ptr;
2369 22 : strm.avail_out = nTmpSize;
2370 22 : strm.next_out = (Bytef*) pTmp;
2371 22 : ret = deflate(&strm, Z_FINISH);
2372 22 : if( ret != Z_STREAM_END )
2373 : {
2374 0 : if( pTmp != outptr )
2375 0 : VSIFree(pTmp);
2376 0 : if( pnOutBytes != NULL )
2377 0 : *pnOutBytes = 0;
2378 0 : return NULL;
2379 : }
2380 22 : if( pnOutBytes != NULL )
2381 22 : *pnOutBytes = nTmpSize - strm.avail_out;
2382 22 : deflateEnd(&strm);
2383 22 : return pTmp;
2384 : }
2385 :
2386 : /************************************************************************/
2387 : /* CPLZLibInflate() */
2388 : /************************************************************************/
2389 :
2390 : /**
2391 : * \brief Uncompress a buffer compressed with ZLib DEFLATE compression.
2392 : *
2393 : * @param ptr input buffer.
2394 : * @param nBytes size of input buffer in bytes.
2395 : * @param outptr output buffer, or NULL to let the function allocate it.
2396 : * @param nOutAvailableBytes size of output buffer if provided, or ignored.
2397 : * @param pnOutBytes pointer to a size_t, where to store the size of the
2398 : * output buffer.
2399 : *
2400 : * @return the output buffer (to be freed with VSIFree() if not provided)
2401 : * or NULL in case of error.
2402 : *
2403 : * @since GDAL 1.10.0
2404 : */
2405 :
2406 91 : void* CPLZLibInflate( const void* ptr, size_t nBytes,
2407 : void* outptr, size_t nOutAvailableBytes,
2408 : size_t* pnOutBytes )
2409 : {
2410 : z_stream strm;
2411 91 : strm.zalloc = Z_NULL;
2412 91 : strm.zfree = Z_NULL;
2413 91 : strm.opaque = Z_NULL;
2414 91 : strm.avail_in = nBytes;
2415 91 : strm.next_in = (Bytef*) ptr;
2416 91 : int ret = inflateInit(&strm);
2417 91 : if (ret != Z_OK)
2418 : {
2419 0 : if( pnOutBytes != NULL )
2420 0 : *pnOutBytes = 0;
2421 0 : return NULL;
2422 : }
2423 :
2424 : size_t nTmpSize;
2425 : char* pszTmp;
2426 91 : if( outptr == NULL )
2427 : {
2428 43 : nTmpSize = 2 * nBytes;
2429 43 : pszTmp = (char*) VSIMalloc(nTmpSize + 1);
2430 43 : if( pszTmp == NULL )
2431 : {
2432 0 : inflateEnd(&strm);
2433 0 : if( pnOutBytes != NULL )
2434 0 : *pnOutBytes = 0;
2435 0 : return NULL;
2436 : }
2437 : }
2438 : else
2439 : {
2440 48 : pszTmp = (char*) outptr;
2441 48 : nTmpSize = nOutAvailableBytes;
2442 : }
2443 :
2444 91 : strm.avail_out = nTmpSize;
2445 91 : strm.next_out = (Bytef*) pszTmp;
2446 :
2447 28 : while(TRUE)
2448 : {
2449 119 : ret = inflate(&strm, Z_FINISH);
2450 119 : if( ret == Z_BUF_ERROR )
2451 : {
2452 28 : if( outptr == pszTmp )
2453 : {
2454 0 : inflateEnd(&strm);
2455 0 : if( pnOutBytes != NULL )
2456 0 : *pnOutBytes = 0;
2457 0 : return NULL;
2458 : }
2459 :
2460 28 : size_t nAlreadyWritten = nTmpSize - strm.avail_out;
2461 28 : nTmpSize = nTmpSize * 2;
2462 28 : char* pszTmpNew = (char*) VSIRealloc(pszTmp, nTmpSize + 1);
2463 28 : if( pszTmpNew == NULL )
2464 : {
2465 0 : VSIFree(pszTmp);
2466 0 : inflateEnd(&strm);
2467 0 : if( pnOutBytes != NULL )
2468 0 : *pnOutBytes = 0;
2469 0 : return NULL;
2470 : }
2471 28 : pszTmp = pszTmpNew;
2472 28 : strm.avail_out = nTmpSize - nAlreadyWritten;
2473 28 : strm.next_out = (Bytef*) (pszTmp + nAlreadyWritten);
2474 : }
2475 : else
2476 : break;
2477 : }
2478 :
2479 91 : if (ret == Z_OK || ret == Z_STREAM_END)
2480 : {
2481 90 : size_t nOutBytes = nTmpSize - strm.avail_out;
2482 : /* Nul-terminate if possible */
2483 90 : if( outptr != pszTmp || nOutBytes < nTmpSize )
2484 42 : pszTmp[nOutBytes] = '\0';
2485 90 : inflateEnd(&strm);
2486 90 : if( pnOutBytes != NULL )
2487 3 : *pnOutBytes = nOutBytes;
2488 90 : return pszTmp;
2489 : }
2490 : else
2491 : {
2492 1 : if( outptr != pszTmp )
2493 1 : VSIFree(pszTmp);
2494 1 : inflateEnd(&strm);
2495 1 : if( pnOutBytes != NULL )
2496 1 : *pnOutBytes = 0;
2497 1 : return NULL;
2498 : }
2499 : }
|