1 : /******************************************************************************
2 : * $Id: cpl_vsil_gzip.cpp 17636 2009-09-12 23:19:18Z warmerdam $
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, only reading is supported, for .gz read and write is supported.
74 : */
75 :
76 :
77 : #include "cpl_vsi_virtual.h"
78 : #include "cpl_string.h"
79 : #include "cpl_multiproc.h"
80 : #include <map>
81 :
82 : #include <zlib.h>
83 : #include "cpl_minizip_unzip.h"
84 :
85 : CPL_CVSID("$Id: cpl_vsil_gzip.cpp 17636 2009-09-12 23:19:18Z warmerdam $");
86 :
87 : #define Z_BUFSIZE 65536 /* original size is 16384 */
88 : static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
89 :
90 : /* gzip flag byte */
91 : #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
92 : #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
93 : #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
94 : #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
95 : #define COMMENT 0x10 /* bit 4 set: file comment present */
96 : #define RESERVED 0xE0 /* bits 5..7: reserved */
97 :
98 : #define ALLOC(size) malloc(size)
99 : #define TRYFREE(p) {if (p) free(p);}
100 :
101 : #define CPL_VSIL_GZ_RETURN_MINUS_ONE() \
102 : CPLError(CE_Failure, CPLE_AppDefined, "In file %s, at line %d, return -1", __FILE__, __LINE__)
103 :
104 : #define ENABLE_DEBUG 0
105 :
106 : /************************************************************************/
107 : /* ==================================================================== */
108 : /* VSIGZipHandle */
109 : /* ==================================================================== */
110 : /************************************************************************/
111 :
112 : typedef struct
113 : {
114 : vsi_l_offset uncompressed_pos;
115 : z_stream stream;
116 : uLong crc;
117 : int transparent;
118 : vsi_l_offset in;
119 : vsi_l_offset out;
120 : } GZipSnapshot;
121 :
122 : class VSIGZipHandle : public VSIVirtualHandle
123 : {
124 : VSIVirtualHandle* poBaseHandle;
125 : vsi_l_offset compressed_size;
126 : vsi_l_offset uncompressed_size;
127 : vsi_l_offset offsetEndCompressedData;
128 : unsigned int expected_crc;
129 : char *pszOptionalFileName;
130 :
131 : /* Fields from gz_stream structure */
132 : z_stream stream;
133 : int z_err; /* error code for last stream operation */
134 : int z_eof; /* set if end of input file */
135 : Byte *inbuf; /* input buffer */
136 : Byte *outbuf; /* output buffer */
137 : uLong crc; /* crc32 of uncompressed data */
138 : int transparent; /* 1 if input file is not a .gz file */
139 : vsi_l_offset startOff; /* startOff of compressed data in file (header skipped) */
140 : vsi_l_offset in; /* bytes into deflate or inflate */
141 : vsi_l_offset out; /* bytes out of deflate or inflate */
142 :
143 : GZipSnapshot* snapshots;
144 : vsi_l_offset snapshot_byte_interval; /* number of compressed bytes at which we create a "snapshot" */
145 :
146 : void check_header();
147 : int get_byte();
148 : int gzseek( vsi_l_offset nOffset, int nWhence );
149 : int gzrewind ();
150 : uLong getLong ();
151 :
152 : public:
153 :
154 : VSIGZipHandle(VSIVirtualHandle* poBaseHandle,
155 : const char* pszOptionalFileName,
156 : vsi_l_offset offset = 0,
157 : vsi_l_offset compressed_size = 0,
158 : vsi_l_offset uncompressed_size = 0,
159 : unsigned int expected_crc = 0,
160 : int transparent = 0);
161 : ~VSIGZipHandle();
162 :
163 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
164 : virtual vsi_l_offset Tell();
165 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
166 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
167 : virtual int Eof();
168 : virtual int Flush();
169 : virtual int Close();
170 :
171 : VSIGZipHandle* Duplicate();
172 : void CloseBaseHandle();
173 : };
174 :
175 : /************************************************************************/
176 : /* Duplicate() */
177 : /************************************************************************/
178 :
179 2 : VSIGZipHandle* VSIGZipHandle::Duplicate()
180 : {
181 2 : if (pszOptionalFileName == NULL)
182 0 : return NULL;
183 :
184 : VSIFilesystemHandler *poFSHandler =
185 2 : VSIFileManager::GetHandler( pszOptionalFileName );
186 :
187 : VSIVirtualHandle* poNewBaseHandle =
188 2 : poFSHandler->Open( pszOptionalFileName, "rb" );
189 :
190 2 : if (poNewBaseHandle == NULL)
191 0 : return NULL;
192 :
193 : VSIGZipHandle* poHandle = new VSIGZipHandle(poNewBaseHandle,
194 2 : pszOptionalFileName);
195 :
196 : /* Most important : duplicate the snapshots ! */
197 :
198 : unsigned int i;
199 2 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
200 : {
201 2 : if (snapshots[i].uncompressed_pos == 0)
202 2 : break;
203 :
204 0 : poHandle->snapshots[i].uncompressed_pos = snapshots[i].uncompressed_pos;
205 0 : inflateCopy( &poHandle->snapshots[i].stream, &snapshots[i].stream);
206 0 : poHandle->snapshots[i].crc = snapshots[i].crc;
207 0 : poHandle->snapshots[i].transparent = snapshots[i].transparent;
208 0 : poHandle->snapshots[i].in = snapshots[i].in;
209 0 : poHandle->snapshots[i].out = snapshots[i].out;
210 : }
211 :
212 2 : return poHandle;
213 : }
214 :
215 : /************************************************************************/
216 : /* CloseBaseHandle() */
217 : /************************************************************************/
218 :
219 1 : void VSIGZipHandle::CloseBaseHandle()
220 : {
221 1 : if (poBaseHandle)
222 1 : VSIFCloseL((FILE*)poBaseHandle);
223 1 : poBaseHandle = NULL;
224 1 : }
225 :
226 : /************************************************************************/
227 : /* VSIGZipHandle() */
228 : /************************************************************************/
229 :
230 18 : VSIGZipHandle::VSIGZipHandle(VSIVirtualHandle* poBaseHandle,
231 : const char* pszOptionalFileName,
232 : vsi_l_offset offset,
233 : vsi_l_offset compressed_size,
234 : vsi_l_offset uncompressed_size,
235 : unsigned int expected_crc,
236 18 : int transparent)
237 : {
238 18 : this->poBaseHandle = poBaseHandle;
239 18 : this->expected_crc = expected_crc;
240 18 : this->pszOptionalFileName = (pszOptionalFileName) ? CPLStrdup(pszOptionalFileName) : NULL;
241 18 : if (compressed_size)
242 : {
243 6 : this->compressed_size = compressed_size;
244 : }
245 : else
246 : {
247 12 : VSIFSeekL((FILE*)poBaseHandle, 0, SEEK_END);
248 12 : this->compressed_size = VSIFTellL((FILE*)poBaseHandle) - offset;
249 12 : compressed_size = this->compressed_size;
250 : }
251 18 : this->uncompressed_size = uncompressed_size;
252 18 : offsetEndCompressedData = offset + compressed_size;
253 :
254 18 : VSIFSeekL((FILE*)poBaseHandle, offset, SEEK_SET);
255 :
256 18 : stream.zalloc = (alloc_func)0;
257 18 : stream.zfree = (free_func)0;
258 18 : stream.opaque = (voidpf)0;
259 18 : stream.next_in = inbuf = Z_NULL;
260 18 : stream.next_out = outbuf = Z_NULL;
261 18 : stream.avail_in = stream.avail_out = 0;
262 18 : z_err = Z_OK;
263 18 : z_eof = 0;
264 18 : in = 0;
265 18 : out = 0;
266 18 : crc = crc32(0L, Z_NULL, 0);
267 18 : this->transparent = transparent;
268 :
269 18 : stream.next_in = inbuf = (Byte*)ALLOC(Z_BUFSIZE);
270 :
271 18 : int err = inflateInit2(&(stream), -MAX_WBITS);
272 : /* windowBits is passed < 0 to tell that there is no zlib header.
273 : * Note that in this case inflate *requires* an extra "dummy" byte
274 : * after the compressed stream in order to complete decompression and
275 : * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
276 : * present after the compressed stream.
277 : */
278 18 : if (err != Z_OK || inbuf == Z_NULL) {
279 0 : CPLError(CE_Failure, CPLE_NotSupported, "inflateInit2 init failed");
280 : }
281 18 : stream.avail_out = Z_BUFSIZE;
282 :
283 18 : if (offset == 0) check_header(); /* skip the .gz header */
284 18 : startOff = VSIFTellL((FILE*)poBaseHandle) - stream.avail_in;
285 :
286 18 : if (transparent == 0)
287 : {
288 18 : snapshot_byte_interval = MAX(Z_BUFSIZE, compressed_size / 100);
289 18 : snapshots = (GZipSnapshot*)CPLCalloc(sizeof(GZipSnapshot), (size_t) (compressed_size / snapshot_byte_interval + 1));
290 : }
291 : else
292 : {
293 0 : snapshots = NULL;
294 : }
295 18 : }
296 :
297 : /************************************************************************/
298 : /* ~VSIGZipHandle() */
299 : /************************************************************************/
300 :
301 36 : VSIGZipHandle::~VSIGZipHandle()
302 : {
303 18 : if (stream.state != NULL) {
304 18 : inflateEnd(&(stream));
305 : }
306 :
307 18 : TRYFREE(inbuf);
308 18 : TRYFREE(outbuf);
309 :
310 18 : if (snapshots != NULL)
311 : {
312 : unsigned int i;
313 36 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
314 : {
315 18 : if (snapshots[i].uncompressed_pos)
316 : {
317 11 : inflateEnd(&(snapshots[i].stream));
318 : }
319 : }
320 18 : CPLFree(snapshots);
321 : }
322 18 : CPLFree(pszOptionalFileName);
323 :
324 18 : if (poBaseHandle)
325 17 : VSIFCloseL((FILE*)poBaseHandle);
326 36 : }
327 :
328 : /************************************************************************/
329 : /* check_header() */
330 : /************************************************************************/
331 :
332 21 : void VSIGZipHandle::check_header()
333 : {
334 : int method; /* method byte */
335 : int flags; /* flags byte */
336 : uInt len;
337 : int c;
338 :
339 : /* Assure two bytes in the buffer so we can peek ahead -- handle case
340 : where first byte of header is at the end of the buffer after the last
341 : gzip segment */
342 21 : len = stream.avail_in;
343 21 : if (len < 2) {
344 21 : if (len) inbuf[0] = stream.next_in[0];
345 21 : errno = 0;
346 21 : len = (uInt)VSIFReadL(inbuf + len, 1, Z_BUFSIZE >> len, (FILE*)poBaseHandle);
347 : if (ENABLE_DEBUG) CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
348 : VSIFTellL((FILE*)poBaseHandle), offsetEndCompressedData);
349 21 : if (VSIFTellL((FILE*)poBaseHandle) > offsetEndCompressedData)
350 : {
351 0 : len = len + (uInt) (offsetEndCompressedData - VSIFTellL((FILE*)poBaseHandle));
352 0 : VSIFSeekL((FILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
353 : }
354 21 : if (len == 0 /* && ferror(file)*/)
355 : {
356 9 : if (VSIFTellL((FILE*)poBaseHandle) != offsetEndCompressedData)
357 0 : z_err = Z_ERRNO;
358 : }
359 21 : stream.avail_in += len;
360 21 : stream.next_in = inbuf;
361 21 : if (stream.avail_in < 2) {
362 9 : transparent = stream.avail_in;
363 9 : return;
364 : }
365 : }
366 :
367 : /* Peek ahead to check the gzip magic header */
368 36 : if (stream.next_in[0] != gz_magic[0] ||
369 24 : stream.next_in[1] != gz_magic[1]) {
370 0 : transparent = 1;
371 0 : return;
372 : }
373 12 : stream.avail_in -= 2;
374 12 : stream.next_in += 2;
375 :
376 : /* Check the rest of the gzip header */
377 12 : method = get_byte();
378 12 : flags = get_byte();
379 12 : if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
380 0 : z_err = Z_DATA_ERROR;
381 0 : return;
382 : }
383 :
384 : /* Discard time, xflags and OS code: */
385 12 : for (len = 0; len < 6; len++) (void)get_byte();
386 :
387 12 : if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
388 0 : len = (uInt)get_byte();
389 0 : len += ((uInt)get_byte())<<8;
390 : /* len is garbage if EOF but the loop below will quit anyway */
391 0 : while (len-- != 0 && get_byte() != EOF) ;
392 : }
393 12 : if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
394 0 : while ((c = get_byte()) != 0 && c != EOF) ;
395 : }
396 12 : if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
397 0 : while ((c = get_byte()) != 0 && c != EOF) ;
398 : }
399 12 : if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
400 0 : for (len = 0; len < 2; len++) (void)get_byte();
401 : }
402 12 : z_err = z_eof ? Z_DATA_ERROR : Z_OK;
403 : }
404 :
405 : /************************************************************************/
406 : /* get_byte() */
407 : /************************************************************************/
408 :
409 168 : int VSIGZipHandle::get_byte()
410 : {
411 168 : if (z_eof) return EOF;
412 168 : if (stream.avail_in == 0) {
413 0 : errno = 0;
414 0 : stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (FILE*)poBaseHandle);
415 : if (ENABLE_DEBUG) CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
416 : VSIFTellL((FILE*)poBaseHandle), offsetEndCompressedData);
417 0 : if (VSIFTellL((FILE*)poBaseHandle) > offsetEndCompressedData)
418 : {
419 0 : stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((FILE*)poBaseHandle));
420 0 : VSIFSeekL((FILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
421 : }
422 0 : if (stream.avail_in == 0) {
423 0 : z_eof = 1;
424 0 : if (VSIFTellL((FILE*)poBaseHandle) != offsetEndCompressedData)
425 0 : z_err = Z_ERRNO;
426 : /*if (ferror(file)) z_err = Z_ERRNO;*/
427 0 : return EOF;
428 : }
429 0 : stream.next_in = inbuf;
430 : }
431 168 : stream.avail_in--;
432 168 : return *(stream.next_in)++;
433 : }
434 :
435 : /************************************************************************/
436 : /* gzrewind() */
437 : /************************************************************************/
438 :
439 16 : int VSIGZipHandle::gzrewind ()
440 : {
441 16 : z_err = Z_OK;
442 16 : z_eof = 0;
443 16 : stream.avail_in = 0;
444 16 : stream.next_in = inbuf;
445 16 : crc = crc32(0L, Z_NULL, 0);
446 16 : if (!transparent) (void)inflateReset(&stream);
447 16 : in = 0;
448 16 : out = 0;
449 16 : return VSIFSeekL((FILE*)poBaseHandle, startOff, SEEK_SET);
450 : }
451 :
452 : /************************************************************************/
453 : /* Seek() */
454 : /************************************************************************/
455 :
456 183 : int VSIGZipHandle::Seek( vsi_l_offset nOffset, int nWhence )
457 : {
458 : /* The semantics of gzseek is different from ::Seek */
459 : /* It returns the current offset, where as ::Seek shoud return 0 */
460 : /* if successfull */
461 183 : int ret = gzseek(nOffset, nWhence);
462 183 : return (ret >= 0) ? 0 : ret;
463 : }
464 :
465 : /************************************************************************/
466 : /* gzseek() */
467 : /************************************************************************/
468 :
469 183 : int VSIGZipHandle::gzseek( vsi_l_offset offset, int whence )
470 : {
471 183 : vsi_l_offset original_offset = offset;
472 183 : int original_nWhence = whence;
473 :
474 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Seek(" CPL_FRMT_GUIB ",%d)", offset, whence);
475 :
476 183 : if (transparent)
477 : {
478 0 : stream.avail_in = 0;
479 0 : stream.next_in = inbuf;
480 0 : if (whence == SEEK_CUR)
481 : {
482 0 : if (out + offset < 0 || out + offset > compressed_size)
483 : {
484 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
485 0 : return -1L;
486 : }
487 :
488 0 : offset = startOff + out + offset;
489 : }
490 0 : else if (whence == SEEK_SET)
491 : {
492 0 : if (offset < 0 || offset > compressed_size)
493 : {
494 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
495 0 : return -1L;
496 : }
497 :
498 0 : offset = startOff + offset;
499 : }
500 0 : else if (whence == SEEK_END)
501 : {
502 : /* Commented test : because vsi_l_offset is unsigned (for the moment) */
503 : /* so no way to seek backward. See #1590 */
504 0 : if (offset > 0 /*|| -offset > compressed_size*/)
505 : {
506 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
507 0 : return -1L;
508 : }
509 :
510 0 : offset = startOff + compressed_size - offset;
511 : }
512 : else
513 : {
514 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
515 0 : return -1L;
516 : }
517 0 : if (VSIFSeekL((FILE*)poBaseHandle, offset, SEEK_SET) < 0)
518 : {
519 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
520 0 : return -1L;
521 : }
522 :
523 0 : in = out = offset - startOff;
524 : if (ENABLE_DEBUG) CPLDebug("GZIP", "return " CPL_FRMT_GUIB, offset);
525 0 : return (int) in;
526 : }
527 :
528 : /* whence == SEEK_END is unsuppored in original gzseek. */
529 183 : if (whence == SEEK_END)
530 : {
531 : /* If we known the uncompressed size, we can fake a jump to */
532 : /* the end of the stream */
533 12 : if (offset == 0 && uncompressed_size != 0)
534 : {
535 7 : out = uncompressed_size;
536 7 : return 1;
537 : }
538 :
539 : /* We don't know the uncompressed size. This is unfortunate. Let's do the slow version... */
540 : static int firstWarning = 1;
541 5 : if (compressed_size > 10 * 1024 * 1024 && firstWarning)
542 : {
543 : CPLError(CE_Warning, CPLE_AppDefined,
544 0 : "VSIFSeekL(xxx, SEEK_END) may be really slow on GZip streams.");
545 0 : firstWarning = 0;
546 : }
547 :
548 5 : whence = SEEK_CUR;
549 5 : offset = 1024 * 1024 * 1024;
550 5 : offset *= 1024 * 1024;
551 : }
552 :
553 176 : if (/*whence == SEEK_END ||*/
554 : z_err == Z_ERRNO || z_err == Z_DATA_ERROR) {
555 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
556 0 : return -1L;
557 : }
558 :
559 : /* Rest of function is for reading only */
560 :
561 : /* compute absolute position */
562 176 : if (whence == SEEK_CUR) {
563 8 : offset += out;
564 : }
565 : if (offset < 0) {
566 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
567 : return -1L;
568 : }
569 :
570 : /* For a negative seek, rewind and use positive seek */
571 176 : if (offset >= out) {
572 160 : offset -= out;
573 16 : } else if (gzrewind() < 0) {
574 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
575 0 : return -1L;
576 : }
577 :
578 : unsigned int i;
579 176 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
580 : {
581 176 : if (snapshots[i].uncompressed_pos == 0)
582 36 : break;
583 140 : if (snapshots[i].out <= out + offset &&
584 0 : (i == compressed_size / snapshot_byte_interval || snapshots[i+1].out == 0 || snapshots[i+1].out > out+offset))
585 : {
586 140 : if (out >= snapshots[i].out)
587 140 : break;
588 :
589 : if (ENABLE_DEBUG)
590 : CPLDebug("SNAPSHOT", "using snapshot %d : uncompressed_pos(snapshot)=" CPL_FRMT_GUIB
591 : " in(snapshot)=" CPL_FRMT_GUIB
592 : " out(snapshot)=" CPL_FRMT_GUIB
593 : " out=" CPL_FRMT_GUIB
594 : " offset=" CPL_FRMT_GUIB,
595 : i, snapshots[i].uncompressed_pos, snapshots[i].in, snapshots[i].out, out, offset);
596 0 : offset = out + offset - snapshots[i].out;
597 0 : VSIFSeekL((FILE*)poBaseHandle, snapshots[i].uncompressed_pos, SEEK_SET);
598 0 : inflateEnd(&stream);
599 0 : inflateCopy(&stream, &snapshots[i].stream);
600 0 : crc = snapshots[i].crc;
601 0 : transparent = snapshots[i].transparent;
602 0 : in = snapshots[i].in;
603 0 : out = snapshots[i].out;
604 0 : break;
605 : }
606 : }
607 :
608 : /* offset is now the number of bytes to skip. */
609 :
610 176 : if (offset != 0 && outbuf == Z_NULL) {
611 11 : outbuf = (Byte*)ALLOC(Z_BUFSIZE);
612 11 : if (outbuf == Z_NULL) {
613 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
614 0 : return -1L;
615 : }
616 : }
617 :
618 176 : if (original_nWhence == SEEK_END && z_err == Z_STREAM_END)
619 : {
620 : if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out);
621 1 : return (int) out;
622 : }
623 :
624 383 : while (offset > 0) {
625 37 : int size = Z_BUFSIZE;
626 37 : if (offset < Z_BUFSIZE) size = (int)offset;
627 :
628 37 : int read_size = Read(outbuf, 1, (uInt)size);
629 37 : if (read_size == 0) {
630 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
631 0 : return -1L;
632 : }
633 37 : if (original_nWhence == SEEK_END)
634 : {
635 4 : if (size != read_size)
636 : {
637 4 : z_err = Z_STREAM_END;
638 4 : break;
639 : }
640 : }
641 33 : offset -= read_size;
642 : }
643 : if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out);
644 :
645 175 : if (original_offset == 0 && original_nWhence == SEEK_END)
646 4 : uncompressed_size = out;
647 :
648 175 : return (int) out;
649 : }
650 :
651 : /************************************************************************/
652 : /* Tell() */
653 : /************************************************************************/
654 :
655 324 : vsi_l_offset VSIGZipHandle::Tell()
656 : {
657 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Tell() = " CPL_FRMT_GUIB, out);
658 324 : return out;
659 : }
660 :
661 : /************************************************************************/
662 : /* Read() */
663 : /************************************************************************/
664 :
665 882 : size_t VSIGZipHandle::Read( void *buf, size_t nSize, size_t nMemb )
666 : {
667 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read(%p, %d, %d)", buf, (int)nSize, (int)nMemb);
668 :
669 882 : unsigned len = nSize * nMemb;
670 :
671 882 : Bytef *pStart = (Bytef*)buf; /* startOffing point for crc computation */
672 : Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
673 :
674 882 : if (z_err == Z_DATA_ERROR || z_err == Z_ERRNO)
675 : {
676 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
677 0 : return 0;
678 : }
679 882 : if (z_err == Z_STREAM_END)
680 : {
681 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read: Eof");
682 3 : return 0; /* EOF */
683 : }
684 :
685 879 : next_out = (Byte*)buf;
686 879 : stream.next_out = (Bytef*)buf;
687 879 : stream.avail_out = len;
688 :
689 2622 : while (stream.avail_out != 0) {
690 :
691 879 : if (transparent) {
692 : /* Copy first the lookahead bytes: */
693 0 : uInt n = stream.avail_in;
694 0 : if (n > stream.avail_out) n = stream.avail_out;
695 0 : if (n > 0) {
696 0 : memcpy (stream.next_out, stream.next_in, n);
697 0 : next_out += n;
698 0 : stream.next_out = next_out;
699 0 : stream.next_in += n;
700 0 : stream.avail_out -= n;
701 0 : stream.avail_in -= n;
702 : }
703 0 : if (stream.avail_out > 0) {
704 : stream.avail_out -=
705 0 : (uInt)VSIFReadL(next_out, 1, stream.avail_out, (FILE*)poBaseHandle);
706 : }
707 0 : len -= stream.avail_out;
708 0 : in += len;
709 0 : out += len;
710 0 : if (len == 0) z_eof = 1;
711 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read return %d", (int)(len / nSize));
712 0 : return (int)len / nSize;
713 : }
714 879 : if (stream.avail_in == 0 && !z_eof)
715 : {
716 21 : vsi_l_offset uncompressed_pos = VSIFTellL((FILE*)poBaseHandle);
717 21 : GZipSnapshot* snapshot = &snapshots[(uncompressed_pos - startOff) / snapshot_byte_interval];
718 21 : if (uncompressed_pos >= 0 && snapshot->uncompressed_pos == 0)
719 : {
720 11 : snapshot->crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
721 : if (ENABLE_DEBUG)
722 : CPLDebug("SNAPSHOT",
723 : "creating snapshot %d : uncompressed_pos=" CPL_FRMT_GUIB
724 : " in=" CPL_FRMT_GUIB
725 : " out=" CPL_FRMT_GUIB
726 : " crc=%X",
727 : (int)((uncompressed_pos - startOff) / snapshot_byte_interval),
728 : uncompressed_pos, in, out, (unsigned int)snapshot->crc);
729 11 : snapshot->uncompressed_pos = uncompressed_pos;
730 11 : inflateCopy(&snapshot->stream, &stream);
731 11 : snapshot->transparent = transparent;
732 11 : snapshot->in = in;
733 11 : snapshot->out = out;
734 : }
735 :
736 21 : errno = 0;
737 21 : stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (FILE*)poBaseHandle);
738 : if (ENABLE_DEBUG)
739 : CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
740 : VSIFTellL((FILE*)poBaseHandle), offsetEndCompressedData);
741 21 : if (VSIFTellL((FILE*)poBaseHandle) > offsetEndCompressedData)
742 : {
743 : if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in before = %d", stream.avail_in);
744 9 : stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((FILE*)poBaseHandle));
745 9 : VSIFSeekL((FILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
746 9 : if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in after = %d", stream.avail_in);
747 : }
748 21 : if (stream.avail_in == 0) {
749 0 : z_eof = 1;
750 0 : if (VSIFTellL((FILE*)poBaseHandle) != offsetEndCompressedData)
751 : {
752 0 : z_err = Z_ERRNO;
753 0 : break;
754 : }
755 : /*if (ferror (file)) {
756 : z_err = Z_ERRNO;
757 : break;
758 : }*/
759 : }
760 21 : stream.next_in = inbuf;
761 : }
762 879 : in += stream.avail_in;
763 879 : out += stream.avail_out;
764 879 : z_err = inflate(& (stream), Z_NO_FLUSH);
765 879 : in -= stream.avail_in;
766 879 : out -= stream.avail_out;
767 :
768 879 : if (z_err == Z_STREAM_END) {
769 : /* Check CRC and original size */
770 15 : crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
771 15 : pStart = stream.next_out;
772 15 : if (expected_crc)
773 : {
774 6 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Computed CRC = %X. Expected CRC = %X", (unsigned int)crc, expected_crc);
775 : }
776 15 : if (expected_crc != 0 && expected_crc != crc)
777 : {
778 0 : CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, expected_crc);
779 0 : z_err = Z_DATA_ERROR;
780 : }
781 15 : else if (expected_crc == 0)
782 : {
783 9 : unsigned int read_crc = getLong();
784 9 : if (read_crc != crc)
785 : {
786 0 : CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, read_crc);
787 0 : z_err = Z_DATA_ERROR;
788 : }
789 : else
790 : {
791 9 : (void)getLong();
792 : /* The uncompressed length returned by above getlong() may be
793 : * different from out in case of concatenated .gz files.
794 : * Check for such files:
795 : */
796 9 : check_header();
797 9 : if (z_err == Z_OK) {
798 0 : inflateReset(& (stream));
799 0 : crc = crc32(0L, Z_NULL, 0);
800 : }
801 : }
802 : }
803 : }
804 879 : if (z_err != Z_OK || z_eof) break;
805 : }
806 879 : crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
807 :
808 879 : if (len == stream.avail_out &&
809 : (z_err == Z_DATA_ERROR || z_err == Z_ERRNO))
810 : {
811 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
812 0 : return 0;
813 : }
814 : if (ENABLE_DEBUG)
815 : CPLDebug("GZIP", "Read return %d (z_err=%d, z_eof=%d)",
816 : (int)((len - stream.avail_out) / nSize), z_err, z_eof);
817 879 : return (int)(len - stream.avail_out) / nSize;
818 : }
819 :
820 : /************************************************************************/
821 : /* getLong() */
822 : /************************************************************************/
823 :
824 18 : uLong VSIGZipHandle::getLong ()
825 : {
826 18 : uLong x = (uLong)get_byte();
827 : int c;
828 :
829 18 : x += ((uLong)get_byte())<<8;
830 18 : x += ((uLong)get_byte())<<16;
831 18 : c = get_byte();
832 18 : if (c == EOF) z_err = Z_DATA_ERROR;
833 18 : x += ((uLong)c)<<24;
834 18 : return x;
835 : }
836 :
837 : /************************************************************************/
838 : /* Write() */
839 : /************************************************************************/
840 :
841 0 : size_t VSIGZipHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb )
842 : {
843 0 : CPLError(CE_Failure, CPLE_NotSupported, "VSIFWriteL is not supported on GZip streams\n");
844 0 : return 0;
845 : }
846 :
847 : /************************************************************************/
848 : /* Eof() */
849 : /************************************************************************/
850 :
851 :
852 0 : int VSIGZipHandle::Eof()
853 : {
854 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Eof()");
855 0 : if (z_eof) return 1;
856 0 : return z_err == Z_STREAM_END;
857 : }
858 :
859 : /************************************************************************/
860 : /* Flush() */
861 : /************************************************************************/
862 :
863 0 : int VSIGZipHandle::Flush()
864 : {
865 0 : return 0;
866 : }
867 :
868 : /************************************************************************/
869 : /* Close() */
870 : /************************************************************************/
871 :
872 17 : int VSIGZipHandle::Close()
873 : {
874 17 : return 0;
875 : }
876 :
877 :
878 : /************************************************************************/
879 : /* ==================================================================== */
880 : /* VSIGZipWriteHandle */
881 : /* ==================================================================== */
882 : /************************************************************************/
883 :
884 : class VSIGZipWriteHandle : public VSIVirtualHandle
885 : {
886 : VSIVirtualHandle* poBaseHandle;
887 : z_stream sStream;
888 : Byte *pabyInBuf;
889 : Byte *pabyOutBuf;
890 : bool bCompressActive;
891 : vsi_l_offset nCurOffset;
892 : GUInt32 nCRC;
893 :
894 : public:
895 :
896 : VSIGZipWriteHandle(VSIVirtualHandle* poBaseHandle);
897 :
898 : ~VSIGZipWriteHandle();
899 :
900 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
901 : virtual vsi_l_offset Tell();
902 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
903 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
904 : virtual int Eof();
905 : virtual int Flush();
906 : virtual int Close();
907 : };
908 :
909 : /************************************************************************/
910 : /* VSIGZipWriteHandle() */
911 : /************************************************************************/
912 :
913 17 : VSIGZipWriteHandle::VSIGZipWriteHandle( VSIVirtualHandle *poBaseHandle )
914 :
915 : {
916 17 : nCurOffset = 0;
917 :
918 17 : this->poBaseHandle = poBaseHandle;
919 :
920 17 : nCRC = crc32(0L, Z_NULL, 0);
921 17 : sStream.zalloc = (alloc_func)0;
922 17 : sStream.zfree = (free_func)0;
923 17 : sStream.opaque = (voidpf)0;
924 17 : sStream.next_in = Z_NULL;
925 17 : sStream.next_out = Z_NULL;
926 17 : sStream.avail_in = sStream.avail_out = 0;
927 :
928 17 : pabyInBuf = (Byte *) CPLMalloc( Z_BUFSIZE );
929 17 : sStream.next_in = pabyInBuf;
930 :
931 17 : pabyOutBuf = (Byte *) CPLMalloc( Z_BUFSIZE );
932 :
933 17 : if( deflateInit2( &sStream, Z_DEFAULT_COMPRESSION,
934 : Z_DEFLATED, -MAX_WBITS, 8,
935 : Z_DEFAULT_STRATEGY ) != Z_OK )
936 0 : bCompressActive = false;
937 : else
938 : {
939 : char header[11];
940 :
941 : /* Write a very simple .gz header:
942 : */
943 17 : sprintf( header, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
944 : Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/,
945 17 : 0x03 );
946 17 : poBaseHandle->Write( header, 1, 10 );
947 :
948 17 : bCompressActive = true;
949 : }
950 17 : }
951 :
952 : /************************************************************************/
953 : /* ~VSIGZipWriteHandle() */
954 : /************************************************************************/
955 :
956 34 : VSIGZipWriteHandle::~VSIGZipWriteHandle()
957 :
958 : {
959 17 : if( bCompressActive )
960 0 : Close();
961 :
962 17 : CPLFree( pabyInBuf );
963 17 : CPLFree( pabyOutBuf );
964 34 : }
965 :
966 : /************************************************************************/
967 : /* Close() */
968 : /************************************************************************/
969 :
970 17 : int VSIGZipWriteHandle::Close()
971 :
972 : {
973 17 : if( bCompressActive )
974 : {
975 17 : sStream.next_out = pabyOutBuf;
976 17 : sStream.avail_out = Z_BUFSIZE;
977 :
978 17 : deflate( &sStream, Z_FINISH );
979 :
980 17 : size_t nOutBytes = Z_BUFSIZE - sStream.avail_out;
981 :
982 17 : if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes )
983 0 : return EOF;
984 :
985 17 : deflateEnd( &sStream );
986 :
987 : GUInt32 anTrailer[2];
988 :
989 17 : anTrailer[0] = CPL_LSBWORD32( nCRC );
990 17 : anTrailer[1] = CPL_LSBWORD32( (GUInt32) nCurOffset );
991 :
992 17 : poBaseHandle->Write( anTrailer, 1, 8 );
993 17 : poBaseHandle->Close();
994 :
995 17 : delete poBaseHandle;
996 :
997 17 : bCompressActive = false;
998 : }
999 :
1000 17 : return 0;
1001 : }
1002 :
1003 : /************************************************************************/
1004 : /* Read() */
1005 : /************************************************************************/
1006 :
1007 0 : size_t VSIGZipWriteHandle::Read( void *pBuffer, size_t nSize, size_t nMemb )
1008 :
1009 : {
1010 0 : CPLError(CE_Failure, CPLE_NotSupported, "VSIFReadL is not supported on GZip write streams\n");
1011 0 : return 0;
1012 : }
1013 :
1014 : /************************************************************************/
1015 : /* Write() */
1016 : /************************************************************************/
1017 :
1018 661 : size_t VSIGZipWriteHandle::Write( const void *pBuffer,
1019 : size_t nSize, size_t nMemb )
1020 :
1021 : {
1022 : int nBytesToWrite, nNextByte;
1023 :
1024 661 : nBytesToWrite = (int) (nSize * nMemb);
1025 661 : nNextByte = 0;
1026 :
1027 661 : nCRC = crc32(nCRC, (const Bytef *)pBuffer, nBytesToWrite);
1028 :
1029 661 : if( !bCompressActive )
1030 0 : return 0;
1031 :
1032 1983 : while( nNextByte < nBytesToWrite )
1033 : {
1034 661 : sStream.next_out = pabyOutBuf;
1035 661 : sStream.avail_out = Z_BUFSIZE;
1036 :
1037 661 : if( sStream.avail_in > 0 )
1038 0 : memmove( pabyInBuf, sStream.next_in, sStream.avail_in );
1039 :
1040 661 : int nNewBytesToWrite = MIN((int) (Z_BUFSIZE-sStream.avail_in),
1041 : nBytesToWrite - nNextByte);
1042 : memcpy( pabyInBuf + sStream.avail_in,
1043 : ((Byte *) pBuffer) + nNextByte,
1044 661 : nNewBytesToWrite );
1045 :
1046 661 : sStream.next_in = pabyInBuf;
1047 661 : sStream.avail_in += nNewBytesToWrite;
1048 :
1049 661 : deflate( &sStream, Z_NO_FLUSH );
1050 :
1051 661 : size_t nOutBytes = Z_BUFSIZE - sStream.avail_out;
1052 :
1053 661 : if( nOutBytes > 0 )
1054 : {
1055 0 : if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes )
1056 0 : return 0;
1057 : }
1058 :
1059 661 : nNextByte += nNewBytesToWrite;
1060 661 : nCurOffset += nNewBytesToWrite;
1061 : }
1062 :
1063 661 : return nMemb;
1064 : }
1065 :
1066 : /************************************************************************/
1067 : /* Flush() */
1068 : /************************************************************************/
1069 :
1070 0 : int VSIGZipWriteHandle::Flush()
1071 :
1072 : {
1073 : // we *could* do something for this but for now we choose not to.
1074 :
1075 0 : return 0;
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* Eof() */
1080 : /************************************************************************/
1081 :
1082 0 : int VSIGZipWriteHandle::Eof()
1083 :
1084 : {
1085 0 : return 1;
1086 : }
1087 :
1088 : /************************************************************************/
1089 : /* Seek() */
1090 : /************************************************************************/
1091 :
1092 0 : int VSIGZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence )
1093 :
1094 : {
1095 0 : if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) )
1096 0 : return 0;
1097 0 : else if( nWhence == SEEK_SET && nOffset == nCurOffset )
1098 0 : return 0;
1099 : else
1100 : {
1101 : CPLError(CE_Failure, CPLE_NotSupported,
1102 0 : "Seeking on writable compressed data streams not supported." );
1103 :
1104 0 : return -1;
1105 : }
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* Tell() */
1110 : /************************************************************************/
1111 :
1112 0 : vsi_l_offset VSIGZipWriteHandle::Tell()
1113 :
1114 : {
1115 0 : return nCurOffset;
1116 : }
1117 :
1118 :
1119 : /************************************************************************/
1120 : /* ==================================================================== */
1121 : /* VSIGZipFilesystemHandler */
1122 : /* ==================================================================== */
1123 : /************************************************************************/
1124 :
1125 : class VSIGZipFilesystemHandler : public VSIFilesystemHandler
1126 : {
1127 : void* hMutex;
1128 : char* pszLastStatedFileName;
1129 : VSIGZipHandle* poHandleLastStateFile;
1130 : VSIStatBufL statBuf;
1131 :
1132 : public:
1133 : VSIGZipFilesystemHandler();
1134 : ~VSIGZipFilesystemHandler();
1135 :
1136 : virtual VSIVirtualHandle *Open( const char *pszFilename,
1137 : const char *pszAccess);
1138 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf );
1139 : virtual int Unlink( const char *pszFilename );
1140 : virtual int Rename( const char *oldpath, const char *newpath );
1141 : virtual int Mkdir( const char *pszDirname, long nMode );
1142 : virtual int Rmdir( const char *pszDirname );
1143 : virtual char **ReadDir( const char *pszDirname );
1144 :
1145 : void CacheLastStatedFile( const char *pszFilename,
1146 : VSIGZipHandle* poHandle,
1147 : VSIStatBufL* pStatBuf);
1148 : };
1149 :
1150 :
1151 : /************************************************************************/
1152 : /* VSIGZipFilesystemHandler() */
1153 : /************************************************************************/
1154 :
1155 379 : VSIGZipFilesystemHandler::VSIGZipFilesystemHandler()
1156 : {
1157 379 : hMutex = NULL;
1158 :
1159 379 : pszLastStatedFileName = NULL;
1160 379 : poHandleLastStateFile = NULL;
1161 379 : }
1162 :
1163 : /************************************************************************/
1164 : /* ~VSIGZipFilesystemHandler() */
1165 : /************************************************************************/
1166 :
1167 726 : VSIGZipFilesystemHandler::~VSIGZipFilesystemHandler()
1168 : {
1169 363 : CPLFree(pszLastStatedFileName);
1170 363 : if (poHandleLastStateFile)
1171 0 : delete poHandleLastStateFile;
1172 :
1173 363 : if( hMutex != NULL )
1174 0 : CPLDestroyMutex( hMutex );
1175 363 : hMutex = NULL;
1176 726 : }
1177 :
1178 :
1179 : /************************************************************************/
1180 : /* CacheLastStatedFile() */
1181 : /************************************************************************/
1182 :
1183 1 : void VSIGZipFilesystemHandler::CacheLastStatedFile( const char *pszFilename,
1184 : VSIGZipHandle* poHandle,
1185 : VSIStatBufL* pStatBuf)
1186 : {
1187 1 : CPLMutexHolder oHolder(&hMutex);
1188 :
1189 1 : CPLFree(pszLastStatedFileName);
1190 1 : if (poHandleLastStateFile)
1191 0 : delete poHandleLastStateFile;
1192 :
1193 1 : poHandleLastStateFile = poHandle;
1194 : /* There's no use in keeping the base handle open for that cached */
1195 : /* VSIGZipHandle object */
1196 1 : poHandle->CloseBaseHandle();
1197 1 : pszLastStatedFileName = CPLStrdup(pszFilename);
1198 1 : memcpy(&statBuf, pStatBuf, sizeof(VSIStatBufL));
1199 1 : }
1200 :
1201 : /************************************************************************/
1202 : /* Open() */
1203 : /************************************************************************/
1204 :
1205 45 : VSIVirtualHandle* VSIGZipFilesystemHandler::Open( const char *pszFilename,
1206 : const char *pszAccess)
1207 : {
1208 : VSIFilesystemHandler *poFSHandler =
1209 45 : VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/"));
1210 :
1211 : /* -------------------------------------------------------------------- */
1212 : /* Is this an attempt to write a new file without update (w+) */
1213 : /* access? If so, create a writable handle for the underlying */
1214 : /* filename. */
1215 : /* -------------------------------------------------------------------- */
1216 45 : if (strchr(pszAccess, 'w') != NULL )
1217 : {
1218 17 : if( strchr(pszAccess, '+') != NULL )
1219 : {
1220 : CPLError(CE_Failure, CPLE_AppDefined,
1221 0 : "Write+update (w+) not supported for /vsigzip, only read-only or write-only.");
1222 0 : return NULL;
1223 : }
1224 :
1225 : VSIVirtualHandle* poVirtualHandle =
1226 17 : poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "wb" );
1227 :
1228 17 : if (poVirtualHandle == NULL)
1229 0 : return NULL;
1230 :
1231 : else
1232 17 : return new VSIGZipWriteHandle( poVirtualHandle );
1233 : }
1234 :
1235 : /* -------------------------------------------------------------------- */
1236 : /* Otherwise we are in the read access case. */
1237 : /* -------------------------------------------------------------------- */
1238 28 : CPLMutexHolder oHolder(&hMutex);
1239 :
1240 28 : if (pszLastStatedFileName != NULL &&
1241 : strcmp(pszFilename, pszLastStatedFileName) == 0 &&
1242 : EQUAL(pszAccess, "rb"))
1243 : {
1244 2 : VSIVirtualHandle* poHandle = poHandleLastStateFile->Duplicate();
1245 2 : if (poHandle)
1246 2 : return poHandle;
1247 : }
1248 :
1249 : unsigned char signature[2];
1250 :
1251 : VSIVirtualHandle* poVirtualHandle =
1252 26 : poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "rb" );
1253 :
1254 26 : if (poVirtualHandle == NULL)
1255 16 : return NULL;
1256 :
1257 10 : if (VSIFReadL(signature, 1, 2, (FILE*)poVirtualHandle) != 2)
1258 0 : return NULL;
1259 :
1260 10 : if (signature[0] != gz_magic[0] || signature[1] != gz_magic[1])
1261 0 : return NULL;
1262 :
1263 10 : CPLFree(pszLastStatedFileName);
1264 10 : pszLastStatedFileName = NULL;
1265 10 : if (poHandleLastStateFile)
1266 1 : delete poHandleLastStateFile;
1267 10 : poHandleLastStateFile = NULL;
1268 :
1269 10 : return new VSIGZipHandle(poVirtualHandle, pszFilename + strlen("/vsigzip/"));
1270 : }
1271 :
1272 : /************************************************************************/
1273 : /* Stat() */
1274 : /************************************************************************/
1275 :
1276 16 : int VSIGZipFilesystemHandler::Stat( const char *pszFilename, VSIStatBufL *pStatBuf )
1277 : {
1278 16 : CPLMutexHolder oHolder(&hMutex);
1279 :
1280 16 : if (pszLastStatedFileName != NULL &&
1281 : strcmp(pszFilename, pszLastStatedFileName) == 0)
1282 : {
1283 0 : memcpy(pStatBuf, &statBuf, sizeof(VSIStatBufL));
1284 0 : return 0;
1285 : }
1286 :
1287 : /* Begin by doing a stat on the real file */
1288 16 : int ret = VSIStatL(pszFilename+strlen("/vsigzip/"), pStatBuf);
1289 :
1290 16 : if (ret == 0)
1291 : {
1292 4 : CPLString osCacheFilename(pszFilename+strlen("/vsigzip/"));
1293 4 : osCacheFilename += ".properties";
1294 :
1295 : /* Can we save a bit of seeking by using a .properties file ? */
1296 4 : FILE* fpCacheLength = VSIFOpen(osCacheFilename.c_str(), "rt");
1297 4 : if (fpCacheLength)
1298 : {
1299 : char szBuffer[80];
1300 3 : szBuffer[79] = 0;
1301 3 : GUIntBig nCompressedSize = 0;
1302 3 : GUIntBig nUncompressedSize = 0;
1303 12 : while (CPLFGets(szBuffer, 79, fpCacheLength))
1304 : {
1305 6 : if (EQUALN(szBuffer, "compressed_size=", strlen("compressed_size=")))
1306 : {
1307 3 : char* pszBuffer = szBuffer + strlen("compressed_size=");
1308 : nCompressedSize =
1309 3 : CPLScanUIntBig(pszBuffer, strlen(pszBuffer));
1310 : }
1311 3 : else if (EQUALN(szBuffer, "uncompressed_size=", strlen("uncompressed_size=")))
1312 : {
1313 3 : char* pszBuffer = szBuffer + strlen("uncompressed_size=");
1314 : nUncompressedSize =
1315 3 : CPLScanUIntBig(pszBuffer, strlen(pszBuffer));
1316 : }
1317 : }
1318 :
1319 3 : VSIFClose(fpCacheLength);
1320 :
1321 3 : if (nCompressedSize == (GUIntBig) pStatBuf->st_size)
1322 : {
1323 : /* Patch with the uncompressed size */
1324 3 : pStatBuf->st_size = (long)nUncompressedSize;
1325 3 : return ret;
1326 : }
1327 : }
1328 :
1329 : /* No, then seek at the end of the data (slow) */
1330 : VSIGZipHandle* poHandle =
1331 1 : (VSIGZipHandle*)VSIGZipFilesystemHandler::Open(pszFilename, "rb");
1332 1 : if (poHandle)
1333 : {
1334 : GUIntBig uncompressed_size;
1335 1 : GUIntBig compressed_size = (GUIntBig) pStatBuf->st_size;
1336 1 : poHandle->Seek(0, SEEK_END);
1337 1 : uncompressed_size = (GUIntBig) poHandle->Tell();
1338 1 : poHandle->Seek(0, SEEK_SET);
1339 :
1340 : /* Patch with the uncompressed size */
1341 1 : pStatBuf->st_size = (long)uncompressed_size;
1342 1 : CacheLastStatedFile(pszFilename, poHandle, pStatBuf);
1343 :
1344 : /* Write a .properties file to avoid seeking next time */
1345 1 : fpCacheLength = VSIFOpen(osCacheFilename.c_str(), "wt");
1346 1 : if (fpCacheLength)
1347 : {
1348 : char* pszFirstNonSpace;
1349 : char szBuffer[32];
1350 1 : szBuffer[31] = 0;
1351 :
1352 1 : CPLPrintUIntBig(szBuffer, compressed_size, 31);
1353 1 : pszFirstNonSpace = szBuffer;
1354 1 : while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++;
1355 1 : VSIFPrintf(fpCacheLength, "compressed_size=%s\n", pszFirstNonSpace);
1356 :
1357 1 : CPLPrintUIntBig(szBuffer, uncompressed_size, 31);
1358 1 : pszFirstNonSpace = szBuffer;
1359 1 : while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++;
1360 1 : VSIFPrintf(fpCacheLength, "uncompressed_size=%s\n", pszFirstNonSpace);
1361 :
1362 1 : VSIFClose(fpCacheLength);
1363 : }
1364 : }
1365 : else
1366 : {
1367 0 : ret = -1;
1368 0 : }
1369 : }
1370 :
1371 13 : return ret;
1372 : }
1373 :
1374 : /************************************************************************/
1375 : /* Unlink() */
1376 : /************************************************************************/
1377 :
1378 0 : int VSIGZipFilesystemHandler::Unlink( const char *pszFilename )
1379 : {
1380 0 : return -1;
1381 : }
1382 :
1383 : /************************************************************************/
1384 : /* Rename() */
1385 : /************************************************************************/
1386 :
1387 0 : int VSIGZipFilesystemHandler::Rename( const char *oldpath, const char *newpath )
1388 : {
1389 0 : return -1;
1390 : }
1391 :
1392 : /************************************************************************/
1393 : /* Mkdir() */
1394 : /************************************************************************/
1395 :
1396 0 : int VSIGZipFilesystemHandler::Mkdir( const char *pszDirname, long nMode )
1397 : {
1398 0 : return -1;
1399 : }
1400 : /************************************************************************/
1401 : /* Rmdir() */
1402 : /************************************************************************/
1403 :
1404 0 : int VSIGZipFilesystemHandler::Rmdir( const char *pszDirname )
1405 : {
1406 0 : return -1;
1407 : }
1408 :
1409 : /************************************************************************/
1410 : /* ReadDir() */
1411 : /************************************************************************/
1412 :
1413 3 : char** VSIGZipFilesystemHandler::ReadDir( const char *pszDirname )
1414 : {
1415 3 : return NULL;
1416 : }
1417 :
1418 : /************************************************************************/
1419 : /* VSIInstallGZipFileHandler() */
1420 : /************************************************************************/
1421 :
1422 :
1423 : /**
1424 : * \brief Install GZip file system handler.
1425 : *
1426 : * A special file handler is installed that allows reading on-the-fly and
1427 : * writing in GZip (.gz) files.
1428 : *
1429 : * All portions of the file system underneath the base
1430 : * path "/vsigzip/" will be handled by this driver.
1431 : *
1432 : * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
1433 : *
1434 : */
1435 :
1436 379 : void VSIInstallGZipFileHandler(void)
1437 : {
1438 379 : VSIFileManager::InstallHandler( "/vsigzip/", new VSIGZipFilesystemHandler );
1439 379 : }
1440 :
1441 :
1442 :
1443 : /************************************************************************/
1444 : /* ==================================================================== */
1445 : /* VSIZipFilesystemHandler */
1446 : /* ==================================================================== */
1447 : /************************************************************************/
1448 :
1449 : typedef struct
1450 : {
1451 : char *fileName;
1452 : vsi_l_offset uncompressed_size;
1453 : unz_file_pos file_pos;
1454 : int bIsDir;
1455 : } ZIPEntry;
1456 :
1457 : typedef struct
1458 : {
1459 : int nEntries;
1460 : ZIPEntry* entries;
1461 : } ZIPContent;
1462 :
1463 : class VSIZipFilesystemHandler : public VSIFilesystemHandler
1464 : {
1465 : void* hMutex;
1466 : /* We use a cache that contains the list of files containes in a ZIP file as */
1467 : /* unzip.c is quite inefficient in listing them. This speeds up access to ZIP files */
1468 : /* containing ~1000 files like a CADRG product */
1469 : std::map<CPLString,ZIPContent*> oFileList;
1470 :
1471 : public:
1472 : VSIZipFilesystemHandler();
1473 : ~VSIZipFilesystemHandler();
1474 :
1475 : virtual VSIVirtualHandle *Open( const char *pszFilename,
1476 : const char *pszAccess);
1477 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf );
1478 : virtual int Unlink( const char *pszFilename );
1479 : virtual int Rename( const char *oldpath, const char *newpath );
1480 : virtual int Mkdir( const char *pszDirname, long nMode );
1481 : virtual int Rmdir( const char *pszDirname );
1482 : virtual char **ReadDir( const char *pszDirname );
1483 :
1484 : const ZIPContent* GetContentOfZip(const char* zipFilename, unzFile unzF = NULL);
1485 : char* SplitFilename(const char *pszFilename, CPLString &osZipInFileName);
1486 : unzFile OpenZIPFile(const char* zipFilename, const char* zipInFileName);
1487 : int FindFileInZip(const char* zipFilename, const char* zipInFileName, const ZIPEntry** zipEntry);
1488 : };
1489 :
1490 : /************************************************************************/
1491 : /* VSIZipFilesystemHandler() */
1492 : /************************************************************************/
1493 :
1494 379 : VSIZipFilesystemHandler::VSIZipFilesystemHandler()
1495 : {
1496 379 : hMutex = NULL;
1497 379 : }
1498 :
1499 : /************************************************************************/
1500 : /* ~VSIZipFilesystemHandler() */
1501 : /************************************************************************/
1502 :
1503 726 : VSIZipFilesystemHandler::~VSIZipFilesystemHandler()
1504 :
1505 : {
1506 363 : std::map<CPLString,ZIPContent*>::const_iterator iter;
1507 :
1508 363 : for( iter = oFileList.begin(); iter != oFileList.end(); iter++ )
1509 : {
1510 0 : ZIPContent* content = iter->second;
1511 : int i;
1512 0 : for(i=0;i<content->nEntries;i++)
1513 : {
1514 0 : CPLFree(content->entries[i].fileName);
1515 : }
1516 0 : CPLFree(content->entries);
1517 0 : delete content;
1518 : }
1519 :
1520 363 : if( hMutex != NULL )
1521 0 : CPLDestroyMutex( hMutex );
1522 363 : hMutex = NULL;
1523 726 : }
1524 :
1525 : /************************************************************************/
1526 : /* GetContentOfZip() */
1527 : /************************************************************************/
1528 :
1529 29 : const ZIPContent* VSIZipFilesystemHandler::GetContentOfZip(const char* zipFilename, unzFile unzF)
1530 : {
1531 29 : CPLMutexHolder oHolder( &hMutex );
1532 :
1533 29 : if (oFileList.find(zipFilename) != oFileList.end() )
1534 : {
1535 27 : return oFileList[zipFilename];
1536 : }
1537 :
1538 2 : int bMustCloseUnzf = (unzF == NULL);
1539 2 : if (unzF == NULL)
1540 : {
1541 2 : unzF = cpl_unzOpen(zipFilename);
1542 2 : if (!unzF)
1543 0 : return NULL;
1544 : }
1545 :
1546 2 : if (cpl_unzGoToFirstFile(unzF) != UNZ_OK)
1547 : {
1548 0 : if (bMustCloseUnzf)
1549 0 : cpl_unzClose(unzF);
1550 0 : return NULL;
1551 : }
1552 :
1553 2 : ZIPContent* content = new ZIPContent;
1554 2 : content->nEntries = 0;
1555 2 : content->entries = NULL;
1556 2 : oFileList[zipFilename] = content;
1557 :
1558 2 : do
1559 : {
1560 : char fileName[512];
1561 : unz_file_info file_info;
1562 2 : cpl_unzGetCurrentFileInfo (unzF, &file_info, fileName, 512, NULL, 0, NULL, 0);
1563 2 : content->entries = (ZIPEntry*)CPLRealloc(content->entries, sizeof(ZIPEntry) * (content->nEntries + 1));
1564 2 : content->entries[content->nEntries].fileName = CPLStrdup(fileName);
1565 2 : content->entries[content->nEntries].uncompressed_size = file_info.uncompressed_size;
1566 2 : content->entries[content->nEntries].bIsDir =
1567 : strlen(fileName) > 0 &&
1568 2 : (fileName[strlen(fileName)-1] == '/' || fileName[strlen(fileName)-1] == '\\');
1569 2 : if (content->entries[content->nEntries].bIsDir)
1570 : {
1571 : /* Remove trailing slash */
1572 0 : content->entries[content->nEntries].fileName[strlen(fileName)-1] = 0;
1573 : }
1574 2 : cpl_unzGetFilePos(unzF, &(content->entries[content->nEntries].file_pos));
1575 : if (ENABLE_DEBUG)
1576 : CPLDebug("ZIP", "[%d] %s : " CPL_FRMT_GUIB " bytes", content->nEntries+1,
1577 : content->entries[content->nEntries].fileName,
1578 : content->entries[content->nEntries].uncompressed_size);
1579 2 : content->nEntries++;
1580 : } while(cpl_unzGoToNextFile(unzF) == UNZ_OK);
1581 :
1582 2 : if (bMustCloseUnzf)
1583 2 : cpl_unzClose(unzF);
1584 :
1585 2 : return content;
1586 : }
1587 :
1588 : /************************************************************************/
1589 : /* FindFileInZip() */
1590 : /************************************************************************/
1591 :
1592 27 : int VSIZipFilesystemHandler::FindFileInZip(const char* zipFilename,
1593 : const char* zipInFileName,
1594 : const ZIPEntry** zipEntry)
1595 : {
1596 27 : if (zipInFileName == NULL)
1597 0 : return FALSE;
1598 :
1599 27 : const ZIPContent* content = GetContentOfZip(zipFilename);
1600 27 : if (content)
1601 : {
1602 : int i;
1603 48 : for(i=0;i<content->nEntries;i++)
1604 : {
1605 27 : if (strcmp(zipInFileName, content->entries[i].fileName) == 0)
1606 : {
1607 6 : if (zipEntry)
1608 6 : *zipEntry = &content->entries[i];
1609 6 : return TRUE;
1610 : }
1611 : }
1612 : }
1613 21 : return FALSE;
1614 : }
1615 :
1616 : /************************************************************************/
1617 : /* SplitFilename() */
1618 : /************************************************************************/
1619 :
1620 39 : char* VSIZipFilesystemHandler::SplitFilename(const char *pszFilename,
1621 : CPLString &osZipInFileName)
1622 : {
1623 39 : int i = 0;
1624 :
1625 : /* Allow natural chaining of VSI drivers without requiring double slash */
1626 39 : if (strncmp(pszFilename, "/vsizip/vsi", strlen("/vsizip/vsi")) == 0)
1627 13 : pszFilename += strlen("/vsizip");
1628 : else
1629 26 : pszFilename += strlen("/vsizip/");
1630 :
1631 704 : while(pszFilename[i])
1632 : {
1633 658 : if (EQUALN(pszFilename + i, ".zip", 4))
1634 : {
1635 : VSIStatBufL statBuf;
1636 32 : char* zipFilename = CPLStrdup(pszFilename);
1637 32 : int bZipFileExists = FALSE;
1638 32 : zipFilename[i + 4] = 0;
1639 :
1640 : {
1641 32 : CPLMutexHolder oHolder( &hMutex );
1642 :
1643 32 : if (oFileList.find(zipFilename) != oFileList.end() )
1644 : {
1645 30 : bZipFileExists = TRUE;
1646 32 : }
1647 : }
1648 :
1649 32 : if (!bZipFileExists)
1650 : {
1651 : VSIFilesystemHandler *poFSHandler =
1652 2 : VSIFileManager::GetHandler( zipFilename );
1653 2 : if (poFSHandler->Stat(zipFilename, &statBuf) == 0 &&
1654 : !VSI_ISDIR(statBuf.st_mode))
1655 : {
1656 2 : bZipFileExists = TRUE;
1657 : }
1658 : }
1659 :
1660 32 : if (bZipFileExists)
1661 : {
1662 32 : if (pszFilename[i + 4] != 0)
1663 : {
1664 27 : char* pszZipInFileName = CPLStrdup(pszFilename + i + 5);
1665 :
1666 : /* Replace a/../b by b and foo/a/../b by foo/b */
1667 0 : while(TRUE)
1668 : {
1669 27 : char* pszPrevDir = strstr(pszZipInFileName, "/../");
1670 27 : if (pszPrevDir == NULL || pszPrevDir == pszZipInFileName)
1671 : break;
1672 :
1673 0 : char* pszPrevSlash = pszPrevDir - 1;
1674 0 : while(pszPrevSlash != pszZipInFileName &&
1675 : *pszPrevSlash != '/')
1676 0 : pszPrevSlash --;
1677 0 : if (pszPrevSlash == pszZipInFileName)
1678 0 : memmove(pszZipInFileName, pszPrevDir + 4, strlen(pszPrevDir + 4) + 1);
1679 : else
1680 0 : memmove(pszPrevSlash + 1, pszPrevDir + 4, strlen(pszPrevDir + 4) + 1);
1681 : }
1682 :
1683 54 : osZipInFileName = pszZipInFileName;
1684 27 : CPLFree(pszZipInFileName);
1685 : }
1686 : else
1687 5 : osZipInFileName = "";
1688 :
1689 : /* Remove trailing slash */
1690 32 : if (strlen(osZipInFileName))
1691 : {
1692 27 : char lastC = osZipInFileName[strlen(osZipInFileName) - 1];
1693 27 : if (lastC == '\\' || lastC == '/')
1694 0 : osZipInFileName[strlen(osZipInFileName) - 1] = 0;
1695 : }
1696 :
1697 32 : return zipFilename;
1698 : }
1699 0 : CPLFree(zipFilename);
1700 : }
1701 626 : i++;
1702 : }
1703 7 : return NULL;
1704 : }
1705 :
1706 : /************************************************************************/
1707 : /* OpenZIPFile() */
1708 : /************************************************************************/
1709 :
1710 17 : unzFile VSIZipFilesystemHandler::OpenZIPFile(const char* zipFilename,
1711 : const char* zipInFileName)
1712 : {
1713 17 : unzFile unzF = cpl_unzOpen(zipFilename);
1714 :
1715 17 : if (unzF == NULL)
1716 : {
1717 0 : return NULL;
1718 : }
1719 :
1720 20 : if (zipInFileName == NULL || strlen(zipInFileName) == 0)
1721 : {
1722 3 : if (cpl_unzGoToFirstFile(unzF) != UNZ_OK)
1723 : {
1724 0 : cpl_unzClose(unzF);
1725 0 : return NULL;
1726 : }
1727 :
1728 : char fileName[512];
1729 3 : int isSecondFile = FALSE;
1730 3 : cpl_unzGetCurrentFileInfo (unzF, NULL, fileName, 512, NULL, 0, NULL, 0);
1731 3 : if (fileName[strlen(fileName)-1] == '/' || fileName[strlen(fileName)-1] == '\\')
1732 : {
1733 0 : if (cpl_unzGoToNextFile(unzF) != UNZ_OK)
1734 : {
1735 0 : cpl_unzClose(unzF);
1736 0 : return NULL;
1737 : }
1738 0 : isSecondFile = TRUE;
1739 : }
1740 :
1741 3 : if (cpl_unzGoToNextFile(unzF) != UNZ_END_OF_LIST_OF_FILE)
1742 : {
1743 0 : CPLString msg;
1744 : msg.Printf("Support only 1 file in ZIP file %s when no explicit in-zip filename is specified",
1745 0 : zipFilename);
1746 0 : const ZIPContent* content = GetContentOfZip(zipFilename, unzF);
1747 0 : if (content)
1748 : {
1749 : int i;
1750 0 : msg += "\nYou could try one of the following :\n";
1751 0 : for(i=0;i<content->nEntries;i++)
1752 : {
1753 0 : msg += CPLString().Printf(" /vsizip/%s/%s\n", zipFilename, content->entries[i].fileName);
1754 : }
1755 : }
1756 :
1757 0 : CPLError(CE_Failure, CPLE_NotSupported, "%s", msg.c_str());
1758 :
1759 0 : cpl_unzClose(unzF);
1760 0 : return NULL;
1761 : }
1762 :
1763 3 : cpl_unzGoToFirstFile(unzF);
1764 3 : if (isSecondFile)
1765 0 : cpl_unzGoToNextFile(unzF);
1766 : }
1767 : else
1768 : {
1769 14 : const ZIPEntry* zipEntry = NULL;
1770 14 : if (FindFileInZip(zipFilename, zipInFileName, &zipEntry) == FALSE ||
1771 : zipEntry->bIsDir == TRUE)
1772 : {
1773 10 : cpl_unzClose(unzF);
1774 10 : return NULL;
1775 : }
1776 4 : cpl_unzGoToFilePos(unzF, (unz_file_pos*)&(zipEntry->file_pos));
1777 : }
1778 7 : return unzF;
1779 : }
1780 :
1781 : /************************************************************************/
1782 : /* Open() */
1783 : /************************************************************************/
1784 :
1785 18 : VSIVirtualHandle* VSIZipFilesystemHandler::Open( const char *pszFilename,
1786 : const char *pszAccess)
1787 : {
1788 : char* zipFilename;
1789 18 : CPLString osZipInFileName;
1790 :
1791 18 : if (strchr(pszAccess, 'w') != NULL ||
1792 : strchr(pszAccess, '+') != NULL)
1793 : {
1794 : CPLError(CE_Failure, CPLE_AppDefined,
1795 0 : "Only read-only mode is supported for /vsizip");
1796 0 : return NULL;
1797 : }
1798 :
1799 18 : zipFilename = SplitFilename(pszFilename, osZipInFileName);
1800 18 : if (zipFilename == NULL)
1801 2 : return NULL;
1802 :
1803 16 : unzFile unzF = OpenZIPFile(zipFilename, osZipInFileName);
1804 16 : if (unzF == NULL)
1805 : {
1806 10 : CPLFree(zipFilename);
1807 10 : return NULL;
1808 : }
1809 :
1810 : VSIFilesystemHandler *poFSHandler =
1811 6 : VSIFileManager::GetHandler( zipFilename);
1812 :
1813 : VSIVirtualHandle* poVirtualHandle =
1814 6 : poFSHandler->Open( zipFilename, "rb" );
1815 :
1816 6 : CPLFree(zipFilename);
1817 6 : zipFilename = NULL;
1818 :
1819 6 : if (poVirtualHandle == NULL)
1820 : {
1821 0 : return NULL;
1822 : }
1823 :
1824 6 : cpl_unzOpenCurrentFile(unzF);
1825 :
1826 6 : uLong64 pos = cpl_unzGetCurrentFileZStreamPos(unzF);
1827 :
1828 : unz_file_info file_info;
1829 6 : cpl_unzGetCurrentFileInfo (unzF, &file_info, NULL, 0, NULL, 0, NULL, 0);
1830 :
1831 6 : cpl_unzCloseCurrentFile(unzF);
1832 :
1833 6 : cpl_unzClose(unzF);
1834 :
1835 : return new VSIGZipHandle(poVirtualHandle,
1836 : NULL,
1837 : pos,
1838 : file_info.compressed_size,
1839 : file_info.uncompressed_size,
1840 : file_info.crc,
1841 6 : file_info.compression_method == 0);
1842 : }
1843 :
1844 : /************************************************************************/
1845 : /* Stat() */
1846 : /************************************************************************/
1847 :
1848 18 : int VSIZipFilesystemHandler::Stat( const char *pszFilename, VSIStatBufL *pStatBuf )
1849 : {
1850 : int ret;
1851 18 : CPLString osZipInFileName;
1852 18 : char* zipFilename = SplitFilename(pszFilename, osZipInFileName);
1853 18 : if (zipFilename == NULL)
1854 4 : return -1;
1855 :
1856 14 : if (strlen(osZipInFileName) != 0)
1857 : {
1858 : if (ENABLE_DEBUG) CPLDebug("ZIP", "Looking for %s %s\n",
1859 : zipFilename, osZipInFileName.c_str());
1860 :
1861 13 : const ZIPEntry* zipEntry = NULL;
1862 13 : if (FindFileInZip(zipFilename, osZipInFileName, &zipEntry) == FALSE)
1863 : {
1864 11 : ret = -1;
1865 : }
1866 : else
1867 : {
1868 : /* Patching st_size with uncompressed file size */
1869 2 : pStatBuf->st_size = (long)zipEntry->uncompressed_size;
1870 2 : if (zipEntry->bIsDir)
1871 0 : pStatBuf->st_mode = S_IFDIR;
1872 : else
1873 2 : pStatBuf->st_mode = S_IFREG;
1874 2 : ret = 0;
1875 : }
1876 : }
1877 : else
1878 : {
1879 1 : unzFile unzF = OpenZIPFile(zipFilename, NULL);
1880 1 : if (unzF)
1881 : {
1882 1 : cpl_unzOpenCurrentFile(unzF);
1883 :
1884 : unz_file_info file_info;
1885 1 : cpl_unzGetCurrentFileInfo (unzF, &file_info, NULL, 0, NULL, 0, NULL, 0);
1886 :
1887 : /* Patching st_size with uncompressed file size */
1888 1 : pStatBuf->st_size = (long)file_info.uncompressed_size;
1889 1 : pStatBuf->st_mode = S_IFREG;
1890 :
1891 1 : cpl_unzCloseCurrentFile(unzF);
1892 :
1893 1 : cpl_unzClose(unzF);
1894 1 : ret = 0;
1895 : }
1896 : else
1897 : {
1898 0 : ret = -1;
1899 : }
1900 : }
1901 :
1902 14 : CPLFree(zipFilename);
1903 14 : return ret;
1904 : }
1905 :
1906 : /************************************************************************/
1907 : /* ReadDir() */
1908 : /************************************************************************/
1909 :
1910 0 : int VSIZipFilesystemHandler::Unlink( const char *pszFilename )
1911 : {
1912 0 : return -1;
1913 : }
1914 :
1915 : /************************************************************************/
1916 : /* Rename() */
1917 : /************************************************************************/
1918 :
1919 0 : int VSIZipFilesystemHandler::Rename( const char *oldpath, const char *newpath )
1920 : {
1921 0 : return -1;
1922 : }
1923 :
1924 : /************************************************************************/
1925 : /* Mkdir() */
1926 : /************************************************************************/
1927 :
1928 0 : int VSIZipFilesystemHandler::Mkdir( const char *pszDirname, long nMode )
1929 : {
1930 0 : return -1;
1931 : }
1932 :
1933 : /************************************************************************/
1934 : /* Rmdir() */
1935 : /************************************************************************/
1936 :
1937 0 : int VSIZipFilesystemHandler::Rmdir( const char *pszDirname )
1938 : {
1939 0 : return -1;
1940 : }
1941 :
1942 : /************************************************************************/
1943 : /* ReadDir() */
1944 : /************************************************************************/
1945 :
1946 3 : char** VSIZipFilesystemHandler::ReadDir( const char *pszDirname )
1947 : {
1948 3 : CPLString osInZipSubDir;
1949 3 : char* zipFilename = SplitFilename(pszDirname, osInZipSubDir);
1950 3 : if (zipFilename == NULL)
1951 1 : return NULL;
1952 2 : int lenInZipSubDir = strlen(osInZipSubDir);
1953 :
1954 2 : char **papszDir = NULL;
1955 :
1956 2 : const ZIPContent* content = GetContentOfZip(zipFilename);
1957 2 : if (!content)
1958 : {
1959 0 : CPLFree(zipFilename);
1960 0 : return NULL;
1961 : }
1962 :
1963 : if (ENABLE_DEBUG) CPLDebug("ZIP", "Read dir %s", pszDirname);
1964 : int i;
1965 4 : for(i=0;i<content->nEntries;i++)
1966 : {
1967 2 : const char* fileName = content->entries[i].fileName;
1968 : /* Only list entries at the same level of inZipSubDir */
1969 2 : if (lenInZipSubDir != 0 &&
1970 : strncmp(fileName, osInZipSubDir, lenInZipSubDir) == 0 &&
1971 0 : (fileName[lenInZipSubDir] == '/' || fileName[lenInZipSubDir] == '\\') &&
1972 0 : fileName[lenInZipSubDir + 1] != 0)
1973 : {
1974 0 : const char* slash = strchr(fileName + lenInZipSubDir + 1, '/');
1975 0 : if (slash == NULL)
1976 0 : slash = strchr(fileName + lenInZipSubDir + 1, '\\');
1977 0 : if (slash == NULL || slash[1] == 0)
1978 : {
1979 0 : char* tmpFileName = CPLStrdup(fileName);
1980 0 : if (slash != NULL)
1981 : {
1982 0 : tmpFileName[strlen(tmpFileName)-1] = 0;
1983 : }
1984 : if (ENABLE_DEBUG)
1985 : CPLDebug("ZIP", "Add %s as in directory %s\n",
1986 : tmpFileName + lenInZipSubDir + 1, pszDirname);
1987 0 : papszDir = CSLAddString(papszDir, tmpFileName + lenInZipSubDir + 1);
1988 0 : CPLFree(tmpFileName);
1989 : }
1990 : }
1991 2 : else if (lenInZipSubDir == 0 &&
1992 : strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL)
1993 : {
1994 : /* Only list toplevel files and directories */
1995 : if (ENABLE_DEBUG) CPLDebug("ZIP", "Add %s as in directory %s\n", fileName, pszDirname);
1996 2 : papszDir = CSLAddString(papszDir, fileName);
1997 : }
1998 : }
1999 :
2000 2 : CPLFree(zipFilename);
2001 2 : return papszDir;
2002 : }
2003 :
2004 : /************************************************************************/
2005 : /* VSIInstallZipFileHandler() */
2006 : /************************************************************************/
2007 :
2008 :
2009 : /**
2010 : * \brief Install ZIP file system handler.
2011 : *
2012 : * A special file handler is installed that allows reading on-the-fly in ZIP (.zip) archives.
2013 : * All portions of the file system underneath the base
2014 : * path "/vsizip/" will be handled by this driver.
2015 : *
2016 : * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
2017 : *
2018 : */
2019 :
2020 379 : void VSIInstallZipFileHandler(void)
2021 : {
2022 379 : VSIFileManager::InstallHandler( "/vsizip/", new VSIZipFilesystemHandler() );
2023 379 : }
2024 :
|