File: cpl_vsi_error.cpp

package info (click to toggle)
gdal 3.6.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 89,664 kB
  • sloc: cpp: 1,136,033; ansic: 197,355; python: 35,910; java: 5,511; xml: 4,011; sh: 3,950; cs: 2,443; yacc: 1,047; makefile: 288
file content (311 lines) | stat: -rw-r--r-- 10,220 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/******************************************************************************
 *
 * Project:  VSI Virtual File System
 * Purpose:  Implement an error system for reporting file system errors.
 *           Filesystem errors need to be handled separately from the
 *           CPLError architecture because they are potentially ignored.
 * Author:   Rob Emanuele, rdemanuele at gmail.com
 *
 ******************************************************************************
 * Copyright (c) 2016, Rob Emanuele <rdemanuele at gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "cpl_vsi_error.h"

#include <cstdarg>
#include <cstdio>

#include "cpl_config.h"
#include "cpl_conv.h"
#include "cpl_error.h"
#include "cpl_multiproc.h"
#include "cpl_string.h"
#include "cpl_vsi.h"

#if !defined(va_copy) && defined(__va_copy)
#define va_copy __va_copy
#endif

// TODO(rouault): Why is this here?
#if !defined(WIN32)
#include <string.h>
#endif

#define TIMESTAMP_DEBUG
// #define MEMORY_DEBUG

constexpr int DEFAULT_LAST_ERR_MSG_SIZE =
#if !defined(HAVE_VSNPRINTF)
    20000
#else
    500
#endif
    ;

typedef struct
{
    VSIErrorNum nLastErrNo;
    int nLastErrMsgMax;
    char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE];
    // Do not add anything here. szLastErrMsg must be the last field. See
    // CPLRealloc() below.
} VSIErrorContext;

/************************************************************************/
/*                         CPLGetErrorContext()                         */
/************************************************************************/

static VSIErrorContext *VSIGetErrorContext()

{
    int bError = FALSE;
    VSIErrorContext *psCtx = reinterpret_cast<VSIErrorContext *>(
        CPLGetTLSEx(CTLS_VSIERRORCONTEXT, &bError));
    if (bError)
        return nullptr;

    if (psCtx == nullptr)
    {
        psCtx = static_cast<VSIErrorContext *>(
            VSICalloc(sizeof(VSIErrorContext), 1));
        if (psCtx == nullptr)
        {
            fprintf(stderr, /*ok*/
                    "Out of memory attempting to record a VSI error.\n");
            return nullptr;
        }
        psCtx->nLastErrNo = VSIE_None;
        psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
        CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
    }

    return psCtx;
}

/************************************************************************/
/*                             VSIErrorV()                              */
/************************************************************************/

static void VSIErrorV(VSIErrorNum err_no, const char *fmt, va_list args)
{
    VSIErrorContext *psCtx = VSIGetErrorContext();
    if (psCtx == nullptr)
        return;

/* -------------------------------------------------------------------- */
/*      Expand the error message                                        */
/* -------------------------------------------------------------------- */
#if defined(HAVE_VSNPRINTF)
    {
        va_list wrk_args;

#ifdef va_copy
        va_copy(wrk_args, args);
#else
        wrk_args = args;
#endif

        int nPreviousSize = 0;
        int nPR = 0;
        while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
                                    psCtx->nLastErrMsgMax - nPreviousSize, fmt,
                                    wrk_args)) == -1 ||
                nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
               psCtx->nLastErrMsgMax < 1000000)
        {
#ifdef va_copy
            va_end(wrk_args);
            va_copy(wrk_args, args);
#else
            wrk_args = args;
#endif
            psCtx->nLastErrMsgMax *= 3;
            psCtx = static_cast<VSIErrorContext *>(CPLRealloc(
                psCtx, sizeof(VSIErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
                           psCtx->nLastErrMsgMax + 1));
            CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
        }

        va_end(wrk_args);
    }
#else  // !HAVE_VSNPRINTF
    CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
#endif

    psCtx->nLastErrNo = err_no;
}

/**********************************************************************
 *                          VSIError()
 **********************************************************************/

/**
 * Report an VSI filesystem error.
 *
 * This function records an error in the filesystem that may or may not be
 * used in the future, for example converted into a CPLError. This allows
 * filesystem errors to be available to error handling functionality, but
 * reported only when necessary.
 *
 * @param err_no the error number (VSIE_*) from cpl_vsi_error.h.
 * @param fmt a printf() style format string.  Any additional arguments
 * will be treated as arguments to fill in this format in a manner
 * similar to printf().
 */

