Coverage Report

Created: 2026-02-14 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/fs/unix_misc.cpp
Line
Count
Source
1
/*
2
 * The Sleuth Kit
3
 *
4
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
5
 * Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
6
 *
7
 * This software is distributed under the Common Public License 1.0
8
 */
9
10
11
/**
12
 * \file unix_misc.c
13
 * Contains code that is common to both UFS1/2 and Ext2/3 file systems.
14
 */
15
16
#include "tsk_fs_i.h"
17
#include "tsk_ffs.h"
18
#include "tsk_ext2fs.h"
19
20
21
/*********** MAKE DATA RUNS ***************/
22
23
/** \internal
24
 * Process an array of addresses and turn them into runs
25
 *
26
 * @param fs File system to analyze
27
 * @param fs_attr Data attribute to add runs to
28
 * @param addrs Buffer of addresses to process and turn into runs
29
 * @param addr_len Number of addresses in buffer
30
 * @param length Length of file remaining
31
 *
32
 * @returns the number of bytes processed and -1 if an error occurred
33
 */
34
static TSK_OFF_T
35
unix_make_data_run_direct(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr,
36
    TSK_DADDR_T * addrs, size_t addr_len, TSK_OFF_T length)
37
900k
{
38
900k
    TSK_DADDR_T run_start = 0;
39
900k
    TSK_DADDR_T run_len = 0;
40
900k
    TSK_DADDR_T blks_processed = 0;
41
900k
    size_t i;
42
900k
    size_t fs_blen;             // how big is each "block" (in fragments)
43
44
900k
    if (addr_len == 0) {
45
0
        return 0;
46
0
    }
47
48
    // block_size is a fragment size in UFS, so we need to maintain length in fragments
49
900k
    if (TSK_FS_TYPE_ISFFS(fs->ftype)) {
50
0
        FFS_INFO *ffs = (FFS_INFO *) fs;
51
0
        fs_blen = ffs->ffsbsize_f;
52
0
    }
53
900k
    else {
54
900k
        fs_blen = 1;
55
900k
    }
56
57
900k
    run_start = addrs[0];
58
900k
    run_len = fs_blen;
59
60
    /* Note that we are lazy about length.  We stop only when a run is past length,
61
     * we do not end exactly at length -- although that should happen anyway.
62
     */
63
180M
    for (i = 0; i < addr_len; i++) {
64
65
        /* Make a new run if:
66
         * - This is the last addresss in the buffer
67
         * - The next address is not part of the current run
68
         * -- special case for sparse since they use 0 as an address
69
         */
70
179M
        if ((i + 1 == addr_len) ||
71
179M
            ((run_start + run_len != addrs[i + 1]) && (run_start != 0)) ||
72
178M
            ((run_start == 0) && (addrs[i + 1] != 0))) {
73
74
1.86M
            TSK_FS_ATTR_RUN *data_run;
75
76
            // make a non-resident run
77
1.86M
            data_run = tsk_fs_attr_run_alloc();
78
1.86M
            if (data_run == NULL)
79
0
                return -1;
80
81
1.86M
            data_run->addr = run_start;
82
1.86M
            data_run->len = run_len;
83
84
1.86M
            if (run_start == 0)
85
1.17M
                data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
86
87
            // save the run
88
1.86M
            tsk_fs_attr_append_run(fs, fs_attr, data_run);
89
90
            // get ready for the next run
91
1.86M
            if (i + 1 != addr_len)
92
1.16M
                run_start = addrs[i + 1];
93
1.86M
            run_len = 0;
94
95
            // stop if we are past the length requested
96
1.86M
            if (blks_processed * fs->block_size > (TSK_DADDR_T) length)
97
214k
                break;
98
1.86M
        }
99
179M
        run_len += fs_blen;
100
179M
        blks_processed += fs_blen;
101
179M
    }
102
103
900k
    return blks_processed * fs->block_size;
104
900k
}
105
106
107
/** \internal
108
 * Read an indirect block and process the contents to make a runlist from the pointers.
109
 *
110
 * @param fs File system to analyze
111
 * @param fs_attr Structure to save run data into
112
 * @param fs_attr_indir Structure to save addresses of indirect block pointers in
113
 * @param buf Buffers to read block data into (0 is block sized, 1+ are DADDR_T arrays based on FS type)
114
 * @param level Indirection level that this will process at (1+)
115
 * @param addr Address of block to read
116
 * @param length Length of file remaining
117
 *
118
 * @returns the number of bytes processed during call and -1 if an error occurred
119
 */
120
static TSK_OFF_T
121
unix_make_data_run_indirect(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr,
122
    TSK_FS_ATTR * fs_attr_indir, char *buf[], int level, TSK_DADDR_T addr,
123
    TSK_OFF_T length)
