LCOV - code coverage report
Current view: top level - port - cpl_vsil_libarchive.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 4 0.0 %
Date: 2025-09-10 17:48:50 Functions: 0 2 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for /vsi7z/ and /vsirar/
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_vsi_virtual.h"
      15             : 
      16             : #ifndef HAVE_LIBARCHIVE
      17             : 
      18             : /************************************************************************/
      19             : /*                    VSIInstall7zFileHandler()                         */
      20             : /************************************************************************/
      21             : 
      22             : /*!
      23             :  \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
      24             : 
      25             :  \verbatim embed:rst
      26             :  See :ref:`/vsi7z/ documentation <vsi7z>`
      27             :  \endverbatim
      28             : 
      29             :  @since GDAL 3.7
      30             :  */
      31           0 : void VSIInstall7zFileHandler(void)
      32             : {
      33             :     // dummy
      34           0 : }
      35             : 
      36             : /************************************************************************/
      37             : /*                    VSIInstallRarFileHandler()                         */
      38             : /************************************************************************/
      39             : 
      40             : /*!
      41             :  \brief Install /vsirar/ RAR file system handler (requires libarchive)
      42             : 
      43             :  \verbatim embed:rst
      44             :  See :ref:`/vsirar/ documentation <vsirar>`
      45             :  \endverbatim
      46             : 
      47             :  @since GDAL 3.7
      48             :  */
      49           0 : void VSIInstallRarFileHandler(void)
      50             : {
      51             :     // dummy
      52           0 : }
      53             : 
      54             : #else
      55             : 
      56             : //! @cond Doxygen_Suppress
      57             : 
      58             : #include <algorithm>
      59             : #include <limits>
      60             : #include <memory>
      61             : 
      62             : // libarchive
      63             : #ifdef USE_INTERNAL_LIBARCHIVE
      64             : #include "archive_gdal_config.h"
      65             : #endif
      66             : 
      67             : #include "archive.h"
      68             : #include "archive_entry.h"
      69             : 
      70             : /************************************************************************/
      71             : /* ==================================================================== */
      72             : /*                      VSILibArchiveClientData                         */
      73             : /* ==================================================================== */
      74             : /************************************************************************/
      75             : 
      76             : struct VSILibArchiveClientData
      77             : {
      78             :     CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveClientData)
      79             : 
      80             :     const std::string m_osFilename;
      81             :     VSIVirtualHandle *m_poBaseHandle = nullptr;
      82             :     std::vector<GByte> m_abyBuffer{};
      83             : 
      84             :     VSILibArchiveClientData(const char *pszFilename) : m_osFilename(pszFilename)
      85             :     {
      86             :         m_abyBuffer.resize(4096);
      87             :     }
      88             : 
      89             :     static int openCbk(struct archive *pArchive, void *pClientData)
      90             :     {
      91             :         auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
      92             :         CPLDebug("VSIARCH", "Opening %s", poClientData->m_osFilename.c_str());
      93             :         poClientData->m_poBaseHandle = reinterpret_cast<VSIVirtualHandle *>(
      94             :             VSIFOpenL(poClientData->m_osFilename.c_str(), "rb"));
      95             :         if (poClientData->m_poBaseHandle == nullptr)
      96             :         {
      97             :             archive_set_error(pArchive, -1, "Cannot open file");
      98             :             return ARCHIVE_FATAL;
      99             :         }
     100             :         return ARCHIVE_OK;
     101             :     }
     102             : 
     103             :     static int closeCbk(struct archive *pArchive, void *pClientData)
     104             :     {
     105             :         auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
     106             :         int ret = 0;
     107             :         if (poClientData->m_poBaseHandle)
     108             :         {
     109             :             ret = poClientData->m_poBaseHandle->Close();
     110             :             delete poClientData->m_poBaseHandle;
     111             :         }
     112             :         delete poClientData;
     113             :         if (ret == 0)
     114             :             return ARCHIVE_OK;
     115             :         archive_set_error(pArchive, -1, "Cannot close file");
     116             :         return ARCHIVE_FATAL;
     117             :     }
     118             : 
     119             :     static la_ssize_t readCbk(struct archive *, void *pClientData,
     120             :                               const void **ppBuffer)
     121             :     {
     122             :         auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
     123             :         *ppBuffer = poClientData->m_abyBuffer.data();
     124             :         return static_cast<la_ssize_t>(poClientData->m_poBaseHandle->Read(
     125             :             poClientData->m_abyBuffer.data(), 1,
     126             :             poClientData->m_abyBuffer.size()));
     127             :     }
     128             : 
     129             :     static la_int64_t seekCkb(struct archive *, void *pClientData,
     130             :                               la_int64_t offset, int whence)
     131             :     {
     132             :         auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
     133             :         if (whence == SEEK_CUR && offset < 0)
     134             :         {
     135             :             whence = SEEK_SET;
     136             :             offset = poClientData->m_poBaseHandle->Tell() + offset;
     137             :         }
     138             :         if (poClientData->m_poBaseHandle->Seek(
     139             :                 static_cast<vsi_l_offset>(offset), whence) != 0)
     140             :             return ARCHIVE_FATAL;
     141             :         return static_cast<la_int64_t>(poClientData->m_poBaseHandle->Tell());
     142             :     }
     143             : };
     144             : 
     145             : /************************************************************************/
     146             : /*                    VSILibArchiveReadOpen()                           */
     147             : /************************************************************************/
     148             : 
     149             : /**Open an archive, with the base handle being a VSIVirtualHandle* */
     150             : static int VSILibArchiveReadOpen(struct archive *pArchive,
     151             :                                  const char *pszFilename)
     152             : {
     153             :     archive_read_set_seek_callback(pArchive, VSILibArchiveClientData::seekCkb);
     154             :     return archive_read_open(pArchive, new VSILibArchiveClientData(pszFilename),
     155             :                              VSILibArchiveClientData::openCbk,
     156             :                              VSILibArchiveClientData::readCbk,
     157             :                              VSILibArchiveClientData::closeCbk);
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                    VSICreateArchiveHandle()                          */
     162             : /************************************************************************/
     163             : 
     164             : static struct archive *VSICreateArchiveHandle(const std::string &osFSPrefix)
     165             : {
     166             :     auto pArchive = archive_read_new();
     167             : 
     168             :     if (osFSPrefix == "/vsi7z")
     169             :     {
     170             :         archive_read_support_format_7zip(pArchive);
     171             :     }
     172             :     else
     173             :     {
     174             :         archive_read_support_format_rar(pArchive);
     175             : #ifdef ARCHIVE_FORMAT_RAR_V5
     176             :         archive_read_support_format_rar5(pArchive);
     177             : #endif
     178             :     }
     179             : 
     180             :     return pArchive;
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /* ==================================================================== */
     185             : /*                      VSILibArchiveReader                             */
     186             : /* ==================================================================== */
     187             : /************************************************************************/
     188             : 
     189             : class VSILibArchiveReader final : public VSIArchiveReader
     190             : {
     191             :     CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveReader)
     192             : 
     193             :     std::string m_osArchiveFileName;
     194             :     struct archive *m_pArchive;
     195             :     std::string m_osPrefix;
     196             :     bool m_bFirst = true;
     197             :     std::string m_osFilename{};
     198             :     GUIntBig m_nFilesize = 0;
     199             :     GIntBig m_nMTime = 0;
     200             : 
     201             :   public:
     202             :     VSILibArchiveReader(const char *pszArchiveFileName,
     203             :                         struct archive *pArchive, const std::string &osPrefix)
     204             :         : m_osArchiveFileName(pszArchiveFileName), m_pArchive(pArchive),
     205             :           m_osPrefix(osPrefix)
     206             :     {
     207             :     }
     208             : 
     209             :     ~VSILibArchiveReader() override;
     210             : 
     211             :     struct archive *GetArchiveHandler()
     212             :     {
     213             :         return m_pArchive;
     214             :     }
     215             : 
     216             :     int GotoFirstFileForced()
     217             :     {
     218             :         m_bFirst = false;
     219             :         return GotoFirstFile();
     220             :     }
     221             : 
     222             :     int GotoFirstFile() override;
     223             :     int GotoNextFile() override;
     224             :     VSIArchiveEntryFileOffset *GetFileOffset() override;
     225             : 
     226             :     GUIntBig GetFileSize() override
     227             :     {
     228             :         return m_nFilesize;
     229             :     }
     230             : 
     231             :     CPLString GetFileName() override
     232             :     {
     233             :         return m_osFilename;
     234             :     }
     235             : 
     236             :     GIntBig GetModifiedTime() override
     237             :     {
     238             :         return m_nMTime;
     239             :     }
     240             : 
     241             :     int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
     242             : 
     243             :     int GotoFileOffsetForced(VSIArchiveEntryFileOffset *pOffset);
     244             : };
     245             : 
     246             : /************************************************************************/
     247             : /*                       ~VSILibArchiveReader()                         */
     248             : /************************************************************************/
     249             : 
     250             : VSILibArchiveReader::~VSILibArchiveReader()
     251             : {
     252             :     archive_free(m_pArchive);
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                           GotoFirstFile()                            */
     257             : /************************************************************************/
     258             : 
     259             : int VSILibArchiveReader::GotoFirstFile()
     260             : {
     261             :     if (!m_bFirst)
     262             :     {
     263             :         archive_free(m_pArchive);
     264             : 
     265             :         m_pArchive = VSICreateArchiveHandle(m_osPrefix);
     266             : 
     267             :         if (VSILibArchiveReadOpen(m_pArchive, m_osArchiveFileName.c_str()))
     268             :         {
     269             :             CPLDebug("VSIARCH", "%s: %s", m_osArchiveFileName.c_str(),
     270             :                      archive_error_string(m_pArchive));
     271             :             return false;
     272             :         }
     273             :         m_bFirst = true;
     274             :     }
     275             :     return GotoNextFile();
     276             : }
     277             : 
     278             : /************************************************************************/
     279             : /*                           GotoNextFile()                             */
     280             : /************************************************************************/
     281             : 
     282             : int VSILibArchiveReader::GotoNextFile()
     283             : {
     284             :     struct archive_entry *entry;
     285             :     int r = archive_read_next_header(m_pArchive, &entry);
     286             :     if (r == ARCHIVE_EOF)
     287             :         return FALSE;
     288             :     if (r != ARCHIVE_OK)
     289             :     {
     290             :         CPLDebug("VSIARCH", "%s", archive_error_string(m_pArchive));
     291             :         return FALSE;
     292             :     }
     293             :     m_osFilename = archive_entry_pathname_utf8(entry);
     294             :     m_nFilesize = archive_entry_size(entry);
     295             :     m_nMTime = archive_entry_mtime(entry);
     296             :     return TRUE;
     297             : }
     298             : 
     299             : /************************************************************************/
     300             : /*                      VSILibArchiveEntryFileOffset                    */
     301             : /************************************************************************/
     302             : 
     303             : struct VSILibArchiveEntryFileOffset : public VSIArchiveEntryFileOffset
     304             : {
     305             :     const std::string m_osFilename;
     306             : 
     307             :     VSILibArchiveEntryFileOffset(const std::string &osFilename)
     308             :         : m_osFilename(osFilename)
     309             :     {
     310             :     }
     311             : 
     312             :     ~VSILibArchiveEntryFileOffset() override;
     313             : };
     314             : 
     315             : VSILibArchiveEntryFileOffset::~VSILibArchiveEntryFileOffset() = default;
     316             : 
     317             : /************************************************************************/
     318             : /*                          GetFileOffset()                             */
     319             : /************************************************************************/
     320             : 
     321             : VSIArchiveEntryFileOffset *VSILibArchiveReader::GetFileOffset()
     322             : {
     323             :     return new VSILibArchiveEntryFileOffset(m_osFilename);
     324             : }
     325             : 
     326             : /************************************************************************/
     327             : /*                         GotoFileOffset()                             */
     328             : /************************************************************************/
     329             : 
     330             : int VSILibArchiveReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
     331             : {
     332             :     VSILibArchiveEntryFileOffset *pMyOffset =
     333             :         static_cast<VSILibArchiveEntryFileOffset *>(pOffset);
     334             :     if (!GotoFirstFile())
     335             :         return false;
     336             :     while (m_osFilename != pMyOffset->m_osFilename)
     337             :     {
     338             :         if (!GotoNextFile())
     339             :             return false;
     340             :     }
     341             :     return true;
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                       GotoFileOffsetForced()                         */
     346             : /************************************************************************/
     347             : 
     348             : int VSILibArchiveReader::GotoFileOffsetForced(
     349             :     VSIArchiveEntryFileOffset *pOffset)
     350             : {
     351             :     m_bFirst = false;
     352             :     return GotoFileOffset(pOffset);
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /* ==================================================================== */
     357             : /*                      VSILibArchiveHandler                            */
     358             : /* ==================================================================== */
     359             : /************************************************************************/
     360             : 
     361             : class VSILibArchiveHandler final : public VSIVirtualHandle
     362             : {
     363             :     const std::string m_osFilename;
     364             :     std::unique_ptr<VSILibArchiveReader> m_poReader;
     365             :     std::unique_ptr<VSIArchiveEntryFileOffset> m_pOffset;
     366             :     vsi_l_offset m_nOffset = 0;
     367             :     bool m_bEOF = false;
     368             :     bool m_bError = false;
     369             : 
     370             :   public:
     371             :     VSILibArchiveHandler(const std::string &osFilename,
     372             :                          VSILibArchiveReader *poReader)
     373             :         : m_osFilename(osFilename), m_poReader(poReader),
     374             :           m_pOffset(poReader->GetFileOffset())
     375             :     {
     376             :     }
     377             : 
     378             :     size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
     379             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     380             : 
     381             :     vsi_l_offset Tell() override
     382             :     {
     383             :         return m_nOffset;
     384             :     }
     385             : 
     386             :     size_t Write(const void *, size_t, size_t) override
     387             :     {
     388             :         return 0;
     389             :     }
     390             : 
     391             :     void ClearErr() override
     392             :     {
     393             :         m_bEOF = false;
     394             :         m_bError = false;
     395             :     }
     396             : 
     397             :     int Eof() override
     398             :     {
     399             :         return m_bEOF ? 1 : 0;
     400             :     }
     401             : 
     402             :     int Error() override
     403             :     {
     404             :         return m_bError ? 1 : 0;
     405             :     }
     406             : 
     407             :     int Close() override
     408             :     {
     409             :         m_poReader.reset();
     410             :         return 0;
     411             :     }
     412             : };
     413             : 
     414             : /************************************************************************/
     415             : /*                                Read()                                */
     416             : /************************************************************************/
     417             : 
     418             : size_t VSILibArchiveHandler::Read(void *pBuffer, size_t nSize, size_t nCount)
     419             : {
     420             :     if (m_bError || nSize == 0 || nCount == 0)
     421             :         return 0;
     422             :     const auto nFileSize = m_poReader->GetFileSize();
     423             :     if (m_nOffset == nFileSize)
     424             :     {
     425             :         m_bEOF = true;
     426             :         return 0;
     427             :     }
     428             :     size_t nToRead = nSize * nCount;
     429             :     auto pArchive = m_poReader->GetArchiveHandler();
     430             :     auto nReadUnsigned = archive_read_data(pArchive, pBuffer, nToRead);
     431             :     if (nReadUnsigned < 0)
     432             :     {
     433             :         m_bError = true;
     434             :         CPLDebug("VSIARCH", "Read(): %s", archive_error_string(pArchive));
     435             :         return 0;
     436             :     }
     437             :     const auto nRead = static_cast<size_t>(nReadUnsigned);
     438             :     if (nRead < nToRead)
     439             :     {
     440             :         if (m_nOffset + nRead == nFileSize)
     441             :             m_bEOF = true;
     442             :         else
     443             :             m_bError = true;
     444             :     }
     445             :     m_nOffset += nRead;
     446             :     return nRead / nSize;
     447             : }
     448             : 
     449             : /************************************************************************/
     450             : /*                                Seek()                                */
     451             : /************************************************************************/
     452             : 
     453             : int VSILibArchiveHandler::Seek(vsi_l_offset nOffset, int nWhence)
     454             : {
     455             :     if (m_bError)
     456             :         return -1;
     457             :     m_bEOF = false;
     458             :     if (nWhence == SEEK_END && nOffset == 0)
     459             :     {
     460             :         m_nOffset = m_poReader->GetFileSize();
     461             :         return 0;
     462             :     }
     463             :     auto nNewOffset = m_nOffset;
     464             :     if (nWhence == SEEK_CUR)
     465             :         nNewOffset += nOffset;
     466             :     else
     467             :         nNewOffset = nOffset;
     468             :     if (nNewOffset == m_nOffset)
     469             :         return 0;
     470             : 
     471             :     if (nNewOffset < m_nOffset)
     472             :     {
     473             :         CPLDebug("VSIARCH", "Seeking backwards in %s", m_osFilename.c_str());
     474             :         // If we need to go backwards, we must completely reset the
     475             :         // reader!
     476             :         if (!m_poReader->GotoFileOffsetForced(m_pOffset.get()))
     477             :         {
     478             :             m_bError = true;
     479             :             return -1;
     480             :         }
     481             :         m_nOffset = 0;
     482             :     }
     483             : 
     484             :     std::vector<GByte> abyBuffer(4096);
     485             :     while (m_nOffset < nNewOffset)
     486             :     {
     487             :         size_t nToRead = static_cast<size_t>(
     488             :             std::min<vsi_l_offset>(abyBuffer.size(), nNewOffset - m_nOffset));
     489             :         if (Read(abyBuffer.data(), 1, nToRead) != nToRead)
     490             :             break;
     491             :     }
     492             : 
     493             :     return 0;
     494             : }
     495             : 
     496             : /************************************************************************/
     497             : /* ==================================================================== */
     498             : /*                      VSILibArchiveFilesystemHandler                  */
     499             : /* ==================================================================== */
     500             : /************************************************************************/
     501             : 
     502             : class VSILibArchiveFilesystemHandler final : public VSIArchiveFilesystemHandler
     503             : {
     504             :     CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveFilesystemHandler)
     505             : 
     506             :     const std::string m_osPrefix;
     507             : 
     508             :     const char *GetPrefix() const override
     509             :     {
     510             :         return m_osPrefix.c_str();
     511             :     }
     512             : 
     513             :     std::vector<CPLString> GetExtensions() const override
     514             :     {
     515             :         if (m_osPrefix == "/vsi7z")
     516             :         {
     517             :             return {".7z", ".lpk", ".lpkx", ".mpk", ".mpkx", ".ppkx"};
     518             :         }
     519             :         else
     520             :         {
     521             :             return {".rar"};
     522             :         }
     523             :     }
     524             : 
     525             :     virtual std::unique_ptr<VSIArchiveReader>
     526             :     CreateReader(const char *pszArchiveFileName) override;
     527             : 
     528             :   public:
     529             :     VSILibArchiveFilesystemHandler(const std::string &osPrefix)
     530             :         : m_osPrefix(osPrefix)
     531             :     {
     532             :     }
     533             : 
     534             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
     535             :                                    const char *pszAccess, bool bSetError,
     536             :                                    CSLConstList papszOptions) override;
     537             : };
     538             : 
     539             : /************************************************************************/
     540             : /*                                 Open()                               */
     541             : /************************************************************************/
     542             : 
     543             : VSIVirtualHandleUniquePtr
     544             : VSILibArchiveFilesystemHandler::Open(const char *pszFilename,
     545             :                                      const char *pszAccess, bool bSetError,
     546             :                                      CSLConstList)
     547             : {
     548             :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
     549             :     {
     550             :         CPLError(CE_Failure, CPLE_AppDefined,
     551             :                  "Only read-only mode is supported for %s", m_osPrefix.c_str());
     552             :         return nullptr;
     553             :     }
     554             : 
     555             :     CPLString osFileInArchive;
     556             :     char *pszArchiveFileName =
     557             :         SplitFilename(pszFilename, osFileInArchive, true, bSetError);
     558             :     if (pszArchiveFileName == nullptr)
     559             :         return nullptr;
     560             : 
     561             :     auto poReader = std::unique_ptr<VSILibArchiveReader>(
     562             :         cpl::down_cast<VSILibArchiveReader *>(
     563             :             OpenArchiveFile(pszArchiveFileName, osFileInArchive).release()));
     564             :     CPLFree(pszArchiveFileName);
     565             :     if (poReader == nullptr)
     566             :     {
     567             :         return nullptr;
     568             :     }
     569             : 
     570             :     if (osFileInArchive.empty())
     571             :         poReader->GotoFirstFileForced();
     572             : 
     573             :     return VSIVirtualHandleUniquePtr(
     574             :         std::make_unique<VSILibArchiveHandler>(pszFilename, poReader.release())
     575             :             .release());
     576             : }
     577             : 
     578             : /************************************************************************/
     579             : /*                           CreateReader()                             */
     580             : /************************************************************************/
     581             : 
     582             : std::unique_ptr<VSIArchiveReader>
     583             : VSILibArchiveFilesystemHandler::CreateReader(const char *pszArchiveFileName)
     584             : {
     585             :     auto pArchive = VSICreateArchiveHandle(m_osPrefix);
     586             : 
     587             :     if (VSILibArchiveReadOpen(pArchive, pszArchiveFileName))
     588             :     {
     589             :         CPLDebug("VSIARCH", "%s: %s", pszArchiveFileName,
     590             :                  archive_error_string(pArchive));
     591             :         archive_read_free(pArchive);
     592             :         return nullptr;
     593             :     }
     594             :     return std::make_unique<VSILibArchiveReader>(pszArchiveFileName, pArchive,
     595             :                                                  m_osPrefix);
     596             : }
     597             : 
     598             : //! @endcond
     599             : 
     600             : /************************************************************************/
     601             : /*                    VSIInstall7zFileHandler()                         */
     602             : /************************************************************************/
     603             : 
     604             : /*!
     605             :  \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
     606             : 
     607             :  \verbatim embed:rst
     608             :  See :ref:`/vsi7z/ documentation <vsi7z>`
     609             :  \endverbatim
     610             : 
     611             :  @since GDAL 3.7
     612             :  */
     613             : void VSIInstall7zFileHandler(void)
     614             : {
     615             :     VSIFileManager::InstallHandler(
     616             :         "/vsi7z/", new VSILibArchiveFilesystemHandler("/vsi7z"));
     617             : }
     618             : 
     619             : /************************************************************************/
     620             : /*                    VSIInstallRarFileHandler()                         */
     621             : /************************************************************************/
     622             : 
     623             : /*!
     624             :  \brief Install /vsirar/ rar file system handler (requires libarchive)
     625             : 
     626             :  \verbatim embed:rst
     627             :  See :ref:`/vsirar/ documentation <vsirar>`
     628             :  \endverbatim
     629             : 
     630             :  @since GDAL 3.7
     631             :  */
     632             : void VSIInstallRarFileHandler(void)
     633             : {
     634             :     VSIFileManager::InstallHandler(
     635             :         "/vsirar/", new VSILibArchiveFilesystemHandler("/vsirar"));
     636             : }
     637             : 
     638             : #endif

Generated by: LCOV version 1.14