void VSIError(VSIErrorNum err_no, CPL_FORMAT_STRING(const char *fmt), ...)
{
    va_list args;

    // Expand the error message.
    va_start(args, fmt);
    VSIErrorV(err_no, fmt, args);
    va_end(args);
}

/**********************************************************************
 *                          VSIErrorReset()
 **********************************************************************/

/**
 * Erase any traces of previous errors.
 *
 * This is used to clear out the latest file system error when it is either
 * translated into a CPLError call or when it is determined to be ignorable.
 */

void CPL_STDCALL VSIErrorReset()
{
    VSIErrorContext *psCtx = VSIGetErrorContext();
    if (psCtx == nullptr)
        return;

    psCtx->nLastErrNo = VSIE_None;
    psCtx->szLastErrMsg[0] = '\0';
}

/**********************************************************************
 *                          VSIGetLastErrorNo()
 **********************************************************************/

/**
 * Fetch the last error number.
 *
 * Fetches the last error number posted with VSIError(), that hasn't
 * been cleared by VSIErrorReset().  This is the error number, not the error
 * class.
 *
 * @return the error number of the last error to occur, or VSIE_None (0)
 * if there are no posted errors.
 */

VSIErrorNum CPL_STDCALL VSIGetLastErrorNo()
{
    VSIErrorContext *psCtx = VSIGetErrorContext();
    if (psCtx == nullptr)
        return 0;

    return psCtx->nLastErrNo;
}

/**********************************************************************
 *                          VSIGetLastErrorMsg()
 **********************************************************************/

/**
 * Get the last error message.
 *
 * Fetches the last error message posted with VSIError(), that hasn't
 * been cleared by VSIErrorReset().  The returned pointer is to an internal
 * string that should not be altered or freed.
 *
 * @return the last error message, or NULL if there is no posted error
 * message.
 */

const char *CPL_STDCALL VSIGetLastErrorMsg()
{
    VSIErrorContext *psCtx = VSIGetErrorContext();
    if (psCtx == nullptr)
        return "";

    return psCtx->szLastErrMsg;
}

/**********************************************************************
 *                          VSItoCPLError()
 **********************************************************************/

/**
 * Translate the VSI error into a CPLError call
 *
 * If there is a VSIError that is set, translate it to a CPLError call
 * with the given CPLErr error class, and either an appropriate CPLErrorNum
 * given the VSIErrorNum, or the given default CPLErrorNum.
 *
 * @return TRUE if a CPLError was issued, or FALSE if not.
 */

int CPL_DLL CPL_STDCALL VSIToCPLError(CPLErr eErrClass,
                                      CPLErrorNum eDefaultErrorNo)
{
    const int err = VSIGetLastErrorNo();
    switch (err)
    {
        case VSIE_None:
            return FALSE;
        case VSIE_FileError:
            CPLError(eErrClass, eDefaultErrorNo, "%s", VSIGetLastErrorMsg());
            break;
        case VSIE_HttpError:
            CPLError(eErrClass, CPLE_HttpResponse, "%s", VSIGetLastErrorMsg());
            break;
        case VSIE_AWSError:
            CPLError(eErrClass, CPLE_AWSError, "%s", VSIGetLastErrorMsg());
            break;
        case VSIE_AWSAccessDenied:
            CPLError(eErrClass, CPLE_AWSAccessDenied, "%s",
                     VSIGetLastErrorMsg());
            break;
        case VSIE_AWSBucketNotFound:
            CPLError(eErrClass, CPLE_AWSBucketNotFound, "%s",
                     VSIGetLastErrorMsg());
            break;
        case VSIE_AWSObjectNotFound:
            CPLError(eErrClass, CPLE_AWSObjectNotFound, "%s",
                     VSIGetLastErrorMsg());
            break;
        case VSIE_AWSInvalidCredentials:
            CPLError(eErrClass, CPLE_AWSInvalidCredentials, "%s",
                     VSIGetLastErrorMsg());
            break;
        case VSIE_AWSSignatureDoesNotMatch:
            CPLError(eErrClass, CPLE_AWSSignatureDoesNotMatch, "%s",
                     VSIGetLastErrorMsg());
            break;
        default:
            CPLError(eErrClass, CPLE_HttpResponse,
                     "A filesystem error with code %d occurred", err);
            break;
    }

    return TRUE;
}