Coverage Report

Created: 2025-07-18 07:08

/src/sleuthkit/tsk/base/tsk_error.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The Sleuth Kit
3
 *
4
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
5
 * Copyright (c) 2006-2011 Brian Carrier.  All Rights reserved
6
 *
7
 * This software is distributed under the Common Public License 1.0
8
 */
9
10
#include "tsk_base_i.h"
11
#include "tsk_base.h"
12
13
/**
14
 * \file tsk_error.c
15
 * Contains the error handling code and variables.
16
 */
17
18
19
/* Global variables that fit here as well as anywhere */
20
char *progname = "unknown";
21
int tsk_verbose = 0;
22
23
/* Optional error listener */
24
TSK_ERROR_LISTENER_CB error_listener = NULL;
25
26
/* Error messages */
27
static const char *tsk_err_aux_str[TSK_ERR_IMG_MAX] = {
28
    "Insufficient memory",
29
    "TSK Error"
30
};
31
32
/* imagetools specific error strings */
33
static const char *tsk_err_img_str[TSK_ERR_IMG_MAX] = {
34
    "Missing image file names", // 0
35
    "Invalid image offset",
36
    "Cannot determine image type",
37
    "Unsupported image type",
38
    "Error opening image file",
39
    "Error stat(ing) image file",       // 5
40
    "Error seeking in image file",
41
    "Error reading image file",
42
    "Read offset too large for image file",
43
    "Invalid API argument",
44
    "Invalid magic value",      // 10
45
    "Error writing data",
46
    "Error converting file name",
47
    "Incorrect or missing password"
48
};
49
50
51
static const char *tsk_err_mm_str[TSK_ERR_VS_MAX] = {
52
    "Cannot determine partition type",  // 0
53
    "Unsupported partition type",
54
    "Error reading image file",
55
    "Invalid magic value",
56
    "Invalid walk range",
57
    "Invalid buffer size",      // 5
58
    "Invalid sector address",
59
    "Invalid API argument",
60
    "Encryption detected",
61
    "Multiple volume system types detected",
62
};
63
64
static const char *tsk_err_fs_str[TSK_ERR_FS_MAX] = {
65
    "Cannot determine file system type",        // 0
66
    "Unsupported file system type",
67
    "Function/Feature not supported",
68
    "Invalid walk range",
69
    "Error reading image file",
70
    "Invalid file offset",      // 5
71
    "Invalid API argument",
72
    "Invalid block address",
73
    "Invalid metadata address",
74
    "Error in metadata structure",
75
    "Invalid magic value",      // 10
76
    "Error extracting file from image",
77
    "Error writing data",
78
    "Error converting Unicode",
79
    "Error recovering deleted file",
80
    "General file system error",        // 15
81
    "File system is corrupt",
82
    "Attribute not found in file",
83
    "Encryption detected",
84
    "Possible encryption detected",
85
    "Multiple file system types detected",   // 20
86
    "BitLocker initialization failed",
87
    "Error loading large directory",
88
};
89
90
static const char *tsk_err_hdb_str[TSK_ERR_HDB_MAX] = {
91
    "Cannot determine hash database type",      // 0
92
    "Unsupported hash database type",
93
    "Error reading hash database file",
94
    "Error reading hash database index",
95
    "Invalid argument",
96
    "Error writing data",       // 5
97
    "Error creating file",
98
    "Error deleting file",
99
    "Missing file",
100
    "Error creating process",
101
    "Error opening file",       // 10
102
    "Corrupt hash database"
103
};
104
105
static const char *tsk_err_auto_str[TSK_ERR_AUTO_MAX] = {
106
    "Database Error",
107
    "Corrupt file data",
108
    "Error converting Unicode",
109
    "Image not opened yet"
110
};
111
112
static const char *tsk_err_pool_str[TSK_ERR_POOL_MAX] = {
113
    "Cannot determine pool container type",
114
    "Unsupported pool container type",
115
    "Invalid API argument",
116
    "General pool error"
117
};
118
119
120
#ifdef TSK_MULTITHREAD_LIB
121
122
#ifdef TSK_WIN32
123
TSK_ERROR_INFO *
124
tsk_error_get_info()
125
{
126
    return (TSK_ERROR_INFO *)
127
        tsk_error_win32_get_per_thread_(sizeof(TSK_ERROR_INFO));
128
}
129
130
    // non-windows
