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 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "cpl_vsi_error.h"
32 :
33 : #include <cstdarg>
34 : #include <cstdio>
35 :
36 : #include "cpl_config.h"
37 : #include "cpl_conv.h"
38 : #include "cpl_error.h"
39 : #include "cpl_multiproc.h"
40 : #include "cpl_string.h"
41 : #include "cpl_vsi.h"
42 :
43 : #if !defined(va_copy) && defined(__va_copy)
44 : #define va_copy __va_copy
45 : #endif
46 :
47 : // TODO(rouault): Why is this here?
48 : #if !defined(_WIN32)
49 : #include <string.h>
50 : #endif
51 :
52 : #define TIMESTAMP_DEBUG
53 : // #define MEMORY_DEBUG
54 :
55 : constexpr int DEFAULT_LAST_ERR_MSG_SIZE =
56 : #if !defined(HAVE_VSNPRINTF)
57 : 20000
58 : #else
59 : 500
60 : #endif
61 : ;
62 :
63 : typedef struct
64 : {
65 : VSIErrorNum nLastErrNo;
66 : int nLastErrMsgMax;
67 : char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE];
68 : // Do not add anything here. szLastErrMsg must be the last field. See
69 : // CPLRealloc() below.
70 : } VSIErrorContext;
71 :
72 : /************************************************************************/
73 : /* CPLGetErrorContext() */
74 : /************************************************************************/
75 :
76 133492 : static VSIErrorContext *VSIGetErrorContext()
77 :
78 : {
79 133492 : int bError = FALSE;
80 : VSIErrorContext *psCtx = reinterpret_cast<VSIErrorContext *>(
81 133492 : CPLGetTLSEx(CTLS_VSIERRORCONTEXT, &bError));
82 133473 : if (bError)
83 0 : return nullptr;
84 :
85 133473 : if (psCtx == nullptr)
86 : {
87 : psCtx = static_cast<VSIErrorContext *>(
88 1098 : VSICalloc(sizeof(VSIErrorContext), 1));
89 1098 : if (psCtx == nullptr)
90 : {
91 0 : fprintf(stderr, /*ok*/
92 : "Out of memory attempting to record a VSI error.\n");
93 0 : return nullptr;
94 : }
95 1098 : psCtx->nLastErrNo = VSIE_None;
96 1098 : psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg);
97 1098 : CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
98 : }
99 :
100 133462 : return psCtx;
101 : }
102 :
103 : /************************************************************************/
104 : /* VSIErrorV() */
105 : /************************************************************************/
106 :
107 18612 : static void VSIErrorV(VSIErrorNum err_no, const char *fmt, va_list args)
108 : {
109 18612 : VSIErrorContext *psCtx = VSIGetErrorContext();
110 18612 : if (psCtx == nullptr)
111 0 : return;
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Expand the error message */
115 : /* -------------------------------------------------------------------- */
116 : #if defined(HAVE_VSNPRINTF)
117 : {
118 : va_list wrk_args;
119 :
120 : #ifdef va_copy
121 18612 : va_copy(wrk_args, args);
122 : #else
123 : wrk_args = args;
124 : #endif
125 :
126 18612 : int nPreviousSize = 0;
127 18612 : int nPR = 0;
128 53 : while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize,
129 18665 : psCtx->nLastErrMsgMax - nPreviousSize, fmt,
130 18665 : wrk_args)) == -1 ||
131 18718 : nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) &&
132 53 : psCtx->nLastErrMsgMax < 1000000)
133 : {
134 : #ifdef va_copy
135 53 : va_end(wrk_args);
136 53 : va_copy(wrk_args, args);
137 : #else
138 : wrk_args = args;
139 : #endif
140 53 : psCtx->nLastErrMsgMax *= 3;
141 106 : psCtx = static_cast<VSIErrorContext *>(CPLRealloc(
142 : psCtx, sizeof(VSIErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE +
143 53 : psCtx->nLastErrMsgMax + 1));
144 53 : CPLSetTLS(CTLS_VSIERRORCONTEXT, psCtx, TRUE);
145 : }
146 :
147 18612 : va_end(wrk_args);
148 : }
149 : #else // !HAVE_VSNPRINTF
150 : CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args);
151 : #endif
152 :
153 18612 : psCtx->nLastErrNo = err_no;
154 : }
155 :
156 : /**********************************************************************
157 : * VSIError()
158 : **********************************************************************/
159 :
160 : /**
161 : * Report an VSI filesystem error.
162 : *
163 : * This function records an error in the filesystem that may or may not be
164 : * used in the future, for example converted into a CPLError. This allows
165 : * filesystem errors to be available to error handling functionality, but
166 : * reported only when necessary.
167 : *
168 : * @param err_no the error number (VSIE_*) from cpl_vsi_error.h.
169 : * @param fmt a printf() style format string. Any additional arguments
170 : * will be treated as arguments to fill in this format in a manner
171 : * similar to printf().
172 : */
173 :
174 18612 : void VSIError(VSIErrorNum err_no, CPL_FORMAT_STRING(const char *fmt), ...)
175 : {
176 : va_list args;
177 :
178 : // Expand the error message.
179 18612 : va_start(args, fmt);
180 18612 : VSIErrorV(err_no, fmt, args);
181 18612 : va_end(args);
182 18612 : }
183 :
184 : /**********************************************************************
185 : * VSIErrorReset()
186 : **********************************************************************/
187 :
188 : /**
189 : * Erase any traces of previous errors.
190 : *
191 : * This is used to clear out the latest file system error when it is either
192 : * translated into a CPLError call or when it is determined to be ignorable.
193 : */
194 :
195 103637 : void CPL_STDCALL VSIErrorReset()
196 : {
197 103637 : VSIErrorContext *psCtx = VSIGetErrorContext();
198 103635 : if (psCtx == nullptr)
199 0 : return;
200 :
201 103635 : psCtx->nLastErrNo = VSIE_None;
202 103635 : psCtx->szLastErrMsg[0] = '\0';
203 : }
204 :
205 : /**********************************************************************
206 : * VSIGetLastErrorNo()
207 : **********************************************************************/
208 :
209 : /**
210 : * Fetch the last error number.
211 : *
212 : * Fetches the last error number posted with VSIError(), that hasn't
213 : * been cleared by VSIErrorReset(). This is the error number, not the error
214 : * class.
215 : *
216 : * @return the error number of the last error to occur, or VSIE_None (0)
217 : * if there are no posted errors.
218 : */
219 :
220 5686 : VSIErrorNum CPL_STDCALL VSIGetLastErrorNo()
221 : {
222 5686 : VSIErrorContext *psCtx = VSIGetErrorContext();
223 5658 : if (psCtx == nullptr)
224 0 : return 0;
225 :
226 5658 : return psCtx->nLastErrNo;
227 : }
228 :
229 : /**********************************************************************
230 : * VSIGetLastErrorMsg()
231 : **********************************************************************/
232 :
233 : /**
234 : * Get the last error message.
235 : *
236 : * Fetches the last error message posted with VSIError(), that hasn't
237 : * been cleared by VSIErrorReset(). The returned pointer is to an internal
238 : * string that should not be altered or freed.
239 : *
240 : * @return the last error message, or NULL if there is no posted error
241 : * message.
242 : */
243 :
244 5575 : const char *CPL_STDCALL VSIGetLastErrorMsg()
245 : {
246 5575 : VSIErrorContext *psCtx = VSIGetErrorContext();
247 5555 : if (psCtx == nullptr)
248 0 : return "";
249 :
250 5555 : return psCtx->szLastErrMsg;
251 : }
252 :
253 : /**********************************************************************
254 : * VSItoCPLError()
255 : **********************************************************************/
256 :
257 : /**
258 : * Translate the VSI error into a CPLError call
259 : *
260 : * If there is a VSIError that is set, translate it to a CPLError call
261 : * with the given CPLErr error class, and either an appropriate CPLErrorNum
262 : * given the VSIErrorNum, or the given default CPLErrorNum.
263 : *
264 : * @return TRUE if a CPLError was issued, or FALSE if not.
265 : */
266 :
267 5666 : int CPL_DLL CPL_STDCALL VSIToCPLError(CPLErr eErrClass,
268 : CPLErrorNum eDefaultErrorNo)
269 : {
270 5666 : const int err = VSIGetLastErrorNo();
271 5652 : switch (err)
272 : {
273 146 : case VSIE_None:
274 146 : return FALSE;
275 5503 : case VSIE_FileError:
276 5503 : CPLError(eErrClass, eDefaultErrorNo, "%s", VSIGetLastErrorMsg());
277 5522 : break;
278 4 : case VSIE_HttpError:
279 4 : CPLError(eErrClass, CPLE_HttpResponse, "%s", VSIGetLastErrorMsg());
280 4 : break;
281 0 : case VSIE_AWSError:
282 0 : CPLError(eErrClass, CPLE_AWSError, "%s", VSIGetLastErrorMsg());
283 0 : break;
284 0 : case VSIE_AWSAccessDenied:
285 0 : CPLError(eErrClass, CPLE_AWSAccessDenied, "%s",
286 : VSIGetLastErrorMsg());
287 0 : break;
288 0 : case VSIE_AWSBucketNotFound:
289 0 : CPLError(eErrClass, CPLE_AWSBucketNotFound, "%s",
290 : VSIGetLastErrorMsg());
291 0 : break;
292 0 : case VSIE_AWSObjectNotFound:
293 0 : CPLError(eErrClass, CPLE_AWSObjectNotFound, "%s",
294 : VSIGetLastErrorMsg());
295 0 : break;
296 0 : case VSIE_AWSInvalidCredentials:
297 0 : CPLError(eErrClass, CPLE_AWSInvalidCredentials, "%s",
298 : VSIGetLastErrorMsg());
299 0 : break;
300 0 : case VSIE_AWSSignatureDoesNotMatch:
301 0 : CPLError(eErrClass, CPLE_AWSSignatureDoesNotMatch, "%s",
302 : VSIGetLastErrorMsg());
303 0 : break;
304 0 : default:
305 0 : CPLError(eErrClass, CPLE_HttpResponse,
306 : "A filesystem error with code %d occurred", err);
307 0 : break;
308 : }
309 :
310 5526 : return TRUE;
311 : }
|