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: 2024-11-21 22:18:42 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             : 
     309             : /************************************************************************/
     310             : /*                          GetFileOffset()                             */
     311             : /************************************************************************/
     312             : 
     313             : VSIArchiveEntryFileOffset *VSILibArchiveReader::GetFileOffset()
     314             : {
     315             :     return new VSILibArchiveEntryFileOffset(m_osFilename);
     316             : }
     317             : 
     318             : /************************************************************************/
     319             : /*                         GotoFileOffset()                             */
     320             : /************************************************************************/
     321             : 
     322             : int VSILibArchiveReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
     323             : {
     324             :     VSILibArchiveEntryFileOffset *pMyOffset =
     325             :         static_cast<VSILibArchiveEntryFileOffset *>(pOffset);
     326             :     if (!GotoFirstFile())
     327             :         return false;
     328             :     while (m_osFilename != pMyOffset->m_osFilename)
     329             :     {
     330             :         if (!GotoNextFile())
     331             :             return false;
     332             :     }
     333             :     return true;
     334             : }
     335             : 
     336             : /************************************************************************/
     337             : /*                       GotoFileOffsetForced()                         */
     338             : /************************************************************************/
     339             : 
     340             : int VSILibArchiveReader::GotoFileOffsetForced(
     341             :     VSIArchiveEntryFileOffset *pOffset)
     342             : {
     343             :     m_bFirst = false;
     344             :     return GotoFileOffset(pOffset);
     345             : }
     346             : 
     347             : /************************************************************************/
     348             : /* ==================================================================== */
     349             : /*                      VSILibArchiveHandler                            */
     350             : /* ==================================================================== */
     351             : /************************************************************************/
     352             : 
     353             : class VSILibArchiveHandler final : public VSIVirtualHandle
     354             : {
     355             :     const std::string m_osFilename;
     356             :     std::unique_ptr<VSILibArchiveReader> m_poReader;
     357             :     std::unique_ptr<VSIArchiveEntryFileOffset> m_pOffset;
     358             :     vsi_l_offset m_nOffset = 0;
     359             :     bool m_bEOF = false;
     360             :     bool m_bError = false;
     361             : 
     362             :   public:
     363             :     VSILibArchiveHandler(const std::string &osFilename,
     364             :                          VSILibArchiveReader *poReader)
     365             :         : m_osFilename(osFilename), m_poReader(poReader),
     366             :           m_pOffset(poReader->GetFileOffset())
     367             :     {
     368             :     }
     369             : 
     370             :     virtual size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
     371             :     virtual int Seek(vsi_l_offset nOffset, int nWhence) override;
     372             : 
     373             :     virtual vsi_l_offset Tell() override
     374             :     {
     375             :         return m_nOffset;
     376             :     }
     377             : 
     378             :     virtual size_t Write(const void *, size_t, size_t) override
     379             :     {
     380             :         return 0;
     381             :     }
     382             : 
     383             :     virtual void ClearErr() override
     384             :     {
     385             :         m_bEOF = false;
     386             :         m_bError = false;
     387             :     }
     388             : 
     389             :     virtual int Eof() override
     390             :     {
     391             :         return m_bEOF ? 1 : 0;
     392             :     }
     393             : 
     394             :     virtual int Error() override
     395             :     {
     396             :         return m_bError ? 1 : 0;
     397             :     }
     398             : 
     399             :     virtual int Close() override
     400             :     {
     401             :         return 0;
     402             :     }
     403             : };
     404             : 
     405             : /************************************************************************/
     406             : /*                                Read()                                */
     407             : /************************************************************************/
     408             : 
     409             : size_t VSILibArchiveHandler::Read(void *pBuffer, size_t nSize, size_t nCount)
     410             : {
     411             :     if (m_bError || nSize == 0 || nCount == 0)
     412             :         return 0;
     413             :     if (m_nOffset == m_poReader->GetFileSize())
     414             :     {
     415             :         m_bEOF = true;
     416             :         return 0;
     417             :     }
     418             :     size_t nToRead = nSize * nCount;
     419             :     auto nRead = static_cast<size_t>(
     420             :         archive_read_data(m_poReader->GetArchiveHandler(), pBuffer, nToRead));
     421             :     if (nRead < nToRead)
     422             :     {
     423             :         if (m_nOffset + nRead == m_poReader->GetFileSize())
     424             :             m_bEOF = true;
     425             :         else
     426             :             m_bError = true;
     427             :     }
     428             :     m_nOffset += nRead;
     429             :     return nRead / nSize;
     430             : }
     431             : 
     432             : /************************************************************************/
     433             : /*                                Seek()                                */
     434             : /************************************************************************/
     435             : 
     436             : int VSILibArchiveHandler::Seek(vsi_l_offset nOffset, int nWhence)
     437             : {
     438             :     if (m_bError)
     439             :         return -1;
     440             :     m_bEOF = false;
     441             :     if (nWhence == SEEK_END && nOffset == 0)
     442             :     {
     443             :         m_nOffset = m_poReader->GetFileSize();
     444             :         return 0;
     445             :     }
     446             :     auto nNewOffset = m_nOffset;
     447             :     if (nWhence == SEEK_CUR)
     448             :         nNewOffset += nOffset;
     449             :     else
     450             :         nNewOffset = nOffset;
     451             :     if (nNewOffset == m_nOffset)
     452             :         return 0;
     453             : 
     454             :     if (nNewOffset < m_nOffset)
     455             :     {
     456             :         CPLDebug("VSIARCH", "Seeking backwards in %s", m_osFilename.c_str());
     457             :         // If we need to go backwards, we must completely reset the
     458             :         // reader!
     459             :         if (!m_poReader->GotoFileOffsetForced(m_pOffset.get()))
     460             :         {
     461             :             m_bError = true;
     462             :             return -1;
     463             :         }
     464             :         m_nOffset = 0;
     465             :     }
     466             : 
     467             :     std::vector<GByte> abyBuffer(4096);
     468             :     while (m_nOffset < nNewOffset)
     469             :     {
     470             :         size_t nToRead = static_cast<size_t>(
     471             :             std::min<vsi_l_offset>(abyBuffer.size(), nNewOffset - m_nOffset));
     472             :         if (Read(abyBuffer.data(), 1, nToRead) != nToRead)
     473             :             break;
     474             :     }
     475             : 
     476             :     return 0;
     477             : }
     478             : 
     479             : /************************************************************************/
     480             : /* ==================================================================== */
     481             : /*                      VSILibArchiveFilesystemHandler                  */
     482             : /* ==================================================================== */
     483             : /************************************************************************/
     484             : 
     485             : class VSILibArchiveFilesystemHandler final : public VSIArchiveFilesystemHandler
     486             : {
     487             :     CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveFilesystemHandler)
     488             : 
     489             :     const std::string m_osPrefix;
     490             : 
     491             :     virtual const char *GetPrefix() override
     492             :     {
     493             :         return m_osPrefix.c_str();
     494             :     }
     495             : 
     496             :     virtual std::vector<CPLString> GetExtensions() override
     497             :     {
     498             :         if (m_osPrefix == "/vsi7z")
     499             :         {
     500             :             return {".7z", ".lpk", ".lpkx", ".mpk", ".mpkx", ".ppkx"};
     501             :         }
     502             :         else
     503             :         {
     504             :             return {".rar"};
     505             :         }
     506             :     }
     507             : 
     508             :     virtual VSIArchiveReader *
     509             :     CreateReader(const char *pszArchiveFileName) override;
     510             : 
     511             :   public:
     512             :     VSILibArchiveFilesystemHandler(const std::string &osPrefix)
     513             :         : m_osPrefix(osPrefix)
     514             :     {
     515             :     }
     516             : 
     517             :     virtual VSIVirtualHandle *Open(const char *pszFilename,
     518             :                                    const char *pszAccess, bool bSetError,
     519             :                                    CSLConstList papszOptions) override;
     520             : };
     521             : 
     522             : /************************************************************************/
     523             : /*                                 Open()                               */
     524             : /************************************************************************/
     525             : 
     526             : VSIVirtualHandle *VSILibArchiveFilesystemHandler::Open(const char *pszFilename,
     527             :                                                        const char *pszAccess,
     528             :                                                        bool, CSLConstList)
     529             : {
     530             :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
     531             :     {
     532             :         CPLError(CE_Failure, CPLE_AppDefined,
     533             :                  "Only read-only mode is supported for %s", m_osPrefix.c_str());
     534             :         return nullptr;
     535             :     }
     536             : 
     537             :     CPLString osFileInArchive;
     538             :     char *pszArchiveFileName =
     539             :         SplitFilename(pszFilename, osFileInArchive, TRUE);
     540             :     if (pszArchiveFileName == nullptr)
     541             :         return nullptr;
     542             : 
     543             :     VSILibArchiveReader *poReader = cpl::down_cast<VSILibArchiveReader *>(
     544             :         OpenArchiveFile(pszArchiveFileName, osFileInArchive));
     545             :     CPLFree(pszArchiveFileName);
     546             :     if (poReader == nullptr)
     547             :     {
     548             :         return nullptr;
     549             :     }
     550             : 
     551             :     return new VSILibArchiveHandler(pszFilename, poReader);
     552             : }
     553             : 
     554             : /************************************************************************/
     555             : /*                           CreateReader()                             */
     556             : /************************************************************************/
     557             : 
     558             : VSIArchiveReader *
     559             : VSILibArchiveFilesystemHandler::CreateReader(const char *pszArchiveFileName)
     560             : {
     561             :     auto pArchive = VSICreateArchiveHandle(m_osPrefix);
     562             : 
     563             :     if (VSILibArchiveReadOpen(pArchive, pszArchiveFileName))
     564             :     {
     565             :         CPLDebug("VSIARCH", "%s: %s", pszArchiveFileName,
     566             :                  archive_error_string(pArchive));
     567             :         archive_read_free(pArchive);
     568             :         return nullptr;
     569             :     }
     570             :     return new VSILibArchiveReader(pszArchiveFileName, pArchive, m_osPrefix);
     571             : }
     572             : 
     573             : //! @endcond
     574             : 
     575             : /************************************************************************/
     576             : /*                    VSIInstall7zFileHandler()                         */
     577             : /************************************************************************/
     578             : 
     579             : /*!
     580             :  \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
     581             : 
     582             :  \verbatim embed:rst
     583             :  See :ref:`/vsi7z/ documentation <vsi7z>`
     584             :  \endverbatim
     585             : 
     586             :  @since GDAL 3.7
     587             :  */
     588             : void VSIInstall7zFileHandler(void)
     589             : {
     590             :     VSIFileManager::InstallHandler(
     591             :         "/vsi7z/", new VSILibArchiveFilesystemHandler("/vsi7z"));
     592             : }
     593             : 
     594             : /************************************************************************/
     595             : /*                    VSIInstallRarFileHandler()                         */
     596             : /************************************************************************/
     597             : 
     598             : /*!
     599             :  \brief Install /vsirar/ rar file system handler (requires libarchive)
     600             : 
     601             :  \verbatim embed:rst
     602             :  See :ref:`/vsirar/ documentation <vsirar>`
     603             :  \endverbatim
     604             : 
     605             :  @since GDAL 3.7
     606             :  */
     607             : void VSIInstallRarFileHandler(void)
     608             : {
     609             :     VSIFileManager::InstallHandler(
     610             :         "/vsirar/", new VSILibArchiveFilesystemHandler("/vsirar"));
     611             : }
     612             : 
     613             : #endif

Generated by: LCOV version 1.14