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-08-01 10:10:57 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             :     virtual int GotoFirstFile() override;
     219             :     virtual int GotoNextFile() override;
     220             :     virtual VSIArchiveEntryFileOffset *GetFileOffset() override;
     221             : 
     222             :     virtual GUIntBig GetFileSize() override
     223             :     {
     224             :         return m_nFilesize;
     225             :     }
     226             : 
     227             :     virtual CPLString GetFileName() override
     228             :     {
     229             :         return m_osFilename;
     230             :     }
     231             : 
     232             :     virtual GIntBig GetModifiedTime() override
     233             :     {
     234             :         return m_nMTime;
     235             :     }
     236             : 
     237             :     virtual int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
     238             : 
     239             :     int GotoFileOffsetForced(VSIArchiveEntryFileOffset *pOffset);
     240             : };
     241             : 
     242             : /************************************************************************/
     243             : /*                       ~VSILibArchiveReader()                         */
     244             : /************************************************************************/
     245             : 
     246             : VSILibArchiveReader::~VSILibArchiveReader()
     247             : {
     248             :     archive_free(m_pArchive);
     249             : }
     250             : 
     251             : /************************************************************************/
     252             : /*                           GotoFirstFile()                            */
     253             : /************************************************************************/
     254             : 
     255             : int VSILibArchiveReader::GotoFirstFile()
     256             : {
     257             :     if (!m_bFirst)
     258             :     {
     259             :         archive_free(m_pArchive);
     260             : 
     261             :         m_pArchive = VSICreateArchiveHandle(m_osPrefix);
     262             : 
     263             :         if (VSILibArchiveReadOpen(m_pArchive, m_osArchiveFileName.c_str()))
     264             :         {
     265             :             CPLDebug("VSIARCH", "%s: %s", m_osArchiveFileName.c_str(),
     266             :                      archive_error_string(m_pArchive));
     267             :             return false;
     268             :         }
     269             :         m_bFirst = true;
     270             :     }
     271             :     return GotoNextFile();
     272             : }
     273             : 
     274             : /************************************************************************/
     275             : /*                           GotoNextFile()                             */
     276             : /************************************************************************/
     277             : 
     278             : int VSILibArchiveReader::GotoNextFile()
     279             : {
     280             :     struct archive_entry *entry;
     281             :     int r = archive_read_next_header(m_pArchive, &entry);
     282             :     if (r == ARCHIVE_EOF)
     283             :         return FALSE;
     284             :     if (r != ARCHIVE_OK)
     285             :     {
     286             :         CPLDebug("VSIARCH", "%s", archive_error_string(m_pArchive));
     287             :         return FALSE;
     288             :     }
     289             :     m_osFilename = archive_entry_pathname_utf8(entry);
     290             :     m_nFilesize = archive_entry_size(entry);
     291             :     m_nMTime = archive_entry_mtime(entry);
     292             :     return TRUE;
     293             : }
     294             : 
     295             : /************************************************************************/
     296             : /*                      VSILibArchiveEntryFileOffset                    */
     297             : /************************************************************************/
     298             : 
     299             : struct VSILibArchiveEntryFileOffset : public VSIArchiveEntryFileOffset
     300             : {
     301             :     const std::string m_osFilename;
     302             : 
     303             :     VSILibArchiveEntryFileOffset(const std::string &osFilename)
     304             :         : m_osFilename(osFilename)
     305             :     {
     306             :     }
     307             : 
     308             :     ~VSILibArchiveEntryFileOffset() override;
     309             : };
     310             : 
     311             : VSILibArchiveEntryFileOffset::~VSILibArchiveEntryFileOffset() = default;
     312             : 
     313             : /************************************************************************/
     314             : /*                          GetFileOffset()                             */
     315             : /************************************************************************/
     316             : 
     317             : VSIArchiveEntryFileOffset *VSILibArchiveReader::GetFileOffset()
     318             : {
     319             :     return new VSILibArchiveEntryFileOffset(m_osFilename);
     320             : }
     321             : 
     322             : /************************************************************************/
     323             : /*                         GotoFileOffset()                             */
     324             : /************************************************************************/
     325             : 
     326             : int VSILibArchiveReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
     327             : {
     328             :     VSILibArchiveEntryFileOffset *pMyOffset =
     329             :         static_cast<VSILibArchiveEntryFileOffset *>(pOffset);
     330             :     if (!GotoFirstFile())
     331             :         return false;
     332             :     while (m_osFilename != pMyOffset->m_osFilename)
     333             :     {
     334             :         if (!GotoNextFile())
     335             :             return false;
     336             :     }
     337             :     return true;
     338             : }
     339             : 
     340             : /************************************************************************/
     341             : /*                       GotoFileOffsetForced()                         */
     342             : /************************************************************************/
     343             : 
     344             : int VSILibArchiveReader::GotoFileOffsetForced(
     345             :     VSIArchiveEntryFileOffset *pOffset)
     346             : {
     347             :     m_bFirst = false;
     348             :     return GotoFileOffset(pOffset);
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /* ==================================================================== */
     353             : /*                      VSILibArchiveHandler                            */
     354             : /* ==================================================================== */
     355             : /************************************************************************/
     356             : 
     357             : class VSILibArchiveHandler final : public VSIVirtualHandle
     358             : {
     359             :     const std::string m_osFilename;
     360             :     std::unique_ptr<VSILibArchiveReader> m_poReader;
     361             :     std::unique_ptr<VSIArchiveEntryFileOffset> m_pOffset;
     362             :     vsi_l_offset m_nOffset = 0;
     363             :     bool m_bEOF = false;
     364             :     bool m_bError = false;
     365             : 
     366             :   public:
     367             :     VSILibArchiveHandler(const std::string &osFilename,
     368             :                          VSILibArchiveReader *poReader)
     369             :         : m_osFilename(osFilename), m_poReader(poReader),
     370             :           m_pOffset(poReader->GetFileOffset())
     371             :     {
     372             :     }
     373             : 
     374             :     virtual size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
     375             :     virtual int Seek(vsi_l_offset nOffset, int nWhence) override;
     376             : 
     377             :     virtual vsi_l_offset Tell() override
     378             :     {
     379             :         return m_nOffset;
     380             :     }
     381             : 
     382             :     virtual size_t Write(const void *, size_t, size_t) override
     383             :     {
     384             :         return 0;
     385             :     }
     386             : 
     387             :     virtual void ClearErr() override
     388             :     {
     389             :         m_bEOF = false;
     390             :         m_bError = false;
     391             :     }
     392             : 
     393             :     virtual int Eof() override
     394             :     {
     395             :         return m_bEOF ? 1 : 0;
     396             :     }
     397             : 
     398             :     virtual int Error() override
     399             :     {
     400             :         return m_bError ? 1 : 0;
     401             :     }
     402             : 
     403             :     virtual int Close() override
     404             :     {
     405             :         m_poReader.reset();
     406             :         return 0;
     407             :     }
     408             : };
     409             : 
     410             : /************************************************************************/
     411             : /*                                Read()                                */
     412             : /************************************************************************/
     413             : 
     414             : size_t VSILibArchiveHandler::Read(void *pBuffer, size_t nSize, size_t nCount)
     415             : {
     416             :     if (m_bError || nSize == 0 || nCount == 0)
     417             :         return 0;
     418             :     if (m_nOffset == m_poReader->GetFileSize())
     419             :     {
     420             :         m_bEOF = true;
     421             :         return 0;
     422             :     }
     423             :     size_t nToRead = nSize * nCount;
     424             :     auto nRead = static_cast<size_t>(
     425             :         archive_read_data(m_poReader->GetArchiveHandler(), pBuffer, nToRead));
     426             :     if (nRead < nToRead)
     427             :     {
     428             :         if (m_nOffset + nRead == m_poReader->GetFileSize())
     429             :             m_bEOF = true;
     430             :         else
     431             :             m_bError = true;
     432             :     }
     433             :     m_nOffset += nRead;
     434             :     return nRead / nSize;
     435             : }
     436             : 
     437             : /************************************************************************/
     438             : /*                                Seek()                                */
     439             : /************************************************************************/
     440             : 
     441             : int VSILibArchiveHandler::Seek(vsi_l_offset nOffset, int nWhence)
     442             : {
     443             :     if (m_bError)
     444             :         return -1;
     445             :     m_bEOF = false;
     446             :     if (nWhence == SEEK_END && nOffset == 0)
     447             :     {
     448             :         m_nOffset = m_poReader->GetFileSize();
     449             :         return 0;
     450             :     }
     451             :     auto nNewOffset = m_nOffset;
     452             :     if (nWhence == SEEK_CUR)
     453             :         nNewOffset += nOffset;
     454             :     else
     455             :         nNewOffset = nOffset;
     456             :     if (nNewOffset == m_nOffset)
     457             :         return 0;
     458             : 
     459             :     if (nNewOffset < m_nOffset)
     460             :     {
     461             :         CPLDebug("VSIARCH", "Seeking backwards in %s", m_osFilename.c_str());
     462             :         // If we need to go backwards, we must completely reset the
     463             :         // reader!
     464             :         if (!m_poReader->GotoFileOffsetForced(m_pOffset.get()))
     465             :         {
     466             :             m_bError = true;
     467             :             return -1;
     468             :         }
     469             :         m_nOffset = 0;
     470             :     }
     471             : 
     472             :     std::vector<GByte> abyBuffer(4096);
     473             :     while (m_nOffset < nNewOffset)
     474             :     {
     475             :         size_t nToRead = static_cast<size_t>(
     476             :             std::min<vsi_l_offset>(abyBuffer.size(), nNewOffset - m_nOffset));
     477             :         if (Read(abyBuffer.data(), 1, nToRead) != nToRead)
     478             :             break;
     479             :     }
     480             : 
     481             :     return 0;
     482             : }
     483             : 
     484             : /************************************************************************/
     485             : /* ==================================================================== */
     486             : /*                      VSILibArchiveFilesystemHandler                  */
     487             : /* ==================================================================== */
     488             : /************************************************************************/
     489             : 
     490             : class VSILibArchiveFilesystemHandler final : public VSIArchiveFilesystemHandler
     491             : {
     492             :     CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveFilesystemHandler)
     493             : 
     494             :     const std::string m_osPrefix;
     495             : 
     496             :     virtual const char *GetPrefix() override
     497             :     {
     498             :         return m_osPrefix.c_str();
     499             :     }
     500             : 
     501             :     virtual std::vector<CPLString> GetExtensions() override
     502             :     {
     503             :         if (m_osPrefix == "/vsi7z")
     504             :         {
     505             :             return {".7z", ".lpk", ".lpkx", ".mpk", ".mpkx", ".ppkx"};
     506             :         }
     507             :         else
     508             :         {
     509             :             return {".rar"};
     510             :         }
     511             :     }
     512             : 
     513             :     virtual VSIArchiveReader *
     514             :     CreateReader(const char *pszArchiveFileName) override;
     515             : 
     516             :   public:
     517             :     VSILibArchiveFilesystemHandler(const std::string &osPrefix)
     518             :         : m_osPrefix(osPrefix)
     519             :     {
     520             :     }
     521             : 
     522             :     virtual VSIVirtualHandle *Open(const char *pszFilename,
     523             :                                    const char *pszAccess, bool bSetError,
     524             :                                    CSLConstList papszOptions) override;
     525             : };
     526             : 
     527             : /************************************************************************/
     528             : /*                                 Open()                               */
     529             : /************************************************************************/
     530             : 
     531             : VSIVirtualHandle *VSILibArchiveFilesystemHandler::Open(const char *pszFilename,
     532             :                                                        const char *pszAccess,
     533             :                                                        bool bSetError,
     534             :                                                        CSLConstList)
     535             : {
     536             :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
     537             :     {
     538             :         CPLError(CE_Failure, CPLE_AppDefined,
     539             :                  "Only read-only mode is supported for %s", m_osPrefix.c_str());
     540             :         return nullptr;
     541             :     }
     542             : 
     543             :     CPLString osFileInArchive;
     544             :     char *pszArchiveFileName =
     545             :         SplitFilename(pszFilename, osFileInArchive, true, bSetError);
     546             :     if (pszArchiveFileName == nullptr)
     547             :         return nullptr;
     548             : 
     549             :     VSILibArchiveReader *poReader = cpl::down_cast<VSILibArchiveReader *>(
     550             :         OpenArchiveFile(pszArchiveFileName, osFileInArchive));
     551             :     CPLFree(pszArchiveFileName);
     552             :     if (poReader == nullptr)
     553             :     {
     554             :         return nullptr;
     555             :     }
     556             : 
     557             :     return new VSILibArchiveHandler(pszFilename, poReader);
     558             : }
     559             : 
     560             : /************************************************************************/
     561             : /*                           CreateReader()                             */
     562             : /************************************************************************/
     563             : 
     564             : VSIArchiveReader *
     565             : VSILibArchiveFilesystemHandler::CreateReader(const char *pszArchiveFileName)
     566             : {
     567             :     auto pArchive = VSICreateArchiveHandle(m_osPrefix);
     568             : 
     569             :     if (VSILibArchiveReadOpen(pArchive, pszArchiveFileName))
     570             :     {
     571             :         CPLDebug("VSIARCH", "%s: %s", pszArchiveFileName,
     572             :                  archive_error_string(pArchive));
     573             :         archive_read_free(pArchive);
     574             :         return nullptr;
     575             :     }
     576             :     return new VSILibArchiveReader(pszArchiveFileName, pArchive, m_osPrefix);
     577             : }
     578             : 
     579             : //! @endcond
     580             : 
     581             : /************************************************************************/
     582             : /*                    VSIInstall7zFileHandler()                         */
     583             : /************************************************************************/
     584             : 
     585             : /*!
     586             :  \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
     587             : 
     588             :  \verbatim embed:rst
     589             :  See :ref:`/vsi7z/ documentation <vsi7z>`
     590             :  \endverbatim
     591             : 
     592             :  @since GDAL 3.7
     593             :  */
     594             : void VSIInstall7zFileHandler(void)
     595             : {
     596             :     VSIFileManager::InstallHandler(
     597             :         "/vsi7z/", new VSILibArchiveFilesystemHandler("/vsi7z"));
     598             : }
     599             : 
     600             : /************************************************************************/
     601             : /*                    VSIInstallRarFileHandler()                         */
     602             : /************************************************************************/
     603             : 
     604             : /*!
     605             :  \brief Install /vsirar/ rar file system handler (requires libarchive)
     606             : 
     607             :  \verbatim embed:rst
     608             :  See :ref:`/vsirar/ documentation <vsirar>`
     609             :  \endverbatim
     610             : 
     611             :  @since GDAL 3.7
     612             :  */
     613             : void VSIInstallRarFileHandler(void)
     614             : {
     615             :     VSIFileManager::InstallHandler(
     616             :         "/vsirar/", new VSILibArchiveFilesystemHandler("/vsirar"));
     617             : }
     618             : 
     619             : #endif

Generated by: LCOV version 1.14