LCOV - code coverage report
Current view: directory - port - cpl_vsil_gzip.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 745 521 69.9 %
Date: 2010-01-09 Functions: 54 38 70.4 %

       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                 : 

Generated by: LCOV version 1.7