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

Generated by: LCOV version 1.14