LCOV - code coverage report
Current view: top level - gcore - gdalopeninfo.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 184 187 98.4 %
Date: 2025-02-20 10:14:44 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Implementation of GDALOpenInfo class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam
       9             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_priv.h"  // Must be included first for mingw VSIStatBufL.
      15             : #include "cpl_port.h"
      16             : 
      17             : #include <cstdlib>
      18             : #include <cstring>
      19             : #ifdef HAVE_UNISTD_H
      20             : #include <unistd.h>
      21             : #endif
      22             : 
      23             : #include <algorithm>
      24             : #include <map>
      25             : #include <mutex>
      26             : #include <vector>
      27             : 
      28             : #include "cpl_config.h"
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_string.h"
      32             : #include "cpl_vsi.h"
      33             : #include "gdal.h"
      34             : 
      35             : // Keep in sync prototype of those 2 functions between gdalopeninfo.cpp,
      36             : // ogrsqlitedatasource.cpp and ogrgeopackagedatasource.cpp
      37             : void GDALOpenInfoDeclareFileNotToOpen(const char *pszFilename,
      38             :                                       const GByte *pabyHeader,
      39             :                                       int nHeaderBytes);
      40             : void GDALOpenInfoUnDeclareFileNotToOpen(const char *pszFilename);
      41             : 
      42             : /************************************************************************/
      43             : 
      44             : /* This whole section helps for SQLite/GPKG, especially with write-ahead
      45             :  * log enabled. The issue is that sqlite3 relies on POSIX advisory locks to
      46             :  * properly work and decide when to create/delete the wal related files.
      47             :  * One issue with POSIX advisory locks is that if within the same process
      48             :  * you do
      49             :  * f1 = open('somefile')
      50             :  * set locks on f1
      51             :  * f2 = open('somefile')
      52             :  * close(f2)
      53             :  * The close(f2) will cancel the locks set on f1. The work on f1 is done by
      54             :  * libsqlite3 whereas the work on f2 is done by GDALOpenInfo.
      55             :  * So as soon as sqlite3 has opened a file we should make sure not to re-open
      56             :  * it (actually close it) ourselves.
      57             :  */
      58             : 
      59             : namespace
      60             : {
      61             : struct FileNotToOpen
      62             : {
      63             :     CPLString osFilename{};
      64             :     int nRefCount{};
      65             :     GByte *pabyHeader{nullptr};
      66             :     int nHeaderBytes{0};
      67             : };
      68             : }  // namespace
      69             : 
      70             : static std::mutex sFNTOMutex;
      71             : static std::map<CPLString, FileNotToOpen> *pMapFNTO = nullptr;
      72             : 
      73        1786 : void GDALOpenInfoDeclareFileNotToOpen(const char *pszFilename,
      74             :                                       const GByte *pabyHeader, int nHeaderBytes)
      75             : {
      76        3572 :     std::lock_guard<std::mutex> oLock(sFNTOMutex);
      77        1786 :     if (pMapFNTO == nullptr)
      78        1474 :         pMapFNTO = new std::map<CPLString, FileNotToOpen>();
      79        1786 :     auto oIter = pMapFNTO->find(pszFilename);
      80        1786 :     if (oIter != pMapFNTO->end())
      81             :     {
      82         122 :         oIter->second.nRefCount++;
      83             :     }
      84             :     else
      85             :     {
      86        1664 :         FileNotToOpen fnto;
      87        1664 :         fnto.osFilename = pszFilename;
      88        1664 :         fnto.nRefCount = 1;
      89        1664 :         fnto.pabyHeader = static_cast<GByte *>(CPLMalloc(nHeaderBytes + 1));
      90        1664 :         memcpy(fnto.pabyHeader, pabyHeader, nHeaderBytes);
      91        1664 :         fnto.pabyHeader[nHeaderBytes] = 0;
      92        1664 :         fnto.nHeaderBytes = nHeaderBytes;
      93        1664 :         (*pMapFNTO)[pszFilename] = std::move(fnto);
      94             :     }
      95        1786 : }
      96             : 
      97        1785 : void GDALOpenInfoUnDeclareFileNotToOpen(const char *pszFilename)
      98             : {
      99        3570 :     std::lock_guard<std::mutex> oLock(sFNTOMutex);
     100        1785 :     CPLAssert(pMapFNTO);
     101        1785 :     auto oIter = pMapFNTO->find(pszFilename);
     102        1785 :     CPLAssert(oIter != pMapFNTO->end());
     103        1785 :     oIter->second.nRefCount--;
     104        1785 :     if (oIter->second.nRefCount == 0)
     105             :     {
     106        1663 :         CPLFree(oIter->second.pabyHeader);
     107        1663 :         pMapFNTO->erase(oIter);
     108             :     }
     109        1785 :     if (pMapFNTO->empty())
     110             :     {
     111        1473 :         delete pMapFNTO;
     112        1473 :         pMapFNTO = nullptr;
     113             :     }
     114        1785 : }
     115             : 
     116      101082 : static GByte *GDALOpenInfoGetFileNotToOpen(const char *pszFilename,
     117             :                                            int *pnHeaderBytes)
     118             : {
     119      202165 :     std::lock_guard<std::mutex> oLock(sFNTOMutex);
     120      101083 :     *pnHeaderBytes = 0;
     121      101083 :     if (pMapFNTO == nullptr)
     122             :     {
     123       94895 :         return nullptr;
     124             :     }
     125        6188 :     auto oIter = pMapFNTO->find(pszFilename);
     126        6188 :     if (oIter == pMapFNTO->end())
     127             :     {
     128        4678 :         return nullptr;
     129             :     }
     130        1510 :     *pnHeaderBytes = oIter->second.nHeaderBytes;
     131        1510 :     GByte *pabyHeader = static_cast<GByte *>(CPLMalloc(*pnHeaderBytes + 1));
     132        1510 :     memcpy(pabyHeader, oIter->second.pabyHeader, *pnHeaderBytes);
     133        1510 :     pabyHeader[*pnHeaderBytes] = 0;
     134        1510 :     return pabyHeader;
     135             : }
     136             : 
     137             : /************************************************************************/
     138             : /* ==================================================================== */
     139             : /*                             GDALOpenInfo                             */
     140             : /* ==================================================================== */
     141             : /************************************************************************/
     142             : 
     143             : /************************************************************************/
     144             : /*                            GDALOpenInfo()                            */
     145             : /************************************************************************/
     146             : 
     147             : /** Constructor/
     148             :  * @param pszFilenameIn filename
     149             :  * @param nOpenFlagsIn open flags
     150             :  * @param papszSiblingsIn list of sibling files, or NULL.
     151             :  */
     152      101931 : GDALOpenInfo::GDALOpenInfo(const char *pszFilenameIn, int nOpenFlagsIn,
     153      101931 :                            const char *const *papszSiblingsIn)
     154      203861 :     : pszFilename(CPLStrdup(pszFilenameIn)),
     155             :       osExtension(CPLGetExtensionSafe(pszFilenameIn)),
     156      101930 :       eAccess(nOpenFlagsIn & GDAL_OF_UPDATE ? GA_Update : GA_ReadOnly),
     157      101931 :       nOpenFlags(nOpenFlagsIn)
     158             : {
     159      101930 :     if (STARTS_WITH(pszFilename, "MVT:/vsi"))
     160         850 :         return;
     161             : 
     162             : /* -------------------------------------------------------------------- */
     163             : /*      Ensure that C: is treated as C:\ so we can stat it on           */
     164             : /*      Windows.  Similar to what is done in CPLStat().                 */
     165             : /* -------------------------------------------------------------------- */
     166             : #ifdef _WIN32
     167             :     if (strlen(pszFilenameIn) == 2 && pszFilenameIn[1] == ':')
     168             :     {
     169             :         char szAltPath[10];
     170             : 
     171             :         strcpy(szAltPath, pszFilenameIn);
     172             :         strcat(szAltPath, "\\");
     173             :         CPLFree(pszFilename);
     174             :         pszFilename = CPLStrdup(szAltPath);
     175             :     }
     176             : #endif  // WIN32
     177             : 
     178             :     /* -------------------------------------------------------------------- */
     179             :     /*      Collect information about the file.                             */
     180             :     /* -------------------------------------------------------------------- */
     181             : 
     182             : #ifdef HAVE_READLINK
     183      101080 :     bool bHasRetried = false;
     184             : 
     185      101083 : retry:  // TODO(schwehr): Stop using goto.
     186             : 
     187             : #endif  // HAVE_READLINK
     188             : 
     189             : #if !(defined(_WIN32) || defined(__linux__) || defined(__ANDROID__) ||         \
     190             :       (defined(__MACH__) && defined(__APPLE__)))
     191             :     /* On BSDs, fread() on a directory returns non zero, so we have to */
     192             :     /* do a stat() before to check the nature of pszFilename. */
     193             :     bool bPotentialDirectory = (eAccess == GA_ReadOnly);
     194             : #else
     195      101083 :     bool bPotentialDirectory = false;
     196             : #endif
     197             : 
     198             :     /* Check if the filename might be a directory of a special virtual file
     199             :      * system */
     200      101083 :     if (STARTS_WITH(pszFilename, "/vsizip/") ||
     201      100910 :         STARTS_WITH(pszFilename, "/vsitar/") ||
     202      100891 :         STARTS_WITH(pszFilename, "/vsi7z/") ||
     203      100891 :         STARTS_WITH(pszFilename, "/vsirar/"))
     204             :     {
     205         191 :         const char *pszExt = osExtension.c_str();
     206         165 :         if (EQUAL(pszExt, "zip") || EQUAL(pszExt, "tar") ||
     207         163 :             EQUAL(pszExt, "gz") || EQUAL(pszExt, "7z") ||
     208         162 :             EQUAL(pszExt, "rar") ||
     209         162 :             pszFilename[strlen(pszFilename) - 1] == '}'
     210             : #ifdef DEBUG
     211             :             // For AFL, so that .cur_input is detected as the archive filename.
     212         355 :             || EQUAL(CPLGetFilename(pszFilename), ".cur_input")
     213             : #endif  // DEBUG
     214             :         )
     215             :         {
     216          39 :             bPotentialDirectory = true;
     217         190 :         }
     218             :     }
     219      100892 :     else if (STARTS_WITH(pszFilename, "/vsicurl/"))
     220             :     {
     221          43 :         bPotentialDirectory = true;
     222             :     }
     223             : 
     224      101082 :     if (bPotentialDirectory)
     225             :     {
     226          82 :         int nStatFlags = VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG;
     227          82 :         if (nOpenFlagsIn & GDAL_OF_VERBOSE_ERROR)
     228          50 :             nStatFlags |= VSI_STAT_SET_ERROR_FLAG;
     229             : 
     230             :         // For those special files, opening them with VSIFOpenL() might result
     231             :         // in content, even if they should be considered as directories, so
     232             :         // use stat.
     233             :         VSIStatBufL sStat;
     234             : 
     235          82 :         if (VSIStatExL(pszFilename, &sStat, nStatFlags) == 0)
     236             :         {
     237          74 :             bStatOK = TRUE;
     238          74 :             if (VSI_ISDIR(sStat.st_mode))
     239          21 :                 bIsDirectory = TRUE;
     240             :         }
     241             :     }
     242             : 
     243      101082 :     pabyHeader = GDALOpenInfoGetFileNotToOpen(pszFilename, &nHeaderBytes);
     244             : 
     245      101083 :     if (!bIsDirectory && pabyHeader == nullptr)
     246             :     {
     247       99552 :         fpL = VSIFOpenExL(pszFilename, (eAccess == GA_Update) ? "r+b" : "rb",
     248       99552 :                           (nOpenFlagsIn & GDAL_OF_VERBOSE_ERROR) > 0);
     249             :     }
     250      101083 :     if (pabyHeader)
     251             :     {
     252        1510 :         bStatOK = TRUE;
     253        1510 :         nHeaderBytesTried = nHeaderBytes;
     254             :     }
     255       99573 :     else if (fpL != nullptr)
     256             :     {
     257       54672 :         bStatOK = TRUE;
     258             :         int nBufSize =
     259       54672 :             atoi(CPLGetConfigOption("GDAL_INGESTED_BYTES_AT_OPEN", "1024"));
     260       54677 :         if (nBufSize < 1024)
     261           0 :             nBufSize = 1024;
     262       54677 :         else if (nBufSize > 10 * 1024 * 1024)
     263           0 :             nBufSize = 10 * 1024 * 1024;
     264       54677 :         pabyHeader = static_cast<GByte *>(CPLCalloc(nBufSize + 1, 1));
     265       54671 :         nHeaderBytesTried = nBufSize;
     266       54669 :         nHeaderBytes =
     267       54671 :             static_cast<int>(VSIFReadL(pabyHeader, 1, nHeaderBytesTried, fpL));
     268       54669 :         VSIRewindL(fpL);
     269             : 
     270             :         /* If we cannot read anything, check if it is not a directory instead */
     271             :         VSIStatBufL sStat;
     272      110365 :         if (nHeaderBytes == 0 &&
     273        1051 :             VSIStatExL(pszFilename, &sStat,
     274       55708 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
     275        1051 :             VSI_ISDIR(sStat.st_mode))
     276             :         {
     277         928 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
     278         928 :             fpL = nullptr;
     279         928 :             CPLFree(pabyHeader);
     280         928 :             pabyHeader = nullptr;
     281         928 :             bIsDirectory = TRUE;
     282             :         }
     283             :     }
     284       44901 :     else if (!bStatOK)
     285             :     {
     286             :         VSIStatBufL sStat;
     287       89740 :         if (!bPotentialDirectory &&
     288       44867 :             VSIStatExL(pszFilename, &sStat,
     289             :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0)
     290             :         {
     291        1257 :             bStatOK = TRUE;
     292        1257 :             if (VSI_ISDIR(sStat.st_mode))
     293        1252 :                 bIsDirectory = TRUE;
     294             :         }
     295             : #ifdef HAVE_READLINK
     296       43616 :         else if (!bHasRetried && !STARTS_WITH(pszFilename, "/vsi"))
     297             :         {
     298             :             // If someone creates a file with "ln -sf
     299             :             // /vsicurl/http://download.osgeo.org/gdal/data/gtiff/utm.tif
     300             :             // my_remote_utm.tif" we will be able to open it by passing
     301             :             // my_remote_utm.tif.  This helps a lot for GDAL based readers that
     302             :             // only provide file explorers to open datasets.
     303       22925 :             const int nBufSize = 2048;
     304       22925 :             std::vector<char> oFilename(nBufSize);
     305       22925 :             char *szPointerFilename = &oFilename[0];
     306             :             int nBytes = static_cast<int>(
     307       22925 :                 readlink(pszFilename, szPointerFilename, nBufSize));
     308       22926 :             if (nBytes != -1)
     309             :             {
     310           2 :                 szPointerFilename[std::min(nBytes, nBufSize - 1)] = 0;
     311           2 :                 CPLFree(pszFilename);
     312           2 :                 pszFilename = CPLStrdup(szPointerFilename);
     313           2 :                 osExtension = CPLGetExtensionSafe(pszFilename);
     314           2 :                 papszSiblingsIn = nullptr;
     315           2 :                 bHasRetried = true;
     316           2 :                 goto retry;
     317             :             }
     318             :         }
     319             : #endif  // HAVE_READLINK
     320             :     }
     321             : 
     322             :     /* -------------------------------------------------------------------- */
     323             :     /*      Capture sibling list either from passed in values, or by        */
     324             :     /*      scanning for them only if requested through GetSiblingFiles().  */
     325             :     /* -------------------------------------------------------------------- */
     326      101065 :     if (papszSiblingsIn != nullptr)
     327             :     {
     328         252 :         papszSiblingFiles = CSLDuplicate(papszSiblingsIn);
     329         252 :         bHasGotSiblingFiles = true;
     330             :     }
     331      100813 :     else if (bStatOK && !bIsDirectory)
     332             :     {
     333       54985 :         papszSiblingFiles = VSISiblingFiles(pszFilename);
     334       55010 :         if (papszSiblingFiles != nullptr)
     335             :         {
     336           6 :             bHasGotSiblingFiles = true;
     337             :         }
     338             :         else
     339             :         {
     340      110010 :             const char *pszOptionVal = VSIGetPathSpecificOption(
     341       55004 :                 pszFilename, "GDAL_DISABLE_READDIR_ON_OPEN", "NO");
     342       55006 :             if (EQUAL(pszOptionVal, "EMPTY_DIR"))
     343             :             {
     344          65 :                 papszSiblingFiles =
     345          65 :                     CSLAddString(nullptr, CPLGetFilename(pszFilename));
     346          65 :                 bHasGotSiblingFiles = true;
     347             :             }
     348       54941 :             else if (CPLTestBool(pszOptionVal))
     349             :             {
     350             :                 /* skip reading the directory */
     351          18 :                 papszSiblingFiles = nullptr;
     352          18 :                 bHasGotSiblingFiles = true;
     353             :             }
     354             :             else
     355             :             {
     356             :                 /* will be lazy loaded */
     357       54923 :                 papszSiblingFiles = nullptr;
     358       54923 :                 bHasGotSiblingFiles = false;
     359             :             }
     360       55012 :         }
     361             :     }
     362             :     else
     363             :     {
     364       45828 :         papszSiblingFiles = nullptr;
     365       45828 :         bHasGotSiblingFiles = true;
     366             :     }
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                           ~GDALOpenInfo()                            */
     371             : /************************************************************************/
     372             : 
     373      101847 : GDALOpenInfo::~GDALOpenInfo()
     374             : 
     375             : {
     376      101892 :     VSIFree(pabyHeader);
     377      101800 :     CPLFree(pszFilename);
     378             : 
     379      101872 :     if (fpL != nullptr)
     380       12549 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
     381      101872 :     CSLDestroy(papszSiblingFiles);
     382      101800 : }
     383             : 
     384             : /************************************************************************/
     385             : /*                         GetSiblingFiles()                            */
     386             : /************************************************************************/
     387             : 
     388             : /** Return sibling files.
     389             :  *
     390             :  * If the list of sibling files has not already been established, it will be,
     391             :  * unless the GDAL_DISABLE_READDIR_ON_OPEN configuration option has been set to
     392             :  * YES or EMPTY_DIR when this instance was constructed.
     393             :  *
     394             :  * @return sibling files. Ownership belongs to "this".
     395             :  */
     396       41374 : char **GDALOpenInfo::GetSiblingFiles()
     397             : {
     398       41374 :     if (bHasGotSiblingFiles)
     399       24095 :         return papszSiblingFiles;
     400       17279 :     bHasGotSiblingFiles = true;
     401             : 
     402       17279 :     papszSiblingFiles = VSISiblingFiles(pszFilename);
     403       17278 :     if (papszSiblingFiles != nullptr)
     404             :     {
     405           0 :         return papszSiblingFiles;
     406             :     }
     407             : 
     408       17278 :     const CPLString osDir = CPLGetDirnameSafe(pszFilename);
     409       34554 :     const int nMaxFiles = atoi(VSIGetPathSpecificOption(
     410       17276 :         pszFilename, "GDAL_READDIR_LIMIT_ON_OPEN", "1000"));
     411       17278 :     papszSiblingFiles = VSIReadDirEx(osDir, nMaxFiles);
     412       17278 :     if (nMaxFiles > 0 && CSLCount(papszSiblingFiles) > nMaxFiles)
     413             :     {
     414           1 :         CPLDebug("GDAL", "GDAL_READDIR_LIMIT_ON_OPEN reached on %s",
     415             :                  osDir.c_str());
     416           1 :         CSLDestroy(papszSiblingFiles);
     417           1 :         papszSiblingFiles = nullptr;
     418             :     }
     419             : 
     420       17278 :     return papszSiblingFiles;
     421             : }
     422             : 
     423             : /************************************************************************/
     424             : /*                         StealSiblingFiles()                          */
     425             : /*                                                                      */
     426             : /*      Same as GetSiblingFiles() except that the list is stealed       */
     427             : /*      (ie ownership transferred to the caller) and the associated     */
     428             : /*      member variable is set to NULL.                                 */
     429             : /************************************************************************/
     430             : 
     431             : /** Return sibling files and steal reference
     432             :  * @return sibling files. Ownership below to the caller (must be freed with
     433             :  * CSLDestroy)
     434             :  */
     435        8464 : char **GDALOpenInfo::StealSiblingFiles()
     436             : {
     437        8464 :     char **papszRet = GetSiblingFiles();
     438        8464 :     papszSiblingFiles = nullptr;
     439        8464 :     return papszRet;
     440             : }
     441             : 
     442             : /************************************************************************/
     443             : /*                        AreSiblingFilesLoaded()                       */
     444             : /************************************************************************/
     445             : 
     446             : /** Return whether sibling files have been loaded.
     447             :  * @return true or false.
     448             :  */
     449       49391 : bool GDALOpenInfo::AreSiblingFilesLoaded() const
     450             : {
     451       49391 :     return bHasGotSiblingFiles;
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                           TryToIngest()                              */
     456             : /************************************************************************/
     457             : 
     458             : /** Ingest bytes from the file.
     459             :  * @param nBytes number of bytes to ingest.
     460             :  * @return TRUE if successful
     461             :  */
     462       22825 : int GDALOpenInfo::TryToIngest(int nBytes)
     463             : {
     464       22825 :     if (fpL == nullptr)
     465          63 :         return FALSE;
     466       22762 :     if (nHeaderBytes < nHeaderBytesTried)
     467       13316 :         return TRUE;
     468        9446 :     pabyHeader = static_cast<GByte *>(CPLRealloc(pabyHeader, nBytes + 1));
     469        9446 :     memset(pabyHeader, 0, nBytes + 1);
     470        9446 :     VSIRewindL(fpL);
     471        9446 :     nHeaderBytesTried = nBytes;
     472        9446 :     nHeaderBytes = static_cast<int>(VSIFReadL(pabyHeader, 1, nBytes, fpL));
     473        9446 :     VSIRewindL(fpL);
     474             : 
     475        9446 :     return TRUE;
     476             : }
     477             : 
     478             : /************************************************************************/
     479             : /*                       IsSingleAllowedDriver()                        */
     480             : /************************************************************************/
     481             : 
     482             : /** Returns true if the driver name is the single in the list of allowed
     483             :  * drivers.
     484             :  *
     485             :  * @param pszDriverName Driver name to test.
     486             :  * @return true if the driver name is the single in the list of allowed
     487             :  * drivers.
     488             :  * @since GDAL 3.10
     489             :  */
     490      617464 : bool GDALOpenInfo::IsSingleAllowedDriver(const char *pszDriverName) const
     491             : {
     492        2922 :     return papszAllowedDrivers && papszAllowedDrivers[0] &&
     493      621082 :            !papszAllowedDrivers[1] &&
     494      618160 :            EQUAL(papszAllowedDrivers[0], pszDriverName);
     495             : }

Generated by: LCOV version 1.14