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

Generated by: LCOV version 1.14