124
689k
{
125
689k
    size_t addr_cnt = 0;
126
689k
    TSK_DADDR_T *myaddrs = (TSK_DADDR_T *) buf[level];
127
689k
    TSK_OFF_T length_remain = length;
128
689k
    TSK_OFF_T retval;
129
689k
    size_t fs_bufsize;
130
689k
    size_t fs_blen;
131
689k
    TSK_FS_ATTR_RUN *data_run;
132
133
689k
    if (tsk_verbose)
134
0
        tsk_fprintf(stderr, "%s: level %d block %" PRIuDADDR "\n", "unix_make_data_run_indirect",
135
0
            level, addr);
136
137
    // block_size is a fragment size in UFS, so we need to maintain length in fragments
138
689k
    if (TSK_FS_TYPE_ISFFS(fs->ftype)) {
139
0
        FFS_INFO *ffs = (FFS_INFO *) fs;
140
0
        fs_blen = ffs->ffsbsize_f;
141
0
        fs_bufsize = ffs->ffsbsize_b;
142
0
    }
143
689k
    else {
144
689k
        fs_blen = 1;
145
689k
        fs_bufsize = fs->block_size;
146
689k
    }
147
148
689k
    if (addr > fs->last_block) {
149
7.40k
        tsk_error_reset();
150
7.40k
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
151
7.40k
        tsk_error_set_errstr("unix: Indirect block address too large: %"
152
7.40k
            PRIuDADDR "", addr);
153
7.40k
        return -1;
154
7.40k
    }
155
156
    // make a non-resident run
157
682k
    data_run = tsk_fs_attr_run_alloc();
158
682k
    if (data_run == NULL)
159
0
        return -1;
160
161
682k
    data_run->addr = addr;
162
682k
    data_run->len = fs_blen;
163
164
    /*
165
     * Read a block of disk addresses.
166
     */
167
    // sparse
168
682k
    if (addr == 0) {
169
678k
        memset(buf[0], 0, fs_bufsize);
170
678k
        data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
171
678k
    }
172
3.46k
    else {
173
3.46k
        ssize_t cnt;
174
        // read the data into the scratch buffer
175
3.46k
        cnt = tsk_fs_read_block(fs, addr, buf[0], fs_bufsize);
176
3.46k
        if (cnt != (ssize_t)fs_bufsize) {
177
3.02k
            if (cnt >= 0) {
178
0
                tsk_error_reset();
179
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
180
0
            }
181
3.02k
            tsk_error_set_errstr2("unix_make_data_run_indir: Block %"
182
3.02k
                PRIuDADDR, addr);
183
3.02k
            free(data_run);
184
3.02k
            return -1;
185
3.02k
        }
186
3.46k
    }
187
188
    // save the run
189
679k
    tsk_fs_attr_append_run(fs, fs_attr_indir, data_run);
190
191
679k
    data_run = NULL;
192
193
    // convert the raw addresses to the correct endian ordering
194
679k
    if ((fs->ftype == TSK_FS_TYPE_FFS1)
195
679k
        || (fs->ftype == TSK_FS_TYPE_FFS1B)
196
679k
        || (TSK_FS_TYPE_ISEXT(fs->ftype))) {
197
679k
        size_t n;
198
679k
        uint32_t *iaddr = (uint32_t *) buf[0];
199
679k
        addr_cnt = fs_bufsize / sizeof(*iaddr);
200
178M
        for (n = 0; n < addr_cnt; n++) {
201
178M
            myaddrs[n] = tsk_getu32(fs->endian, (uint8_t *) & iaddr[n]);
202
178M
        }
203
679k
    }
204
0
    else if (fs->ftype == TSK_FS_TYPE_FFS2) {
205
0
        size_t n;
206
0
        uint64_t *iaddr = (uint64_t *) buf[0];
207
0
        addr_cnt = fs_bufsize / sizeof(*iaddr);
208
0
        for (n = 0; n < addr_cnt; n++) {
209
0
            myaddrs[n] = tsk_getu64(fs->endian, (uint8_t *) & iaddr[n]);
210
0
        }
211
0
    }
212
213
    // pass the addresses to the next level
214
679k
    if (level == 1) {
215
676k
        retval =
216
676k
            unix_make_data_run_direct(fs, fs_attr, myaddrs, addr_cnt,
217
676k
            length_remain);
218
676k
        if (retval != -1) {
219
676k
            length_remain -= retval;
220
676k
        }
221
676k
    }
222
2.95k
    else {
223
2.95k
        size_t i;
224
2.95k
        retval = 0;
225
671k
        for (i = 0; i < addr_cnt && retval != -1; i++) {
226
668k
            retval =
227
668k
                unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir,
228
668k
                buf, level - 1, myaddrs[i], length_remain);
229
668k
            if (retval == -1) {
230
377
                break;
231
377
            }
232
668k
            else {
233
668k
                length_remain -= retval;
234
668k
            }
235
668k
        }
236
2.95k
    }