131
#else
132
static pthread_key_t pt_tls_key;
133
static pthread_once_t pt_tls_key_once = PTHREAD_ONCE_INIT;
134
135
static void
136
free_error_info(void *per_thread_error_info)
137
0
{
138
0
    if (per_thread_error_info != 0) {
139
0
        free(per_thread_error_info);
140
0
        pthread_setspecific(pt_tls_key, 0);
141
0
    }
142
0
}
143
144
static void
145
make_pt_tls_key()
146
11
{
147
11
    (void) pthread_key_create(&pt_tls_key, free_error_info);
148
11
}
149
150
TSK_ERROR_INFO *
151
tsk_error_get_info()
152
18.0M
{
153
18.0M
    TSK_ERROR_INFO *ptr = NULL;
154
18.0M
    (void) pthread_once(&pt_tls_key_once, make_pt_tls_key);
155
18.0M
    if ((ptr = (TSK_ERROR_INFO *) pthread_getspecific(pt_tls_key)) == 0) {
156
        // Under high memory pressure malloc will return NULL.
157
11
        ptr = (TSK_ERROR_INFO *) malloc(sizeof(TSK_ERROR_INFO));
158
159
11
        if( ptr != NULL ) {
160
11
            ptr->t_errno = 0;
161
11
            ptr->errstr[0] = 0;
162
11
            ptr->errstr2[0] = 0;
163
11
        }
164
11
        (void) pthread_setspecific(pt_tls_key, ptr);
165
11
    }
166
18.0M
    return ptr;
167
18.0M
}
168
#endif
169
170
// single-threaded
171
#else
172
173
static TSK_ERROR_INFO error_info = { 0, {0}, {0} };
174
175
TSK_ERROR_INFO *
176
tsk_error_get_info()
177
{
178
    return &error_info;
179
}
180
181
#endif
182
183
/**
184
 * \ingroup baselib
185
 * Return the string with the current error message.  The string does not end with a
186
 * newline.
187
 *
188
 * @returns String with error message or NULL if there is no error
189
 */
190
const char *
191
tsk_error_get()
192
0
{
193
0
    size_t pidx = 0;
194
0
    TSK_ERROR_INFO *error_info = tsk_error_get_info();
195
0
    int t_errno = error_info->t_errno;
196
0
    char *errstr_print = error_info->errstr_print;
197
198
0
    if (t_errno == 0) {
199
0
        return NULL;
200
0
    }
201
202
0
    memset(errstr_print, 0, TSK_ERROR_STRING_MAX_LENGTH);
203
0
    if (t_errno & TSK_ERR_AUX) {
204
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_AUX_MAX)
205
0
            snprintf(&errstr_print[pidx],
206
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
207
0
                tsk_err_aux_str[t_errno & TSK_ERR_MASK]);
208
0
        else
209
0
            snprintf(&errstr_print[pidx],
210
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx,
211
0
                "auxtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
212
0
    }
213
0
    else if (t_errno & TSK_ERR_IMG) {
214
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_IMG_MAX)
215
0
            snprintf(&errstr_print[pidx],
216
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
217
0
                tsk_err_img_str[t_errno & TSK_ERR_MASK]);
218
0
        else
219
0
            snprintf(&errstr_print[pidx],
220
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx,
221
0
                "imgtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
222
0
    }
223
0
    else if (t_errno & TSK_ERR_VS) {
224
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_VS_MAX)
225
0
            snprintf(&errstr_print[pidx],
226
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
227
0
                tsk_err_mm_str[t_errno & TSK_ERR_MASK]);
228
0
        else
229
0
            snprintf(&errstr_print[pidx],
230
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx,
231
0
                "mmtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
232
0
    }
233
0
    else if (t_errno & TSK_ERR_FS) {
234
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_FS_MAX)
235
0
            snprintf(&errstr_print[pidx],
236
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
237
0
                tsk_err_fs_str[t_errno & TSK_ERR_MASK]);
238
0
        else
239
0
            snprintf(&errstr_print[pidx],
240
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx,
241
0
                "fstools error: %" PRIu32, TSK_ERR_MASK & t_errno);
242
0
    }
