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 172215 : static VSIErrorContext *VSIGetErrorContext()
61 :
62 : {
63 172215 : int bError = FALSE;
64 : VSIErrorContext *psCtx = reinterpret_cast<VSIErrorContext *>(
65 172215 : CPLGetTLSEx(CTLS_VSIERRORCONTEXT, &bError));
66 172128 : if (bError)
67 0 : return nullptr;
68 :
69 172128 : if (psCtx == nullptr)
70 : {
71 : psCtx = static_cast<VSIErrorContext *>(
72 1325 : VSICalloc(sizeof(VSIErrorContext), 1));
73 1325 : 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 1325 : psCtx->nLastErrNo = VSIE_None;
80 1325 : psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
81 1325 : CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
82 : }
83 :
84 172166 : return psCtx;
85 : }
86 :
87 : /************************************************************************/
88 : /* VSIErrorV() */
89 : /************************************************************************/
90 :
91 21166 : static void VSIErrorV(VSIErrorNum err_no, const char *fmt, va_list args)
92 : {
93 21166 : VSIErrorContext *psCtx = VSIGetErrorContext();
94 21166 : 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 21166 : va_copy(wrk_args, args);
106 : #else
107 : wrk_args = args;
108 : #endif
109 :
110 21166 : int nPreviousSize = 0;
111 21166 : int nPR = 0;
112 60 : while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
113 21226 : psCtx->nLastErrMsgMax - nPreviousSize, fmt,
114 21226 : wrk_args)) == -1 ||
115 21286 : nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
116 60 : psCtx->nLastErrMsgMax < 1000000)
117 : {
118 : #ifdef va_copy
119 60 : va_end(wrk_args);
120 60 : va_copy(wrk_args, args);
121 : #else
122 : wrk_args = args;
123 : #endif
124 60 : psCtx->nLastErrMsgMax *= 3;
125 120 : psCtx = static_cast<VSIErrorContext *>(CPLRealloc(
126 : psCtx, sizeof(VSIErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
127 60 : psCtx->nLastErrMsgMax + 1));
128 60 : CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
129 : }
130 :
131 21166 : va_end(wrk_args);
132 : }
133 : #else // !HAVE_VSNPRINTF
134 : CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
135 : #endif
136 :
137 21166 : 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 21166 : void VSIError(VSIErrorNum err_no, CPL_FORMAT_STRING(const char *fmt), ...)
159 : {
160 : va_list args;
161 :
162 : // Expand the error message.
163 21166 : va_start(args, fmt);
164 21166 : VSIErrorV(err_no, fmt, args);
165 21166 : va_end(args);
166 21166 : }
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 139978 : void CPL_STDCALL VSIErrorReset()
180 : {
181 139978 : VSIErrorContext *psCtx = VSIGetErrorContext();
182 139848 : if (psCtx == nullptr)
183 0 : return;
184 :
185 139848 : psCtx->nLastErrNo = VSIE_None;
186 139848 : 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 5742 : if (psCtx == nullptr)
208 0 : return 0;
209 :
210 5742 : 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 5320 : const char *CPL_STDCALL VSIGetLastErrorMsg()
229 : {
230 5320 : VSIErrorContext *psCtx = VSIGetErrorContext();
231 5318 : if (psCtx == nullptr)
232 0 : return "";
233 :
234 5318 : return psCtx->szLastErrMsg;
235 : }
236 :
237 : /**********************************************************************
238 : * VSIErrorNumToString()
239 : **********************************************************************/
240 :
241 : /** Translate a VSI error number into a string.
242 : *
243 : * @since GDAL 3.12
244 : */
245 8 : const char *VSIErrorNumToString(int eErr)
246 : {
247 : #define CASE(x) \
248 : case VSIE_##x: \
249 : return #x;
250 8 : switch (eErr)
251 : {
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 8 : CASE(InvalidCredentials)
260 0 : CASE(SignatureDoesNotMatch)
261 0 : default:
262 0 : break;
263 : }
264 : #undef CASE
265 0 : CPLAssert(false);
266 : return "UnknownError";
267 : }
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 5585 : int CPL_STDCALL VSIToCPLError(CPLErr eErrClass, CPLErrorNum eDefaultErrorNo)
284 : {
285 5585 : const int err = VSIGetLastErrorNo();
286 5583 : switch (err)
287 : {
288 351 : case VSIE_None:
289 351 : return FALSE;
290 5229 : case VSIE_FileError:
291 5229 : CPLError(eErrClass, eDefaultErrorNo, "%s", VSIGetLastErrorMsg());
292 5229 : break;
293 4 : case VSIE_HttpError:
294 4 : CPLError(eErrClass, CPLE_HttpResponse, "%s", VSIGetLastErrorMsg());
295 4 : break;
296 0 : case VSIE_ObjectStorageGenericError:
297 0 : CPLError(eErrClass, CPLE_ObjectStorageGenericError, "%s",
298 : VSIGetLastErrorMsg());
299 0 : break;
300 0 : case VSIE_AccessDenied:
301 0 : CPLError(eErrClass, CPLE_AccessDenied, "%s: %s",
302 : VSIErrorNumToString(err), VSIGetLastErrorMsg());
303 0 : break;
304 0 : case VSIE_BucketNotFound:
305 0 : CPLError(eErrClass, CPLE_BucketNotFound, "%s: %s",
306 : VSIErrorNumToString(err), VSIGetLastErrorMsg());
307 0 : break;
308 0 : case VSIE_ObjectNotFound:
309 0 : CPLError(eErrClass, CPLE_ObjectNotFound, "%s: %s",
310 : VSIErrorNumToString(err), VSIGetLastErrorMsg());
311 0 : break;
312 1 : case VSIE_InvalidCredentials:
313 1 : CPLError(eErrClass, CPLE_InvalidCredentials, "%s: %s",
314 : VSIErrorNumToString(err), VSIGetLastErrorMsg());
315 1 : break;
316 0 : case VSIE_SignatureDoesNotMatch:
317 0 : CPLError(eErrClass, CPLE_SignatureDoesNotMatch, "%s: %s",
318 : VSIErrorNumToString(err), VSIGetLastErrorMsg());
319 0 : break;
320 0 : default:
321 0 : CPLError(eErrClass, CPLE_HttpResponse,
322 : "A filesystem error with code %d occurred", err);
323 0 : break;
324 : }
325 :
326 5234 : return TRUE;
327 : }
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 21 : void VSIToCPLErrorWithMsg(CPLErr eErrClass, CPLErrorNum eDefaultErrorNo,
342 : const char *pszMsg)
343 : {
344 21 : const int err = VSIGetLastErrorNo();
345 21 : switch (err)
346 : {
347 0 : case VSIE_None:
348 0 : CPLError(eErrClass, eDefaultErrorNo, "%s", pszMsg);
349 0 : break;
350 21 : case VSIE_FileError:
351 21 : CPLError(eErrClass, eDefaultErrorNo, "%s: %s", pszMsg,
352 : VSIGetLastErrorMsg());
353 21 : break;
354 0 : case VSIE_HttpError:
355 0 : CPLError(eErrClass, CPLE_HttpResponse, "%s: %s", pszMsg,
356 : VSIGetLastErrorMsg());
357 0 : break;
358 0 : case VSIE_ObjectStorageGenericError:
359 0 : CPLError(eErrClass, CPLE_ObjectStorageGenericError, "%s: %s",
360 : pszMsg, VSIGetLastErrorMsg());
361 0 : break;
362 0 : case VSIE_AccessDenied:
363 0 : CPLError(eErrClass, CPLE_AccessDenied, "%s: %s", pszMsg,
364 : VSIGetLastErrorMsg());
365 0 : break;
366 0 : case VSIE_BucketNotFound:
367 0 : CPLError(eErrClass, CPLE_BucketNotFound, "%s: %s", pszMsg,
368 : VSIGetLastErrorMsg());
369 0 : break;
370 0 : case VSIE_ObjectNotFound:
371 0 : CPLError(eErrClass, CPLE_ObjectNotFound, "%s: %s", pszMsg,
372 : VSIGetLastErrorMsg());
373 0 : break;
374 0 : case VSIE_InvalidCredentials:
375 0 : CPLError(eErrClass, CPLE_InvalidCredentials, "%s: %s", pszMsg,
376 : VSIGetLastErrorMsg());
377 0 : break;
378 0 : case VSIE_SignatureDoesNotMatch:
379 0 : CPLError(eErrClass, CPLE_SignatureDoesNotMatch, "%s: %s", pszMsg,
380 : VSIGetLastErrorMsg());
381 0 : break;
382 0 : default:
383 0 : CPLError(eErrClass, CPLE_HttpResponse,
384 : "%s: A filesystem error with code %d occurred", pszMsg,
385 : err);
386 0 : break;
387 : }
388 21 : }
|