Coverage Report

Created: 2026-02-26 07:07

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
946k
{
38
946k
    TSK_DADDR_T run_start = 0;
39
946k
    TSK_DADDR_T run_len = 0;
40
946k
    TSK_DADDR_T blks_processed = 0;
41
946k
    size_t i;
42
946k
    size_t fs_blen;             // how big is each "block" (in fragments)
43
44
946k
    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
946k
    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
946k
    else {
54
946k
        fs_blen = 1;
55
946k
    }
56
57
946k
    run_start = addrs[0];
58
946k
    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
204M
    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
203M
        if ((i + 1 == addr_len) ||
71
202M
            ((run_start + run_len != addrs[i + 1]) && (run_start != 0)) ||
72
202M
            ((run_start == 0) && (addrs[i + 1] != 0))) {
73
74
1.76M
            TSK_FS_ATTR_RUN *data_run;
75
76
            // make a non-resident run
77
1.76M
            data_run = tsk_fs_attr_run_alloc();
78
1.76M
            if (data_run == NULL)
79
0
                return -1;
80
81
1.76M
            data_run->addr = run_start;
82
1.76M
            data_run->len = run_len;
83
84
1.76M
            if (run_start == 0)
85
1.18M
                data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
86
87
            // save the run
88
1.76M
            tsk_fs_attr_append_run(fs, fs_attr, data_run);
89
90
            // get ready for the next run
91
1.76M
            if (i + 1 != addr_len)
92
984k
                run_start = addrs[i + 1];
93
1.76M
            run_len = 0;
94
95
            // stop if we are past the length requested
96
1.76M
            if (blks_processed * fs->block_size > (TSK_DADDR_T) length)
97
179k
                break;
98
1.76M
        }
99
203M
        run_len += fs_blen;
100
203M
        blks_processed += fs_blen;
101
203M
    }
102
103
946k
    return blks_processed * fs->block_size;
104
946k
}
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
770k
{
125
770k
    size_t addr_cnt = 0;
126
770k
    TSK_DADDR_T *myaddrs = (TSK_DADDR_T *) buf[level];
127
770k
    TSK_OFF_T length_remain = length;
128
770k
    TSK_OFF_T retval;
129
770k
    size_t fs_bufsize;
130
770k
    size_t fs_blen;
131
770k
    TSK_FS_ATTR_RUN *data_run;
132
133
770k
    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
770k
    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
770k
    else {
144
770k
        fs_blen = 1;
145
770k
        fs_bufsize = fs->block_size;
146
770k
    }
147
148
770k
    if (addr > fs->last_block) {
149
6.60k
        tsk_error_reset();
150
6.60k
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
151
6.60k
        tsk_error_set_errstr("unix: Indirect block address too large: %"
152
6.60k
            PRIuDADDR "", addr);
153
6.60k
        return -1;
154
6.60k
    }
155
156
    // make a non-resident run
157
763k
    data_run = tsk_fs_attr_run_alloc();
158
763k
    if (data_run == NULL)
159
0
        return -1;
160
161
763k
    data_run->addr = addr;
162
763k
    data_run->len = fs_blen;
163
164
    /*
165
     * Read a block of disk addresses.
166
     */
167
    // sparse
168
763k
    if (addr == 0) {
169
759k
        memset(buf[0], 0, fs_bufsize);
170
759k
        data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
171
759k
    }
172
4.06k
    else {
173
4.06k
        ssize_t cnt;
174
        // read the data into the scratch buffer
175
4.06k
        cnt = tsk_fs_read_block(fs, addr, buf[0], fs_bufsize);
176
4.06k
        if (cnt != (ssize_t)fs_bufsize) {
177
3.62k
            if (cnt >= 0) {
178
0
                tsk_error_reset();
179
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
180
0
            }
181
3.62k
            tsk_error_set_errstr2("unix_make_data_run_indir: Block %"
182
3.62k
                PRIuDADDR, addr);
183
3.62k
            free(data_run);
184
3.62k
            return -1;
185
3.62k
        }
186
4.06k
    }
187
188
    // save the run
189
760k
    tsk_fs_attr_append_run(fs, fs_attr_indir, data_run);
190
191
760k
    data_run = NULL;
192
193
    // convert the raw addresses to the correct endian ordering
194
760k
    if ((fs->ftype == TSK_FS_TYPE_FFS1)
195
760k
        || (fs->ftype == TSK_FS_TYPE_FFS1B)
196
760k
        || (TSK_FS_TYPE_ISEXT(fs->ftype))) {
197
760k
        size_t n;
198
760k
        uint32_t *iaddr = (uint32_t *) buf[0];
199
760k
        addr_cnt = fs_bufsize / sizeof(*iaddr);
200
203M
        for (n = 0; n < addr_cnt; n++) {
201
202M
            myaddrs[n] = tsk_getu32(fs->endian, (uint8_t *) & iaddr[n]);
202
202M
        }
203
760k
    }
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
760k
    if (level == 1) {
215
756k
        retval =
216
756k
            unix_make_data_run_direct(fs, fs_attr, myaddrs, addr_cnt,
217
756k
            length_remain);
218
756k
        if (retval != -1) {
219
756k
            length_remain -= retval;
220
756k
        }
221
756k
    }
222
3.23k
    else {
223
3.23k
        size_t i;
224
3.23k
        retval = 0;
225
752k
        for (i = 0; i < addr_cnt && retval != -1; i++) {
226
749k
            retval =
227
749k
                unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir,
228
749k
                buf, level - 1, myaddrs[i], length_remain);
229
749k
            if (retval == -1) {
230
372
                break;
231
372
            }
232
749k
            else {
233
749k
                length_remain -= retval;
234
749k
            }
235
749k
        }
236
3.23k
    }
