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 : }
|