LCOV - code coverage report
Current view: top level - port - cpl_vsi_error.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 67 122 54.9 %
Date: 2024-11-25 23:50:41 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VSI Virtual File System
       4             :  * Purpose:  Implement an error system for reporting file system errors.
       5             :  *           Filesystem errors need to be handled separately from the
       6             :  *           CPLError architecture because they are potentially ignored.
       7             :  * Author:   Rob Emanuele, rdemanuele at gmail.com
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2016, Rob Emanuele <rdemanuele at gmail.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_vsi_error.h"
      16             : 
      17             : #include <cstdarg>
      18             : #include <cstdio>
      19             : 
      20             : #include "cpl_config.h"
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_multiproc.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : 
      27             : #if !defined(va_copy) && defined(__va_copy)
      28             : #define va_copy __va_copy
      29             : #endif
      30             : 
      31             : // TODO(rouault): Why is this here?
      32             : #if !defined(_WIN32)
      33             : #include <string.h>
      34             : #endif
      35             : 
      36             : #define TIMESTAMP_DEBUG
      37             : // #define MEMORY_DEBUG
      38             : 
      39             : constexpr int DEFAULT_LAST_ERR_MSG_SIZE =
      40             : #if !defined(HAVE_VSNPRINTF)
      41             :     20000
      42             : #else
      43             :     500
      44             : #endif
      45             :     ;
      46             : 
      47             : typedef struct
      48             : {
      49             :     VSIErrorNum nLastErrNo;
      50             :     int nLastErrMsgMax;
      51             :     char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE];
      52             :     // Do not add anything here. szLastErrMsg must be the last field. See
      53             :     // CPLRealloc() below.
      54             : } VSIErrorContext;
      55             : 
      56             : /************************************************************************/
      57             : /*                         CPLGetErrorContext()                         */
      58             : /************************************************************************/
      59             : 
      60      152744 : static VSIErrorContext *VSIGetErrorContext()
      61             : 
      62             : {
      63      152744 :     int bError = FALSE;
      64             :     VSIErrorContext *psCtx = reinterpret_cast<VSIErrorContext *>(
      65      152744 :         CPLGetTLSEx(CTLS_VSIERRORCONTEXT, &bError));
      66      152666 :     if (bError)
      67           0 :         return nullptr;
      68             : 
      69      152666 :     if (psCtx == nullptr)
      70             :     {
      71             :         psCtx = static_cast<VSIErrorContext *>(
      72        1197 :             VSICalloc(sizeof(VSIErrorContext), 1));
      73        1197 :         if (psCtx == nullptr)
      74             :         {
      75           0 :             fprintf(stderr, /*ok*/
      76             :                     "Out of memory attempting to record a VSI error.\n");
      77           0 :             return nullptr;
      78             :         }
      79        1197 :         psCtx->nLastErrNo = VSIE_None;
      80        1197 :         psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
      81        1197 :         CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
      82             :     }
      83             : 
      84      152670 :     return psCtx;
      85             : }
      86             : 
      87             : /************************************************************************/
      88             : /*                             VSIErrorV()                              */
      89             : /************************************************************************/
      90             : 
      91       19174 : static void VSIErrorV(VSIErrorNum err_no, const char *fmt, va_list args)
      92             : {
      93       19174 :     VSIErrorContext *psCtx = VSIGetErrorContext();
      94       19174 :     if (psCtx == nullptr)
      95           0 :         return;
      96             : 
      97             : /* -------------------------------------------------------------------- */
      98             : /*      Expand the error message                                        */
      99             : /* -------------------------------------------------------------------- */
     100             : #if defined(HAVE_VSNPRINTF)
     101             :     {
     102             :         va_list wrk_args;
     103             : 
     104             : #ifdef va_copy
     105       19174 :         va_copy(wrk_args, args);
     106             : #else
     107             :         wrk_args = args;
     108             : #endif
     109             : 
     110       19174 :         int nPreviousSize = 0;
     111       19174 :         int nPR = 0;
     112          51 :         while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
     113       19225 :                                     psCtx->nLastErrMsgMax - nPreviousSize, fmt,
     114       19225 :                                     wrk_args)) == -1 ||
     115       19276 :                 nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
     116          51 :                psCtx->nLastErrMsgMax < 1000000)
     117             :         {
     118             : #ifdef va_copy
     119          51 :             va_end(wrk_args);
     120          51 :             va_copy(wrk_args, args);
     121             : #else
     122             :             wrk_args = args;
     123             : #endif
     124          51 :             psCtx->nLastErrMsgMax *= 3;
     125         102 :             psCtx = static_cast<VSIErrorContext *>(CPLRealloc(
     126             :                 psCtx, sizeof(VSIErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
     127          51 :                            psCtx->nLastErrMsgMax + 1));
     128          51 :             CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
     129             :         }
     130             : 
     131       19174 :         va_end(wrk_args);
     132             :     }
     133             : #else  // !HAVE_VSNPRINTF
     134             :     CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
     135             : #endif
     136             : 
     137       19174 :     psCtx->nLastErrNo = err_no;
     138             : }
     139             : 
     140             : /**********************************************************************
     141             :  *                          VSIError()
     142             :  **********************************************************************/
     143             : 
     144             : /**
     145             :  * Report an VSI filesystem error.
     146             :  *
     147             :  * This function records an error in the filesystem that may or may not be
     148             :  * used in the future, for example converted into a CPLError. This allows
     149             :  * filesystem errors to be available to error handling functionality, but
     150             :  * reported only when necessary.
     151             :  *
     152             :  * @param err_no the error number (VSIE_*) from cpl_vsi_error.h.
     153             :  * @param fmt a printf() style format string.  Any additional arguments
     154             :  * will be treated as arguments to fill in this format in a manner
     155             :  * similar to printf().
     156             :  */
     157             : 
     158       19173 : void VSIError(VSIErrorNum err_no, CPL_FORMAT_STRING(const char *fmt), ...)
     159             : {
     160             :     va_list args;
     161             : 
     162             :     // Expand the error message.
     163       19173 :     va_start(args, fmt);
     164       19173 :     VSIErrorV(err_no, fmt, args);
     165       19173 :     va_end(args);
     166       19173 : }
     167             : 
     168             : /**********************************************************************
     169             :  *                          VSIErrorReset()
     170             :  **********************************************************************/
     171             : 
     172             : /**
     173             :  * Erase any traces of previous errors.
     174             :  *
     175             :  * This is used to clear out the latest file system error when it is either
     176             :  * translated into a CPLError call or when it is determined to be ignorable.
     177             :  */
     178             : 
     179      122232 : void CPL_STDCALL VSIErrorReset()
     180             : {
     181      122232 :     VSIErrorContext *psCtx = VSIGetErrorContext();
     182      122136 :     if (psCtx == nullptr)
     183           0 :         return;
     184             : 
     185      122136 :     psCtx->nLastErrNo = VSIE_None;
     186      122136 :     psCtx->szLastErrMsg[0] = '\0';
     187             : }
     188             : 
     189             : /**********************************************************************
     190             :  *                          VSIGetLastErrorNo()
     191             :  **********************************************************************/
     192             : 
     193             : /**
     194             :  * Fetch the last error number.
     195             :  *
     196             :  * Fetches the last error number posted with VSIError(), that hasn't
     197             :  * been cleared by VSIErrorReset().  This is the error number, not the error
     198             :  * class.
     199             :  *
     200             :  * @return the error number of the last error to occur, or VSIE_None (0)
     201             :  * if there are no posted errors.
     202             :  */
     203             : 
     204        5743 : VSIErrorNum CPL_STDCALL VSIGetLastErrorNo()
     205             : {
     206        5743 :     VSIErrorContext *psCtx = VSIGetErrorContext();
     207        5712 :     if (psCtx == nullptr)
     208           0 :         return 0;
     209             : 
     210        5712 :     return psCtx->nLastErrNo;
     211             : }
     212             : 
     213             : /**********************************************************************
     214             :  *                          VSIGetLastErrorMsg()
     215             :  **********************************************************************/
     216             : 
     217             : /**
     218             :  * Get the last error message.
     219             :  *
     220             :  * Fetches the last error message posted with VSIError(), that hasn't
     221             :  * been cleared by VSIErrorReset().  The returned pointer is to an internal
     222             :  * string that should not be altered or freed.
     223             :  *
     224             :  * @return the last error message, or NULL if there is no posted error
     225             :  * message.
     226             :  */
     227             : 
     228        5618 : const char *CPL_STDCALL VSIGetLastErrorMsg()
     229             : {
     230        5618 :     VSIErrorContext *psCtx = VSIGetErrorContext();
     231        5605 :     if (psCtx == nullptr)
     232           0 :         return "";
     233             : 
     234        5605 :     return psCtx->szLastErrMsg;
     235             : }
     236             : 
     237             : /**********************************************************************
     238             :  *                          VSItoCPLError()
     239             :  **********************************************************************/
     240             : 
     241             : /**
     242             :  * Translate the VSI error into a CPLError call
     243             :  *
     244             :  * If there is a VSIError that is set, translate it to a CPLError call
     245             :  * with the given CPLErr error class, and either an appropriate CPLErrorNum
     246             :  * given the VSIErrorNum, or the given default CPLErrorNum.
     247             :  *
     248             :  * @return TRUE if a CPLError was issued, or FALSE if not.
     249             :  */
     250             : 
     251        5704 : int CPL_STDCALL VSIToCPLError(CPLErr eErrClass, CPLErrorNum eDefaultErrorNo)
     252             : {
     253        5704 :     const int err = VSIGetLastErrorNo();
     254        5691 :     switch (err)
     255             :     {
     256         152 :         case VSIE_None:
     257         152 :             return FALSE;
     258        5538 :         case VSIE_FileError:
     259        5538 :             CPLError(eErrClass, eDefaultErrorNo, "%s", VSIGetLastErrorMsg());
     260        5555 :             break;
     261           4 :         case VSIE_HttpError:
     262           4 :             CPLError(eErrClass, CPLE_HttpResponse, "%s", VSIGetLastErrorMsg());
     263           4 :             break;
     264           0 :         case VSIE_AWSError:
     265           0 :             CPLError(eErrClass, CPLE_AWSError, "%s", VSIGetLastErrorMsg());
     266           0 :             break;
     267           0 :         case VSIE_AWSAccessDenied:
     268           0 :             CPLError(eErrClass, CPLE_AWSAccessDenied, "%s",
     269             :                      VSIGetLastErrorMsg());
     270           0 :             break;
     271           0 :         case VSIE_AWSBucketNotFound:
     272           0 :             CPLError(eErrClass, CPLE_AWSBucketNotFound, "%s",
     273             :                      VSIGetLastErrorMsg());
     274           0 :             break;
     275           0 :         case VSIE_AWSObjectNotFound:
     276           0 :             CPLError(eErrClass, CPLE_AWSObjectNotFound, "%s",
     277             :                      VSIGetLastErrorMsg());
     278           0 :             break;
     279           0 :         case VSIE_AWSInvalidCredentials:
     280           0 :             CPLError(eErrClass, CPLE_AWSInvalidCredentials, "%s",
     281             :                      VSIGetLastErrorMsg());
     282           0 :             break;
     283           0 :         case VSIE_AWSSignatureDoesNotMatch:
     284           0 :             CPLError(eErrClass, CPLE_AWSSignatureDoesNotMatch, "%s",
     285             :                      VSIGetLastErrorMsg());
     286           0 :             break;
     287           0 :         default:
     288           0 :             CPLError(eErrClass, CPLE_HttpResponse,
     289             :                      "A filesystem error with code %d occurred", err);
     290           0 :             break;
     291             :     }
     292             : 
     293        5559 :     return TRUE;
     294             : }
     295             : 
     296             : /**********************************************************************
     297             :  *                        VSIToCPLErrorWithMsg()
     298             :  **********************************************************************/
     299             : 
     300             : /**
     301             :  * Translate the VSI error into a CPLError call
     302             :  *
     303             :  * If there is a VSIError that is set, translate it to a CPLError call
     304             :  * with the given CPLErr error class, and either an appropriate CPLErrorNum
     305             :  * given the VSIErrorNum, or the given default CPLErrorNum.
     306             :  */
     307             : 
     308          15 : void VSIToCPLErrorWithMsg(CPLErr eErrClass, CPLErrorNum eDefaultErrorNo,
     309             :                           const char *pszMsg)
     310             : {
     311          15 :     const int err = VSIGetLastErrorNo();
     312          15 :     switch (err)
     313             :     {
     314           0 :         case VSIE_None:
     315           0 :             CPLError(eErrClass, eDefaultErrorNo, "%s", pszMsg);
     316           0 :             break;
     317          15 :         case VSIE_FileError:
     318          15 :             CPLError(eErrClass, eDefaultErrorNo, "%s: %s", pszMsg,
     319             :                      VSIGetLastErrorMsg());
     320          15 :             break;
     321           0 :         case VSIE_HttpError:
     322           0 :             CPLError(eErrClass, CPLE_HttpResponse, "%s: %s", pszMsg,
     323             :                      VSIGetLastErrorMsg());
     324           0 :             break;
     325           0 :         case VSIE_AWSError:
     326           0 :             CPLError(eErrClass, CPLE_AWSError, "%s: %s", pszMsg,
     327             :                      VSIGetLastErrorMsg());
     328           0 :             break;
     329           0 :         case VSIE_AWSAccessDenied:
     330           0 :             CPLError(eErrClass, CPLE_AWSAccessDenied, "%s: %s", pszMsg,
     331             :                      VSIGetLastErrorMsg());
     332           0 :             break;
     333           0 :         case VSIE_AWSBucketNotFound:
     334           0 :             CPLError(eErrClass, CPLE_AWSBucketNotFound, "%s: %s", pszMsg,
     335             :                      VSIGetLastErrorMsg());
     336           0 :             break;
     337           0 :         case VSIE_AWSObjectNotFound:
     338           0 :             CPLError(eErrClass, CPLE_AWSObjectNotFound, "%s: %s", pszMsg,
     339             :                      VSIGetLastErrorMsg());
     340           0 :             break;
     341           0 :         case VSIE_AWSInvalidCredentials:
     342           0 :             CPLError(eErrClass, CPLE_AWSInvalidCredentials, "%s: %s", pszMsg,
     343             :                      VSIGetLastErrorMsg());
     344           0 :             break;
     345           0 :         case VSIE_AWSSignatureDoesNotMatch:
     346           0 :             CPLError(eErrClass, CPLE_AWSSignatureDoesNotMatch, "%s: %s", pszMsg,
     347             :                      VSIGetLastErrorMsg());
     348           0 :             break;
     349           0 :         default:
     350           0 :             CPLError(eErrClass, CPLE_HttpResponse,
     351             :                      "%s: A filesystem error with code %d occurred", pszMsg,
     352             :                      err);
     353           0 :             break;
     354             :     }
     355          15 : }

Generated by: LCOV version 1.14