1 : /******************************************************************************
2 : * $Id: cpl_vsil_gzip.cpp 23588 2011-12-17 13:36:47Z 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 23588 2011-12-17 13:36:47Z 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 94 : vsi_l_offset GetLastReadOffset() { return nLastReadOffset; }
179 290 : 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 47 : VSIGZipHandle* VSIGZipHandle::Duplicate()
216 : {
217 47 : CPLAssert (offset == 0);
218 47 : CPLAssert (compressed_size != 0);
219 47 : CPLAssert (pszBaseFileName != NULL);
220 :
221 : VSIFilesystemHandler *poFSHandler =
222 47 : VSIFileManager::GetHandler( pszBaseFileName );
223 :
224 : VSIVirtualHandle* poNewBaseHandle =
225 47 : poFSHandler->Open( pszBaseFileName, "rb" );
226 :
227 47 : if (poNewBaseHandle == NULL)
228 0 : return NULL;
229 :
230 : VSIGZipHandle* poHandle = new VSIGZipHandle(poNewBaseHandle,
231 : pszBaseFileName,
232 : 0,
233 : compressed_size,
234 47 : uncompressed_size);
235 :
236 47 : poHandle->nLastReadOffset = nLastReadOffset;
237 :
238 : /* Most important : duplicate the snapshots ! */
239 :
240 : unsigned int i;
241 50 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
242 : {
243 47 : if (snapshots[i].uncompressed_pos == 0)
244 44 : 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 47 : 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 108 : 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 108 : int transparent)
279 : {
280 108 : this->poBaseHandle = poBaseHandle;
281 108 : this->expected_crc = expected_crc;
282 108 : this->pszBaseFileName = (pszBaseFileName) ? CPLStrdup(pszBaseFileName) : NULL;
283 108 : this->offset = offset;
284 108 : if (compressed_size)
285 : {
286 97 : 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 108 : this->uncompressed_size = uncompressed_size;
295 108 : offsetEndCompressedData = offset + compressed_size;
296 :
297 108 : VSIFSeekL((VSILFILE*)poBaseHandle, offset, SEEK_SET);
298 :
299 108 : nLastReadOffset = 0;
300 108 : stream.zalloc = (alloc_func)0;
301 108 : stream.zfree = (free_func)0;
302 108 : stream.opaque = (voidpf)0;
303 108 : stream.next_in = inbuf = Z_NULL;
304 108 : stream.next_out = outbuf = Z_NULL;
305 108 : stream.avail_in = stream.avail_out = 0;
306 108 : z_err = Z_OK;
307 108 : z_eof = 0;
308 108 : in = 0;
309 108 : out = 0;
310 108 : crc = crc32(0L, Z_NULL, 0);
311 108 : this->transparent = transparent;
312 :
313 108 : stream.next_in = inbuf = (Byte*)ALLOC(Z_BUFSIZE);
314 :
315 108 : 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 108 : if (err != Z_OK || inbuf == Z_NULL) {
323 0 : CPLError(CE_Failure, CPLE_NotSupported, "inflateInit2 init failed");
324 : }
325 108 : stream.avail_out = Z_BUFSIZE;
326 :
327 108 : if (offset == 0) check_header(); /* skip the .gz header */
328 108 : startOff = VSIFTellL((VSILFILE*)poBaseHandle) - stream.avail_in;
329 :
330 108 : if (transparent == 0)
331 : {
332 106 : snapshot_byte_interval = MAX(Z_BUFSIZE, compressed_size / 100);
333 106 : snapshots = (GZipSnapshot*)CPLCalloc(sizeof(GZipSnapshot), (size_t) (compressed_size / snapshot_byte_interval + 1));
334 : }
335 : else
336 : {
337 2 : snapshots = NULL;
338 : }
339 108 : }
340 :
341 : /************************************************************************/
342 : /* ~VSIGZipHandle() */
343 : /************************************************************************/
344 :
345 108 : VSIGZipHandle::~VSIGZipHandle()
346 : {
347 :
348 108 : if (pszBaseFileName)
349 : {
350 : VSIFilesystemHandler *poFSHandler =
351 58 : VSIFileManager::GetHandler( "/vsigzip/" );
352 58 : ((VSIGZipFilesystemHandler*)poFSHandler)->SaveInfo(this);
353 : }
354 :
355 108 : if (stream.state != NULL) {
356 108 : inflateEnd(&(stream));
357 : }
358 :
359 108 : TRYFREE(inbuf);
360 108 : TRYFREE(outbuf);
361 :
362 108 : if (snapshots != NULL)
363 : {
364 : unsigned int i;
365 238 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
366 : {
367 132 : if (snapshots[i].uncompressed_pos)
368 : {
369 66 : inflateEnd(&(snapshots[i].stream));
370 : }
371 : }
372 106 : CPLFree(snapshots);
373 : }
374 108 : CPLFree(pszBaseFileName);
375 :
376 108 : if (poBaseHandle)
377 97 : VSIFCloseL((VSILFILE*)poBaseHandle);
378 108 : }
379 :
380 : /************************************************************************/
381 : /* check_header() */
382 : /************************************************************************/
383 :
384 77 : 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 77 : len = stream.avail_in;
395 77 : if (len < 2) {
396 77 : if (len) inbuf[0] = stream.next_in[0];
397 77 : errno = 0;
398 77 : 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 77 : 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 77 : if (len == 0 /* && ferror(file)*/)
407 : {
408 19 : if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData)
409 0 : z_err = Z_ERRNO;
410 : }
411 77 : stream.avail_in += len;
412 77 : stream.next_in = inbuf;
413 77 : if (stream.avail_in < 2) {
414 19 : transparent = stream.avail_in;
415 19 : return;
416 : }
417 : }
418 :
419 : /* Peek ahead to check the gzip magic header */
420 174 : if (stream.next_in[0] != gz_magic[0] ||
421 116 : stream.next_in[1] != gz_magic[1]) {
422 0 : transparent = 1;
423 0 : return;
424 : }
425 58 : stream.avail_in -= 2;
426 58 : stream.next_in += 2;
427 :
428 : /* Check the rest of the gzip header */
429 58 : method = get_byte();
430 58 : flags = get_byte();
431 58 : 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 58 : for (len = 0; len < 6; len++) (void)get_byte();
438 :
439 58 : 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 58 : if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
446 0 : while ((c = get_byte()) != 0 && c != EOF) ;
447 : }
448 58 : if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
449 0 : while ((c = get_byte()) != 0 && c != EOF) ;
450 : }
451 58 : if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
452 0 : for (len = 0; len < 2; len++) (void)get_byte();
453 : }
454 58 : z_err = z_eof ? Z_DATA_ERROR : Z_OK;
455 : }
456 :
457 : /************************************************************************/
458 : /* get_byte() */
459 : /************************************************************************/
460 :
461 616 : int VSIGZipHandle::get_byte()
462 : {
463 616 : if (z_eof) return EOF;
464 616 : 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 616 : stream.avail_in--;
484 616 : return *(stream.next_in)++;
485 : }
486 :
487 : /************************************************************************/
488 : /* gzrewind() */
489 : /************************************************************************/
490 :
491 56 : int VSIGZipHandle::gzrewind ()
492 : {
493 56 : z_err = Z_OK;
494 56 : z_eof = 0;
495 56 : stream.avail_in = 0;
496 56 : stream.next_in = inbuf;
497 56 : crc = crc32(0L, Z_NULL, 0);
498 56 : if (!transparent) (void)inflateReset(&stream);
499 56 : in = 0;
500 56 : out = 0;
501 56 : return VSIFSeekL((VSILFILE*)poBaseHandle, startOff, SEEK_SET);
502 : }
503 :
504 : /************************************************************************/
505 : /* Seek() */
506 : /************************************************************************/
507 :
508 311 : 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 311 : int ret = gzseek(nOffset, nWhence);
514 311 : return (ret >= 0) ? 0 : ret;
515 : }
516 :
517 : /************************************************************************/
518 : /* gzseek() */
519 : /************************************************************************/
520 :
521 311 : int VSIGZipHandle::gzseek( vsi_l_offset offset, int whence )
522 : {
523 311 : vsi_l_offset original_offset = offset;
524 311 : int original_nWhence = whence;
525 :
526 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Seek(" CPL_FRMT_GUIB ",%d)", offset, whence);
527 :
528 311 : 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 308 : 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 20 : if (offset == 0 && uncompressed_size != 0)
586 : {
587 14 : out = uncompressed_size;
588 14 : 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 6 : 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 6 : whence = SEEK_CUR;
601 6 : offset = 1024 * 1024 * 1024;
602 6 : offset *= 1024 * 1024;
603 : }
604 :
605 294 : 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 294 : if (whence == SEEK_CUR) {
615 6 : offset += out;
616 : }
617 :
618 : /* For a negative seek, rewind and use positive seek */
619 294 : if (offset >= out) {
620 238 : offset -= out;
621 56 : } else if (gzrewind() < 0) {
622 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
623 0 : return -1L;
624 : }
625 :
626 : unsigned int i;
627 298 : for(i=0;i<compressed_size / snapshot_byte_interval + 1;i++)
628 : {
629 298 : if (snapshots[i].uncompressed_pos == 0)
630 222 : break;
631 99 : if (snapshots[i].out <= out + offset &&
632 23 : (i == compressed_size / snapshot_byte_interval || snapshots[i+1].out == 0 || snapshots[i+1].out > out+offset))
633 : {
634 72 : if (out >= snapshots[i].out)
635 72 : 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 294 : if (offset != 0 && outbuf == Z_NULL) {
659 41 : outbuf = (Byte*)ALLOC(Z_BUFSIZE);
660 41 : if (outbuf == Z_NULL) {
661 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
662 0 : return -1L;
663 : }
664 : }
665 :
666 294 : if (original_nWhence == SEEK_END && z_err == Z_STREAM_END)
667 : {
668 : if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out);
669 1 : return (int) out;
670 : }
671 :
672 788 : 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 5 : if (size != read_size)
684 : {
685 5 : z_err = Z_STREAM_END;
686 5 : break;
687 : }
688 : }
689 202 : offset -= read_size;
690 : }
691 : if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out);
692 :
693 292 : if (original_offset == 0 && original_nWhence == SEEK_END)
694 : {
695 5 : uncompressed_size = out;
696 :
697 5 : if (pszBaseFileName)
698 : {
699 5 : CPLString osCacheFilename (pszBaseFileName);
700 5 : osCacheFilename += ".properties";
701 :
702 : /* Write a .properties file to avoid seeking next time */
703 5 : VSILFILE* fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "wb");
704 5 : if (fpCacheLength)
705 : {
706 : char* pszFirstNonSpace;
707 : char szBuffer[32];
708 5 : szBuffer[31] = 0;
709 :
710 5 : CPLPrintUIntBig(szBuffer, compressed_size, 31);
711 5 : pszFirstNonSpace = szBuffer;
712 5 : while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++;
713 5 : VSIFPrintfL(fpCacheLength, "compressed_size=%s\n", pszFirstNonSpace);
714 :
715 5 : CPLPrintUIntBig(szBuffer, uncompressed_size, 31);
716 5 : pszFirstNonSpace = szBuffer;
717 5 : while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++;
718 5 : VSIFPrintfL(fpCacheLength, "uncompressed_size=%s\n", pszFirstNonSpace);
719 :
720 5 : VSIFCloseL(fpCacheLength);
721 5 : }
722 : }
723 : }
724 :
725 292 : return (int) out;
726 : }
727 :
728 : /************************************************************************/
729 : /* Tell() */
730 : /************************************************************************/
731 :
732 21 : vsi_l_offset VSIGZipHandle::Tell()
733 : {
734 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Tell() = " CPL_FRMT_GUIB, out);
735 21 : return out;
736 : }
737 :
738 : /************************************************************************/
739 : /* Read() */
740 : /************************************************************************/
741 :
742 1763 : 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 1763 : unsigned len = nSize * nMemb;
747 :
748 1763 : Bytef *pStart = (Bytef*)buf; /* startOffing point for crc computation */
749 : Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
750 :
751 1763 : 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 1763 : if (z_err == Z_STREAM_END)
757 : {
758 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read: Eof");
759 7 : return 0; /* EOF */
760 : }
761 :
762 1756 : next_out = (Byte*)buf;
763 1756 : stream.next_out = (Bytef*)buf;
764 1756 : stream.avail_out = len;
765 :
766 5217 : while (stream.avail_out != 0) {
767 :
768 1758 : if (transparent) {
769 : /* Copy first the lookahead bytes: */
770 2 : uInt nRead = 0;
771 2 : uInt n = stream.avail_in;
772 2 : if (n > stream.avail_out) n = stream.avail_out;
773 2 : if (n > 0) {
774 0 : memcpy (stream.next_out, stream.next_in, n);
775 0 : next_out += n;
776 0 : stream.next_out = next_out;
777 0 : stream.next_in += n;
778 0 : stream.avail_out -= n;
779 0 : stream.avail_in -= n;
780 0 : nRead += n;
781 : }
782 2 : if (stream.avail_out > 0) {
783 2 : uInt nToRead = (uInt) MIN(compressed_size - (in + nRead), stream.avail_out);
784 : uInt nReadFromFile =
785 2 : (uInt)VSIFReadL(next_out, 1, nToRead, (VSILFILE*)poBaseHandle);
786 2 : stream.avail_out -= nReadFromFile;
787 2 : nRead += nReadFromFile;
788 : }
789 2 : in += nRead;
790 2 : out += nRead;
791 2 : if (nRead < len) z_eof = 1;
792 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Read return %d", (int)(nRead / nSize));
793 2 : return (int)nRead / nSize;
794 : }
795 1756 : if (stream.avail_in == 0 && !z_eof)
796 : {
797 102 : vsi_l_offset uncompressed_pos = VSIFTellL((VSILFILE*)poBaseHandle);
798 102 : GZipSnapshot* snapshot = &snapshots[(uncompressed_pos - startOff) / snapshot_byte_interval];
799 102 : if (snapshot->uncompressed_pos == 0)
800 : {
801 63 : snapshot->crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
802 : if (ENABLE_DEBUG)
803 : CPLDebug("SNAPSHOT",
804 : "creating snapshot %d : uncompressed_pos=" CPL_FRMT_GUIB
805 : " in=" CPL_FRMT_GUIB
806 : " out=" CPL_FRMT_GUIB
807 : " crc=%X",
808 : (int)((uncompressed_pos - startOff) / snapshot_byte_interval),
809 : uncompressed_pos, in, out, (unsigned int)snapshot->crc);
810 63 : snapshot->uncompressed_pos = uncompressed_pos;
811 63 : inflateCopy(&snapshot->stream, &stream);
812 63 : snapshot->transparent = transparent;
813 63 : snapshot->in = in;
814 63 : snapshot->out = out;
815 :
816 63 : if (out > nLastReadOffset)
817 1 : nLastReadOffset = out;
818 : }
819 :
820 102 : errno = 0;
821 102 : stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (VSILFILE*)poBaseHandle);
822 : if (ENABLE_DEBUG)
823 : CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB,
824 : VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData);
825 102 : if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData)
826 : {
827 : if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in before = %d", stream.avail_in);
828 72 : stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle));
829 72 : VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET);
830 72 : if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in after = %d", stream.avail_in);
831 : }
832 102 : if (stream.avail_in == 0) {
833 0 : z_eof = 1;
834 0 : if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData)
835 : {
836 0 : z_err = Z_ERRNO;
837 0 : break;
838 : }
839 : /*if (ferror (file)) {
840 : z_err = Z_ERRNO;
841 : break;
842 : }*/
843 : }
844 102 : stream.next_in = inbuf;
845 : }
846 1756 : in += stream.avail_in;
847 1756 : out += stream.avail_out;
848 1756 : z_err = inflate(& (stream), Z_NO_FLUSH);
849 1756 : in -= stream.avail_in;
850 1756 : out -= stream.avail_out;
851 :
852 1756 : if (z_err == Z_STREAM_END) {
853 : /* Check CRC and original size */
854 51 : crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
855 51 : pStart = stream.next_out;
856 51 : if (expected_crc)
857 : {
858 32 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Computed CRC = %X. Expected CRC = %X", (unsigned int)crc, expected_crc);
859 : }
860 51 : if (expected_crc != 0 && expected_crc != crc)
861 : {
862 0 : CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, expected_crc);
863 0 : z_err = Z_DATA_ERROR;
864 : }
865 51 : else if (expected_crc == 0)
866 : {
867 19 : unsigned int read_crc = getLong();
868 19 : if (read_crc != crc)
869 : {
870 0 : CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, read_crc);
871 0 : z_err = Z_DATA_ERROR;
872 : }
873 : else
874 : {
875 19 : (void)getLong();
876 : /* The uncompressed length returned by above getlong() may be
877 : * different from out in case of concatenated .gz files.
878 : * Check for such files:
879 : */
880 19 : check_header();
881 19 : if (z_err == Z_OK) {
882 0 : inflateReset(& (stream));
883 0 : crc = crc32(0L, Z_NULL, 0);
884 : }
885 : }
886 : }
887 : }
888 1756 : if (z_err != Z_OK || z_eof) break;
889 : }
890 1754 : crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart));
891 :
892 1754 : if (len == stream.avail_out &&
893 : (z_err == Z_DATA_ERROR || z_err == Z_ERRNO))
894 : {
895 0 : CPL_VSIL_GZ_RETURN_MINUS_ONE();
896 0 : return 0;
897 : }
898 : if (ENABLE_DEBUG)
899 : CPLDebug("GZIP", "Read return %d (z_err=%d, z_eof=%d)",
900 : (int)((len - stream.avail_out) / nSize), z_err, z_eof);
901 1754 : return (int)(len - stream.avail_out) / nSize;
902 : }
903 :
904 : /************************************************************************/
905 : /* getLong() */
906 : /************************************************************************/
907 :
908 38 : uLong VSIGZipHandle::getLong ()
909 : {
910 38 : uLong x = (uLong)get_byte();
911 : int c;
912 :
913 38 : x += ((uLong)get_byte())<<8;
914 38 : x += ((uLong)get_byte())<<16;
915 38 : c = get_byte();
916 38 : if (c == EOF) z_err = Z_DATA_ERROR;
917 38 : x += ((uLong)c)<<24;
918 38 : return x;
919 : }
920 :
921 : /************************************************************************/
922 : /* Write() */
923 : /************************************************************************/
924 :
925 0 : size_t VSIGZipHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb )
926 : {
927 0 : CPLError(CE_Failure, CPLE_NotSupported, "VSIFWriteL is not supported on GZip streams");
928 0 : return 0;
929 : }
930 :
931 : /************************************************************************/
932 : /* Eof() */
933 : /************************************************************************/
934 :
935 :
936 1555 : int VSIGZipHandle::Eof()
937 : {
938 : if (ENABLE_DEBUG) CPLDebug("GZIP", "Eof()");
939 1555 : if (z_eof) return 1;
940 1553 : return z_err == Z_STREAM_END;
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 97 : int VSIGZipHandle::Close()
957 : {
958 97 : 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 :
978 : public:
979 :
980 : VSIGZipWriteHandle(VSIVirtualHandle* poBaseHandle);
981 :
982 : ~VSIGZipWriteHandle();
983 :
984 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
985 : virtual vsi_l_offset Tell();
986 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
987 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
988 : virtual int Eof();
989 : virtual int Flush();
990 : virtual int Close();
991 : };
992 :
993 : /************************************************************************/
994 : /* VSIGZipWriteHandle() */
995 : /************************************************************************/
996 :
997 24 : VSIGZipWriteHandle::VSIGZipWriteHandle( VSIVirtualHandle *poBaseHandle )
998 :
999 : {
1000 24 : nCurOffset = 0;
1001 :
1002 24 : this->poBaseHandle = poBaseHandle;
1003 :
1004 24 : nCRC = crc32(0L, Z_NULL, 0);
1005 24 : sStream.zalloc = (alloc_func)0;
1006 24 : sStream.zfree = (free_func)0;
1007 24 : sStream.opaque = (voidpf)0;
1008 24 : sStream.next_in = Z_NULL;
1009 24 : sStream.next_out = Z_NULL;
1010 24 : sStream.avail_in = sStream.avail_out = 0;
1011 :
1012 24 : pabyInBuf = (Byte *) CPLMalloc( Z_BUFSIZE );
1013 24 : sStream.next_in = pabyInBuf;
1014 :
1015 24 : pabyOutBuf = (Byte *) CPLMalloc( Z_BUFSIZE );
1016 :
1017 24 : if( deflateInit2( &sStream, Z_DEFAULT_COMPRESSION,
1018 : Z_DEFLATED, -MAX_WBITS, 8,
1019 : Z_DEFAULT_STRATEGY ) != Z_OK )
1020 0 : bCompressActive = false;
1021 : else
1022 : {
1023 : char header[11];
1024 :
1025 : /* Write a very simple .gz header:
1026 : */
1027 24 : sprintf( header, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
1028 : Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/,
1029 24 : 0x03 );
1030 24 : poBaseHandle->Write( header, 1, 10 );
1031 :
1032 24 : bCompressActive = true;
1033 : }
1034 24 : }
1035 :
1036 : /************************************************************************/
1037 : /* ~VSIGZipWriteHandle() */
1038 : /************************************************************************/
1039 :
1040 24 : VSIGZipWriteHandle::~VSIGZipWriteHandle()
1041 :
1042 : {
1043 24 : if( bCompressActive )
1044 0 : Close();
1045 :
1046 24 : CPLFree( pabyInBuf );
1047 24 : CPLFree( pabyOutBuf );
1048 24 : }
1049 :
1050 : /************************************************************************/
1051 : /* Close() */
1052 : /************************************************************************/
1053 :
1054 24 : int VSIGZipWriteHandle::Close()
1055 :
1056 : {
1057 24 : if( bCompressActive )
1058 : {
1059 24 : sStream.next_out = pabyOutBuf;
1060 24 : sStream.avail_out = Z_BUFSIZE;
1061 :
1062 24 : deflate( &sStream, Z_FINISH );
1063 :
1064 24 : size_t nOutBytes = Z_BUFSIZE - sStream.avail_out;
1065 :
1066 24 : if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes )
1067 0 : return EOF;
1068 :
1069 24 : deflateEnd( &sStream );
1070 :
1071 : GUInt32 anTrailer[2];
1072 :
1073 24 : anTrailer[0] = CPL_LSBWORD32( nCRC );
1074 24 : anTrailer[1] = CPL_LSBWORD32( (GUInt32) nCurOffset );
1075 :
1076 24 : poBaseHandle->Write( anTrailer, 1, 8 );
1077 24 : poBaseHandle->Close();
1078 :
1079 24 : delete poBaseHandle;
1080 :
1081 24 : bCompressActive = false;
1082 : }
1083 :
1084 24 : return 0;
1085 : }
1086 :
1087 : /************************************************************************/
1088 : /* Read() */
1089 : /************************************************************************/
1090 :
1091 0 : size_t VSIGZipWriteHandle::Read( void *pBuffer, size_t nSize, size_t nMemb )
1092 :
1093 : {
1094 0 : CPLError(CE_Failure, CPLE_NotSupported, "VSIFReadL is not supported on GZip write streams\n");
1095 0 : return 0;
1096 : }
1097 :
1098 : /************************************************************************/
1099 : /* Write() */
1100 : /************************************************************************/
1101 :
1102 1167 : size_t VSIGZipWriteHandle::Write( const void *pBuffer,
1103 : size_t nSize, size_t nMemb )
1104 :
1105 : {
1106 : int nBytesToWrite, nNextByte;
1107 :
1108 1167 : nBytesToWrite = (int) (nSize * nMemb);
1109 1167 : nNextByte = 0;
1110 :
1111 1167 : nCRC = crc32(nCRC, (const Bytef *)pBuffer, nBytesToWrite);
1112 :
1113 1167 : if( !bCompressActive )
1114 0 : return 0;
1115 :
1116 3501 : while( nNextByte < nBytesToWrite )
1117 : {
1118 1167 : sStream.next_out = pabyOutBuf;
1119 1167 : sStream.avail_out = Z_BUFSIZE;
1120 :
1121 1167 : if( sStream.avail_in > 0 )
1122 0 : memmove( pabyInBuf, sStream.next_in, sStream.avail_in );
1123 :
1124 1167 : int nNewBytesToWrite = MIN((int) (Z_BUFSIZE-sStream.avail_in),
1125 : nBytesToWrite - nNextByte);
1126 : memcpy( pabyInBuf + sStream.avail_in,
1127 : ((Byte *) pBuffer) + nNextByte,
1128 1167 : nNewBytesToWrite );
1129 :
1130 1167 : sStream.next_in = pabyInBuf;
1131 1167 : sStream.avail_in += nNewBytesToWrite;
1132 :
1133 1167 : deflate( &sStream, Z_NO_FLUSH );
1134 :
1135 1167 : size_t nOutBytes = Z_BUFSIZE - sStream.avail_out;
1136 :
1137 1167 : if( nOutBytes > 0 )
1138 : {
1139 0 : if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes )
1140 0 : return 0;
1141 : }
1142 :
1143 1167 : nNextByte += nNewBytesToWrite;
1144 1167 : nCurOffset += nNewBytesToWrite;
1145 : }
1146 :
1147 1167 : return nMemb;
1148 : }
1149 :
1150 : /************************************************************************/
1151 : /* Flush() */
1152 : /************************************************************************/
1153 :
1154 0 : int VSIGZipWriteHandle::Flush()
1155 :
1156 : {
1157 : // we *could* do something for this but for now we choose not to.
1158 :
1159 0 : return 0;
1160 : }
1161 :
1162 : /************************************************************************/
1163 : /* Eof() */
1164 : /************************************************************************/
1165 :
1166 0 : int VSIGZipWriteHandle::Eof()
1167 :
1168 : {
1169 0 : return 1;
1170 : }
1171 :
1172 : /************************************************************************/
1173 : /* Seek() */
1174 : /************************************************************************/
1175 :
1176 0 : int VSIGZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence )
1177 :
1178 : {
1179 0 : if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) )
1180 0 : return 0;
1181 0 : else if( nWhence == SEEK_SET && nOffset == nCurOffset )
1182 0 : return 0;
1183 : else
1184 : {
1185 : CPLError(CE_Failure, CPLE_NotSupported,
1186 0 : "Seeking on writable compressed data streams not supported." );
1187 :
1188 0 : return -1;
1189 : }
1190 : }
1191 :
1192 : /************************************************************************/
1193 : /* Tell() */
1194 : /************************************************************************/
1195 :
1196 0 : vsi_l_offset VSIGZipWriteHandle::Tell()
1197 :
1198 : {
1199 0 : return nCurOffset;
1200 : }
1201 :
1202 :
1203 : /************************************************************************/
1204 : /* ==================================================================== */
1205 : /* VSIGZipFilesystemHandler */
1206 : /* ==================================================================== */
1207 : /************************************************************************/
1208 :
1209 :
1210 : /************************************************************************/
1211 : /* VSIGZipFilesystemHandler() */
1212 : /************************************************************************/
1213 :
1214 647 : VSIGZipFilesystemHandler::VSIGZipFilesystemHandler()
1215 : {
1216 647 : hMutex = NULL;
1217 :
1218 647 : poHandleLastGZipFile = NULL;
1219 647 : bInSaveInfo = FALSE;
1220 647 : }
1221 :
1222 : /************************************************************************/
1223 : /* ~VSIGZipFilesystemHandler() */
1224 : /************************************************************************/
1225 :
1226 628 : VSIGZipFilesystemHandler::~VSIGZipFilesystemHandler()
1227 : {
1228 628 : if (poHandleLastGZipFile)
1229 1 : delete poHandleLastGZipFile;
1230 :
1231 628 : if( hMutex != NULL )
1232 1 : CPLDestroyMutex( hMutex );
1233 628 : hMutex = NULL;
1234 628 : }
1235 :
1236 : /************************************************************************/
1237 : /* SaveInfo() */
1238 : /************************************************************************/
1239 :
1240 58 : void VSIGZipFilesystemHandler::SaveInfo( VSIGZipHandle* poHandle )
1241 : {
1242 58 : CPLMutexHolder oHolder(&hMutex);
1243 :
1244 58 : if (bInSaveInfo)
1245 : return;
1246 58 : bInSaveInfo = TRUE;
1247 :
1248 58 : CPLAssert(poHandle->GetBaseFileName() != NULL);
1249 :
1250 58 : if (poHandleLastGZipFile &&
1251 : strcmp(poHandleLastGZipFile->GetBaseFileName(), poHandle->GetBaseFileName()) == 0)
1252 : {
1253 47 : if (poHandle->GetLastReadOffset() > poHandleLastGZipFile->GetLastReadOffset())
1254 : {
1255 0 : VSIGZipHandle* poTmp = poHandleLastGZipFile;
1256 0 : poHandleLastGZipFile = NULL;
1257 0 : delete poTmp;
1258 0 : poHandleLastGZipFile = poHandle->Duplicate();
1259 0 : poHandleLastGZipFile->CloseBaseHandle();
1260 : }
1261 : }
1262 : else
1263 : {
1264 11 : VSIGZipHandle* poTmp = poHandleLastGZipFile;
1265 11 : poHandleLastGZipFile = NULL;
1266 11 : delete poTmp;
1267 11 : poHandleLastGZipFile = poHandle->Duplicate();
1268 11 : poHandleLastGZipFile->CloseBaseHandle();
1269 : }
1270 :
1271 58 : bInSaveInfo = FALSE;
1272 : }
1273 :
1274 : /************************************************************************/
1275 : /* Open() */
1276 : /************************************************************************/
1277 :
1278 121 : VSIVirtualHandle* VSIGZipFilesystemHandler::Open( const char *pszFilename,
1279 : const char *pszAccess)
1280 : {
1281 : VSIFilesystemHandler *poFSHandler =
1282 121 : VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/"));
1283 :
1284 : /* -------------------------------------------------------------------- */
1285 : /* Is this an attempt to write a new file without update (w+) */
1286 : /* access? If so, create a writable handle for the underlying */
1287 : /* filename. */
1288 : /* -------------------------------------------------------------------- */
1289 121 : if (strchr(pszAccess, 'w') != NULL )
1290 : {
1291 26 : if( strchr(pszAccess, '+') != NULL )
1292 : {
1293 : CPLError(CE_Failure, CPLE_AppDefined,
1294 0 : "Write+update (w+) not supported for /vsigzip, only read-only or write-only.");
1295 0 : return NULL;
1296 : }
1297 :
1298 : VSIVirtualHandle* poVirtualHandle =
1299 26 : poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "wb" );
1300 :
1301 26 : if (poVirtualHandle == NULL)
1302 2 : return NULL;
1303 :
1304 : else
1305 24 : return new VSIGZipWriteHandle( poVirtualHandle );
1306 : }
1307 :
1308 : /* -------------------------------------------------------------------- */
1309 : /* Otherwise we are in the read access case. */
1310 : /* -------------------------------------------------------------------- */
1311 :
1312 95 : VSIGZipHandle* poGZIPHandle = OpenGZipReadOnly(pszFilename, pszAccess);
1313 95 : if (poGZIPHandle)
1314 : /* Wrap the VSIGZipHandle inside a buffered reader that will */
1315 : /* improve dramatically performance when doing small backward */
1316 : /* seeks */
1317 47 : return VSICreateBufferedReaderHandle(poGZIPHandle);
1318 : else
1319 48 : return NULL;
1320 : }
1321 :
1322 : /************************************************************************/
1323 : /* OpenGZipReadOnly() */
1324 : /************************************************************************/
1325 :
1326 95 : VSIGZipHandle* VSIGZipFilesystemHandler::OpenGZipReadOnly( const char *pszFilename,
1327 : const char *pszAccess)
1328 : {
1329 : VSIFilesystemHandler *poFSHandler =
1330 95 : VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/"));
1331 :
1332 95 : CPLMutexHolder oHolder(&hMutex);
1333 :
1334 95 : if (poHandleLastGZipFile != NULL &&
1335 : strcmp(pszFilename + strlen("/vsigzip/"), poHandleLastGZipFile->GetBaseFileName()) == 0 &&
1336 : EQUAL(pszAccess, "rb"))
1337 : {
1338 36 : VSIGZipHandle* poHandle = poHandleLastGZipFile->Duplicate();
1339 36 : if (poHandle)
1340 36 : return poHandle;
1341 : }
1342 :
1343 : unsigned char signature[2];
1344 :
1345 : VSIVirtualHandle* poVirtualHandle =
1346 59 : poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "rb" );
1347 :
1348 59 : if (poVirtualHandle == NULL)
1349 48 : return NULL;
1350 :
1351 11 : if (VSIFReadL(signature, 1, 2, (VSILFILE*)poVirtualHandle) != 2)
1352 0 : return NULL;
1353 :
1354 11 : if (signature[0] != gz_magic[0] || signature[1] != gz_magic[1])
1355 0 : return NULL;
1356 :
1357 11 : if (poHandleLastGZipFile)
1358 10 : delete poHandleLastGZipFile;
1359 11 : poHandleLastGZipFile = NULL;
1360 :
1361 11 : return new VSIGZipHandle(poVirtualHandle, pszFilename + strlen("/vsigzip/"));
1362 : }
1363 :
1364 : /************************************************************************/
1365 : /* Stat() */
1366 : /************************************************************************/
1367 :
1368 44 : int VSIGZipFilesystemHandler::Stat( const char *pszFilename,
1369 : VSIStatBufL *pStatBuf,
1370 : int nFlags )
1371 : {
1372 44 : CPLMutexHolder oHolder(&hMutex);
1373 :
1374 44 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
1375 :
1376 44 : if (poHandleLastGZipFile != NULL &&
1377 : strcmp(pszFilename+strlen("/vsigzip/"), poHandleLastGZipFile->GetBaseFileName()) == 0)
1378 : {
1379 5 : if (poHandleLastGZipFile->GetUncompressedSize() != 0)
1380 : {
1381 0 : pStatBuf->st_mode = S_IFREG;
1382 0 : pStatBuf->st_size = poHandleLastGZipFile->GetUncompressedSize();
1383 0 : return 0;
1384 : }
1385 : }
1386 :
1387 : /* Begin by doing a stat on the real file */
1388 44 : int ret = VSIStatExL(pszFilename+strlen("/vsigzip/"), pStatBuf, nFlags);
1389 :
1390 44 : if (ret == 0 && (nFlags & VSI_STAT_SIZE_FLAG))
1391 : {
1392 0 : CPLString osCacheFilename(pszFilename+strlen("/vsigzip/"));
1393 0 : osCacheFilename += ".properties";
1394 :
1395 : /* Can we save a bit of seeking by using a .properties file ? */
1396 0 : VSILFILE* fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "rb");
1397 0 : if (fpCacheLength)
1398 : {
1399 : const char* pszLine;
1400 0 : GUIntBig nCompressedSize = 0;
1401 0 : GUIntBig nUncompressedSize = 0;
1402 0 : while ((pszLine = CPLReadLineL(fpCacheLength)) != NULL)
1403 : {
1404 0 : if (EQUALN(pszLine, "compressed_size=", strlen("compressed_size=")))
1405 : {
1406 0 : const char* pszBuffer = pszLine + strlen("compressed_size=");
1407 : nCompressedSize =
1408 0 : CPLScanUIntBig(pszBuffer, strlen(pszBuffer));
1409 : }
1410 0 : else if (EQUALN(pszLine, "uncompressed_size=", strlen("uncompressed_size=")))
1411 : {
1412 0 : const char* pszBuffer = pszLine + strlen("uncompressed_size=");
1413 : nUncompressedSize =
1414 0 : CPLScanUIntBig(pszBuffer, strlen(pszBuffer));
1415 : }
1416 : }
1417 :
1418 0 : VSIFCloseL(fpCacheLength);
1419 :
1420 0 : if (nCompressedSize == (GUIntBig) pStatBuf->st_size)
1421 : {
1422 : /* Patch with the uncompressed size */
1423 0 : pStatBuf->st_size = (long)nUncompressedSize;
1424 :
1425 : VSIGZipHandle* poHandle =
1426 0 : VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb");
1427 0 : if (poHandle)
1428 : {
1429 0 : poHandle->SetUncompressedSize(nUncompressedSize);
1430 0 : SaveInfo(poHandle);
1431 0 : delete poHandle;
1432 : }
1433 :
1434 0 : return ret;
1435 : }
1436 : }
1437 :
1438 : /* No, then seek at the end of the data (slow) */
1439 : VSIGZipHandle* poHandle =
1440 0 : VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb");
1441 0 : if (poHandle)
1442 : {
1443 : GUIntBig uncompressed_size;
1444 0 : poHandle->Seek(0, SEEK_END);
1445 0 : uncompressed_size = (GUIntBig) poHandle->Tell();
1446 0 : poHandle->Seek(0, SEEK_SET);
1447 :
1448 : /* Patch with the uncompressed size */
1449 0 : pStatBuf->st_size = (long)uncompressed_size;
1450 :
1451 0 : delete poHandle;
1452 : }
1453 : else
1454 : {
1455 0 : ret = -1;
1456 0 : }
1457 : }
1458 :
1459 44 : return ret;
1460 : }
1461 :
1462 : /************************************************************************/
1463 : /* Unlink() */
1464 : /************************************************************************/
1465 :
1466 0 : int VSIGZipFilesystemHandler::Unlink( const char *pszFilename )
1467 : {
1468 0 : return -1;
1469 : }
1470 :
1471 : /************************************************************************/
1472 : /* Rename() */
1473 : /************************************************************************/
1474 :
1475 0 : int VSIGZipFilesystemHandler::Rename( const char *oldpath, const char *newpath )
1476 : {
1477 0 : return -1;
1478 : }
1479 :
1480 : /************************************************************************/
1481 : /* Mkdir() */
1482 : /************************************************************************/
1483 :
1484 0 : int VSIGZipFilesystemHandler::Mkdir( const char *pszDirname, long nMode )
1485 : {
1486 0 : return -1;
1487 : }
1488 : /************************************************************************/
1489 : /* Rmdir() */
1490 : /************************************************************************/
1491 :
1492 0 : int VSIGZipFilesystemHandler::Rmdir( const char *pszDirname )
1493 : {
1494 0 : return -1;
1495 : }
1496 :
1497 : /************************************************************************/
1498 : /* ReadDir() */
1499 : /************************************************************************/
1500 :
1501 5 : char** VSIGZipFilesystemHandler::ReadDir( const char *pszDirname )
1502 : {
1503 5 : return NULL;
1504 : }
1505 :
1506 : /************************************************************************/
1507 : /* VSIInstallGZipFileHandler() */
1508 : /************************************************************************/
1509 :
1510 :
1511 : /**
1512 : * \brief Install GZip file system handler.
1513 : *
1514 : * A special file handler is installed that allows reading on-the-fly and
1515 : * writing in GZip (.gz) files.
1516 : *
1517 : * All portions of the file system underneath the base
1518 : * path "/vsigzip/" will be handled by this driver.
1519 : *
1520 : * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
1521 : *
1522 : * @since GDAL 1.6.0
1523 : */
1524 :
1525 647 : void VSIInstallGZipFileHandler(void)
1526 : {
1527 647 : VSIFileManager::InstallHandler( "/vsigzip/", new VSIGZipFilesystemHandler );
1528 647 : }
1529 :
1530 :
1531 : /************************************************************************/
1532 : /* ==================================================================== */
1533 : /* VSIZipEntryFileOffset */
1534 : /* ==================================================================== */
1535 : /************************************************************************/
1536 :
1537 : class VSIZipEntryFileOffset : public VSIArchiveEntryFileOffset
1538 188 : {
1539 : public:
1540 : unz_file_pos file_pos;
1541 :
1542 188 : VSIZipEntryFileOffset(unz_file_pos file_pos)
1543 188 : {
1544 188 : this->file_pos.pos_in_zip_directory = file_pos.pos_in_zip_directory;
1545 188 : this->file_pos.num_of_file = file_pos.num_of_file;
1546 188 : }
1547 : };
1548 :
1549 : /************************************************************************/
1550 : /* ==================================================================== */
1551 : /* VSIZipReader */
1552 : /* ==================================================================== */
1553 : /************************************************************************/
1554 :
1555 : class VSIZipReader : public VSIArchiveReader
1556 : {
1557 : private:
1558 : unzFile unzF;
1559 : unz_file_pos file_pos;
1560 : GUIntBig nNextFileSize;
1561 : CPLString osNextFileName;
1562 : GIntBig nModifiedTime;
1563 :
1564 : void SetInfo();
1565 :
1566 : public:
1567 : VSIZipReader(const char* pszZipFileName);
1568 : virtual ~VSIZipReader();
1569 :
1570 97 : int IsValid() { return unzF != NULL; }
1571 :
1572 50 : unzFile GetUnzFileHandle() { return unzF; }
1573 :
1574 : virtual int GotoFirstFile();
1575 : virtual int GotoNextFile();
1576 188 : virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSIZipEntryFileOffset(file_pos); }
1577 191 : virtual GUIntBig GetFileSize() { return nNextFileSize; }
1578 202 : virtual CPLString GetFileName() { return osNextFileName; }
1579 195 : virtual GIntBig GetModifiedTime() { return nModifiedTime; }
1580 : virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset);
1581 : };
1582 :
1583 :
1584 : /************************************************************************/
1585 : /* VSIZipReader() */
1586 : /************************************************************************/
1587 :
1588 97 : VSIZipReader::VSIZipReader(const char* pszZipFileName)
1589 : {
1590 97 : unzF = cpl_unzOpen(pszZipFileName);
1591 97 : nNextFileSize = 0;
1592 97 : nModifiedTime = 0;
1593 97 : }
1594 :
1595 : /************************************************************************/
1596 : /* ~VSIZipReader() */
1597 : /************************************************************************/
1598 :
1599 97 : VSIZipReader::~VSIZipReader()
1600 : {
1601 97 : if (unzF)
1602 97 : cpl_unzClose(unzF);
1603 97 : }
1604 :
1605 : /************************************************************************/
1606 : /* SetInfo() */
1607 : /************************************************************************/
1608 :
1609 351 : void VSIZipReader::SetInfo()
1610 : {
1611 : char fileName[512];
1612 : unz_file_info file_info;
1613 351 : cpl_unzGetCurrentFileInfo (unzF, &file_info, fileName, 512, NULL, 0, NULL, 0);
1614 351 : osNextFileName = fileName;
1615 351 : nNextFileSize = file_info.uncompressed_size;
1616 : struct tm brokendowntime;
1617 351 : brokendowntime.tm_sec = file_info.tmu_date.tm_sec;
1618 351 : brokendowntime.tm_min = file_info.tmu_date.tm_min;
1619 351 : brokendowntime.tm_hour = file_info.tmu_date.tm_hour;
1620 351 : brokendowntime.tm_mday = file_info.tmu_date.tm_mday;
1621 351 : brokendowntime.tm_mon = file_info.tmu_date.tm_mon;
1622 351 : brokendowntime.tm_year = file_info.tmu_date.tm_year - 1900; /* the minizip conventions differs from the Unix one */
1623 351 : nModifiedTime = CPLYMDHMSToUnixTime(&brokendowntime);
1624 :
1625 351 : cpl_unzGetFilePos(unzF, &this->file_pos);
1626 351 : }
1627 :
1628 : /************************************************************************/
1629 : /* GotoNextFile() */
1630 : /************************************************************************/
1631 :
1632 205 : int VSIZipReader::GotoNextFile()
1633 : {
1634 205 : if (cpl_unzGoToNextFile(unzF) != UNZ_OK)
1635 28 : return FALSE;
1636 :
1637 177 : SetInfo();
1638 :
1639 177 : return TRUE;
1640 : }
1641 :
1642 : /************************************************************************/
1643 : /* GotoFirstFile() */
1644 : /************************************************************************/
1645 :
1646 130 : int VSIZipReader::GotoFirstFile()
1647 : {
1648 130 : if (cpl_unzGoToFirstFile(unzF) != UNZ_OK)
1649 0 : return FALSE;
1650 :
1651 130 : SetInfo();
1652 :
1653 130 : return TRUE;
1654 : }
1655 :
1656 : /************************************************************************/
1657 : /* GotoFileOffset() */
1658 : /************************************************************************/
1659 :
1660 44 : int VSIZipReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset)
1661 : {
1662 44 : VSIZipEntryFileOffset* pZipEntryOffset = (VSIZipEntryFileOffset*)pOffset;
1663 44 : cpl_unzGoToFilePos(unzF, &(pZipEntryOffset->file_pos));
1664 :
1665 44 : SetInfo();
1666 :
1667 44 : return TRUE;
1668 : }
1669 :
1670 : /************************************************************************/
1671 : /* ==================================================================== */
1672 : /* VSIZipFilesystemHandler */
1673 : /* ==================================================================== */
1674 : /************************************************************************/
1675 :
1676 : class VSIZipWriteHandle;
1677 :
1678 : class VSIZipFilesystemHandler : public VSIArchiveFilesystemHandler
1679 647 : {
1680 : std::map<CPLString, VSIZipWriteHandle*> oMapZipWriteHandles;
1681 :
1682 : public:
1683 : virtual ~VSIZipFilesystemHandler();
1684 :
1685 807 : virtual const char* GetPrefix() { return "/vsizip"; }
1686 : virtual std::vector<CPLString> GetExtensions();
1687 : virtual VSIArchiveReader* CreateReader(const char* pszZipFileName);
1688 :
1689 : virtual VSIVirtualHandle *Open( const char *pszFilename,
1690 : const char *pszAccess);
1691 :
1692 : virtual VSIVirtualHandle *OpenForWrite( const char *pszFilename,
1693 : const char *pszAccess );
1694 :
1695 : virtual int Mkdir( const char *pszDirname, long nMode );
1696 : virtual char **ReadDir( const char *pszDirname );
1697 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags );
1698 :
1699 : void RemoveFromMap(VSIZipWriteHandle* poHandle);
1700 : };
1701 :
1702 : /************************************************************************/
1703 : /* ==================================================================== */
1704 : /* VSIZipWriteHandle */
1705 : /* ==================================================================== */
1706 : /************************************************************************/
1707 :
1708 : class VSIZipWriteHandle : public VSIVirtualHandle
1709 : {
1710 : VSIZipFilesystemHandler *poFS;
1711 : void *hZIP;
1712 : VSIZipWriteHandle *poChildInWriting;
1713 : VSIZipWriteHandle *poParent;
1714 : int bAutoDeleteParent;
1715 :
1716 : public:
1717 :
1718 : VSIZipWriteHandle(VSIZipFilesystemHandler* poFS,
1719 : void *hZIP,
1720 : VSIZipWriteHandle* poParent);
1721 :
1722 : ~VSIZipWriteHandle();
1723 :
1724 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
1725 : virtual vsi_l_offset Tell();
1726 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
1727 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
1728 : virtual int Eof();
1729 : virtual int Flush();
1730 : virtual int Close();
1731 :
1732 : void StartNewFile(VSIZipWriteHandle* poSubFile);
1733 : void StopCurrentFile();
1734 9 : void* GetHandle() { return hZIP; }
1735 10 : VSIZipWriteHandle* GetChildInWriting() { return poChildInWriting; };
1736 3 : void SetAutoDeleteParent() { bAutoDeleteParent = TRUE; }
1737 : };
1738 :
1739 : /************************************************************************/
1740 : /* ~VSIZipFilesystemHandler() */
1741 : /************************************************************************/
1742 :
1743 628 : VSIZipFilesystemHandler::~VSIZipFilesystemHandler()
1744 : {
1745 628 : std::map<CPLString,VSIZipWriteHandle*>::const_iterator iter;
1746 :
1747 628 : for( iter = oMapZipWriteHandles.begin(); iter != oMapZipWriteHandles.end(); ++iter )
1748 : {
1749 : CPLError(CE_Failure, CPLE_AppDefined, "%s has not been closed",
1750 0 : iter->first.c_str());
1751 : }
1752 628 : }
1753 :
1754 : /************************************************************************/
1755 : /* GetExtensions() */
1756 : /************************************************************************/
1757 :
1758 8034 : std::vector<CPLString> VSIZipFilesystemHandler::GetExtensions()
1759 : {
1760 8034 : std::vector<CPLString> oList;
1761 8034 : oList.push_back(".zip");
1762 8034 : oList.push_back(".kmz");
1763 0 : return oList;
1764 : }
1765 :
1766 : /************************************************************************/
1767 : /* CreateReader() */
1768 : /************************************************************************/
1769 :
1770 97 : VSIArchiveReader* VSIZipFilesystemHandler::CreateReader(const char* pszZipFileName)
1771 : {
1772 97 : VSIZipReader* poReader = new VSIZipReader(pszZipFileName);
1773 :
1774 97 : if (!poReader->IsValid())
1775 : {
1776 0 : delete poReader;
1777 0 : return NULL;
1778 : }
1779 :
1780 97 : if (!poReader->GotoFirstFile())
1781 : {
1782 0 : delete poReader;
1783 0 : return NULL;
1784 : }
1785 :
1786 97 : return poReader;
1787 : }
1788 :
1789 : /************************************************************************/
1790 : /* Open() */
1791 : /************************************************************************/
1792 :
1793 98 : VSIVirtualHandle* VSIZipFilesystemHandler::Open( const char *pszFilename,
1794 : const char *pszAccess)
1795 : {
1796 : char* zipFilename;
1797 98 : CPLString osZipInFileName;
1798 :
1799 98 : if (strchr(pszAccess, 'w') != NULL)
1800 : {
1801 11 : return OpenForWrite(pszFilename, pszAccess);
1802 : }
1803 :
1804 87 : if (strchr(pszAccess, '+') != NULL)
1805 : {
1806 : CPLError(CE_Failure, CPLE_AppDefined,
1807 0 : "Random access not supported for /vsizip");
1808 0 : return NULL;
1809 : }
1810 :
1811 87 : zipFilename = SplitFilename(pszFilename, osZipInFileName, TRUE);
1812 87 : if (zipFilename == NULL)
1813 16 : return NULL;
1814 :
1815 : {
1816 71 : CPLMutexHolder oHolder(&hMutex);
1817 71 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() )
1818 : {
1819 : CPLError(CE_Failure, CPLE_AppDefined,
1820 1 : "Cannot read a zip file being written");
1821 1 : CPLFree(zipFilename);
1822 1 : return NULL;
1823 0 : }
1824 : }
1825 :
1826 70 : VSIArchiveReader* poReader = OpenArchiveFile(zipFilename, osZipInFileName);
1827 70 : if (poReader == NULL)
1828 : {
1829 20 : CPLFree(zipFilename);
1830 20 : return NULL;
1831 : }
1832 :
1833 : VSIFilesystemHandler *poFSHandler =
1834 50 : VSIFileManager::GetHandler( zipFilename);
1835 :
1836 : VSIVirtualHandle* poVirtualHandle =
1837 50 : poFSHandler->Open( zipFilename, "rb" );
1838 :
1839 50 : CPLFree(zipFilename);
1840 50 : zipFilename = NULL;
1841 :
1842 50 : if (poVirtualHandle == NULL)
1843 : {
1844 0 : delete poReader;
1845 0 : return NULL;
1846 : }
1847 :
1848 50 : unzFile unzF = ((VSIZipReader*)poReader)->GetUnzFileHandle();
1849 :
1850 50 : cpl_unzOpenCurrentFile(unzF);
1851 :
1852 50 : uLong64 pos = cpl_unzGetCurrentFileZStreamPos(unzF);
1853 :
1854 : unz_file_info file_info;
1855 50 : cpl_unzGetCurrentFileInfo (unzF, &file_info, NULL, 0, NULL, 0, NULL, 0);
1856 :
1857 50 : cpl_unzCloseCurrentFile(unzF);
1858 :
1859 50 : delete poReader;
1860 :
1861 : VSIGZipHandle* poGZIPHandle = new VSIGZipHandle(poVirtualHandle,
1862 : NULL,
1863 : pos,
1864 : file_info.compressed_size,
1865 : file_info.uncompressed_size,
1866 : file_info.crc,
1867 50 : file_info.compression_method == 0);
1868 : /* Wrap the VSIGZipHandle inside a buffered reader that will */
1869 : /* improve dramatically performance when doing small backward */
1870 : /* seeks */
1871 100 : return VSICreateBufferedReaderHandle(poGZIPHandle);
1872 : }
1873 :
1874 : /************************************************************************/
1875 : /* Mkdir() */
1876 : /************************************************************************/
1877 :
1878 1 : int VSIZipFilesystemHandler::Mkdir( const char *pszDirname, long nMode )
1879 : {
1880 1 : CPLString osDirname = pszDirname;
1881 1 : if (osDirname.size() != 0 && osDirname[osDirname.size() - 1] != '/')
1882 1 : osDirname += "/";
1883 1 : VSIVirtualHandle* poZIPHandle = OpenForWrite(osDirname, "wb");
1884 1 : if (poZIPHandle == NULL)
1885 0 : return -1;
1886 1 : delete poZIPHandle;
1887 1 : return 0;
1888 : }
1889 :
1890 : /************************************************************************/
1891 : /* ReadDir() */
1892 : /************************************************************************/
1893 :
1894 31 : char **VSIZipFilesystemHandler::ReadDir( const char *pszDirname )
1895 : {
1896 31 : CPLString osInArchiveSubDir;
1897 31 : char* zipFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE);
1898 31 : if (zipFilename == NULL)
1899 3 : return NULL;
1900 :
1901 : {
1902 28 : CPLMutexHolder oHolder(&hMutex);
1903 :
1904 28 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() )
1905 : {
1906 : CPLError(CE_Failure, CPLE_AppDefined,
1907 1 : "Cannot read a zip file being written");
1908 1 : CPLFree(zipFilename);
1909 1 : return NULL;
1910 0 : }
1911 : }
1912 27 : CPLFree(zipFilename);
1913 :
1914 27 : return VSIArchiveFilesystemHandler::ReadDir(pszDirname);
1915 : }
1916 :
1917 :
1918 : /************************************************************************/
1919 : /* Stat() */
1920 : /************************************************************************/
1921 :
1922 58 : int VSIZipFilesystemHandler::Stat( const char *pszFilename,
1923 : VSIStatBufL *pStatBuf, int nFlags )
1924 : {
1925 58 : CPLString osInArchiveSubDir;
1926 :
1927 58 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
1928 :
1929 58 : char* zipFilename = SplitFilename(pszFilename, osInArchiveSubDir, TRUE);
1930 58 : if (zipFilename == NULL)
1931 7 : return -1;
1932 :
1933 : {
1934 51 : CPLMutexHolder oHolder(&hMutex);
1935 :
1936 51 : if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() )
1937 : {
1938 : CPLError(CE_Failure, CPLE_AppDefined,
1939 0 : "Cannot read a zip file being written");
1940 0 : CPLFree(zipFilename);
1941 0 : return -1;
1942 0 : }
1943 : }
1944 51 : CPLFree(zipFilename);
1945 :
1946 51 : return VSIArchiveFilesystemHandler::Stat(pszFilename, pStatBuf, nFlags);
1947 : }
1948 :
1949 : /************************************************************************/
1950 : /* RemoveFromMap() */
1951 : /************************************************************************/
1952 :
1953 5 : void VSIZipFilesystemHandler::RemoveFromMap(VSIZipWriteHandle* poHandle)
1954 : {
1955 5 : CPLMutexHolder oHolder( &hMutex );
1956 5 : std::map<CPLString,VSIZipWriteHandle*>::iterator iter;
1957 :
1958 5 : for( iter = oMapZipWriteHandles.begin();
1959 : iter != oMapZipWriteHandles.end(); ++iter )
1960 : {
1961 5 : if (iter->second == poHandle)
1962 : {
1963 5 : oMapZipWriteHandles.erase(iter);
1964 5 : break;
1965 : }
1966 5 : }
1967 5 : }
1968 :
1969 : /************************************************************************/
1970 : /* OpenForWrite() */
1971 : /************************************************************************/
1972 :
1973 15 : VSIVirtualHandle* VSIZipFilesystemHandler::OpenForWrite( const char *pszFilename,
1974 : const char *pszAccess)
1975 : {
1976 : char* zipFilename;
1977 15 : CPLString osZipInFileName;
1978 :
1979 15 : CPLMutexHolder oHolder( &hMutex );
1980 :
1981 15 : zipFilename = SplitFilename(pszFilename, osZipInFileName, FALSE);
1982 15 : if (zipFilename == NULL)
1983 0 : return NULL;
1984 15 : CPLString osZipFilename = zipFilename;
1985 15 : CPLFree(zipFilename);
1986 15 : zipFilename = NULL;
1987 :
1988 : /* Invalidate cached file list */
1989 15 : std::map<CPLString,VSIArchiveContent*>::iterator iter = oFileList.find(osZipFilename);
1990 15 : if (iter != oFileList.end())
1991 : {
1992 2 : VSIArchiveContent* content = iter->second;
1993 : int i;
1994 5 : for(i=0;i<content->nEntries;i++)
1995 : {
1996 3 : delete content->entries[i].file_pos;
1997 3 : CPLFree(content->entries[i].fileName);
1998 : }
1999 2 : CPLFree(content->entries);
2000 2 : delete content;
2001 :
2002 2 : oFileList.erase(iter);
2003 : }
2004 :
2005 : VSIZipWriteHandle* poZIPHandle;
2006 :
2007 15 : if (oMapZipWriteHandles.find(osZipFilename) != oMapZipWriteHandles.end() )
2008 : {
2009 10 : if (strchr(pszAccess, '+') != NULL)
2010 : {
2011 : CPLError(CE_Failure, CPLE_AppDefined,
2012 0 : "Random access not supported for writable file in /vsizip");
2013 0 : return NULL;
2014 : }
2015 :
2016 10 : poZIPHandle = oMapZipWriteHandles[osZipFilename];
2017 :
2018 10 : if (poZIPHandle->GetChildInWriting() != NULL)
2019 : {
2020 : CPLError(CE_Failure, CPLE_AppDefined,
2021 : "Cannot create %s while another file is being written in the .zip",
2022 1 : osZipInFileName.c_str());
2023 1 : return NULL;
2024 : }
2025 :
2026 9 : poZIPHandle->StopCurrentFile();
2027 :
2028 9 : if (CPLCreateFileInZip(poZIPHandle->GetHandle(),
2029 : osZipInFileName, NULL) != CE_None)
2030 0 : return NULL;
2031 :
2032 : VSIZipWriteHandle* poChildHandle =
2033 9 : new VSIZipWriteHandle(this, NULL, poZIPHandle);
2034 :
2035 9 : poZIPHandle->StartNewFile(poChildHandle);
2036 :
2037 9 : return poChildHandle;
2038 : }
2039 : else
2040 : {
2041 5 : char** papszOptions = NULL;
2042 5 : if ((strchr(pszAccess, '+') && osZipInFileName.size() == 0) ||
2043 : osZipInFileName.size() != 0)
2044 : {
2045 : VSIStatBufL sBuf;
2046 3 : if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0)
2047 2 : papszOptions = CSLAddNameValue(papszOptions, "APPEND", "TRUE");
2048 : }
2049 :
2050 5 : void* hZIP = CPLCreateZip(osZipFilename, papszOptions);
2051 5 : CSLDestroy(papszOptions);
2052 :
2053 5 : if (hZIP == NULL)
2054 0 : return NULL;
2055 :
2056 : oMapZipWriteHandles[osZipFilename] =
2057 5 : new VSIZipWriteHandle(this, hZIP, NULL);
2058 :
2059 5 : if (osZipInFileName.size() != 0)
2060 : {
2061 : VSIZipWriteHandle* poRes =
2062 3 : (VSIZipWriteHandle*)OpenForWrite(pszFilename, pszAccess);
2063 3 : if (poRes == NULL)
2064 : {
2065 0 : delete oMapZipWriteHandles[osZipFilename];
2066 0 : return NULL;
2067 : }
2068 :
2069 3 : poRes->SetAutoDeleteParent();
2070 :
2071 3 : return poRes;
2072 : }
2073 :
2074 2 : return oMapZipWriteHandles[osZipFilename];
2075 0 : }
2076 : }
2077 :
2078 :
2079 : /************************************************************************/
2080 : /* VSIZipWriteHandle() */
2081 : /************************************************************************/
2082 :
2083 14 : VSIZipWriteHandle::VSIZipWriteHandle(VSIZipFilesystemHandler* poFS,
2084 : void* hZIP,
2085 14 : VSIZipWriteHandle* poParent)
2086 : {
2087 14 : this->poFS = poFS;
2088 14 : this->hZIP = hZIP;
2089 14 : this->poParent = poParent;
2090 14 : poChildInWriting = NULL;
2091 14 : bAutoDeleteParent = FALSE;
2092 14 : }
2093 :
2094 : /************************************************************************/
2095 : /* ~VSIZipWriteHandle() */
2096 : /************************************************************************/
2097 :
2098 14 : VSIZipWriteHandle::~VSIZipWriteHandle()
2099 : {
2100 14 : Close();
2101 14 : }
2102 :
2103 : /************************************************************************/
2104 : /* Seek() */
2105 : /************************************************************************/
2106 :
2107 0 : int VSIZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence )
2108 : {
2109 : CPLError(CE_Failure, CPLE_NotSupported,
2110 0 : "VSIFSeekL() is not supported on writable Zip files");
2111 0 : return -1;
2112 : }
2113 :
2114 : /************************************************************************/
2115 : /* Tell() */
2116 : /************************************************************************/
2117 :
2118 0 : vsi_l_offset VSIZipWriteHandle::Tell()
2119 : {
2120 : CPLError(CE_Failure, CPLE_NotSupported,
2121 0 : "VSIFTellL() is not supported on writable Zip files");
2122 0 : return 0;
2123 : }
2124 :
2125 : /************************************************************************/
2126 : /* Read() */
2127 : /************************************************************************/
2128 :
2129 0 : size_t VSIZipWriteHandle::Read( void *pBuffer, size_t nSize, size_t nMemb )
2130 : {
2131 : CPLError(CE_Failure, CPLE_NotSupported,
2132 0 : "VSIFReadL() is not supported on writable Zip files");
2133 0 : return 0;
2134 : }
2135 :
2136 : /************************************************************************/
2137 : /* Write() */
2138 : /************************************************************************/
2139 :
2140 7 : size_t VSIZipWriteHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb )
2141 : {
2142 7 : if (poParent == NULL)
2143 : {
2144 : CPLError(CE_Failure, CPLE_NotSupported,
2145 0 : "VSIFWriteL() is not supported on main Zip file or closed subfiles");
2146 0 : return 0;
2147 : }
2148 :
2149 7 : if (CPLWriteFileInZip( poParent->hZIP, pBuffer, (int)(nSize * nMemb) ) != CE_None)
2150 0 : return 0;
2151 :
2152 7 : return nMemb;
2153 : }
2154 :
2155 : /************************************************************************/
2156 : /* Eof() */
2157 : /************************************************************************/
2158 :
2159 0 : int VSIZipWriteHandle::Eof()
2160 : {
2161 : CPLError(CE_Failure, CPLE_NotSupported,
2162 0 : "VSIFEofL() is not supported on writable Zip files");
2163 0 : return FALSE;
2164 : }
2165 :
2166 : /************************************************************************/
2167 : /* Flush() */
2168 : /************************************************************************/
2169 :
2170 0 : int VSIZipWriteHandle::Flush()
2171 : {
2172 : CPLError(CE_Failure, CPLE_NotSupported,
2173 0 : "VSIFFlushL() is not supported on writable Zip files");
2174 0 : return 0;
2175 : }
2176 :
2177 : /************************************************************************/
2178 : /* Close() */
2179 : /************************************************************************/
2180 :
2181 24 : int VSIZipWriteHandle::Close()
2182 : {
2183 24 : if (poParent)
2184 : {
2185 9 : CPLCloseFileInZip(poParent->hZIP);
2186 9 : poParent->poChildInWriting = NULL;
2187 9 : if (bAutoDeleteParent)
2188 3 : delete poParent;
2189 9 : poParent = NULL;
2190 : }
2191 24 : if (poChildInWriting)
2192 : {
2193 0 : poChildInWriting->Close();
2194 0 : poChildInWriting = NULL;
2195 : }
2196 24 : if (hZIP)
2197 : {
2198 5 : CPLCloseZip(hZIP);
2199 5 : hZIP = NULL;
2200 :
2201 5 : poFS->RemoveFromMap(this);
2202 : }
2203 :
2204 24 : return 0;
2205 : }
2206 :
2207 : /************************************************************************/
2208 : /* StopCurrentFile() */
2209 : /************************************************************************/
2210 :
2211 9 : void VSIZipWriteHandle::StopCurrentFile()
2212 : {
2213 9 : if (poChildInWriting)
2214 0 : poChildInWriting->Close();
2215 9 : poChildInWriting = NULL;
2216 9 : }
2217 :
2218 : /************************************************************************/
2219 : /* StartNewFile() */
2220 : /************************************************************************/
2221 :
2222 9 : void VSIZipWriteHandle::StartNewFile(VSIZipWriteHandle* poSubFile)
2223 : {
2224 9 : poChildInWriting = poSubFile;
2225 9 : }
2226 :
2227 : /************************************************************************/
2228 : /* VSIInstallZipFileHandler() */
2229 : /************************************************************************/
2230 :
2231 :
2232 : /**
2233 : * \brief Install ZIP file system handler.
2234 : *
2235 : * A special file handler is installed that allows reading on-the-fly in ZIP
2236 : * (.zip) archives.
2237 : *
2238 : * All portions of the file system underneath the base path "/vsizip/" will be
2239 : * handled by this driver.
2240 : *
2241 : * The syntax to open a file inside a zip file is /vsizip/path/to/the/file.zip/path/inside/the/zip/file
2242 : * were path/to/the/file.zip is relative or absolute and path/inside/the/zip/file
2243 : * is the relative path to the file inside the archive.
2244 : *
2245 : * If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows),
2246 : * so the line looks like /vsizip//home/gdal/...
2247 : * For example gdalinfo /vsizip/myarchive.zip/subdir1/file1.tif
2248 : *
2249 : * Syntaxic sugar : if the .zip file contains only one file located at its root,
2250 : * just mentionning "/vsizip/path/to/the/file.zip" will work
2251 : *
2252 : * VSIStatL() will return the uncompressed size in st_size member and file
2253 : * nature- file or directory - in st_mode member.
2254 : *
2255 : * Directory listing is available through VSIReadDir().
2256 : *
2257 : * Since GDAL 1.8.0, write capabilities are available. They allow creating
2258 : * a new zip file and adding new files to an already existing (or just created)
2259 : * zip file. Read and write operations cannot be interleaved : the new zip must
2260 : * be closed before being re-opened for read.
2261 : *
2262 : * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip
2263 : *
2264 : * @since GDAL 1.6.0
2265 : */
2266 :
2267 647 : void VSIInstallZipFileHandler(void)
2268 : {
2269 647 : VSIFileManager::InstallHandler( "/vsizip/", new VSIZipFilesystemHandler() );
2270 647 : }
2271 :
|