243
0
    else if (t_errno & TSK_ERR_HDB) {
244
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_HDB_MAX)
245
0
            snprintf(&errstr_print[pidx],
246
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
247
0
                tsk_err_hdb_str[t_errno & TSK_ERR_MASK]);
248
0
        else
249
0
            snprintf(&errstr_print[pidx],
250
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx,
251
0
                "hashtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
252
0
    }
253
0
    else if (t_errno & TSK_ERR_AUTO) {
254
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_AUTO_MAX)
255
0
            snprintf(&errstr_print[pidx],
256
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
257
0
                tsk_err_auto_str[t_errno & TSK_ERR_MASK]);
258
0
        else
259
0
            snprintf(&errstr_print[pidx],
260
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "auto error: %" PRIu32,
261
0
                TSK_ERR_MASK & t_errno);
262
0
    }
263
0
    else if (t_errno & TSK_ERR_POOL) {
264
0
        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_POOL_MAX)
265
0
            snprintf(&errstr_print[pidx],
266
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "%s",
267
0
                tsk_err_pool_str[t_errno & TSK_ERR_MASK]);
268
0
        else
269
0
            snprintf(&errstr_print[pidx],
270
0
                TSK_ERROR_STRING_MAX_LENGTH - pidx, "pool error: %" PRIu32,
271
0
                TSK_ERR_MASK & t_errno);
272
0
    }
273
0
    else {
274
0
        snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
275
0
            "Unknown Error: %" PRIu32, t_errno);
276
0
    }
277
0
    pidx = strlen(errstr_print);
278
279
    /* Print the unique string, if it exists */
280
0
    if (error_info->errstr[0] != '\0') {
281
0
        snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
282
0
            " (%s)", error_info->errstr);
283
0
        pidx = strlen(errstr_print);
284
0
    }
285
286
0
    if (error_info->errstr2[0] != '\0') {
287
0
        snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
288
0
            " (%s)", error_info->errstr2);
289
0
        pidx = strlen(errstr_print);
290
0
    }
291
0
    return (char *) error_info->errstr_print;
292
0
}
293
294
/**
295
 * \ingroup baselib
296
 * Return the current error number.
297
 * @returns the current error number.
298
 */
299
uint32_t
300
tsk_error_get_errno()
301
158k
{
302
158k
    return tsk_error_get_info()->t_errno;
303
158k
}
304
305
/**
306
 * \ingroup baselib
307
 * Set the current TSK error number.
308
 * @param t_errno the error number.
309
 */
310
void
311
tsk_error_set_errno(uint32_t t_errno)
312
4.32M
{
313
4.32M
    tsk_error_get_info()->t_errno = t_errno;
314
4.32M
}
315
316
/**
317
 * \ingroup baselib
318
 * Retrieve the current, basic error string.
319
 * Additional information is in errstr2.
320
 * Use tsk_error_get() to get a fully formatted string.
321
 * @returns the string. This is only valid until the next call to a tsk function.
322
 */
323
char *
324
tsk_error_get_errstr()
325
0
{
326
0
    return tsk_error_get_info()->errstr;
327
0
}
328
329
/**
330
 * \ingroup baselib
331
 * Set the error string #1. This should contain the basic message.
332
 * @param format the printf-style format string
333
 */
334
void
335
tsk_error_set_errstr(const char *format, ...)
336
4.12M
{
337
4.12M
    va_list args;
338
4.12M
    va_start(args, format);
339
4.12M
    vsnprintf(tsk_error_get_info()->errstr, TSK_ERROR_STRING_MAX_LENGTH,
340
4.12M
        format, args);
341
4.12M
    va_end(args);
342
4.12M
    if (error_listener != NULL) {
343
0
        error_listener((uint32_t)tsk_error_get_info()->t_errno, tsk_error_get_info()->errstr);
344
0
    }
345
4.12M
}
346
347
/**
348
 * \ingroup baselib
349
 * Set the error string
350
 * @param format the printf-style format string
351
 * @param args the printf-style args
352
 */
