LCOV - code coverage report
Current view: top level - port - cpl_vsil_abstract_archive.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 336 379 88.7 %
Date: 2024-04-28 21:03:45 Functions: 15 22 68.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for archive files.
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : #include "cpl_vsi_virtual.h"
      31             : 
      32             : #include <cstring>
      33             : #if HAVE_SYS_STAT_H
      34             : #include <sys/stat.h>
      35             : #endif
      36             : #include <ctime>
      37             : #include <map>
      38             : #include <set>
      39             : #include <string>
      40             : #include <utility>
      41             : #include <vector>
      42             : 
      43             : #include "cpl_conv.h"
      44             : #include "cpl_error.h"
      45             : #include "cpl_multiproc.h"
      46             : #include "cpl_string.h"
      47             : #include "cpl_vsi.h"
      48             : 
      49             : //! @cond Doxygen_Suppress
      50             : 
      51      577126 : static bool IsEitherSlash(char c)
      52             : {
      53      577126 :     return c == '/' || c == '\\';
      54             : }
      55             : 
      56             : /************************************************************************/
      57             : /*                    ~VSIArchiveEntryFileOffset()                      */
      58             : /************************************************************************/
      59             : 
      60        1507 : VSIArchiveEntryFileOffset::~VSIArchiveEntryFileOffset()
      61             : {
      62        1507 : }
      63             : 
      64             : /************************************************************************/
      65             : /*                        ~VSIArchiveReader()                           */
      66             : /************************************************************************/
      67             : 
      68        4517 : VSIArchiveReader::~VSIArchiveReader()
      69             : {
      70        4517 : }
      71             : 
      72             : /************************************************************************/
      73             : /*                        ~VSIArchiveContent()                          */
      74             : /************************************************************************/
      75             : 
      76         128 : VSIArchiveContent::~VSIArchiveContent()
      77             : {
      78        1648 :     for (int i = 0; i < nEntries; i++)
      79             :     {
      80        1584 :         delete entries[i].file_pos;
      81        1584 :         CPLFree(entries[i].fileName);
      82             :     }
      83          64 :     CPLFree(entries);
      84          64 : }
      85             : 
      86             : /************************************************************************/
      87             : /*                   VSIArchiveFilesystemHandler()                      */
      88             : /************************************************************************/
      89             : 
      90        2450 : VSIArchiveFilesystemHandler::VSIArchiveFilesystemHandler()
      91             : {
      92        2450 :     hMutex = nullptr;
      93        2450 : }
      94             : 
      95             : /************************************************************************/
      96             : /*                   ~VSIArchiveFilesystemHandler()                     */
      97             : /************************************************************************/
      98             : 
      99        1698 : VSIArchiveFilesystemHandler::~VSIArchiveFilesystemHandler()
     100             : 
     101             : {
     102        1746 :     for (const auto &iter : oFileList)
     103             :     {
     104          48 :         delete iter.second;
     105             :     }
     106             : 
     107        1698 :     if (hMutex != nullptr)
     108          15 :         CPLDestroyMutex(hMutex);
     109        1698 :     hMutex = nullptr;
     110        1698 : }
     111             : 
     112             : /************************************************************************/
     113             : /*                       GetStrippedFilename()                          */
     114             : /************************************************************************/
     115             : 
     116        4364 : static CPLString GetStrippedFilename(const CPLString &osFileName, bool &bIsDir)
     117             : {
     118        4364 :     bIsDir = false;
     119        4364 :     const char *fileName = osFileName.c_str();
     120             : 
     121             :     // Remove ./ pattern at the beginning of a filename.
     122        4364 :     if (fileName[0] == '.' && fileName[1] == '/')
     123             :     {
     124           0 :         fileName += 2;
     125           0 :         if (fileName[0] == '\0')
     126           0 :             return CPLString();
     127             :     }
     128             : 
     129        4364 :     char *pszStrippedFileName = CPLStrdup(fileName);
     130        4364 :     char *pszIter = nullptr;
     131      158967 :     for (pszIter = pszStrippedFileName; *pszIter; pszIter++)
     132             :     {
     133      154603 :         if (*pszIter == '\\')
     134           0 :             *pszIter = '/';
     135             :     }
     136             : 
     137        4364 :     const size_t nLen = strlen(fileName);
     138        4364 :     bIsDir = nLen > 0 && fileName[nLen - 1] == '/';
     139        4364 :     if (bIsDir)
     140             :     {
     141             :         // Remove trailing slash.
     142         173 :         pszStrippedFileName[nLen - 1] = '\0';
     143             :     }
     144        8728 :     CPLString osRet(pszStrippedFileName);
     145        4364 :     CPLFree(pszStrippedFileName);
     146        4364 :     return osRet;
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                       GetContentOfArchive()                          */
     151             : /************************************************************************/
     152             : 
     153             : const VSIArchiveContent *
     154        7717 : VSIArchiveFilesystemHandler::GetContentOfArchive(const char *archiveFilename,
     155             :                                                  VSIArchiveReader *poReader)
     156             : {
     157       15434 :     CPLMutexHolder oHolder(&hMutex);
     158             : 
     159             :     VSIStatBufL sStat;
     160        7717 :     if (VSIStatL(archiveFilename, &sStat) != 0)
     161           0 :         return nullptr;
     162             : 
     163        7717 :     if (oFileList.find(archiveFilename) != oFileList.end())
     164             :     {
     165        7531 :         VSIArchiveContent *content = oFileList[archiveFilename];
     166        7531 :         if (static_cast<time_t>(sStat.st_mtime) > content->mTime ||
     167        7528 :             static_cast<vsi_l_offset>(sStat.st_size) != content->nFileSize)
     168             :         {
     169           7 :             CPLDebug("VSIArchive",
     170             :                      "The content of %s has changed since it was cached",
     171             :                      archiveFilename);
     172           7 :             delete content;
     173           7 :             oFileList.erase(archiveFilename);
     174             :         }
     175             :         else
     176             :         {
     177        7524 :             return content;
     178             :         }
     179             :     }
     180             : 
     181         193 :     bool bMustClose = poReader == nullptr;
     182         193 :     if (poReader == nullptr)
     183             :     {
     184         192 :         poReader = CreateReader(archiveFilename);
     185         192 :         if (!poReader)
     186           6 :             return nullptr;
     187             :     }
     188             : 
     189         187 :     if (poReader->GotoFirstFile() == FALSE)
     190             :     {
     191           0 :         if (bMustClose)
     192           0 :             delete (poReader);
     193           0 :         return nullptr;
     194             :     }
     195             : 
     196         187 :     VSIArchiveContent *content = new VSIArchiveContent;
     197         187 :     content->mTime = sStat.st_mtime;
     198         187 :     content->nFileSize = static_cast<vsi_l_offset>(sStat.st_size);
     199         187 :     content->nEntries = 0;
     200         187 :     content->entries = nullptr;
     201         187 :     oFileList[archiveFilename] = content;
     202             : 
     203         187 :     std::set<CPLString> oSet;
     204             : 
     205        4053 :     do
     206             :     {
     207        4240 :         const CPLString osFileName = poReader->GetFileName();
     208        4240 :         bool bIsDir = false;
     209             :         const CPLString osStrippedFilename =
     210        4240 :             GetStrippedFilename(osFileName, bIsDir);
     211        8480 :         if (osStrippedFilename.empty() || osStrippedFilename[0] == '/' ||
     212        4240 :             osStrippedFilename.find("//") != std::string::npos)
     213             :         {
     214           0 :             continue;
     215             :         }
     216             : 
     217        4240 :         if (oSet.find(osStrippedFilename) == oSet.end())
     218             :         {
     219        4240 :             oSet.insert(osStrippedFilename);
     220             : 
     221             :             // Add intermediate directory structure.
     222        4240 :             const char *pszBegin = osStrippedFilename.c_str();
     223      157105 :             for (const char *pszIter = pszBegin; *pszIter; pszIter++)
     224             :             {
     225      152865 :                 if (*pszIter == '/')
     226             :                 {
     227        5596 :                     char *pszStrippedFileName2 = CPLStrdup(osStrippedFilename);
     228        5596 :                     pszStrippedFileName2[pszIter - pszBegin] = 0;
     229        5596 :                     if (oSet.find(pszStrippedFileName2) == oSet.end())
     230             :                     {
     231        1229 :                         oSet.insert(pszStrippedFileName2);
     232             : 
     233        1229 :                         content->entries =
     234        2458 :                             static_cast<VSIArchiveEntry *>(CPLRealloc(
     235        1229 :                                 content->entries, sizeof(VSIArchiveEntry) *
     236        1229 :                                                       (content->nEntries + 1)));
     237        1229 :                         content->entries[content->nEntries].fileName =
     238             :                             pszStrippedFileName2;
     239        2458 :                         content->entries[content->nEntries].nModifiedTime =
     240        1229 :                             poReader->GetModifiedTime();
     241        1229 :                         content->entries[content->nEntries].uncompressed_size =
     242             :                             0;
     243        1229 :                         content->entries[content->nEntries].bIsDir = TRUE;
     244        1229 :                         content->entries[content->nEntries].file_pos = nullptr;
     245             : #ifdef DEBUG_VERBOSE
     246             :                         const int nEntries = content->nEntries;
     247             :                         CPLDebug("VSIArchive",
     248             :                                  "[%d] %s : " CPL_FRMT_GUIB " bytes",
     249             :                                  content->nEntries + 1,
     250             :                                  content->entries[nEntries].fileName,
     251             :                                  content->entries[nEntries].uncompressed_size);
     252             : #endif
     253        1229 :                         content->nEntries++;
     254             :                     }
     255             :                     else
     256             :                     {
     257        4367 :                         CPLFree(pszStrippedFileName2);
     258             :                     }
     259             :                 }
     260             :             }
     261             : 
     262        4240 :             content->entries = static_cast<VSIArchiveEntry *>(
     263        8480 :                 CPLRealloc(content->entries,
     264        4240 :                            sizeof(VSIArchiveEntry) * (content->nEntries + 1)));
     265        8480 :             content->entries[content->nEntries].fileName =
     266        4240 :                 CPLStrdup(osStrippedFilename);
     267        8480 :             content->entries[content->nEntries].nModifiedTime =
     268        4240 :                 poReader->GetModifiedTime();
     269        8480 :             content->entries[content->nEntries].uncompressed_size =
     270        4240 :                 poReader->GetFileSize();
     271        4240 :             content->entries[content->nEntries].bIsDir = bIsDir;
     272        8480 :             content->entries[content->nEntries].file_pos =
     273        4240 :                 poReader->GetFileOffset();
     274             : #ifdef DEBUG_VERBOSE
     275             :             CPLDebug("VSIArchive", "[%d] %s : " CPL_FRMT_GUIB " bytes",
     276             :                      content->nEntries + 1,
     277             :                      content->entries[content->nEntries].fileName,
     278             :                      content->entries[content->nEntries].uncompressed_size);
     279             : #endif
     280        4240 :             content->nEntries++;
     281             :         }
     282             : 
     283        4240 :     } while (poReader->GotoNextFile());
     284             : 
     285         187 :     if (bMustClose)
     286         186 :         delete (poReader);
     287             : 
     288         187 :     return content;
     289             : }
     290             : 
     291             : /************************************************************************/
     292             : /*                        FindFileInArchive()                           */
     293             : /************************************************************************/
     294             : 
     295        6360 : int VSIArchiveFilesystemHandler::FindFileInArchive(
     296             :     const char *archiveFilename, const char *fileInArchiveName,
     297             :     const VSIArchiveEntry **archiveEntry)
     298             : {
     299        6360 :     if (fileInArchiveName == nullptr)
     300           0 :         return FALSE;
     301             : 
     302        6360 :     const VSIArchiveContent *content = GetContentOfArchive(archiveFilename);
     303        6360 :     if (content)
     304             :     {
     305     1045120 :         for (int i = 0; i < content->nEntries; i++)
     306             :         {
     307     1044800 :             if (strcmp(fileInArchiveName, content->entries[i].fileName) == 0)
     308             :             {
     309        6043 :                 if (archiveEntry)
     310        6043 :                     *archiveEntry = &content->entries[i];
     311        6043 :                 return TRUE;
     312             :             }
     313             :         }
     314             :     }
     315         317 :     return FALSE;
     316             : }
     317             : 
     318             : /************************************************************************/
     319             : /*                           CompactFilename()                          */
     320             : /************************************************************************/
     321             : 
     322       12003 : static CPLString CompactFilename(const char *pszArchiveInFileNameIn)
     323             : {
     324       12003 :     char *pszArchiveInFileName = CPLStrdup(pszArchiveInFileNameIn);
     325             : 
     326             :     // Replace a/../b by b and foo/a/../b by foo/b.
     327             :     while (true)
     328             :     {
     329       12003 :         char *pszPrevDir = strstr(pszArchiveInFileName, "/../");
     330       12003 :         if (pszPrevDir == nullptr || pszPrevDir == pszArchiveInFileName)
     331             :             break;
     332             : 
     333           0 :         char *pszPrevSlash = pszPrevDir - 1;
     334           0 :         while (pszPrevSlash != pszArchiveInFileName && *pszPrevSlash != '/')
     335           0 :             pszPrevSlash--;
     336           0 :         if (pszPrevSlash == pszArchiveInFileName)
     337           0 :             memmove(pszArchiveInFileName, pszPrevDir + 4,
     338           0 :                     strlen(pszPrevDir + 4) + 1);
     339             :         else
     340           0 :             memmove(pszPrevSlash + 1, pszPrevDir + 4,
     341           0 :                     strlen(pszPrevDir + 4) + 1);
     342           0 :     }
     343             : 
     344       12003 :     CPLString osFileInArchive = pszArchiveInFileName;
     345       12003 :     CPLFree(pszArchiveInFileName);
     346       12003 :     return osFileInArchive;
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                           SplitFilename()                            */
     351             : /************************************************************************/
     352             : 
     353       13290 : char *VSIArchiveFilesystemHandler::SplitFilename(const char *pszFilename,
     354             :                                                  CPLString &osFileInArchive,
     355             :                                                  int bCheckMainFileExists)
     356             : {
     357             :     // TODO(schwehr): Cleanup redundant calls to GetPrefix and strlen.
     358       13290 :     if (strcmp(pszFilename, GetPrefix()) == 0)
     359           4 :         return nullptr;
     360             : 
     361       13286 :     int i = 0;
     362             : 
     363             :     // Detect extended syntax: /vsiXXX/{archive_filename}/file_in_archive.
     364       13286 :     if (pszFilename[strlen(GetPrefix()) + 1] == '{')
     365             :     {
     366        1210 :         pszFilename += strlen(GetPrefix()) + 1;
     367        1210 :         int nCountCurlies = 0;
     368       45390 :         while (pszFilename[i])
     369             :         {
     370       45389 :             if (pszFilename[i] == '{')
     371        1213 :                 nCountCurlies++;
     372       44176 :             else if (pszFilename[i] == '}')
     373             :             {
     374        1212 :                 nCountCurlies--;
     375        1212 :                 if (nCountCurlies == 0)
     376        1209 :                     break;
     377             :             }
     378       44180 :             i++;
     379             :         }
     380        1210 :         if (nCountCurlies > 0)
     381           1 :             return nullptr;
     382        1209 :         char *archiveFilename = CPLStrdup(pszFilename + 1);
     383        1209 :         archiveFilename[i - 1] = 0;
     384             : 
     385        1209 :         bool bArchiveFileExists = false;
     386        1209 :         if (!bCheckMainFileExists)
     387             :         {
     388          35 :             bArchiveFileExists = true;
     389             :         }
     390             :         else
     391             :         {
     392        2348 :             CPLMutexHolder oHolder(&hMutex);
     393             : 
     394        1174 :             if (oFileList.find(archiveFilename) != oFileList.end())
     395             :             {
     396        1053 :                 bArchiveFileExists = true;
     397             :             }
     398             :         }
     399             : 
     400        1209 :         if (!bArchiveFileExists)
     401             :         {
     402             :             VSIStatBufL statBuf;
     403             :             VSIFilesystemHandler *poFSHandler =
     404         121 :                 VSIFileManager::GetHandler(archiveFilename);
     405         363 :             if (poFSHandler->Stat(archiveFilename, &statBuf,
     406             :                                   VSI_STAT_EXISTS_FLAG |
     407         241 :                                       VSI_STAT_NATURE_FLAG) == 0 &&
     408         120 :                 !VSI_ISDIR(statBuf.st_mode))
     409             :             {
     410         120 :                 bArchiveFileExists = true;
     411             :             }
     412             :         }
     413             : 
     414        1209 :         if (bArchiveFileExists)
     415             :         {
     416        1208 :             if (IsEitherSlash(pszFilename[i + 1]))
     417             :             {
     418        1152 :                 osFileInArchive = CompactFilename(pszFilename + i + 2);
     419             :             }
     420          56 :             else if (pszFilename[i + 1] == '\0')
     421             :             {
     422          55 :                 osFileInArchive = "";
     423             :             }
     424             :             else
     425             :             {
     426           1 :                 CPLFree(archiveFilename);
     427           1 :                 return nullptr;
     428             :             }
     429             : 
     430             :             // Remove trailing slash.
     431        1207 :             if (!osFileInArchive.empty())
     432             :             {
     433        1148 :                 const char lastC = osFileInArchive.back();
     434        1148 :                 if (IsEitherSlash(lastC))
     435           2 :                     osFileInArchive.resize(osFileInArchive.size() - 1);
     436             :             }
     437             : 
     438        1207 :             return archiveFilename;
     439             :         }
     440             : 
     441           1 :         CPLFree(archiveFilename);
     442           1 :         return nullptr;
     443             :     }
     444             : 
     445             :     // Allow natural chaining of VSI drivers without requiring double slash.
     446             : 
     447       24152 :     CPLString osDoubleVsi(GetPrefix());
     448       12076 :     osDoubleVsi += "/vsi";
     449             : 
     450       12076 :     if (strncmp(pszFilename, osDoubleVsi.c_str(), osDoubleVsi.size()) == 0)
     451        4066 :         pszFilename += strlen(GetPrefix());
     452             :     else
     453        8010 :         pszFilename += strlen(GetPrefix()) + 1;
     454             : 
     455             :     // Parsing strings like
     456             :     // /vsitar//vsitar//vsitar//vsitar//vsitar//vsitar//vsitar//vsitar/a.tgzb.tgzc.tgzd.tgze.tgzf.tgz.h.tgz.i.tgz
     457             :     // takes a huge amount of time, so limit the number of nesting of such
     458             :     // file systems.
     459       12076 :     int *pnCounter = static_cast<int *>(CPLGetTLS(CTLS_ABSTRACTARCHIVE_SPLIT));
     460       12076 :     if (pnCounter == nullptr)
     461             :     {
     462          22 :         pnCounter = static_cast<int *>(CPLMalloc(sizeof(int)));
     463          22 :         *pnCounter = 0;
     464          22 :         CPLSetTLS(CTLS_ABSTRACTARCHIVE_SPLIT, pnCounter, TRUE);
     465             :     }
     466       12076 :     if (*pnCounter == 3)
     467             :     {
     468          64 :         CPLError(CE_Failure, CPLE_AppDefined,
     469             :                  "Too deep recursion level in "
     470             :                  "VSIArchiveFilesystemHandler::SplitFilename()");
     471          64 :         return nullptr;
     472             :     }
     473             : 
     474       24024 :     const std::vector<CPLString> oExtensions = GetExtensions();
     475       12012 :     int nAttempts = 0;
     476      346214 :     while (pszFilename[i])
     477             :     {
     478      346045 :         int nToSkip = 0;
     479             : 
     480     2342900 :         for (std::vector<CPLString>::const_iterator iter = oExtensions.begin();
     481     4339750 :              iter != oExtensions.end(); ++iter)
     482             :         {
     483     2009100 :             const CPLString &osExtension = *iter;
     484     2009100 :             if (EQUALN(pszFilename + i, osExtension.c_str(),
     485             :                        osExtension.size()))
     486             :             {
     487       12248 :                 nToSkip = static_cast<int>(osExtension.size());
     488       12248 :                 break;
     489             :             }
     490             :         }
     491             : 
     492             : #ifdef DEBUG
     493             :         // For AFL, so that .cur_input is detected as the archive filename.
     494      346045 :         if (EQUALN(pszFilename + i, ".cur_input", strlen(".cur_input")))
     495             :         {
     496          14 :             nToSkip = static_cast<int>(strlen(".cur_input"));
     497             :         }
     498             : #endif
     499             : 
     500      346045 :         if (nToSkip != 0)
     501             :         {
     502       12262 :             nAttempts++;
     503             :             // Arbitrary threshold to avoid DoS with things like
     504             :             // /vsitar/my.tar/my.tar/my.tar/my.tar/my.tar/my.tar/my.tar
     505       12262 :             if (nAttempts == 5)
     506             :             {
     507          21 :                 break;
     508             :             }
     509             :             VSIStatBufL statBuf;
     510       12241 :             char *archiveFilename = CPLStrdup(pszFilename);
     511       12241 :             bool bArchiveFileExists = false;
     512             : 
     513       12241 :             if (IsEitherSlash(archiveFilename[i + nToSkip]))
     514             :             {
     515       11096 :                 archiveFilename[i + nToSkip] = 0;
     516             :             }
     517             : 
     518       12241 :             if (!bCheckMainFileExists)
     519             :             {
     520         914 :                 bArchiveFileExists = true;
     521             :             }
     522             :             else
     523             :             {
     524       22654 :                 CPLMutexHolder oHolder(&hMutex);
     525             : 
     526       11327 :                 if (oFileList.find(archiveFilename) != oFileList.end())
     527             :                 {
     528       10582 :                     bArchiveFileExists = true;
     529             :                 }
     530             :             }
     531             : 
     532       12241 :             if (!bArchiveFileExists)
     533             :             {
     534         745 :                 (*pnCounter)++;
     535             : 
     536             :                 VSIFilesystemHandler *poFSHandler =
     537         745 :                     VSIFileManager::GetHandler(archiveFilename);
     538        2235 :                 if (poFSHandler->Stat(archiveFilename, &statBuf,
     539             :                                       VSI_STAT_EXISTS_FLAG |
     540        1281 :                                           VSI_STAT_NATURE_FLAG) == 0 &&
     541         536 :                     !VSI_ISDIR(statBuf.st_mode))
     542             :                 {
     543         326 :                     bArchiveFileExists = true;
     544             :                 }
     545             : 
     546         745 :                 (*pnCounter)--;
     547             :             }
     548             : 
     549       12241 :             if (bArchiveFileExists)
     550             :             {
     551       11822 :                 if (IsEitherSlash(pszFilename[i + nToSkip]))
     552             :                 {
     553             :                     osFileInArchive =
     554       10851 :                         CompactFilename(pszFilename + i + nToSkip + 1);
     555             :                 }
     556             :                 else
     557             :                 {
     558         971 :                     osFileInArchive = "";
     559             :                 }
     560             : 
     561             :                 // Remove trailing slash.
     562       11822 :                 if (!osFileInArchive.empty())
     563             :                 {
     564       10845 :                     const char lastC = osFileInArchive.back();
     565       10845 :                     if (IsEitherSlash(lastC))
     566          39 :                         osFileInArchive.resize(osFileInArchive.size() - 1);
     567             :                 }
     568             : 
     569       11822 :                 return archiveFilename;
     570             :             }
     571         419 :             CPLFree(archiveFilename);
     572             :         }
     573      334202 :         i++;
     574             :     }
     575         190 :     return nullptr;
     576             : }
     577             : 
     578             : /************************************************************************/
     579             : /*                           OpenArchiveFile()                          */
     580             : /************************************************************************/
     581             : 
     582             : VSIArchiveReader *
     583        4064 : VSIArchiveFilesystemHandler::OpenArchiveFile(const char *archiveFilename,
     584             :                                              const char *fileInArchiveName)
     585             : {
     586        4064 :     VSIArchiveReader *poReader = CreateReader(archiveFilename);
     587             : 
     588        4064 :     if (poReader == nullptr)
     589             :     {
     590           5 :         return nullptr;
     591             :     }
     592             : 
     593        4059 :     if (fileInArchiveName == nullptr || strlen(fileInArchiveName) == 0)
     594             :     {
     595          57 :         if (poReader->GotoFirstFile() == FALSE)
     596             :         {
     597           0 :             delete (poReader);
     598           1 :             return nullptr;
     599             :         }
     600             : 
     601             :         // Skip optional leading subdir.
     602          57 :         const CPLString osFileName = poReader->GetFileName();
     603          57 :         if (osFileName.empty() || IsEitherSlash(osFileName.back()))
     604             :         {
     605           2 :             if (poReader->GotoNextFile() == FALSE)
     606             :             {
     607           0 :                 delete (poReader);
     608           0 :                 return nullptr;
     609             :             }
     610             :         }
     611             : 
     612          57 :         if (poReader->GotoNextFile())
     613             :         {
     614           1 :             CPLString msg;
     615             :             msg.Printf("Support only 1 file in archive file %s when "
     616             :                        "no explicit in-archive filename is specified",
     617           1 :                        archiveFilename);
     618             :             const VSIArchiveContent *content =
     619           1 :                 GetContentOfArchive(archiveFilename, poReader);
     620           1 :             if (content)
     621             :             {
     622           1 :                 msg += "\nYou could try one of the following :\n";
     623           6 :                 for (int i = 0; i < content->nEntries; i++)
     624             :                 {
     625          10 :                     msg += CPLString().Printf("  %s/{%s}/%s\n", GetPrefix(),
     626             :                                               archiveFilename,
     627           5 :                                               content->entries[i].fileName);
     628             :                 }
     629             :             }
     630             : 
     631           1 :             CPLError(CE_Failure, CPLE_NotSupported, "%s", msg.c_str());
     632             : 
     633           1 :             delete (poReader);
     634           1 :             return nullptr;
     635          56 :         }
     636             :     }
     637             :     else
     638             :     {
     639             :         // Optimization: instead of iterating over all files which can be
     640             :         // slow on .tar.gz files, try reading the first one first.
     641             :         // This can help if it is really huge.
     642             :         {
     643        4002 :             CPLMutexHolder oHolder(&hMutex);
     644             : 
     645        4002 :             if (oFileList.find(archiveFilename) == oFileList.end())
     646             :             {
     647         124 :                 if (poReader->GotoFirstFile() == FALSE)
     648             :                 {
     649           0 :                     delete (poReader);
     650          46 :                     return nullptr;
     651             :                 }
     652             : 
     653         124 :                 const CPLString osFileName = poReader->GetFileName();
     654         124 :                 bool bIsDir = false;
     655             :                 const CPLString osStrippedFilename =
     656         124 :                     GetStrippedFilename(osFileName, bIsDir);
     657         124 :                 if (!osStrippedFilename.empty())
     658             :                 {
     659             :                     const bool bMatch =
     660         124 :                         strcmp(osStrippedFilename, fileInArchiveName) == 0;
     661         124 :                     if (bMatch)
     662             :                     {
     663          46 :                         if (bIsDir)
     664             :                         {
     665           0 :                             delete (poReader);
     666           0 :                             return nullptr;
     667             :                         }
     668          46 :                         return poReader;
     669             :                     }
     670             :                 }
     671             :             }
     672             :         }
     673             : 
     674        3956 :         const VSIArchiveEntry *archiveEntry = nullptr;
     675       11868 :         if (FindFileInArchive(archiveFilename, fileInArchiveName,
     676        7751 :                               &archiveEntry) == FALSE ||
     677        3795 :             archiveEntry->bIsDir)
     678             :         {
     679         165 :             delete (poReader);
     680         165 :             return nullptr;
     681             :         }
     682        3791 :         if (!poReader->GotoFileOffset(archiveEntry->file_pos))
     683             :         {
     684           0 :             delete poReader;
     685           0 :             return nullptr;
     686             :         }
     687             :     }
     688        3847 :     return poReader;
     689             : }
     690             : 
     691             : /************************************************************************/
     692             : /*                                 Stat()                               */
     693             : /************************************************************************/
     694             : 
     695        2750 : int VSIArchiveFilesystemHandler::Stat(const char *pszFilename,
     696             :                                       VSIStatBufL *pStatBuf, int /* nFlags */)
     697             : {
     698        2750 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     699             : 
     700        5500 :     CPLString osFileInArchive;
     701        2750 :     char *archiveFilename = SplitFilename(pszFilename, osFileInArchive, TRUE);
     702        2750 :     if (archiveFilename == nullptr)
     703          85 :         return -1;
     704             : 
     705        2665 :     int ret = -1;
     706        2665 :     if (!osFileInArchive.empty())
     707             :     {
     708             : #ifdef DEBUG_VERBOSE
     709             :         CPLDebug("VSIArchive", "Looking for %s %s", archiveFilename,
     710             :                  osFileInArchive.c_str());
     711             : #endif
     712             : 
     713        2404 :         const VSIArchiveEntry *archiveEntry = nullptr;
     714        2404 :         if (FindFileInArchive(archiveFilename, osFileInArchive, &archiveEntry))
     715             :         {
     716             :             // Patching st_size with uncompressed file size.
     717        2248 :             pStatBuf->st_size = archiveEntry->uncompressed_size;
     718        2248 :             pStatBuf->st_mtime =
     719        2248 :                 static_cast<time_t>(archiveEntry->nModifiedTime);
     720        2248 :             if (archiveEntry->bIsDir)
     721        1127 :                 pStatBuf->st_mode = S_IFDIR;
     722             :             else
     723        1121 :                 pStatBuf->st_mode = S_IFREG;
     724        2248 :             ret = 0;
     725             :         }
     726             :     }
     727             :     else
     728             :     {
     729         261 :         VSIArchiveReader *poReader = CreateReader(archiveFilename);
     730         261 :         CPLFree(archiveFilename);
     731         261 :         archiveFilename = nullptr;
     732             : 
     733         261 :         if (poReader != nullptr && poReader->GotoFirstFile())
     734             :         {
     735             :             // Skip optional leading subdir.
     736         256 :             const CPLString osFileName = poReader->GetFileName();
     737         256 :             if (IsEitherSlash(osFileName.back()))
     738             :             {
     739          16 :                 if (poReader->GotoNextFile() == FALSE)
     740             :                 {
     741           0 :                     delete (poReader);
     742           0 :                     return -1;
     743             :                 }
     744             :             }
     745             : 
     746         256 :             if (poReader->GotoNextFile())
     747             :             {
     748             :                 // Several files in archive --> treat as dir.
     749         243 :                 pStatBuf->st_size = 0;
     750         243 :                 pStatBuf->st_mode = S_IFDIR;
     751             :             }
     752             :             else
     753             :             {
     754             :                 // Patching st_size with uncompressed file size.
     755          13 :                 pStatBuf->st_size = poReader->GetFileSize();
     756          13 :                 pStatBuf->st_mtime =
     757          13 :                     static_cast<time_t>(poReader->GetModifiedTime());
     758          13 :                 pStatBuf->st_mode = S_IFREG;
     759             :             }
     760             : 
     761         256 :             ret = 0;
     762             :         }
     763             : 
     764         261 :         delete (poReader);
     765             :     }
     766             : 
     767        2665 :     CPLFree(archiveFilename);
     768        2665 :     return ret;
     769             : }
     770             : 
     771             : /************************************************************************/
     772             : /*                              Unlink()                                */
     773             : /************************************************************************/
     774             : 
     775           2 : int VSIArchiveFilesystemHandler::Unlink(const char * /* pszFilename */)
     776             : {
     777           2 :     return -1;
     778             : }
     779             : 
     780             : /************************************************************************/
     781             : /*                             Rename()                                 */
     782             : /************************************************************************/
     783             : 
     784           0 : int VSIArchiveFilesystemHandler::Rename(const char * /* oldpath */,
     785             :                                         const char * /* newpath */)
     786             : {
     787           0 :     return -1;
     788             : }
     789             : 
     790             : /************************************************************************/
     791             : /*                             Mkdir()                                  */
     792             : /************************************************************************/
     793             : 
     794           0 : int VSIArchiveFilesystemHandler::Mkdir(const char * /* pszDirname */,
     795             :                                        long /* nMode */)
     796             : {
     797           0 :     return -1;
     798             : }
     799             : 
     800             : /************************************************************************/
     801             : /*                             Rmdir()                                  */
     802             : /************************************************************************/
     803             : 
     804           0 : int VSIArchiveFilesystemHandler::Rmdir(const char * /* pszDirname */)
     805             : {
     806           0 :     return -1;
     807             : }
     808             : 
     809             : /************************************************************************/
     810             : /*                             ReadDirEx()                              */
     811             : /************************************************************************/
     812             : 
     813        1356 : char **VSIArchiveFilesystemHandler::ReadDirEx(const char *pszDirname,
     814             :                                               int nMaxFiles)
     815             : {
     816        2712 :     CPLString osInArchiveSubDir;
     817        1356 :     char *archiveFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE);
     818        1356 :     if (archiveFilename == nullptr)
     819           0 :         return nullptr;
     820             : 
     821        1356 :     const int lenInArchiveSubDir = static_cast<int>(osInArchiveSubDir.size());
     822             : 
     823        2712 :     CPLStringList oDir;
     824             : 
     825        1356 :     const VSIArchiveContent *content = GetContentOfArchive(archiveFilename);
     826        1356 :     if (!content)
     827             :     {
     828           4 :         CPLFree(archiveFilename);
     829           4 :         return nullptr;
     830             :     }
     831             : 
     832             : #ifdef DEBUG_VERBOSE
     833             :     CPLDebug("VSIArchive", "Read dir %s", pszDirname);
     834             : #endif
     835     1077130 :     for (int i = 0; i < content->nEntries; i++)
     836             :     {
     837     1075780 :         const char *fileName = content->entries[i].fileName;
     838             :         /* Only list entries at the same level of inArchiveSubDir */
     839     2115680 :         if (lenInArchiveSubDir != 0 &&
     840     1579450 :             strncmp(fileName, osInArchiveSubDir, lenInArchiveSubDir) == 0 &&
     841     2655220 :             IsEitherSlash(fileName[lenInArchiveSubDir]) &&
     842      538404 :             fileName[lenInArchiveSubDir + 1] != 0)
     843             :         {
     844      538404 :             const char *slash = strchr(fileName + lenInArchiveSubDir + 1, '/');
     845      538404 :             if (slash == nullptr)
     846       38497 :                 slash = strchr(fileName + lenInArchiveSubDir + 1, '\\');
     847      538404 :             if (slash == nullptr || slash[1] == 0)
     848             :             {
     849       38497 :                 char *tmpFileName = CPLStrdup(fileName);
     850       38497 :                 if (slash != nullptr)
     851             :                 {
     852           0 :                     tmpFileName[strlen(tmpFileName) - 1] = 0;
     853             :                 }
     854             : #ifdef DEBUG_VERBOSE
     855             :                 CPLDebug("VSIArchive", "Add %s as in directory %s",
     856             :                          tmpFileName + lenInArchiveSubDir + 1, pszDirname);
     857             : #endif
     858       38497 :                 oDir.AddString(tmpFileName + lenInArchiveSubDir + 1);
     859       38497 :                 CPLFree(tmpFileName);
     860             :             }
     861             :         }
     862      537374 :         else if (lenInArchiveSubDir == 0 && strchr(fileName, '/') == nullptr &&
     863         427 :                  strchr(fileName, '\\') == nullptr)
     864             :         {
     865             :             // Only list toplevel files and directories.
     866             : #ifdef DEBUG_VERBOSE
     867             :             CPLDebug("VSIArchive", "Add %s as in directory %s", fileName,
     868             :                      pszDirname);
     869             : #endif
     870         427 :             oDir.AddString(fileName);
     871             :         }
     872             : 
     873     1075780 :         if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
     874           1 :             break;
     875             :     }
     876             : 
     877        1352 :     CPLFree(archiveFilename);
     878        1352 :     return oDir.StealList();
     879             : }
     880             : 
     881             : /************************************************************************/
     882             : /*                               IsLocal()                              */
     883             : /************************************************************************/
     884             : 
     885           0 : bool VSIArchiveFilesystemHandler::IsLocal(const char *pszPath)
     886             : {
     887           0 :     if (!STARTS_WITH(pszPath, GetPrefix()))
     888           0 :         return false;
     889           0 :     const char *pszBaseFileName = pszPath + strlen(GetPrefix());
     890             :     VSIFilesystemHandler *poFSHandler =
     891           0 :         VSIFileManager::GetHandler(pszBaseFileName);
     892           0 :     return poFSHandler->IsLocal(pszPath);
     893             : }
     894             : 
     895             : //! @endcond

Generated by: LCOV version 1.14