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

Generated by: LCOV version 1.14