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

Generated by: LCOV version 1.14