237
238
679k
    if (retval == -1) {
239
377
        return -1;
240
377
    }
241
678k
    return length - length_remain;
242
679k
}
243
244
245
/** \internal
246
 *
247
 * @returns 1 on error and 0 on success
248
 */
249
uint8_t
250
tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file)
251
224k
{
252
224k
    TSK_OFF_T length = 0;
253
224k
    TSK_OFF_T read_b = 0;
254
224k
    TSK_FS_ATTR *fs_attr;
255
224k
    TSK_FS_META *fs_meta = fs_file->meta;
256
224k
    TSK_FS_INFO *fs = fs_file->fs_info;
257
258
    // clean up any error messages that are lying around
259
224k
    tsk_error_reset();
260
261
224k
    if (tsk_verbose)
262
0
        tsk_fprintf(stderr,
263
0
            "unix_make_data_run: Processing file %" PRIuINUM "\n",
264
0
            fs_meta->addr);
265
266
    // see if we have already loaded the runs
267
224k
    if ((fs_meta->attr != NULL)
268
0
        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
269
0
        return 0;
270
0
    }
271
224k
    if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) {
272
0
        return 1;
273
0
    }
274
275
    // not sure why this would ever happen, but...
276
224k
    if (fs_meta->attr != NULL) {
277
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
278
0
    }
279
224k
    else {
280
224k
        fs_meta->attr = tsk_fs_attrlist_alloc();
281
224k
    }
282
283
224k
    if ((TSK_FS_TYPE_ISFFS(fs->ftype) == 0)
284
224k
        && (TSK_FS_TYPE_ISEXT(fs->ftype) == 0)) {
285
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
286
0
        tsk_error_set_errstr
287
0
            ("unix_make_run: Called with non-Unix file system: %x",
288
0
            fs->ftype);
289
0
        return 1;
290
0
    }
291
292
224k
    length = roundup(fs_meta->size, fs->block_size);
293
294
224k
    if ((fs_attr =
295
224k
            tsk_fs_attrlist_getnew(fs_meta->attr,
296
224k
                TSK_FS_ATTR_NONRES)) == NULL) {
297
0
        return 1;
298
0
    }
299
300
    // initialize the data run
301
224k
    if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL,
302
224k
            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
303
224k
            fs_meta->size, fs_meta->size, roundup(fs_meta->size,
304
224k
                fs->block_size), TSK_FS_ATTR_FLAG_NONE, 0)) {
305
0
        return 1;
306
0
    }
307
308
224k
    read_b =
309
224k
        unix_make_data_run_direct(fs, fs_attr,
310
224k
        (TSK_DADDR_T *) fs_meta->content_ptr, 12, length);
311
224k
    if (read_b == -1) {
312
0
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
313
0
        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
314
0
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
315
0
        return 1;
316
0
    }
317
224k
    length -= read_b;
318
319
    /* if there is still data left, read the indirect */
