Coverage Report

Created: 2025-06-13 06:29

/src/gdal/port/cpl_vsi_error.cpp
Line
Count
Source (jump to first uncovered line)
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
static VSIErrorContext *VSIGetErrorContext()
61
62
0
{
63
0
    int bError = FALSE;
64
0
    VSIErrorContext *psCtx = reinterpret_cast<VSIErrorContext *>(
65
0
        CPLGetTLSEx(CTLS_VSIERRORCONTEXT, &bError));
66
0
    if (bError)
67
0
        return nullptr;
68
69
0
    if (psCtx == nullptr)
70
0
    {
71
0
        psCtx = static_cast<VSIErrorContext *>(
72
0
            VSICalloc(sizeof(VSIErrorContext), 1));
73
0
        if (psCtx == nullptr)
74
0
        {
75
0
            fprintf(stderr, /*ok*/
76
0
                    "Out of memory attempting to record a VSI error.\n");
77
0
            return nullptr;
78
0
        }
79
0
        psCtx->nLastErrNo = VSIE_None;
80
0
        psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
81
0
        CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
82
0
    }
83
84
0
    return psCtx;
85
0
}
86
87
/************************************************************************/
88
/*                             VSIErrorV()                              */
89
/************************************************************************/
90
91
static void VSIErrorV(VSIErrorNum err_no, const char *fmt, va_list args)
92
0
{
93
0
    VSIErrorContext *psCtx = VSIGetErrorContext();
94
0
    if (psCtx == nullptr)
95
0
        return;
96
97
/* -------------------------------------------------------------------- */
98
/*      Expand the error message                                        */
99
/* -------------------------------------------------------------------- */
100
0
#if defined(HAVE_VSNPRINTF)
101
0
    {
102
0
        va_list wrk_args;
103
104
0
#ifdef va_copy
105
0
        va_copy(wrk_args, args);
106
#else
107
        wrk_args = args;
108
#endif
109
110
0
        int nPreviousSize = 0;
111
0
        int nPR = 0;
112
0
        while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
113
0
                                    psCtx->nLastErrMsgMax - nPreviousSize, fmt,
114
0
                                    wrk_args)) == -1 ||
115
0
                nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
116
0
               psCtx->nLastErrMsgMax < 1000000)
117
0
        {
118
0
#ifdef va_copy
119
0
            va_end(wrk_args);
120
0
            va_copy(wrk_args, args);
121
#else
122
            wrk_args = args;
123
#endif
124
0
            psCtx->nLastErrMsgMax *= 3;
125
0
            psCtx = static_cast<VSIErrorContext *>(CPLRealloc(
126
0
                psCtx, sizeof(VSIErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
127
0
                           psCtx->nLastErrMsgMax + 1));
128
0
            CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
129
0
        }
130
131
0
        va_end(wrk_args);
132
0
    }
133
#else  // !HAVE_VSNPRINTF
134
    CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
135
#endif
136
137
0
    psCtx->nLastErrNo = err_no;
138
0
}
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
void VSIError(VSIErrorNum err_no, CPL_FORMAT_STRING(const char *fmt), ...)
159
0
{
160
0
    va_list args;
161
162
    // Expand the error message.
163
0
    va_start(args, fmt);
164
0
    VSIErrorV(err_no, fmt, args);
165
0
    va_end(args);
166
0
}
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
void CPL_STDCALL VSIErrorReset()
180
0
{
181
0
    VSIErrorContext *psCtx = VSIGetErrorContext();
182
0
    if (psCtx == nullptr)
183
0
        return;
184
185
0
    psCtx->nLastErrNo = VSIE_None;
186
0
    psCtx->szLastErrMsg[0] = '\0';
187
0
}
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
VSIErrorNum CPL_STDCALL VSIGetLastErrorNo()
205
0
{
206
0
    VSIErrorContext *psCtx = VSIGetErrorContext();
207
0
    if (psCtx == nullptr)
208
0
        return 0;
209
210
0
    return psCtx->nLastErrNo;
211
0
}
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
const char *CPL_STDCALL VSIGetLastErrorMsg()
229
0
{
230
0
    VSIErrorContext *psCtx = VSIGetErrorContext();
231
0
    if (psCtx == nullptr)
232
0
        return "";
233
234
0
    return psCtx->szLastErrMsg;
235
0
}
236
237
/**********************************************************************
238
 *                     VSIErrorNumToString()
239
 **********************************************************************/
240
241
/** Translate a VSI error number into a string.
242
 *
243
 * @since GDAL 3.12
244
 */
245
const char *VSIErrorNumToString(int eErr)
246
0
{
247
0
#define CASE(x)                                                                \
248
0
    case VSIE_##x:                                                             \
249
0
        return #x;
250
0
    switch (eErr)
251
0
    {
252
0
        CASE(None)
253
0
        CASE(FileError)
254
0
        CASE(HttpError)
255
0
        CASE(ObjectStorageGenericError)
256
0
        CASE(AccessDenied)
257
0
        CASE(BucketNotFound)
258
0
        CASE(ObjectNotFound)
259
0
        CASE(InvalidCredentials)
260
0
        CASE(SignatureDoesNotMatch)
261
0
        default:
262
0
            break;
263
0
    }
264
0
#undef CASE
265
0
    CPLAssert(false);
266
0
    return "UnknownError";
267
0
}
268
269
/**********************************************************************
270
 *                          VSItoCPLError()
271
 **********************************************************************/
