LCOV - code coverage report
Current view: top level - port - cpl_vsil_tar.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 221 254 87.0 %
Date: 2024-04-29 01:40:10 Functions: 21 21 100.0 %

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

Generated by: LCOV version 1.14