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

Generated by: LCOV version 1.14