272
273
/**
274
 * Translate the VSI error into a CPLError call
275
 *
276
 * If there is a VSIError that is set, translate it to a CPLError call
277
 * with the given CPLErr error class, and either an appropriate CPLErrorNum
278
 * given the VSIErrorNum, or the given default CPLErrorNum.
279
 *
280
 * @return TRUE if a CPLError was issued, or FALSE if not.
281
 */
282
283
int CPL_STDCALL VSIToCPLError(CPLErr eErrClass, CPLErrorNum eDefaultErrorNo)
284
0
{
285
0
    const int err = VSIGetLastErrorNo();
286
0
    switch (err)
287
0
    {
288
0
        case VSIE_None:
289
0
            return FALSE;
290
0
        case VSIE_FileError:
291
0
            CPLError(eErrClass, eDefaultErrorNo, "%s", VSIGetLastErrorMsg());
292
0
            break;
293
0
        case VSIE_HttpError:
294
0
            CPLError(eErrClass, CPLE_HttpResponse, "%s", VSIGetLastErrorMsg());
295
0
            break;
296
0
        case VSIE_ObjectStorageGenericError:
297
0
            CPLError(eErrClass, CPLE_ObjectStorageGenericError, "%s",
298
0
                     VSIGetLastErrorMsg());
299
0
            break;
300
0
        case VSIE_AccessDenied:
301
0
            CPLError(eErrClass, CPLE_AccessDenied, "%s: %s",
302
0
                     VSIErrorNumToString(err), VSIGetLastErrorMsg());
303
0
            break;
304
0
        case VSIE_BucketNotFound:
305
0
            CPLError(eErrClass, CPLE_BucketNotFound, "%s: %s",
306
0
                     VSIErrorNumToString(err), VSIGetLastErrorMsg());
307
0
            break;
308
0
        case VSIE_ObjectNotFound:
309
0
            CPLError(eErrClass, CPLE_ObjectNotFound, "%s: %s",
310
0
                     VSIErrorNumToString(err), VSIGetLastErrorMsg());
311
0
            break;
312
0
        case VSIE_InvalidCredentials:
313
0
            CPLError(eErrClass, CPLE_InvalidCredentials, "%s: %s",
314
0
                     VSIErrorNumToString(err), VSIGetLastErrorMsg());
315
0
            break;
316
0
        case VSIE_SignatureDoesNotMatch:
317
0
            CPLError(eErrClass, CPLE_SignatureDoesNotMatch, "%s: %s",
318
0
                     VSIErrorNumToString(err), VSIGetLastErrorMsg());
319
0
            break;
320
0
        default:
321
0
            CPLError(eErrClass, CPLE_HttpResponse,
322
0
                     "A filesystem error with code %d occurred", err);
323
0
            break;
324
0
    }
325
326
0
    return TRUE;
327
0
}
328
329
/**********************************************************************
330
 *                        VSIToCPLErrorWithMsg()
331
 **********************************************************************/
332
333
/**
334
 * Translate the VSI error into a CPLError call
335
 *
336
 * If there is a VSIError that is set, translate it to a CPLError call
337
 * with the given CPLErr error class, and either an appropriate CPLErrorNum
338
 * given the VSIErrorNum, or the given default CPLErrorNum.
339
 */
340
341
void VSIToCPLErrorWithMsg(CPLErr eErrClass, CPLErrorNum eDefaultErrorNo,
342
                          const char *pszMsg)
343
0
{
344
0
    const int err = VSIGetLastErrorNo();
345
0
    switch (err)
346
0
    {
347
0
        case VSIE_None:
348
0
            CPLError(eErrClass, eDefaultErrorNo, "%s", pszMsg);
349
0
            break;
350
0
        case VSIE_FileError:
351
0
            CPLError(eErrClass, eDefaultErrorNo, "%s: %s", pszMsg,
352
0
                     VSIGetLastErrorMsg());
353
0
            break;
354
0
        case VSIE_HttpError:
355
0
            CPLError(eErrClass, CPLE_HttpResponse, "%s: %s", pszMsg,
356
0
                     VSIGetLastErrorMsg());
357
0
            break;
358
0
        case VSIE_ObjectStorageGenericError:
359
0
            CPLError(eErrClass, CPLE_ObjectStorageGenericError, "%s: %s",
360
0
                     pszMsg, VSIGetLastErrorMsg());
361
0
            break;
362
0
        case VSIE_AccessDenied:
363
0
            CPLError(eErrClass, CPLE_AccessDenied, "%s: %s", pszMsg,
364
0
                     VSIGetLastErrorMsg());
365
0
            break;
366
0
        case VSIE_BucketNotFound:
367
0
            CPLError(eErrClass, CPLE_BucketNotFound, "%s: %s", pszMsg,
368
0
                     VSIGetLastErrorMsg());
369
0
            break;
370
0
        case VSIE_ObjectNotFound:
371
0
            CPLError(eErrClass, CPLE_ObjectNotFound, "%s: %s", pszMsg,
372
0
                     VSIGetLastErrorMsg());
373
0
            break;
374
0
        case VSIE_InvalidCredentials:
375
0
            CPLError(eErrClass, CPLE_InvalidCredentials, "%s: %s", pszMsg,
376
0
                     VSIGetLastErrorMsg());
377
0
            break;
378
0
        case VSIE_SignatureDoesNotMatch:
379
0
            CPLError(eErrClass, CPLE_SignatureDoesNotMatch, "%s: %s", pszMsg,
380
0
                     VSIGetLastErrorMsg());
381
0
            break;
382
0
        default:
383
0
            CPLError(eErrClass, CPLE_HttpResponse,
384
0
                     "%s: A filesystem error with code %d occurred", pszMsg,
385
0
                     err);
386
0
            break;
387
0
    }
388
0
}