LCOV - code coverage report
Current view: top level - port - cpl_vsil_tar.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 221 254 87.0 %
Date: 2024-11-21 22:18:42 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for tar files (.tar).
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : //! @cond Doxygen_Suppress
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "cpl_vsi.h"
      17             : 
      18             : #include <cstring>
      19             : 
      20             : #if HAVE_FCNTL_H
      21             : #include <fcntl.h>
      22             : #endif
      23             : 
      24             : #include <string>
      25             : #include <vector>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_string.h"
      30             : #include "cpl_vsi_virtual.h"
      31             : 
      32             : #if (defined(DEBUG) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)) &&   \
      33             :     !defined(HAVE_FUZZER_FRIENDLY_ARCHIVE)
      34             : /* This is a completely custom archive format that is rather inefficient */
      35             : /* but supports random insertions or deletions, since it doesn't record */
      36             : /* explicit file size or rely on files starting on a particular boundary */
      37             : #define HAVE_FUZZER_FRIENDLY_ARCHIVE 1
      38             : #endif
      39             : 
      40             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
      41             : constexpr int HALF_BUFFER_SIZE = 1024;
      42             : constexpr int BUFFER_SIZE = 2 * HALF_BUFFER_SIZE;
      43             : #endif
      44             : 
      45             : /************************************************************************/
      46             : /* ==================================================================== */
      47             : /*                       VSITarEntryFileOffset                          */
      48             : /* ==================================================================== */
      49             : /************************************************************************/
      50             : 
      51             : class VSITarEntryFileOffset final : public VSIArchiveEntryFileOffset
      52             : {
      53             :   public:
      54             :     GUIntBig m_nOffset = 0;
      55             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
      56             :     GUIntBig m_nFileSize = 0;
      57             :     CPLString m_osFileName{};
      58             : #endif
      59             : 
      60          48 :     explicit VSITarEntryFileOffset(GUIntBig nOffset) : m_nOffset(nOffset)
      61             :     {
      62          48 :     }
      63             : 
      64             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
      65           5 :     VSITarEntryFileOffset(GUIntBig nOffset, GUIntBig nFileSize,
      66             :                           const CPLString &osFileName)
      67           5 :         : m_nOffset(nOffset), m_nFileSize(nFileSize), m_osFileName(osFileName)
      68             :     {
      69           5 :     }
      70             : #endif
      71             : };
      72             : 
      73             : /************************************************************************/
      74             : /* ==================================================================== */
      75             : /*                             VSITarReader                             */
      76             : /* ==================================================================== */
      77             : /************************************************************************/
      78             : 
      79             : class VSITarReader final : public VSIArchiveReader
      80             : {
      81             :   private:
      82             :     CPL_DISALLOW_COPY_ASSIGN(VSITarReader)
      83             : 
      84             :     VSILFILE *fp = nullptr;
      85             :     GUIntBig nCurOffset = 0;
      86             :     GUIntBig nNextFileSize = 0;
      87             :     CPLString osNextFileName{};
      88             :     GIntBig nModifiedTime = 0;
      89             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
      90             :     bool m_bIsFuzzerFriendly = false;
      91             :     GByte m_abyBuffer[BUFFER_SIZE + 1] = {};
      92             :     int m_abyBufferIdx = 0;
      93             :     int m_abyBufferSize = 0;
      94             :     GUIntBig m_nCurOffsetOld = 0;
      95             : #endif
      96             : 
      97             :   public:
      98             :     explicit VSITarReader(const char *pszTarFileName);
      99             :     ~VSITarReader() override;
     100             : 
     101          74 :     int IsValid()
     102             :     {
     103          74 :         return fp != nullptr;
     104             :     }
     105             : 
     106             :     int GotoFirstFile() override;
     107             :     int GotoNextFile() override;
     108             :     VSIArchiveEntryFileOffset *GetFileOffset() override;
     109             : 
     110          57 :     GUIntBig GetFileSize() override
     111             :     {
     112          57 :         return nNextFileSize;
     113             :     }
     114             : 
     115          46 :     CPLString GetFileName() override
     116             :     {
     117          46 :         return osNextFileName;
     118             :     }
     119             : 
     120          38 :     GIntBig GetModifiedTime() override
     121             :     {
     122          38 :         return nModifiedTime;
     123             :     }
     124             : 
     125             :     int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
     126             : };
     127             : 
     128             : /************************************************************************/
     129             : /*                               VSIIsTGZ()                             */
     130             : /************************************************************************/
     131             : 
     132          98 : static bool VSIIsTGZ(const char *pszFilename)
     133             : {
     134             :     return (
     135         196 :         !STARTS_WITH_CI(pszFilename, "/vsigzip/") &&
     136          98 :         ((strlen(pszFilename) > 4 &&
     137          98 :           STARTS_WITH_CI(pszFilename + strlen(pszFilename) - 4, ".tgz")) ||
     138          94 :          (strlen(pszFilename) > 7 &&
     139         192 :           STARTS_WITH_CI(pszFilename + strlen(pszFilename) - 7, ".tar.gz"))));
     140             : }
     141             : 
     142             : /************************************************************************/
     143             : /*                           VSITarReader()                             */
     144             : /************************************************************************/
     145             : 
     146             : // TODO(schwehr): What is this ***NEWFILE*** thing?
     147             : // And make it a symbolic constant.
     148             : 
     149          74 : VSITarReader::VSITarReader(const char *pszTarFileName)
     150          74 :     : fp(VSIFOpenL(pszTarFileName, "rb"))
     151             : {
     152             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
     153          74 :     if (fp != nullptr)
     154             :     {
     155          74 :         GByte abySignature[24] = {};
     156          74 :         m_bIsFuzzerFriendly =
     157         148 :             (VSIFReadL(abySignature, 1, 24, fp) == 24) &&
     158          74 :             (memcmp(abySignature, "FUZZER_FRIENDLY_ARCHIVE\n", 24) == 0 ||
     159          71 :              memcmp(abySignature, "***NEWFILE***:", strlen("***NEWFILE***:")) ==
     160             :                  0);
     161          74 :         CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_SET));
     162             :     }
     163             : #endif
     164          74 : }
     165             : 
     166             : /************************************************************************/
     167             : /*                          ~VSITarReader()                             */
     168             : /************************************************************************/
     169             : 
     170         148 : VSITarReader::~VSITarReader()
     171             : {
     172          74 :     if (fp)
     173          74 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     174         148 : }
     175             : 
     176             : /************************************************************************/
     177             : /*                          GetFileOffset()                             */
     178             : /************************************************************************/
     179             : 
     180          53 : VSIArchiveEntryFileOffset *VSITarReader::GetFileOffset()
     181             : {
     182             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
     183          53 :     if (m_bIsFuzzerFriendly)
     184             :     {
     185             :         return new VSITarEntryFileOffset(nCurOffset, nNextFileSize,
     186           5 :                                          osNextFileName);
     187             :     }
     188             : #endif
     189          48 :     return new VSITarEntryFileOffset(nCurOffset);
     190             : }
     191             : 
     192             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
     193             : 
     194             : /************************************************************************/
     195             : /*                           CPLmemmem()                                */
     196             : /************************************************************************/
     197             : 
     198          18 : static void *CPLmemmem(const void *haystack, size_t haystacklen,
     199             :                        const void *needle, size_t needlelen)
     200             : {
     201          18 :     const char *pachHaystack = reinterpret_cast<const char *>(haystack);
     202          18 :     if (haystacklen < needlelen)
     203           3 :         return nullptr;
     204             :     while (true)
     205             :     {
     206             :         const char *pachSubstrStart = reinterpret_cast<const char *>(
     207          15 :             memchr(pachHaystack, reinterpret_cast<const char *>(needle)[0],
     208             :                    haystacklen));
     209          15 :         if (pachSubstrStart == nullptr)
     210           2 :             return nullptr;
     211          13 :         if (static_cast<size_t>(pachSubstrStart - pachHaystack) + needlelen >
     212             :             haystacklen)
     213           0 :             return nullptr;
     214          13 :         if (memcmp(pachSubstrStart, needle, needlelen) == 0)
     215             :         {
     216             :             return const_cast<void *>(
     217          13 :                 static_cast<const void *>(pachSubstrStart));
     218             :         }
     219           0 :         haystacklen -= pachSubstrStart - pachHaystack + 1;
     220           0 :         pachHaystack = pachSubstrStart + 1;
     221           0 :     }
     222             : }
     223             : #endif
     224             : 
     225             : /************************************************************************/
     226             : /*                       IsNumericFieldTerminator()                     */
     227             : /************************************************************************/
     228             : 
     229         723 : static bool IsNumericFieldTerminator(GByte byVal)
     230             : {
     231             :     // See https://github.com/Keruspe/tar-parser.rs/blob/master/tar.specs#L202
     232         723 :     return byVal == '\0' || byVal == ' ';
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /*                           GotoNextFile()                             */
     237             : /************************************************************************/
     238             : 
     239         155 : int VSITarReader::GotoNextFile()
     240             : {
     241             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
     242         155 :     if (m_bIsFuzzerFriendly)
     243             :     {
     244          11 :         const int nNewFileMarkerSize =
     245             :             static_cast<int>(strlen("***NEWFILE***:"));
     246             :         while (true)
     247             :         {
     248          26 :             if (m_abyBufferIdx >= m_abyBufferSize)
     249             :             {
     250          16 :                 if (m_abyBufferSize == 0)
     251             :                 {
     252           6 :                     m_abyBufferSize = static_cast<int>(
     253           6 :                         VSIFReadL(m_abyBuffer, 1, BUFFER_SIZE, fp));
     254           6 :                     if (m_abyBufferSize == 0)
     255           0 :                         return FALSE;
     256           6 :                     m_abyBuffer[m_abyBufferSize] = '\0';
     257             :                 }
     258             :                 else
     259             :                 {
     260          10 :                     if (m_abyBufferSize < BUFFER_SIZE)
     261             :                     {
     262           8 :                         if (nCurOffset > 0 && nCurOffset != m_nCurOffsetOld)
     263             :                         {
     264           5 :                             nNextFileSize = VSIFTellL(fp);
     265           5 :                             if (nNextFileSize >= nCurOffset)
     266             :                             {
     267           5 :                                 nNextFileSize -= nCurOffset;
     268           5 :                                 m_nCurOffsetOld = nCurOffset;
     269           5 :                                 return TRUE;
     270             :                             }
     271             :                         }
     272           3 :                         return FALSE;
     273             :                     }
     274           2 :                     memcpy(m_abyBuffer, m_abyBuffer + HALF_BUFFER_SIZE,
     275             :                            HALF_BUFFER_SIZE);
     276           2 :                     m_abyBufferSize = static_cast<int>(
     277           2 :                         VSIFReadL(m_abyBuffer + HALF_BUFFER_SIZE, 1,
     278             :                                   HALF_BUFFER_SIZE, fp));
     279           2 :                     if (m_abyBufferSize == 0)
     280           0 :                         return FALSE;
     281           2 :                     m_abyBufferIdx = 0;
     282           2 :                     m_abyBufferSize += HALF_BUFFER_SIZE;
     283           2 :                     m_abyBuffer[m_abyBufferSize] = '\0';
     284             :                 }
     285             :             }
     286             : 
     287          18 :             void *pNewFileMarker = CPLmemmem(
     288          18 :                 m_abyBuffer + m_abyBufferIdx, m_abyBufferSize - m_abyBufferIdx,
     289             :                 "***NEWFILE***:", nNewFileMarkerSize);
     290          18 :             if (pNewFileMarker == nullptr)
     291             :             {
     292           5 :                 m_abyBufferIdx = m_abyBufferSize;
     293             :             }
     294             :             else
     295             :             {
     296          13 :                 m_abyBufferIdx = static_cast<int>(
     297          13 :                     static_cast<const GByte *>(pNewFileMarker) - m_abyBuffer);
     298             :                 // 2: space for at least one-char filename and '\n'
     299          13 :                 if (m_abyBufferIdx < m_abyBufferSize - (nNewFileMarkerSize + 2))
     300             :                 {
     301          11 :                     if (nCurOffset > 0 && nCurOffset != m_nCurOffsetOld)
     302             :                     {
     303           3 :                         nNextFileSize = VSIFTellL(fp);
     304           3 :                         nNextFileSize -= m_abyBufferSize;
     305           3 :                         nNextFileSize += m_abyBufferIdx;
     306           3 :                         if (nNextFileSize >= nCurOffset)
     307             :                         {
     308           3 :                             nNextFileSize -= nCurOffset;
     309           3 :                             m_nCurOffsetOld = nCurOffset;
     310           3 :                             return TRUE;
     311             :                         }
     312             :                     }
     313           8 :                     m_abyBufferIdx += nNewFileMarkerSize;
     314           8 :                     const int nFilenameStartIdx = m_abyBufferIdx;
     315          45 :                     for (; m_abyBufferIdx < m_abyBufferSize &&
     316          45 :                            m_abyBuffer[m_abyBufferIdx] != '\n';
     317          37 :                          ++m_abyBufferIdx)
     318             :                     {
     319             :                         // Do nothing.
     320             :                     }
     321           8 :                     if (m_abyBufferIdx < m_abyBufferSize)
     322             :                     {
     323             :                         osNextFileName.assign(
     324           8 :                             reinterpret_cast<const char *>(m_abyBuffer +
     325           8 :                                                            nFilenameStartIdx),
     326           8 :                             m_abyBufferIdx - nFilenameStartIdx);
     327           8 :                         nCurOffset = VSIFTellL(fp);
     328           8 :                         nCurOffset -= m_abyBufferSize;
     329           8 :                         nCurOffset += m_abyBufferIdx + 1;
     330             :                     }
     331             :                 }
     332             :                 else
     333             :                 {
     334           2 :                     m_abyBufferIdx = m_abyBufferSize;
     335             :                 }
     336             :             }
     337          15 :         }
     338             :     }
     339             : #endif
     340             : 
     341         144 :     osNextFileName.clear();
     342             :     while (true)
     343             :     {
     344         149 :         GByte abyHeader[512] = {};
     345         149 :         if (VSIFReadL(abyHeader, 512, 1, fp) != 1)
     346          31 :             return FALSE;
     347             : 
     348         441 :         if (!(abyHeader[100] == 0x80 ||
     349         147 :               IsNumericFieldTerminator(
     350         147 :                   abyHeader[107])) || /* start/end of filemode */
     351         145 :             !(abyHeader[108] == 0x80 ||
     352         143 :               IsNumericFieldTerminator(
     353         143 :                   abyHeader[115])) || /* start/end of owner ID */
     354         145 :             !(abyHeader[116] == 0x80 ||
     355         143 :               IsNumericFieldTerminator(
     356         143 :                   abyHeader[123])) || /* start/end of group ID */
     357         439 :             !IsNumericFieldTerminator(abyHeader[135]) || /* end of file size */
     358         145 :             !IsNumericFieldTerminator(abyHeader[147]))   /* end of mtime */
     359             :         {
     360           2 :             return FALSE;
     361             :         }
     362         145 :         if (!(abyHeader[124] == ' ' ||
     363         145 :               (abyHeader[124] >= '0' && abyHeader[124] <= '7')))
     364          27 :             return FALSE;
     365             : 
     366         118 :         if (osNextFileName.empty())
     367             :         {
     368             :             osNextFileName.assign(
     369             :                 reinterpret_cast<const char *>(abyHeader),
     370         113 :                 CPLStrnlen(reinterpret_cast<const char *>(abyHeader), 100));
     371             :         }
     372             : 
     373         118 :         nNextFileSize = 0;
     374        1416 :         for (int i = 0; i < 11; i++)
     375             :         {
     376        1298 :             if (abyHeader[124 + i] != ' ')
     377             :             {
     378        1298 :                 if (nNextFileSize > static_cast<GUIntBig>(GINTBIG_MAX / 8) ||
     379        1298 :                     abyHeader[124 + i] < '0' || abyHeader[124 + i] >= '8')
     380             :                 {
     381           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     382             :                              "Invalid file size for %s",
     383             :                              osNextFileName.c_str());
     384           0 :                     return FALSE;
     385             :                 }
     386        1298 :                 nNextFileSize = nNextFileSize * 8 + (abyHeader[124 + i] - '0');
     387             :             }
     388             :         }
     389         118 :         if (nNextFileSize > GINTBIG_MAX)
     390             :         {
     391           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid file size for %s",
     392             :                      osNextFileName.c_str());
     393           0 :             return FALSE;
     394             :         }
     395             : 
     396         118 :         nModifiedTime = 0;
     397        1416 :         for (int i = 0; i < 11; i++)
     398             :         {
     399        1298 :             if (abyHeader[136 + i] != ' ')
     400             :             {
     401        1298 :                 if (nModifiedTime > GINTBIG_MAX / 8 ||
     402        1298 :                     abyHeader[136 + i] < '0' || abyHeader[136 + i] >= '8' ||
     403        1298 :                     nModifiedTime * 8 >
     404        1298 :                         GINTBIG_MAX - (abyHeader[136 + i] - '0'))
     405             :                 {
     406           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     407             :                              "Invalid mtime for %s", osNextFileName.c_str());
     408           0 :                     return FALSE;
     409             :                 }
     410        1298 :                 nModifiedTime = nModifiedTime * 8 + (abyHeader[136 + i] - '0');
     411             :             }
     412             :         }
     413             : 
     414         118 :         if (abyHeader[156] == 'L' && nNextFileSize > 0 && nNextFileSize < 32768)
     415             :         {
     416             :             // If this is a large filename record, then read the filename
     417           5 :             osNextFileName.clear();
     418           5 :             osNextFileName.resize(
     419           5 :                 static_cast<size_t>(((nNextFileSize + 511) / 512) * 512));
     420           5 :             if (VSIFReadL(&osNextFileName[0], osNextFileName.size(), 1, fp) !=
     421             :                 1)
     422           0 :                 return FALSE;
     423           5 :             osNextFileName.resize(static_cast<size_t>(nNextFileSize));
     424           5 :             if (osNextFileName.back() == '\0')
     425           5 :                 osNextFileName.pop_back();
     426             :         }
     427             :         else
     428             :         {
     429             :             // Is it a ustar extension ?
     430             :             // Cf https://en.wikipedia.org/wiki/Tar_(computing)#UStar_format
     431         113 :             if (memcmp(abyHeader + 257, "ustar\0", 6) == 0 &&
     432           2 :                 abyHeader[345] != '\0')
     433             :             {
     434           2 :                 std::string osFilenamePrefix;
     435             :                 osFilenamePrefix.assign(
     436             :                     reinterpret_cast<const char *>(abyHeader + 345),
     437             :                     CPLStrnlen(reinterpret_cast<const char *>(abyHeader + 345),
     438           2 :                                155));
     439           2 :                 osNextFileName = osFilenamePrefix + '/' + osNextFileName;
     440             :             }
     441             : 
     442         113 :             break;
     443             :         }
     444           5 :     }
     445             : 
     446         113 :     nCurOffset = VSIFTellL(fp);
     447             : 
     448         113 :     const GUIntBig nBytesToSkip = ((nNextFileSize + 511) / 512) * 512;
     449         113 :     if (nBytesToSkip > (~(static_cast<GUIntBig>(0))) - nCurOffset)
     450             :     {
     451           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Bad .tar structure");
     452           0 :         return FALSE;
     453             :     }
     454             : 
     455         113 :     if (VSIFSeekL(fp, nBytesToSkip, SEEK_CUR) < 0)
     456           0 :         return FALSE;
     457             : 
     458         113 :     return TRUE;
     459             : }
     460             : 
     461             : /************************************************************************/
     462             : /*                          GotoFirstFile()                             */
     463             : /************************************************************************/
     464             : 
     465         101 : int VSITarReader::GotoFirstFile()
     466             : {
     467         101 :     if (VSIFSeekL(fp, 0, SEEK_SET) < 0)
     468           0 :         return FALSE;
     469             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
     470         101 :     m_abyBufferIdx = 0;
     471         101 :     m_abyBufferSize = 0;
     472         101 :     nCurOffset = 0;
     473         101 :     m_nCurOffsetOld = 0;
     474         101 :     osNextFileName = "";
     475         101 :     nNextFileSize = 0;
     476             : #endif
     477         101 :     return GotoNextFile();
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                         GotoFileOffset()                             */
     482             : /************************************************************************/
     483             : 
     484          15 : int VSITarReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
     485             : {
     486          15 :     VSITarEntryFileOffset *pTarEntryOffset =
     487             :         static_cast<VSITarEntryFileOffset *>(pOffset);
     488             : #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE
     489          15 :     if (m_bIsFuzzerFriendly)
     490             :     {
     491           0 :         if (VSIFSeekL(fp,
     492           0 :                       pTarEntryOffset->m_nOffset + pTarEntryOffset->m_nFileSize,
     493           0 :                       SEEK_SET) < 0)
     494           0 :             return FALSE;
     495           0 :         m_abyBufferIdx = 0;
     496           0 :         m_abyBufferSize = 0;
     497           0 :         nCurOffset = pTarEntryOffset->m_nOffset;
     498           0 :         m_nCurOffsetOld = pTarEntryOffset->m_nOffset;
     499           0 :         osNextFileName = pTarEntryOffset->m_osFileName;
     500           0 :         nNextFileSize = pTarEntryOffset->m_nFileSize;
     501           0 :         return TRUE;
     502             :     }
     503             : #endif
     504          30 :     if (pTarEntryOffset->m_nOffset < 512 ||
     505          15 :         VSIFSeekL(fp, pTarEntryOffset->m_nOffset - 512, SEEK_SET) < 0)
     506           0 :         return FALSE;
     507          15 :     return GotoNextFile();
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /* ==================================================================== */
     512             : /*                        VSITarFilesystemHandler                      */
     513             : /* ==================================================================== */
     514             : /************************************************************************/
     515             : 
     516             : class VSITarFilesystemHandler final : public VSIArchiveFilesystemHandler
     517             : {
     518             :   public:
     519         664 :     const char *GetPrefix() override
     520             :     {
     521         664 :         return "/vsitar";
     522             :     }
     523             : 
     524             :     std::vector<CPLString> GetExtensions() override;
     525             :     VSIArchiveReader *CreateReader(const char *pszTarFileName) override;
     526             : 
     527             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
     528             :                            bool bSetError,
     529             :                            CSLConstList /* papszOptions */) override;
     530             : };
     531             : 
     532             : /************************************************************************/
     533             : /*                          GetExtensions()                             */
     534             : /************************************************************************/
     535             : 
     536         100 : std::vector<CPLString> VSITarFilesystemHandler::GetExtensions()
     537             : {
     538         100 :     std::vector<CPLString> oList;
     539         100 :     oList.push_back(".tar.gz");
     540         100 :     oList.push_back(".tar");
     541         100 :     oList.push_back(".tgz");
     542         100 :     return oList;
     543             : }
     544             : 
     545             : /************************************************************************/
     546             : /*                           CreateReader()                             */
     547             : /************************************************************************/
     548             : 
     549             : VSIArchiveReader *
     550          74 : VSITarFilesystemHandler::CreateReader(const char *pszTarFileName)
     551             : {
     552         148 :     CPLString osTarInFileName;
     553             : 
     554          74 :     if (VSIIsTGZ(pszTarFileName))
     555             :     {
     556          12 :         osTarInFileName = "/vsigzip/";
     557          12 :         osTarInFileName += pszTarFileName;
     558             :     }
     559             :     else
     560          62 :         osTarInFileName = pszTarFileName;
     561             : 
     562          74 :     VSITarReader *poReader = new VSITarReader(osTarInFileName);
     563             : 
     564          74 :     if (!poReader->IsValid())
     565             :     {
     566           0 :         delete poReader;
     567           0 :         return nullptr;
     568             :     }
     569             : 
     570          74 :     if (!poReader->GotoFirstFile())
     571             :     {
     572          16 :         delete poReader;
     573          16 :         return nullptr;
     574             :     }
     575             : 
     576          58 :     return poReader;
     577             : }
     578             : 
     579             : /************************************************************************/
     580             : /*                                 Open()                               */
     581             : /************************************************************************/
     582             : 
     583          50 : VSIVirtualHandle *VSITarFilesystemHandler::Open(const char *pszFilename,
     584             :                                                 const char *pszAccess,
     585             :                                                 bool /* bSetError */,
     586             :                                                 CSLConstList /* papszOptions */)
     587             : {
     588             : 
     589          50 :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
     590             :     {
     591           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     592             :                  "Only read-only mode is supported for /vsitar");
     593           0 :         return nullptr;
     594             :     }
     595             : 
     596         100 :     CPLString osTarInFileName;
     597          50 :     char *tarFilename = SplitFilename(pszFilename, osTarInFileName, TRUE);
     598          50 :     if (tarFilename == nullptr)
     599           2 :         return nullptr;
     600             : 
     601          48 :     VSIArchiveReader *poReader = OpenArchiveFile(tarFilename, osTarInFileName);
     602          48 :     if (poReader == nullptr)
     603             :     {
     604          24 :         CPLFree(tarFilename);
     605          24 :         return nullptr;
     606             :     }
     607             : 
     608          48 :     CPLString osSubFileName("/vsisubfile/");
     609             :     VSITarEntryFileOffset *pOffset =
     610          24 :         reinterpret_cast<VSITarEntryFileOffset *>(poReader->GetFileOffset());
     611          24 :     osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, pOffset->m_nOffset);
     612          24 :     osSubFileName += "_";
     613          24 :     osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, poReader->GetFileSize());
     614          24 :     osSubFileName += ",";
     615          24 :     delete pOffset;
     616             : 
     617          24 :     if (VSIIsTGZ(tarFilename))
     618             :     {
     619           7 :         osSubFileName += "/vsigzip/";
     620           7 :         osSubFileName += tarFilename;
     621             :     }
     622             :     else
     623          17 :         osSubFileName += tarFilename;
     624             : 
     625          24 :     delete (poReader);
     626             : 
     627          24 :     CPLFree(tarFilename);
     628          24 :     tarFilename = nullptr;
     629             : 
     630          24 :     return reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osSubFileName, "rb"));
     631             : }
     632             : 
     633             : //! @endcond
     634             : 
     635             : /************************************************************************/
     636             : /*                    VSIInstallTarFileHandler()                        */
     637             : /************************************************************************/
     638             : 
     639             : /*!
     640             :  \brief Install /vsitar/ file system handler.
     641             : 
     642             :  A special file handler is installed that allows reading on-the-fly in TAR
     643             :  (regular .tar, or compressed .tar.gz/.tgz) archives.
     644             : 
     645             :  All portions of the file system underneath the base path "/vsitar/" will be
     646             :  handled by this driver.
     647             : 
     648             :  \verbatim embed:rst
     649             :  See :ref:`/vsitar/ documentation <vsitar>`
     650             :  \endverbatim
     651             : 
     652             :  @since GDAL 1.8.0
     653             :  */
     654             : 
     655        1304 : void VSIInstallTarFileHandler(void)
     656             : {
     657        1304 :     VSIFileManager::InstallHandler("/vsitar/", new VSITarFilesystemHandler());
     658        1304 : }

Generated by: LCOV version 1.14