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

Generated by: LCOV version 1.14