/src/libgit2/src/util/errors.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) the libgit2 contributors. All rights reserved. |
3 | | * |
4 | | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | | * a Linking Exception. For full terms see the included COPYING file. |
6 | | */ |
7 | | |
8 | | #include "git2_util.h" |
9 | | |
10 | | #include "errors.h" |
11 | | #include "posix.h" |
12 | | #include "str.h" |
13 | | #include "runtime.h" |
14 | | |
15 | | /* |
16 | | * Some static error data that is used when we're out of memory, TLS |
17 | | * has not been setup, or TLS has failed. |
18 | | */ |
19 | | |
20 | | static git_error oom_error = { |
21 | | "Out of memory", |
22 | | GIT_ERROR_NOMEMORY |
23 | | }; |
24 | | |
25 | | static git_error uninitialized_error = { |
26 | | "library has not been initialized", |
27 | | GIT_ERROR_INVALID |
28 | | }; |
29 | | |
30 | | static git_error tlsdata_error = { |
31 | | "thread-local data initialization failure", |
32 | | GIT_ERROR_THREAD |
33 | | }; |
34 | | |
35 | | static git_error no_error = { |
36 | | "no error", |
37 | | GIT_ERROR_NONE |
38 | | }; |
39 | | |
40 | | #define IS_STATIC_ERROR(err) \ |
41 | 0 | ((err) == &oom_error || (err) == &uninitialized_error || \ |
42 | 0 | (err) == &tlsdata_error || (err) == &no_error) |
43 | | |
44 | | /* Per-thread error state (TLS) */ |
45 | | |
46 | | static git_tlsdata_key tls_key; |
47 | | |
48 | | struct error_threadstate { |
49 | | /* The error message buffer. */ |
50 | | git_str message; |
51 | | |
52 | | /* Error information, set by `git_error_set` and friends. */ |
53 | | git_error error; |
54 | | |
55 | | /* |
56 | | * The last error to occur; points to the error member of this |
57 | | * struct _or_ a static error. |
58 | | */ |
59 | | git_error *last; |
60 | | }; |
61 | | |
62 | | static void threadstate_dispose(struct error_threadstate *threadstate) |
63 | 0 | { |
64 | 0 | if (!threadstate) |
65 | 0 | return; |
66 | | |
67 | 0 | git_str_dispose(&threadstate->message); |
68 | 0 | } |
69 | | |
70 | | static struct error_threadstate *threadstate_get(void) |
71 | 3.87k | { |
72 | 3.87k | struct error_threadstate *threadstate; |
73 | | |
74 | 3.87k | if ((threadstate = git_tlsdata_get(tls_key)) != NULL) |
75 | 3.87k | return threadstate; |
76 | | |
77 | | /* |
78 | | * Avoid git__malloc here, since if it fails, it sets an error |
79 | | * message, which requires thread state, which would allocate |
80 | | * here, which would fail, which would set an error message... |
81 | | */ |
82 | | |
83 | 1 | if ((threadstate = git__allocator.gmalloc( |
84 | 1 | sizeof(struct error_threadstate), |
85 | 1 | __FILE__, __LINE__)) == NULL) |
86 | 0 | return NULL; |
87 | | |
88 | 1 | memset(threadstate, 0, sizeof(struct error_threadstate)); |
89 | | |
90 | 1 | if (git_str_init(&threadstate->message, 0) < 0) { |
91 | 0 | git__allocator.gfree(threadstate); |
92 | 0 | return NULL; |
93 | 0 | } |
94 | | |
95 | 1 | git_tlsdata_set(tls_key, threadstate); |
96 | 1 | return threadstate; |
97 | 1 | } |
98 | | |
99 | | static void GIT_SYSTEM_CALL threadstate_free(void *threadstate) |
100 | 0 | { |
101 | 0 | threadstate_dispose(threadstate); |
102 | 0 | git__free(threadstate); |
103 | 0 | } |
104 | | |
105 | | static void git_error_global_shutdown(void) |
106 | 0 | { |
107 | 0 | struct error_threadstate *threadstate; |
108 | |
|
109 | 0 | threadstate = git_tlsdata_get(tls_key); |
110 | 0 | git_tlsdata_set(tls_key, NULL); |
111 | |
|
112 | 0 | threadstate_dispose(threadstate); |
113 | 0 | git__free(threadstate); |
114 | |
|
115 | 0 | git_tlsdata_dispose(tls_key); |
116 | 0 | } |
117 | | |
118 | | int git_error_global_init(void) |
119 | 2 | { |
120 | 2 | if (git_tlsdata_init(&tls_key, &threadstate_free) != 0) |
121 | 0 | return -1; |
122 | | |
123 | 2 | return git_runtime_shutdown_register(git_error_global_shutdown); |
124 | 2 | } |
125 | | |
126 | | static void set_error_from_buffer(int error_class) |
127 | 1.93k | { |
128 | 1.93k | struct error_threadstate *threadstate = threadstate_get(); |
129 | 1.93k | git_error *error; |
130 | 1.93k | git_str *buf; |
131 | | |
132 | 1.93k | if (!threadstate) |
133 | 0 | return; |
134 | | |
135 | 1.93k | error = &threadstate->error; |
136 | 1.93k | buf = &threadstate->message; |
137 | | |
138 | 1.93k | error->message = buf->ptr; |
139 | 1.93k | error->klass = error_class; |
140 | | |
141 | 1.93k | threadstate->last = error; |
142 | 1.93k | } |
143 | | |
144 | | static void set_error(int error_class, char *string) |
145 | 0 | { |
146 | 0 | struct error_threadstate *threadstate = threadstate_get(); |
147 | 0 | git_str *buf; |
148 | |
|
149 | 0 | if (!threadstate) |
150 | 0 | return; |
151 | | |
152 | 0 | buf = &threadstate->message; |
153 | |
|
154 | 0 | git_str_clear(buf); |
155 | |
|
156 | 0 | if (string) |
157 | 0 | git_str_puts(buf, string); |
158 | |
|
159 | 0 | if (!git_str_oom(buf)) |
160 | 0 | set_error_from_buffer(error_class); |
161 | 0 | } |
162 | | |
163 | | void git_error_set_oom(void) |
164 | 0 | { |
165 | 0 | struct error_threadstate *threadstate = threadstate_get(); |
166 | |
|
167 | 0 | if (!threadstate) |
168 | 0 | return; |
169 | | |
170 | 0 | threadstate->last = &oom_error; |
171 | 0 | } |
172 | | |
173 | | void git_error_set(int error_class, const char *fmt, ...) |
174 | 1.93k | { |
175 | 1.93k | va_list ap; |
176 | | |
177 | 1.93k | va_start(ap, fmt); |
178 | 1.93k | git_error_vset(error_class, fmt, ap); |
179 | 1.93k | va_end(ap); |
180 | 1.93k | } |
181 | | |
182 | | void git_error_vset(int error_class, const char *fmt, va_list ap) |
183 | 1.93k | { |
184 | | #ifdef GIT_WIN32 |
185 | | DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0; |
186 | | #endif |
187 | | |
188 | 1.93k | struct error_threadstate *threadstate = threadstate_get(); |
189 | 1.93k | int error_code = (error_class == GIT_ERROR_OS) ? errno : 0; |
190 | 1.93k | git_str *buf; |
191 | | |
192 | 1.93k | if (!threadstate) |
193 | 0 | return; |
194 | | |
195 | 1.93k | buf = &threadstate->message; |
196 | | |
197 | 1.93k | git_str_clear(buf); |
198 | | |
199 | 1.93k | if (fmt) { |
200 | 1.93k | git_str_vprintf(buf, fmt, ap); |
201 | 1.93k | if (error_class == GIT_ERROR_OS) |
202 | 0 | git_str_PUTS(buf, ": "); |
203 | 1.93k | } |
204 | | |
205 | 1.93k | if (error_class == GIT_ERROR_OS) { |
206 | | #ifdef GIT_WIN32 |
207 | | char *win32_error = git_win32_get_error_message(win32_error_code); |
208 | | if (win32_error) { |
209 | | git_str_puts(buf, win32_error); |
210 | | git__free(win32_error); |
211 | | |
212 | | SetLastError(0); |
213 | | } |
214 | | else |
215 | | #endif |
216 | 0 | if (error_code) |
217 | 0 | git_str_puts(buf, strerror(error_code)); |
218 | |
|
219 | 0 | if (error_code) |
220 | 0 | errno = 0; |
221 | 0 | } |
222 | | |
223 | 1.93k | if (!git_str_oom(buf)) |
224 | 1.93k | set_error_from_buffer(error_class); |
225 | 1.93k | } |
226 | | |
227 | | int git_error_set_str(int error_class, const char *string) |
228 | 0 | { |
229 | 0 | struct error_threadstate *threadstate = threadstate_get(); |
230 | 0 | git_str *buf; |
231 | |
|
232 | 0 | GIT_ASSERT_ARG(string); |
233 | | |
234 | 0 | if (!threadstate) |
235 | 0 | return -1; |
236 | | |
237 | 0 | buf = &threadstate->message; |
238 | |
|
239 | 0 | git_str_clear(buf); |
240 | 0 | git_str_puts(buf, string); |
241 | |
|
242 | 0 | if (git_str_oom(buf)) |
243 | 0 | return -1; |
244 | | |
245 | 0 | set_error_from_buffer(error_class); |
246 | 0 | return 0; |
247 | 0 | } |
248 | | |
249 | | void git_error_clear(void) |
250 | 0 | { |
251 | 0 | struct error_threadstate *threadstate = threadstate_get(); |
252 | |
|
253 | 0 | if (!threadstate) |
254 | 0 | return; |
255 | | |
256 | 0 | if (threadstate->last != NULL) { |
257 | 0 | set_error(0, NULL); |
258 | 0 | threadstate->last = NULL; |
259 | 0 | } |
260 | |
|
261 | 0 | errno = 0; |
262 | | #ifdef GIT_WIN32 |
263 | | SetLastError(0); |
264 | | #endif |
265 | 0 | } |
266 | | |
267 | | bool git_error_exists(void) |
268 | 0 | { |
269 | 0 | struct error_threadstate *threadstate; |
270 | |
|
271 | 0 | if ((threadstate = threadstate_get()) == NULL) |
272 | 0 | return true; |
273 | | |
274 | 0 | return threadstate->last != NULL; |
275 | 0 | } |
276 | | |
277 | | const git_error *git_error_last(void) |
278 | 0 | { |
279 | 0 | struct error_threadstate *threadstate; |
280 | | |
281 | | /* If the library is not initialized, return a static error. */ |
282 | 0 | if (!git_runtime_init_count()) |
283 | 0 | return &uninitialized_error; |
284 | | |
285 | 0 | if ((threadstate = threadstate_get()) == NULL) |
286 | 0 | return &tlsdata_error; |
287 | | |
288 | 0 | if (!threadstate->last) |
289 | 0 | return &no_error; |
290 | | |
291 | 0 | return threadstate->last; |
292 | 0 | } |
293 | | |
294 | | int git_error_save(git_error **out) |
295 | 0 | { |
296 | 0 | struct error_threadstate *threadstate = threadstate_get(); |
297 | 0 | git_error *error, *dup; |
298 | |
|
299 | 0 | if (!threadstate) { |
300 | 0 | *out = &tlsdata_error; |
301 | 0 | return -1; |
302 | 0 | } |
303 | | |
304 | 0 | error = threadstate->last; |
305 | |
|
306 | 0 | if (!error || error == &no_error) { |
307 | 0 | *out = &no_error; |
308 | 0 | return 0; |
309 | 0 | } else if (IS_STATIC_ERROR(error)) { |
310 | 0 | *out = error; |
311 | 0 | return 0; |
312 | 0 | } |
313 | | |
314 | 0 | if ((dup = git__malloc(sizeof(git_error))) == NULL) { |
315 | 0 | *out = &oom_error; |
316 | 0 | return -1; |
317 | 0 | } |
318 | | |
319 | 0 | dup->klass = error->klass; |
320 | 0 | dup->message = git__strdup(error->message); |
321 | |
|
322 | 0 | if (!dup->message) { |
323 | 0 | *out = &oom_error; |
324 | 0 | return -1; |
325 | 0 | } |
326 | | |
327 | 0 | *out = dup; |
328 | 0 | return 0; |
329 | 0 | } |
330 | | |
331 | | int git_error_restore(git_error *error) |
332 | 0 | { |
333 | 0 | struct error_threadstate *threadstate = threadstate_get(); |
334 | |
|
335 | 0 | GIT_ASSERT_ARG(error); |
336 | | |
337 | 0 | if (IS_STATIC_ERROR(error) && threadstate) |
338 | 0 | threadstate->last = error; |
339 | 0 | else |
340 | 0 | set_error(error->klass, error->message); |
341 | |
|
342 | 0 | git_error_free(error); |
343 | 0 | return 0; |
344 | 0 | } |
345 | | |
346 | | void git_error_free(git_error *error) |
347 | 0 | { |
348 | 0 | if (!error) |
349 | 0 | return; |
350 | | |
351 | 0 | if (IS_STATIC_ERROR(error)) |
352 | 0 | return; |
353 | | |
354 | 0 | git__free(error->message); |
355 | 0 | git__free(error); |
356 | 0 | } |
357 | | |
358 | | int git_error_system_last(void) |
359 | 0 | { |
360 | | #ifdef GIT_WIN32 |
361 | | return GetLastError(); |
362 | | #else |
363 | 0 | return errno; |
364 | 0 | #endif |
365 | 0 | } |
366 | | |
367 | | void git_error_system_set(int code) |
368 | 0 | { |
369 | | #ifdef GIT_WIN32 |
370 | | SetLastError(code); |
371 | | #else |
372 | 0 | errno = code; |
373 | 0 | #endif |
374 | 0 | } |
375 | | |
376 | | /* Deprecated error values and functions */ |
377 | | |
378 | | #ifndef GIT_DEPRECATE_HARD |
379 | | |
380 | | #include "git2/deprecated.h" |
381 | | |
382 | | const git_error *giterr_last(void) |
383 | 0 | { |
384 | 0 | return git_error_last(); |
385 | 0 | } |
386 | | |
387 | | void giterr_clear(void) |
388 | 0 | { |
389 | 0 | git_error_clear(); |
390 | 0 | } |
391 | | |
392 | | void giterr_set_str(int error_class, const char *string) |
393 | 0 | { |
394 | 0 | git_error_set_str(error_class, string); |
395 | 0 | } |
396 | | |
397 | | void giterr_set_oom(void) |
398 | 0 | { |
399 | 0 | git_error_set_oom(); |
400 | 0 | } |
401 | | #endif |