320
224k
    if (length > 0) {
321
16.5k
        int level;
322
16.5k
        char *buf[4] = {NULL};
323
16.5k
        size_t fs_bufsize0;
324
16.5k
        size_t fs_bufsize1;
325
16.5k
        size_t ptrsperblock;
326
16.5k
        int numBlocks = 0;
327
16.5k
        int numSingIndirect = 0;
328
16.5k
        int numDblIndirect = 0;
329
16.5k
        int numTripIndirect = 0;
330
16.5k
        TSK_FS_ATTR *fs_attr_indir;
331
332
333
        /* With FFS/UFS a full block contains the addresses, but block_size is
334
         * only a fragment.  Figure out the scratch buffer size and the buffers to
335
         * store the cleaned addresses (endian converted) */
336
16.5k
        if (TSK_FS_TYPE_ISFFS(fs->ftype)) {
337
0
            FFS_INFO *ffs = (FFS_INFO *) fs;
338
339
0
            fs_bufsize0 = ffs->ffsbsize_b;
340
0
            if ((fs->ftype == TSK_FS_TYPE_FFS1)
341
0
                || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
342
0
                ptrsperblock = fs_bufsize0 / 4;
343
0
            }
344
0
            else {
345
0
                ptrsperblock = fs_bufsize0 / 8;
346
0
            }
347
0
        }
348
16.5k
        else {
349
16.5k
            fs_bufsize0 = fs->block_size;
350
16.5k
            ptrsperblock = fs_bufsize0 / 4;
351
16.5k
        }
352
16.5k
        fs_bufsize1 = sizeof(TSK_DADDR_T) * ptrsperblock;
353
354
        /*
355
         * Initialize a buffer for the 3 levels of indirection that are supported by
356
         * this inode.  Each level of indirection will have a buffer to store
357
         * addresses in.  buf[0] is a special scratch buffer that is used to store
358
         * raw data from the image (before endian conversions are applied).  It is
359
         * equal to one block size.  The others will store TSK_DADDR_T structures
360
         * and will have a size depending on the FS type.
361
         */
362
16.5k
        if ((fs_attr_indir =
363
16.5k
                tsk_fs_attrlist_getnew(fs_meta->attr,
364
16.5k
                    TSK_FS_ATTR_NONRES)) == NULL) {
365
0
            return 1;
366
0
        }
367
368
        // determine number of indirect lbocks needed for file size...
369
16.5k
        numBlocks =
370
16.5k
            (int) (((fs_meta->size + fs_bufsize0 - 1) / fs_bufsize0) - 12);
371
16.5k
        numSingIndirect =
372
16.5k
            (int) ((numBlocks + ptrsperblock - 1) / ptrsperblock);
373
16.5k
        numDblIndirect = 0;
374
16.5k
        numTripIndirect = 0;
375
376
        // double block pointer?
377
16.5k
        if (numSingIndirect > 1) {
378
11.9k
            numDblIndirect = (int)
379
11.9k
                ((numSingIndirect - 1 + ptrsperblock - 1) / ptrsperblock);
380
11.9k
            if (numDblIndirect > 1) {
381
5.22k
                numTripIndirect = (int)
382
5.22k
                    ((numDblIndirect - 1 + ptrsperblock -
383
5.22k
                        1) / ptrsperblock);
384
5.22k
            }
385
11.9k
        }
386
387
        // initialize the data run
388
16.5k
        if (tsk_fs_attr_set_run(fs_file, fs_attr_indir, NULL, NULL,
389
16.5k
                TSK_FS_ATTR_TYPE_UNIX_INDIR, TSK_FS_ATTR_ID_DEFAULT,
390
16.5k
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
391
16.5k
                    numTripIndirect),
392
16.5k
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
393
16.5k
                    numTripIndirect),
394
16.5k
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
395
16.5k
                    numTripIndirect), TSK_FS_ATTR_FLAG_NONE, 0)) {
396
0
            return 1;
397
0
        }
398
399
16.5k
        if ((buf[0] = (char *) tsk_malloc(fs_bufsize0)) == NULL) {
400
0
            return 1;
401
0
        }
402
403
27.0k
        for (level = 1; length > 0 && level < 4; level++) {
404
20.9k
            TSK_DADDR_T *addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
405
406
20.9k
            if ((buf[level] = (char *) tsk_malloc(fs_bufsize1)) == NULL) {
407
0
                int f;
408
0
                for (f = 0; f < level; f++) {
409
0
                    free(buf[f]);
410
0
                }
411
0
                return 1;
412
0
            }
413
414
            /* the indirect addresses are stored in addr_ptr after the 12
415
             * direct addresses */
416
20.9k
            read_b =
417
20.9k
                unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir,
418
20.9k
                buf, level, addr_ptr[12 + level - 1], length);
419
20.9k
            if (read_b == -1)
420
10.4k
                break;
421
10.5k
            length -= read_b;
422
10.5k
        }
423
424
        /*
425
         * Cleanup.
426
         */
427
82.7k
        for (level = 0; level < 4; ++level) {
428
66.2k
            free(buf[level]);
429
66.2k
        }
430
16.5k
    }
431
432
224k
    if (read_b == -1) {
433
10.4k
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
434
10.4k
        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
435
6.20k
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
436
10.4k
        return 1;
437
10.4k
    }
438
439
214k
    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
440
441
214k
    return 0;
442
224k
}
443
444
445
TSK_FS_ATTR_TYPE_ENUM
446
tsk_fs_unix_get_default_attr_type([[maybe_unused]] const TSK_FS_FILE * a_file)
447
10.7M
{
448
10.7M
    return TSK_FS_ATTR_TYPE_DEFAULT;
449
10.7M
}
450
451
int
452
tsk_fs_unix_name_cmp(
453
  [[maybe_unused]] TSK_FS_INFO * a_fs_info,
454
  const char *s1,
455
  const char *s2)
456
0
{
457
0
    return strcmp(s1, s2);
458
0
}