237
238
760k
    if (retval == -1) {
239
372
        return -1;
240
372
    }
241
759k
    return length - length_remain;
242
760k
}
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
189k
{
252
189k
    TSK_OFF_T length = 0;
253
189k
    TSK_OFF_T read_b = 0;
254
189k
    TSK_FS_ATTR *fs_attr;
255
189k
    TSK_FS_META *fs_meta = fs_file->meta;
256
189k
    TSK_FS_INFO *fs = fs_file->fs_info;
257
258
    // clean up any error messages that are lying around
259
189k
    tsk_error_reset();
260
261
189k
    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
189k
    if ((fs_meta->attr != NULL)
268
0
        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
269
0
        return 0;
270
0
    }
271
189k
    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
189k
    if (fs_meta->attr != NULL) {
277
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
278
0
    }
279
189k
    else {
280
189k
        fs_meta->attr = tsk_fs_attrlist_alloc();
281
189k
    }
282
283
189k
    if ((TSK_FS_TYPE_ISFFS(fs->ftype) == 0)
284
189k
        && (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
189k
    length = roundup(fs_meta->size, fs->block_size);
293
294
189k
    if ((fs_attr =
295
189k
            tsk_fs_attrlist_getnew(fs_meta->attr,
296
189k
                TSK_FS_ATTR_NONRES)) == NULL) {
297
0
        return 1;
298
0
    }
299
300
    // initialize the data run
301
189k
    if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL,
302
189k
            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
303
189k
            fs_meta->size, fs_meta->size, roundup(fs_meta->size,
304
189k
                fs->block_size), TSK_FS_ATTR_FLAG_NONE, 0)) {
305
0
        return 1;
306
0
    }
307
308
189k
    read_b =
309
189k
        unix_make_data_run_direct(fs, fs_attr,
310
189k
        (TSK_DADDR_T *) fs_meta->content_ptr, 12, length);
311
189k
    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
189k
    length -= read_b;
318
319
    /* if there is still data left, read the indirect */
320
189k
    if (length > 0) {
321
15.5k
        int level;
322
15.5k
        char *buf[4] = {NULL};
323
15.5k
        size_t fs_bufsize0;
324
15.5k
        size_t fs_bufsize1;
325
15.5k
        size_t ptrsperblock;
326
15.5k
        int numBlocks = 0;
327
15.5k
        int numSingIndirect = 0;
328
15.5k
        int numDblIndirect = 0;
329
15.5k
        int numTripIndirect = 0;
330
15.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
15.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
15.5k
        else {
349
15.5k
            fs_bufsize0 = fs->block_size;
350
15.5k
            ptrsperblock = fs_bufsize0 / 4;
351
15.5k
        }
352
15.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
15.5k
        if ((fs_attr_indir =
363
15.5k
                tsk_fs_attrlist_getnew(fs_meta->attr,
364
15.5k
                    TSK_FS_ATTR_NONRES)) == NULL) {
365
0
            return 1;
366
0
        }
367
368
        // determine number of indirect lbocks needed for file size...
369
15.5k
        numBlocks =
370
15.5k
            (int) (((fs_meta->size + fs_bufsize0 - 1) / fs_bufsize0) - 12);
371
15.5k
        numSingIndirect =
372
15.5k
            (int) ((numBlocks + ptrsperblock - 1) / ptrsperblock);
373
15.5k
        numDblIndirect = 0;
374
15.5k
        numTripIndirect = 0;
375
376
        // double block pointer?
377
15.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.75k
                numTripIndirect = (int)
382
5.75k
                    ((numDblIndirect - 1 + ptrsperblock -
383
5.75k
                        1) / ptrsperblock);
384
5.75k
            }
385
11.9k
        }
386
387
        // initialize the data run
388
15.5k
        if (tsk_fs_attr_set_run(fs_file, fs_attr_indir, NULL, NULL,
389
15.5k
                TSK_FS_ATTR_TYPE_UNIX_INDIR, TSK_FS_ATTR_ID_DEFAULT,
390
15.5k
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
391
15.5k
                    numTripIndirect),
392
15.5k
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
393
15.5k
                    numTripIndirect),
394
15.5k
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
395
15.5k
                    numTripIndirect), TSK_FS_ATTR_FLAG_NONE, 0)) {
396
0
            return 1;
397
0
        }
398
399
15.5k
        if ((buf[0] = (char *) tsk_malloc(fs_bufsize0)) == NULL) {
400
0
            return 1;
401
0
        }
402
403
25.8k
        for (level = 1; length > 0 && level < 4; level++) {
404
20.5k
            TSK_DADDR_T *addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
405
406
20.5k
            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.5k
            read_b =
417
20.5k
                unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir,
418
20.5k
                buf, level, addr_ptr[12 + level - 1], length);
419
20.5k
            if (read_b == -1)
420
10.2k
                break;
421
10.3k
            length -= read_b;
422
10.3k
        }
423
424
        /*
425
         * Cleanup.
426
         */
427
77.5k
        for (level = 0; level < 4; ++level) {
428
62.0k
            free(buf[level]);
429
62.0k
        }
430
15.5k
    }
431
432
189k
    if (read_b == -1) {
433
10.2k
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
434
10.2k
        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
435
5.94k
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
436
10.2k
        return 1;
437
10.2k
    }
438
439
179k
    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
440
441
179k
    return 0;
442
189k
}
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.5M
{
448
10.5M
    return TSK_FS_ATTR_TYPE_DEFAULT;
449
10.5M
}
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
}