353
void
354
tsk_error_vset_errstr(const char *format, va_list args)
355
349
{
356
349
    vsnprintf(tsk_error_get_info()->errstr, TSK_ERROR_STRING_MAX_LENGTH,
357
349
        format, args);
358
349
    if (error_listener != NULL) {
359
0
        error_listener((uint32_t)tsk_error_get_info()->t_errno, tsk_error_get_info()->errstr);
360
0
    }
361
349
}
362
363
/**
364
 * \ingroup baselib
365
 * Retrieve the current error string #2.
366
 * This has additional information than string #1.
367
 * @returns the string. This is only valid until the next call to a tsk function.
368
 */
369
char *
370
tsk_error_get_errstr2()
371
0
{
372
0
    return tsk_error_get_info()->errstr2;
373
0
}
374
375
/**
376
 * \ingroup baselib
377
 * Set the error string #2. This is called by methods who encounter the error,
378
 * but did not set errno.
379
 * @param format the printf-style format string
380
 */
381
void
382
tsk_error_set_errstr2(const char *format, ...)
383
714k
{
384
714k
    va_list args;
385
714k
    va_start(args, format);
386
714k
    vsnprintf(tsk_error_get_info()->errstr2, TSK_ERROR_STRING_MAX_LENGTH,
387
714k
        format, args);
388
714k
    va_end(args);
389
714k
}
390
391
/**
392
 * \ingroup baselib
393
 * Set the error string
394
 * @param format the printf-style format string
395
 * @param args the printf-style format args
396
 */
397
void
398
tsk_error_vset_errstr2(const char *format, va_list args)
399
0
{
400
0
    vsnprintf(tsk_error_get_info()->errstr2, TSK_ERROR_STRING_MAX_LENGTH,
401
0
        format, args);
402
0
}
403
404
/**
405
 * \ingroup baselib
406
 * Concatenate a message onto the end of the errstr2.
407
 * @param format
408
 */
409
void
410
tsk_error_errstr2_concat(const char *format, ...)
411
20.6k
{
412
20.6k
    char *errstr2 = tsk_error_get_info()->errstr2;
413
20.6k
    int current_length = (int) (strlen(errstr2) + 1);   // +1 for a space
414
20.6k
    if (current_length > 0) {
415
20.6k
        va_list args;
416
20.6k
        int remaining = TSK_ERROR_STRING_MAX_LENGTH - current_length;
417
20.6k
        errstr2[current_length - 1] = ' ';
418
20.6k
        va_start(args, format);
419
20.6k
        vsnprintf(&errstr2[current_length], remaining, format, args);
420
20.6k
        va_end(args);
421
20.6k
    }
422
20.6k
}
423
424
/**
425
* Add a method that will be sent most errors (in additional to the processing TSK already does).
426
* 
427
* This is a bit limited since adding an error is a multistep process. The listener is invoked when
428
* tsk_error_set_errstr() is called. Our convention is that tsk_error_set_errno() is called first so
429
* the errno should be accurate. We would miss anything set to errstr2 but this is not very common.
430
* 
431
* @param listener   Method that should take arguments (uint32_t, const char*)
432
*/
433
void
434
0
tsk_error_set_error_listener(TSK_ERROR_LISTENER_CB listener) {
435
0
    error_listener = listener;
436
0
}
437
438
/**
439
 * \ingroup baselib
440
 * Print the current fully formed error message to a file.
441
 *
442
 * @param hFile File to print message to
443
 */
444
void
445
tsk_error_print(FILE * hFile)
446
0
{
447
0
    const char *str;
448
0
    if (tsk_error_get_errno() == 0)
449
0
        return;
450
451
0
    str = tsk_error_get();
452
0
    if (str != NULL) {
453
0
        tsk_fprintf(hFile, "%s\n", str);
454
0
    }
455
0
    else {
456
0
        tsk_fprintf(hFile,
457
0
            "Error creating Sleuth Kit error string (Errno: %d)\n",
458
0
            tsk_error_get_errno());
459
0
    }
460
0
}
461
462
/**
463
 * \ingroup baselib
464
 * Clear the error number and error message.
465
 */
466
void
467
tsk_error_reset()
468
8.70M
{
469
8.70M
    TSK_ERROR_INFO *info = tsk_error_get_info();
470
471
8.70M
    if( info != NULL ) {
472
8.70M
       info->t_errno = 0;
473
8.70M
       info->errstr[0] = 0;
474
8.70M
       info->errstr2[0] = 0;
475
8.70M
       info->errstr_print[0] = 0;
476
8.70M
    }
477
8.70M
}