Coverage Report

Created: 2026-02-26 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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