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-06-28 21:28:23 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             :         return 0;
     406             :     }
     407             : };
     408             : 
     409             : /************************************************************************/
     410             : /*                                Read()                                */
     411             : /************************************************************************/
     412             : 
     413             : size_t VSILibArchiveHandler::Read(void *pBuffer, size_t nSize, size_t nCount)
     414             : {
     415             :     if (m_bError || nSize == 0 || nCount == 0)
     416             :         return 0;
     417             :     if (m_nOffset == m_poReader->GetFileSize())
     418             :     {
     419             :         m_bEOF = true;
     420             :         return 0;
     421             :     }
     422             :     size_t nToRead = nSize * nCount;
     423             :     auto nRead = static_cast<size_t>(
     424             :         archive_read_data(m_poReader->GetArchiveHandler(), pBuffer, nToRead));
     425             :     if (nRead < nToRead)
     426             :     {
     427             :         if (m_nOffset + nRead == m_poReader->GetFileSize())
     428             :             m_bEOF = true;
     429             :         else
     430             :             m_bError = true;
     431             :     }
     432             :     m_nOffset += nRead;
     433             :     return nRead / nSize;
     434             : }
     435             : 
     436             : /************************************************************************/
     437             : /*                                Seek()                                */
     438             : /************************************************************************/
     439             : 
     440             : int VSILibArchiveHandler::Seek(vsi_l_offset nOffset, int nWhence)
     441             : {
     442             :     if (m_bError)
     443             :         return -1;
     444             :     m_bEOF = false;
     445             :     if (nWhence == SEEK_END && nOffset == 0)
     446             :     {
     447             :         m_nOffset = m_poReader->GetFileSize();
     448             :         return 0;
     449             :     }
     450             :     auto nNewOffset = m_nOffset;
     451             :     if (nWhence == SEEK_CUR)
     452             :         nNewOffset += nOffset;
     453             :     else
     454             :         nNewOffset = nOffset;
     455             :     if (nNewOffset == m_nOffset)
     456             :         return 0;
     457             : 
     458             :     if (nNewOffset < m_nOffset)
     459             :     {
     460             :         CPLDebug("VSIARCH", "Seeking backwards in %s", m_osFilename.c_str());
     461             :         // If we need to go backwards, we must completely reset the
     462             :         // reader!
     463             :         if (!m_poReader->GotoFileOffsetForced(m_pOffset.get()))
     464             :         {
     465             :             m_bError = true;
     466             :             return -1;
     467             :         }
     468             :         m_nOffset = 0;
     469             :     }
     470             : 
     471             :     std::vector<GByte> abyBuffer(4096);
     472             :     while (m_nOffset < nNewOffset)
     473             :     {
     474             :         size_t nToRead = static_cast<size_t>(
     475             :             std::min<vsi_l_offset>(abyBuffer.size(), nNewOffset - m_nOffset));
     476             :         if (Read(abyBuffer.data(), 1, nToRead) != nToRead)
     477             :             break;
     478             :     }
     479             : 
     480             :     return 0;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /* ==================================================================== */
     485             : /*                      VSILibArchiveFilesystemHandler                  */
     486             : /* ==================================================================== */
     487             : /************************************************************************/
     488             : 
     489             : class VSILibArchiveFilesystemHandler final : public VSIArchiveFilesystemHandler
     490             : {
     491             :     CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveFilesystemHandler)
     492             : 
     493             :     const std::string m_osPrefix;
     494             : 
     495             :     virtual const char *GetPrefix() override
     496             :     {
     497             :         return m_osPrefix.c_str();
     498             :     }
     499             : 
     500             :     virtual std::vector<CPLString> GetExtensions() override
     501             :     {
     502             :         if (m_osPrefix == "/vsi7z")
     503             :         {
     504             :             return {".7z", ".lpk", ".lpkx", ".mpk", ".mpkx", ".ppkx"};
     505             :         }
     506             :         else
     507             :         {
     508             :             return {".rar"};
     509             :         }
     510             :     }
     511             : 
     512             :     virtual VSIArchiveReader *
     513             :     CreateReader(const char *pszArchiveFileName) override;
     514             : 
     515             :   public:
     516             :     VSILibArchiveFilesystemHandler(const std::string &osPrefix)
     517             :         : m_osPrefix(osPrefix)
     518             :     {
     519             :     }
     520             : 
     521             :     virtual VSIVirtualHandle *Open(const char *pszFilename,
     522             :                                    const char *pszAccess, bool bSetError,
     523             :                                    CSLConstList papszOptions) override;
     524             : };
     525             : 
     526             : /************************************************************************/
     527             : /*                                 Open()                               */
     528             : /************************************************************************/
     529             : 
     530             : VSIVirtualHandle *VSILibArchiveFilesystemHandler::Open(const char *pszFilename,
     531             :                                                        const char *pszAccess,
     532             :                                                        bool bSetError,
     533             :                                                        CSLConstList)
     534             : {
     535             :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
     536             :     {
     537             :         CPLError(CE_Failure, CPLE_AppDefined,
     538             :                  "Only read-only mode is supported for %s", m_osPrefix.c_str());
     539             :         return nullptr;
     540             :     }
     541             : 
     542             :     CPLString osFileInArchive;
     543             :     char *pszArchiveFileName =
     544             :         SplitFilename(pszFilename, osFileInArchive, true, bSetError);
     545             :     if (pszArchiveFileName == nullptr)
     546             :         return nullptr;
     547             : 
     548             :     VSILibArchiveReader *poReader = cpl::down_cast<VSILibArchiveReader *>(
     549             :         OpenArchiveFile(pszArchiveFileName, osFileInArchive));
     550             :     CPLFree(pszArchiveFileName);
     551             :     if (poReader == nullptr)
     552             :     {
     553             :         return nullptr;
     554             :     }
     555             : 
     556             :     return new VSILibArchiveHandler(pszFilename, poReader);
     557             : }
     558             : 
     559             : /************************************************************************/
     560             : /*                           CreateReader()                             */
     561             : /************************************************************************/
     562             : 
     563             : VSIArchiveReader *
     564             : VSILibArchiveFilesystemHandler::CreateReader(const char *pszArchiveFileName)
     565             : {
     566             :     auto pArchive = VSICreateArchiveHandle(m_osPrefix);
     567             : 
     568             :     if (VSILibArchiveReadOpen(pArchive, pszArchiveFileName))
     569             :     {
     570             :         CPLDebug("VSIARCH", "%s: %s", pszArchiveFileName,
     571             :                  archive_error_string(pArchive));
     572             :         archive_read_free(pArchive);
     573             :         return nullptr;
     574             :     }
     575             :     return new VSILibArchiveReader(pszArchiveFileName, pArchive, m_osPrefix);
     576             : }
     577             : 
     578             : //! @endcond
     579             : 
     580             : /************************************************************************/
     581             : /*                    VSIInstall7zFileHandler()                         */
     582             : /************************************************************************/
     583             : 
     584             : /*!
     585             :  \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
     586             : 
     587             :  \verbatim embed:rst
     588             :  See :ref:`/vsi7z/ documentation <vsi7z>`
     589             :  \endverbatim
     590             : 
     591             :  @since GDAL 3.7
     592             :  */
     593             : void VSIInstall7zFileHandler(void)
     594             : {
     595             :     VSIFileManager::InstallHandler(
     596             :         "/vsi7z/", new VSILibArchiveFilesystemHandler("/vsi7z"));
     597             : }
     598             : 
     599             : /************************************************************************/
     600             : /*                    VSIInstallRarFileHandler()                         */
     601             : /************************************************************************/
     602             : 
     603             : /*!
     604             :  \brief Install /vsirar/ rar file system handler (requires libarchive)
     605             : 
     606             :  \verbatim embed:rst
     607             :  See :ref:`/vsirar/ documentation <vsirar>`
     608             :  \endverbatim
     609             : 
     610             :  @since GDAL 3.7
     611             :  */
     612             : void VSIInstallRarFileHandler(void)
     613             : {
     614             :     VSIFileManager::InstallHandler(
     615             :         "/vsirar/", new VSILibArchiveFilesystemHandler("/vsirar"));
     616             : }
     617             : 
     618             : #endif

Generated by: LCOV version 1.14