LCOV - code coverage report
Current view: top level - gcore - gdalpamproxydb.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 121 154 78.6 %
Date: 2025-09-10 17:48:50 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Implementation of the GDAL PAM Proxy database interface.
       5             :  *           The proxy db is used to associate .aux.xml files in a temp
       6             :  *           directory - used for files for which aux.xml files can't be
       7             :  *           created (i.e. read-only file systems).
       8             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       9             :  *
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "cpl_port.h"
      17             : #include "gdal_pam.h"
      18             : 
      19             : #include <cerrno>
      20             : #include <cstddef>
      21             : #include <cstdio>
      22             : #include <cstdlib>
      23             : #include <cstring>
      24             : 
      25             : #include <memory>
      26             : #include <string>
      27             : #include <vector>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_multiproc.h"
      32             : #include "cpl_string.h"
      33             : #include "cpl_vsi.h"
      34             : #include "gdal_pam.h"
      35             : #include "ogr_spatialref.h"
      36             : 
      37             : /************************************************************************/
      38             : /* ==================================================================== */
      39             : /*                            GDALPamProxyDB                            */
      40             : /* ==================================================================== */
      41             : /************************************************************************/
      42             : 
      43             : class GDALPamProxyDB
      44             : {
      45             :   public:
      46             :     CPLString osProxyDBDir{};
      47             : 
      48             :     int nUpdateCounter = -1;
      49             : 
      50             :     std::vector<CPLString> aosOriginalFiles{};
      51             :     std::vector<CPLString> aosProxyFiles{};
      52             : 
      53             :     void CheckLoadDB();
      54             :     void LoadDB();
      55             :     void SaveDB();
      56             : };
      57             : 
      58             : static bool bProxyDBInitialized = FALSE;
      59             : static GDALPamProxyDB *poProxyDB = nullptr;
      60             : static CPLMutex *hProxyDBLock = nullptr;
      61             : 
      62             : /************************************************************************/
      63             : /*                            CheckLoadDB()                             */
      64             : /*                                                                      */
      65             : /*      Eventually we want to check if the file has changed, and if     */
      66             : /*      so, force it to be reloaded.  TODO:                             */
      67             : /************************************************************************/
      68             : 
      69          36 : void GDALPamProxyDB::CheckLoadDB()
      70             : 
      71             : {
      72          36 :     if (nUpdateCounter == -1)
      73           3 :         LoadDB();
      74          36 : }
      75             : 
      76             : /************************************************************************/
      77             : /*                               LoadDB()                               */
      78             : /*                                                                      */
      79             : /*      It is assumed the caller already holds the lock.                */
      80             : /************************************************************************/
      81             : 
      82           3 : void GDALPamProxyDB::LoadDB()
      83             : 
      84             : {
      85             :     /* -------------------------------------------------------------------- */
      86             :     /*      Open the database relating original names to proxy .aux.xml     */
      87             :     /*      file names.                                                     */
      88             :     /* -------------------------------------------------------------------- */
      89             :     const std::string osDBName =
      90           3 :         CPLFormFilenameSafe(osProxyDBDir, "gdal_pam_proxy", "dat");
      91           3 :     VSILFILE *fpDB = VSIFOpenL(osDBName.c_str(), "r");
      92             : 
      93           3 :     nUpdateCounter = 0;
      94           3 :     if (fpDB == nullptr)
      95           2 :         return;
      96             : 
      97             :     /* -------------------------------------------------------------------- */
      98             :     /*      Read header, verify and extract update counter.                 */
      99             :     /* -------------------------------------------------------------------- */
     100           1 :     const size_t nHeaderSize = 100;
     101           1 :     GByte abyHeader[nHeaderSize] = {'\0'};
     102             : 
     103           2 :     if (VSIFReadL(abyHeader, 1, nHeaderSize, fpDB) != nHeaderSize ||
     104           1 :         !STARTS_WITH(reinterpret_cast<char *>(abyHeader), "GDAL_PROXY"))
     105             :     {
     106           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     107             :                  "Problem reading %s header - short or corrupt?",
     108             :                  osDBName.c_str());
     109           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     110           0 :         return;
     111             :     }
     112             : 
     113           1 :     nUpdateCounter = atoi(reinterpret_cast<char *>(abyHeader) + 10);
     114             : 
     115             :     /* -------------------------------------------------------------------- */
     116             :     /*      Read the file in one gulp.                                      */
     117             :     /* -------------------------------------------------------------------- */
     118           1 :     if (VSIFSeekL(fpDB, 0, SEEK_END) != 0)
     119             :     {
     120           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     121           0 :         return;
     122             :     }
     123           1 :     const int nBufLength = static_cast<int>(VSIFTellL(fpDB) - nHeaderSize);
     124           1 :     if (VSIFSeekL(fpDB, nHeaderSize, SEEK_SET) != 0)
     125             :     {
     126           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     127           0 :         return;
     128             :     }
     129           1 :     char *pszDBData = static_cast<char *>(CPLCalloc(1, nBufLength + 1));
     130           1 :     if (VSIFReadL(pszDBData, 1, nBufLength, fpDB) !=
     131           1 :         static_cast<size_t>(nBufLength))
     132             :     {
     133           0 :         CPLFree(pszDBData);
     134           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     135           0 :         return;
     136             :     }
     137             : 
     138           1 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     139             : 
     140             :     /* -------------------------------------------------------------------- */
     141             :     /*      Parse the list of in/out names.                                 */
     142             :     /* -------------------------------------------------------------------- */
     143           1 :     int iNext = 0;
     144             : 
     145           3 :     while (iNext < nBufLength)
     146             :     {
     147           2 :         CPLString osOriginal;
     148           2 :         osOriginal.assign(pszDBData + iNext);
     149             : 
     150          54 :         for (; iNext < nBufLength && pszDBData[iNext] != '\0'; iNext++)
     151             :         {
     152             :         }
     153             : 
     154           2 :         if (iNext == nBufLength)
     155           0 :             break;
     156             : 
     157           2 :         iNext++;
     158             : 
     159           4 :         CPLString osProxy = osProxyDBDir;
     160           2 :         osProxy += "/";
     161           2 :         osProxy += pszDBData + iNext;
     162             : 
     163          74 :         for (; iNext < nBufLength && pszDBData[iNext] != '\0'; iNext++)
     164             :         {
     165             :         }
     166           2 :         iNext++;
     167             : 
     168           2 :         aosOriginalFiles.push_back(std::move(osOriginal));
     169           2 :         aosProxyFiles.push_back(std::move(osProxy));
     170             :     }
     171             : 
     172           1 :     CPLFree(pszDBData);
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                               SaveDB()                               */
     177             : /************************************************************************/
     178             : 
     179           3 : void GDALPamProxyDB::SaveDB()
     180             : 
     181             : {
     182             :     /* -------------------------------------------------------------------- */
     183             :     /*      Open the database relating original names to proxy .aux.xml     */
     184             :     /*      file names.                                                     */
     185             :     /* -------------------------------------------------------------------- */
     186             :     const std::string osDBName =
     187           3 :         CPLFormFilenameSafe(osProxyDBDir, "gdal_pam_proxy", "dat");
     188             : 
     189           3 :     void *hLock = CPLLockFile(osDBName.c_str(), 1.0);
     190             : 
     191             :     // proceed even if lock fails - we need CPLBreakLockFile()!
     192           3 :     if (hLock == nullptr)
     193             :     {
     194           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     195             :                  "GDALPamProxyDB::SaveDB() - "
     196             :                  "Failed to lock %s file, proceeding anyways.",
     197             :                  osDBName.c_str());
     198             :     }
     199             : 
     200           3 :     VSILFILE *fpDB = VSIFOpenL(osDBName.c_str(), "w");
     201           3 :     if (fpDB == nullptr)
     202             :     {
     203           0 :         if (hLock)
     204           0 :             CPLUnlockFile(hLock);
     205           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     206             :                  "Failed to save %s Pam Proxy DB.\n%s", osDBName.c_str(),
     207           0 :                  VSIStrerror(errno));
     208           0 :         return;
     209             :     }
     210             : 
     211             :     /* -------------------------------------------------------------------- */
     212             :     /*      Write header.                                                   */
     213             :     /* -------------------------------------------------------------------- */
     214           3 :     const size_t nHeaderSize = 100;
     215           3 :     GByte abyHeader[nHeaderSize] = {'\0'};
     216             : 
     217           3 :     memset(abyHeader, ' ', sizeof(abyHeader));
     218           3 :     memcpy(reinterpret_cast<char *>(abyHeader), "GDAL_PROXY", 10);
     219           3 :     snprintf(reinterpret_cast<char *>(abyHeader) + 10, sizeof(abyHeader) - 10,
     220             :              "%9d", nUpdateCounter);
     221             : 
     222           3 :     if (VSIFWriteL(abyHeader, 1, nHeaderSize, fpDB) != nHeaderSize)
     223             :     {
     224           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     225             :                  "Failed to write complete %s Pam Proxy DB.\n%s",
     226           0 :                  osDBName.c_str(), VSIStrerror(errno));
     227           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     228           0 :         VSIUnlink(osDBName.c_str());
     229           0 :         if (hLock)
     230           0 :             CPLUnlockFile(hLock);
     231           0 :         return;
     232             :     }
     233             : 
     234             :     /* -------------------------------------------------------------------- */
     235             :     /*      Write names.                                                    */
     236             :     /* -------------------------------------------------------------------- */
     237           7 :     for (unsigned int i = 0; i < aosOriginalFiles.size(); i++)
     238             :     {
     239             :         size_t nCount =
     240           4 :             VSIFWriteL(aosOriginalFiles[i].c_str(),
     241           4 :                        strlen(aosOriginalFiles[i].c_str()) + 1, 1, fpDB);
     242             : 
     243           4 :         const char *pszProxyFile = CPLGetFilename(aosProxyFiles[i]);
     244           4 :         nCount += VSIFWriteL(pszProxyFile, strlen(pszProxyFile) + 1, 1, fpDB);
     245             : 
     246           4 :         if (nCount != 2)
     247             :         {
     248           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     249             :                      "Failed to write complete %s Pam Proxy DB.\n%s",
     250           0 :                      osDBName.c_str(), VSIStrerror(errno));
     251           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpDB));
     252           0 :             VSIUnlink(osDBName.c_str());
     253           0 :             if (hLock)
     254           0 :                 CPLUnlockFile(hLock);
     255           0 :             return;
     256             :         }
     257             :     }
     258             : 
     259           3 :     if (VSIFCloseL(fpDB) != 0)
     260             :     {
     261           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     262             :     }
     263             : 
     264           3 :     if (hLock)
     265           3 :         CPLUnlockFile(hLock);
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                            InitProxyDB()                             */
     270             : /*                                                                      */
     271             : /*      Initialize ProxyDB (if it isn't already initialized).           */
     272             : /************************************************************************/
     273             : 
     274       37704 : static void InitProxyDB()
     275             : 
     276             : {
     277       37704 :     if (!bProxyDBInitialized)
     278             :     {
     279         657 :         CPLMutexHolderD(&hProxyDBLock);
     280             :         // cppcheck-suppress identicalInnerCondition
     281             :         // cppcheck-suppress knownConditionTrueFalse
     282         657 :         if (!bProxyDBInitialized)
     283             :         {
     284             :             const char *pszProxyDir =
     285         657 :                 CPLGetConfigOption("GDAL_PAM_PROXY_DIR", nullptr);
     286             : 
     287         657 :             if (pszProxyDir)
     288             :             {
     289           3 :                 poProxyDB = new GDALPamProxyDB();
     290           3 :                 poProxyDB->osProxyDBDir = pszProxyDir;
     291             :             }
     292             :         }
     293             : 
     294         657 :         bProxyDBInitialized = true;
     295             :     }
     296       37704 : }
     297             : 
     298             : /************************************************************************/
     299             : /*                          PamCleanProxyDB()                           */
     300             : /************************************************************************/
     301             : 
     302        1121 : void PamCleanProxyDB()
     303             : 
     304             : {
     305             :     {
     306        1121 :         CPLMutexHolderD(&hProxyDBLock);
     307             : 
     308        1121 :         bProxyDBInitialized = false;
     309             : 
     310        1121 :         delete poProxyDB;
     311        1121 :         poProxyDB = nullptr;
     312             :     }
     313             : 
     314        1121 :     CPLDestroyMutex(hProxyDBLock);
     315        1121 :     hProxyDBLock = nullptr;
     316        1121 : }
     317             : 
     318             : /************************************************************************/
     319             : /*                            PamGetProxy()                             */
     320             : /************************************************************************/
     321             : 
     322       37665 : const char *PamGetProxy(const char *pszOriginal)
     323             : 
     324             : {
     325       37665 :     InitProxyDB();
     326             : 
     327       37637 :     if (poProxyDB == nullptr)
     328       37601 :         return nullptr;
     329             : 
     330          69 :     CPLMutexHolderD(&hProxyDBLock);
     331             : 
     332          33 :     poProxyDB->CheckLoadDB();
     333             : 
     334          53 :     for (unsigned int i = 0; i < poProxyDB->aosOriginalFiles.size(); i++)
     335             :     {
     336          34 :         if (strcmp(poProxyDB->aosOriginalFiles[i], pszOriginal) == 0)
     337          14 :             return poProxyDB->aosProxyFiles[i];
     338             :     }
     339             : 
     340          19 :     return nullptr;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                          PamAllocateProxy()                          */
     345             : /************************************************************************/
     346             : 
     347          33 : const char *PamAllocateProxy(const char *pszOriginal)
     348             : 
     349             : {
     350          33 :     InitProxyDB();
     351             : 
     352          33 :     if (poProxyDB == nullptr)
     353          30 :         return nullptr;
     354             : 
     355           6 :     CPLMutexHolderD(&hProxyDBLock);
     356             : 
     357           3 :     poProxyDB->CheckLoadDB();
     358             : 
     359             :     /* -------------------------------------------------------------------- */
     360             :     /*      Form the proxy filename based on the original path if           */
     361             :     /*      possible, but dummy out any questionable characters, path       */
     362             :     /*      delimiters and such.  This is intended to make the proxy        */
     363             :     /*      name be identifiable by folks digging around in the proxy       */
     364             :     /*      database directory.                                             */
     365             :     /*                                                                      */
     366             :     /*      We also need to be careful about length.                        */
     367             :     /* -------------------------------------------------------------------- */
     368           6 :     CPLString osRevProxyFile;
     369             : 
     370           3 :     int i = static_cast<int>(strlen(pszOriginal)) - 1;
     371          80 :     while (i >= 0 && osRevProxyFile.size() < 220)
     372             :     {
     373          77 :         if (i > 6 && STARTS_WITH_CI(pszOriginal + i - 5, ":::OVR"))
     374           1 :             i -= 6;
     375             : 
     376             :         // make some effort to break long names at path delimiters.
     377          81 :         if ((pszOriginal[i] == '/' || pszOriginal[i] == '\\') &&
     378           4 :             osRevProxyFile.size() > 200)
     379           0 :             break;
     380             : 
     381          77 :         if ((pszOriginal[i] >= 'A' && pszOriginal[i] <= 'Z') ||
     382          77 :             (pszOriginal[i] >= 'a' && pszOriginal[i] <= 'z') ||
     383           8 :             (pszOriginal[i] >= '0' && pszOriginal[i] <= '9') ||
     384           8 :             pszOriginal[i] == '.')
     385          73 :             osRevProxyFile += pszOriginal[i];
     386             :         else
     387           4 :             osRevProxyFile += '_';
     388             : 
     389          77 :         i--;
     390             :     }
     391             : 
     392           6 :     CPLString osOriginal = pszOriginal;
     393           6 :     CPLString osProxy = poProxyDB->osProxyDBDir + "/";
     394             : 
     395           6 :     CPLString osCounter;
     396           3 :     osCounter.Printf("%06d_", poProxyDB->nUpdateCounter++);
     397           3 :     osProxy += osCounter;
     398             : 
     399          80 :     for (i = static_cast<int>(osRevProxyFile.size()) - 1; i >= 0; i--)
     400          77 :         osProxy += osRevProxyFile[i];
     401             : 
     402           3 :     if (!osOriginal.endsWith(".gmac"))
     403             :     {
     404           2 :         if (osOriginal.find(":::OVR") != CPLString::npos)
     405           1 :             osProxy += ".ovr";
     406             :         else
     407           1 :             osProxy += ".aux.xml";
     408             :     }
     409             : 
     410             :     /* -------------------------------------------------------------------- */
     411             :     /*      Add the proxy and the original to the proxy list and resave     */
     412             :     /*      the database.                                                   */
     413             :     /* -------------------------------------------------------------------- */
     414           3 :     poProxyDB->aosOriginalFiles.push_back(std::move(osOriginal));
     415           3 :     poProxyDB->aosProxyFiles.push_back(std::move(osProxy));
     416             : 
     417           3 :     poProxyDB->SaveDB();
     418             : 
     419           3 :     return PamGetProxy(pszOriginal);
     420             : }

Generated by: LCOV version 1.14