Coverage Report

Created: 2025-07-12 06:14

/src/sleuthkit/tsk/fs/fs_attr.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
** fs_attr
3
** The Sleuth Kit
4
**
5
** Brian Carrier [carrier <at> sleuthkit [dot] org]
6
** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
7
** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
8
**
9
** TASK
10
** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
11
**
12
**
13
** This software is distributed under the Common Public License 1.0
14
**
15
*/
16
17
/**
18
 * \file fs_attr.c
19
 * Functions to allocate and add structures to maintain generic file
20
 * system attributes and run lists.
21
 */
22
23
24
/*
25
 * The TSK_FS_ATTR structure is motivated by NTFS.  NTFS (and others) allow
26
 * one to have more than one data area per file.  Furthermore, there is
27
 * more than one way to store the data (resident in the MFT entry or
28
 * in the Data Area runs).  To handle this in
29
 * a generic format, the TSK_FS_ATTR structure was created.
30
 *
31
 * TSK_FS_ATTR structures have a type and id that describe it and then
32
 * a flag identifies it as a resident stream or a non-resident run
33
 * They form a linked list and are added to the TSK_FS_META structure
34
 */
35
#include "tsk_fs_i.h"
36
#include "tsk_logical_fs.h"
37
38
39
/**
40
 * \internal
41
 * Allocate a run list entry.
42
 *
43
 * @returns NULL on error
44
 */
45
TSK_FS_ATTR_RUN *
46
tsk_fs_attr_run_alloc()
47
3.81M
{
48
3.81M
    TSK_FS_ATTR_RUN *fs_attr_run =
49
3.81M
        (TSK_FS_ATTR_RUN *) tsk_malloc(sizeof(TSK_FS_ATTR_RUN));
50
3.81M
    if (fs_attr_run == NULL)
51
0
        return NULL;
52
53
3.81M
    return fs_attr_run;
54
3.81M
}
55
56
/**
57
 * \internal
58
 * Free a list of data_runs
59
 *
60
 * @param fs_attr_run Head of list to free
61
 */
62
void
63
tsk_fs_attr_run_free(TSK_FS_ATTR_RUN * fs_attr_run)
64
952k
{
65
4.75M
    while (fs_attr_run) {
66
3.80M
        TSK_FS_ATTR_RUN *fs_attr_run_prev = fs_attr_run;
67
3.80M
        fs_attr_run = fs_attr_run->next;
68
3.80M
        fs_attr_run_prev->next = NULL;
69
3.80M
        free(fs_attr_run_prev);
70
3.80M
    }
71
952k
}
72
73
74
75
76
/**
77
 * \internal
78
 * Allocates and initializes a new structure.
79
 *
80
 * @param type The type of attribute to create (Resident or Non-resident)
81
 * @returns NULL on error
82
 */
83
TSK_FS_ATTR *
84
tsk_fs_attr_alloc(TSK_FS_ATTR_FLAG_ENUM type)
85
2.25M
{
86
2.25M
    TSK_FS_ATTR *fs_attr = (TSK_FS_ATTR *) tsk_malloc(sizeof(TSK_FS_ATTR));
87
2.25M
    if (fs_attr == NULL) {
88
0
        return NULL;
89
0
    }
90
91
2.25M
    fs_attr->name_size = 128;
92
2.25M
    if ((fs_attr->name = (char *) tsk_malloc(fs_attr->name_size)) == NULL) {
93
0
        free(fs_attr);
94
0
        return NULL;
95
0
    }
96
97
2.25M
    if (type == TSK_FS_ATTR_NONRES) {
98
548k
        fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_NONRES | TSK_FS_ATTR_INUSE);
99
548k
    }
100
1.70M
    else if (type == TSK_FS_ATTR_RES) {
101
1.70M
        fs_attr->rd.buf_size = 1024;
102
1.70M
        fs_attr->rd.buf = (uint8_t *) tsk_malloc(fs_attr->rd.buf_size);
103
1.70M
        if (fs_attr->rd.buf == NULL) {
104
0
            free(fs_attr->name);
105
0
            return NULL;
106
0
        }
107
1.70M
        fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_RES | TSK_FS_ATTR_INUSE);
108
1.70M
    }
109
0
    else {
110
0
        tsk_error_reset();
111
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
112
0
        tsk_error_set_errstr("tsk_fs_attr_alloc: Invalid Type: %d\n",
113
0
            type);
114
0
        return NULL;
115
0
    }
116
117
2.25M
    return fs_attr;
118
2.25M
}
119
120
121
/**
122
 * \internal
123
 * Free a single TSK_FS_ATTR structure.  This does not free the linked list.
124
 *
125
 * @param a_fs_attr Structure to free.
126
 */
127
void
128
tsk_fs_attr_free(TSK_FS_ATTR * a_fs_attr)
129
2.25M
{
130
2.25M
    if (a_fs_attr == NULL)
131
0
        return;
132
133
2.25M
    if (a_fs_attr->nrd.run)
134
924k
        tsk_fs_attr_run_free(a_fs_attr->nrd.run);
135
2.25M
    a_fs_attr->nrd.run = NULL;
136
137
2.25M
    free(a_fs_attr->rd.buf);
138
2.25M
    a_fs_attr->rd.buf = NULL;
139
140
2.25M
    free(a_fs_attr->name);
141
2.25M
    a_fs_attr->name = NULL;
142
143
2.25M
    free(a_fs_attr);
144
2.25M
}
145
146
147
/**
148
 * \internal
149
 * Clear the run_lists fields of a single FS_DATA structure
150
 *
151
 * @param a_fs_attr Structure to clear for reuse
152
 */
153
void
154
tsk_fs_attr_clear(TSK_FS_ATTR * a_fs_attr)
155
443k
{
156
443k
    a_fs_attr->size = 0;
157
443k
    a_fs_attr->type = TSK_FS_ATTR_TYPE_NOT_FOUND;
158
443k
    a_fs_attr->id = 0;
159
443k
    a_fs_attr->flags = TSK_FS_ATTR_FLAG_NONE;
160
443k
    if (a_fs_attr->nrd.run) {
161
15.5k
        tsk_fs_attr_run_free(a_fs_attr->nrd.run);
162
15.5k
        a_fs_attr->nrd.run = NULL;
163
15.5k
        a_fs_attr->nrd.run_end = NULL;
164
15.5k
        a_fs_attr->nrd.allocsize = 0;
165
15.5k
        a_fs_attr->nrd.initsize = 0;
166
15.5k
    }
167
443k
}
168
169
170
171
172
/**
173
 * Add a name to an existing FS_DATA structure.  Will reallocate
174
 * space for the name if needed.
175
 *
176
 * @param fs_attr Structure to add name to
177
 * @param name UTF-8 name to add
178
 *
179
 * @return 1 on error and 0 on success
180
 */
181
static uint8_t
182
fs_attr_put_name(TSK_FS_ATTR * fs_attr, const char *name)
183
2.38M
{
184
2.38M
    if ((name == NULL) || (strlen(name) == 0)) {
185
1.74M
        if (fs_attr->name_size > 0) {
186
1.69M
            free(fs_attr->name);
187
1.69M
            fs_attr->name_size = 0;
188
1.69M
        }
189
1.74M
        fs_attr->name = NULL;
190
1.74M
        return 0;
191
1.74M
    }
192
193
637k
    if (fs_attr->name_size < (strlen(name) + 1)) {
194
17.5k
        fs_attr->name = (char*) tsk_realloc(fs_attr->name, strlen(name) + 1);
195
17.5k
        if (fs_attr->name == NULL)
196
0
            return 1;
197
17.5k
        fs_attr->name_size = strlen(name) + 1;
198
17.5k
    }
199
637k
    strncpy(fs_attr->name, name, fs_attr->name_size);
200
637k
    return 0;
201
637k
}
202
203
/**
204
 * \internal
205
 * Copy resident data to an attribute.
206
 *
207
 * @param a_fs_attr Attribute to add data to (cannot be NULL)
208
 * @param name Name of the attribute to set
209
 * @param type Type of the attribute to set
210
 * @param id Id of the attribute to set
211
 * @param res_data Pointer to where resident data is located (data will
212
 * be copied from here into FS_DATA)
213
 * @param len Length of resident data
214
 * @return 1 on error and 0 on success
215
 */
216
uint8_t
217
tsk_fs_attr_set_str(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr,
218
    const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id,
219
    void *res_data, size_t len)
220
1.41M
{
221
1.41M
    if (a_fs_attr == NULL) {
222
0
        tsk_error_reset();
223
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
224
0
        tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_str");
225
0
        return 1;
226
0
    }
227
228
1.41M
    a_fs_attr->fs_file = a_fs_file;
229
1.41M
    a_fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_RES);
230
1.41M
    a_fs_attr->type = type;
231
1.41M
    a_fs_attr->id = id;
232
1.41M
    a_fs_attr->nrd.compsize = 0;
233
234
1.41M
    if (fs_attr_put_name(a_fs_attr, name)) {
235
0
        return 1;
236
0
    }
237
238
1.41M
    if (a_fs_attr->rd.buf_size < len) {
239
521
        a_fs_attr->rd.buf =
240
521
            (uint8_t *) tsk_realloc((char *) a_fs_attr->rd.buf, len);
241
521
        if (a_fs_attr->rd.buf == NULL)
242
0
            return 1;
243
521
        a_fs_attr->rd.buf_size = len;
244
521
    }
245
246
1.41M
    memset(a_fs_attr->rd.buf, 0, a_fs_attr->rd.buf_size);
247
1.41M
    memcpy(a_fs_attr->rd.buf, res_data, len);
248
1.41M
    a_fs_attr->size = len;
249
250
1.41M
    return 0;
251
1.41M
}
252
253
254
/**
255
 * \internal
256
 * Set the needed fields along with an initial run list for a data attribute.  To add more
257
 * runs, use ...._add_run().
258
 *
259
 * @param a_fs_file File to add attribute to
260
 * @param a_fs_attr The data attribute to initialize and add the run to
261
 * @param a_data_run_new The set of runs to add (can be NULL).
262
 * @param name Name of the attribute (can be NULL)
263
 * @param type Type of attribute to add run to
264
 * @param id Id of attribute to add run to
265
 * @param size Total size of the attribute (can be larger than length of initial run being added)
266
 * @param init_size Number of bytes in attribute that have been initialized (less then or equal to size)
267
 * (note that this sets the initialized size for the attribute and it will not be updated as more runs are added).
268
 * @param alloc_size Allocated size of the attribute (>= size).  Identifies the slack space.
269
 * (note that this sets the allocated size for the attribute and it will not be updated as more runs are added).
270
 * @param flags Flags about compression, sparse etc. of data
271
 * @param compsize Compression unit size (in case it needs to be created)
272
 *
273
 * @returns 1 on error and 0 on success
274
 */
275
uint8_t
276
tsk_fs_attr_set_run(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr,
277
    TSK_FS_ATTR_RUN * a_data_run_new, const char *name,
278
    TSK_FS_ATTR_TYPE_ENUM type, uint16_t id, TSK_OFF_T size,
279
    TSK_OFF_T init_size, TSK_OFF_T alloc_size,
280
    TSK_FS_ATTR_FLAG_ENUM flags, uint32_t compsize)
281
965k
{
282
965k
    if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) {
283
0
        tsk_error_reset();
284
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
285
0
        tsk_error_set_errstr("Null fs_file in tsk_fs_attr_set_run");
286
0
        return 1;
287
0
    }
288
965k
    if (a_fs_attr == NULL) {
289
0
        tsk_error_reset();
290
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
291
0
        tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_run");
292
0
        return 1;
293
0
    }
294
295
965k
    if (alloc_size < size) {
296
874
        tsk_error_reset();
297
874
        tsk_error_set_errno(TSK_ERR_FS_ARG);
298
874
        tsk_error_set_errstr("tsk_fs_attr_set_run: alloc_size (%" PRIdOFF
299
874
            ") is less than size (%" PRIdOFF ")", alloc_size, size);
300
874
        return 1;
301
874
    }
302
303
964k
    a_fs_attr->fs_file = a_fs_file;
304
964k
    a_fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_NONRES | flags);
305
964k
    a_fs_attr->type = type;
306
964k
    a_fs_attr->id = id;
307
964k
    a_fs_attr->size = size;
308
964k
    a_fs_attr->nrd.allocsize = alloc_size;
309
964k
    a_fs_attr->nrd.initsize = init_size;
310
964k
    a_fs_attr->nrd.compsize = compsize;
311
312
964k
    if (fs_attr_put_name(a_fs_attr, name)) {
313
0
        return 1;
314
0
    }
315
316
    /* Add the a_data_run_new to the attribute. */
317
318
    /* We support the ODD case where the run is NULL.  In this case,
319
     * we set the attribute size info, but set everything else to NULL.
320
     */
321
964k
    if (a_data_run_new == NULL) {
322
270k
        a_fs_attr->nrd.run = NULL;
323
270k
        a_fs_attr->nrd.run_end = NULL;
324
270k
        return 0;
325
270k
    }
326
327
    /*
328
     * If this is not in the beginning, then we need to make a filler
329
     * to account for the cluster numbers we haven't seen yet
330
     *
331
     * This commonly happens when we process an MFT entry that
332
     * is not a base entry and it is referenced in an $ATTR_LIST
333
     *
334
     * The $DATA attribute in the non-base have a non-zero
335
     * a_data_run_new->offset.
336
     */
337
694k
    if (a_data_run_new->offset != 0) {
338
63.0k
        TSK_FS_ATTR_RUN *fill_run = tsk_fs_attr_run_alloc();
339
63.0k
        fill_run->flags = TSK_FS_ATTR_RUN_FLAG_FILLER;
340
63.0k
        fill_run->offset = 0;
341
63.0k
        fill_run->addr = 0;
342
63.0k
        fill_run->len = a_data_run_new->offset;
343
63.0k
        fill_run->next = a_data_run_new;
344
63.0k
        a_data_run_new = fill_run;
345
63.0k
    }
346
347
694k
    a_fs_attr->nrd.run = a_data_run_new;
348
349
    // update the pointer to the end of the list
350
694k
    a_fs_attr->nrd.run_end = a_data_run_new;
351
947k
    while (a_fs_attr->nrd.run_end->next) {
352
253k
        a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next;
353
253k
    }
354
355
694k
    return 0;
356
964k
}
357
358
static void
359
dump_attr(TSK_FS_ATTR * a_fs_attr)
360
0
{
361
0
    TSK_FS_ATTR_RUN *cur_run;
362
0
    cur_run = a_fs_attr->nrd.run;
363
364
0
    fprintf(stderr, "Attribute Run Dump:\n");
365
0
    for (cur_run = a_fs_attr->nrd.run; cur_run; cur_run = cur_run->next) {
366
0
        fprintf(stderr, "  %" PRIuDADDR " to %" PRIuDADDR " %sFiller\n",
367
0
            cur_run->offset, cur_run->offset + cur_run->len - 1,
368
0
            (cur_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ? "" : "Not");
369
0
    }
370
0
}
371
372
/*
373
 * Prints the data runs for a non-resident attribute
374
 */
375
uint8_t
376
0
tsk_fs_attr_print(const TSK_FS_ATTR * a_fs_attr, FILE* hFile) {
377
0
    TSK_FS_ATTR_RUN *fs_attr_run;
378
0
    uint32_t skip_remain;
379
0
    TSK_OFF_T tot_size;
380
0
    TSK_FS_INFO *fs = a_fs_attr->fs_file->fs_info;
381
0
    TSK_OFF_T off = 0;
382
0
    uint8_t stop_loop = 0;
383
384
0
    if ( ! (a_fs_attr->flags & TSK_FS_ATTR_NONRES)) {
385
0
        tsk_error_set_errstr("tsk_fs_attr_print called on non-resident attribute");
386
0
        return TSK_ERR;
387
0
    }
388
389
0
    tot_size = a_fs_attr->size;
390
0
    skip_remain = a_fs_attr->nrd.skiplen;
391
392
0
    for (fs_attr_run = a_fs_attr->nrd.run; fs_attr_run;
393
0
        fs_attr_run = fs_attr_run->next) {
394
0
        TSK_DADDR_T addr, len_idx, run_len, run_start_addr;
395
396
0
        addr = fs_attr_run->addr;
397
0
        run_len = 0;
398
0
        run_start_addr = addr;
399
400
        /* cycle through each block in the run */
401
0
        for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) {
402
403
404
            /* If the address is too large then give an error */
405
0
            if (addr + len_idx > fs->last_block) {
406
0
                if (a_fs_attr->fs_file->
407
0
                    meta->flags & TSK_FS_META_FLAG_UNALLOC)
408
0
                    tsk_error_set_errno(TSK_ERR_FS_RECOVER);
409
0
                else
410
0
                    tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
411
0
                tsk_error_set_errstr
412
0
                    ("Invalid address in run (too large): %" PRIuDADDR "",
413
0
                    addr + len_idx);
414
0
                return TSK_ERR;
415
0
            }
416
417
418
            /* Need to account for the skip length, which is the number of bytes
419
            * in the start of the attribute that are skipped and that are not
420
            * included in the overall length.  We will seek past those and not
421
            * return those in the action.  We just read a block size so check
422
            * if there is data to be returned in this buffer. */
423
424
0
            if (skip_remain >= fs->block_size) {
425
0
                skip_remain -= fs->block_size;
426
0
                run_start_addr++;
427
0
            }
428
0
            else {
429
0
                size_t ret_len;
430
431
                /* Do we want to return a full block, or just the end? */
432
0
                if ((TSK_OFF_T)fs->block_size - skip_remain <
433
0
                    tot_size - off)
434
0
                    ret_len = fs->block_size - skip_remain;
435
0
                else
436
0
                    ret_len = (size_t)(tot_size - off);
437
438
0
                off += ret_len;
439
0
                run_len++;
440
0
                skip_remain = 0;
441
442
0
                if (off >= tot_size) {
443
0
                    stop_loop = 1;
444
0
                    break;
445
0
                }
446
0
            }
447
0
        }
448
449
0
        if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) {
450
0
            tsk_fprintf(hFile, "  Starting address: X, length: %lld  Sparse", run_len);
451
0
        }
452
0
        else if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
453
0
            tsk_fprintf(hFile, "  Starting address: X, length: %lld  Filler", run_len);
454
0
        }
455
0
        else {
456
0
            tsk_fprintf(hFile, "  Starting address: %lld, length: %lld  %s", run_start_addr, run_len,
457
0
                (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_ENCRYPTED) ? "Encrypted": "");
458
0
        }
459
0
        tsk_fprintf(hFile, "\n");
460
0
        if (stop_loop) {
461
0
            break;
462
0
        }
463
0
    }
464
0
    return TSK_OK;
465
0
}
466
467
/**
468
 * \internal
469
 * Add a set of consecutive runs to an attribute. This will add and remove FILLER entries
470
 * as needed and update internal variables.
471
 *
472
 * @param a_fs File system run is from
473
 * @param fs_attr Attribute to add run to
474
 * @param a_data_run_new The set of runs to add.
475
 *
476
 * @returns 1 on error and 0 on succes
477
 */
478
uint8_t
479
tsk_fs_attr_add_run(
480
  [[maybe_unused]] TSK_FS_INFO * a_fs,
481
  TSK_FS_ATTR * a_fs_attr,
482
  TSK_FS_ATTR_RUN * a_data_run_new)
483
52.4k
{
484
52.4k
    TSK_FS_ATTR_RUN *data_run_cur, *data_run_prev;
485
52.4k
    TSK_DADDR_T run_len;
486
487
52.4k
    tsk_error_reset();
488
489
52.4k
    if (a_fs_attr == NULL) {
490
0
        tsk_error_reset();
491
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
492
0
        tsk_error_set_errstr
493
0
            ("tsk_fs_attr_add_run: Error, a_fs_attr is NULL");
494
0
        return 1;
495
0
    }
496
497
    // we only support the case of a null run if it is the only run...
498
52.4k
    if (a_data_run_new == NULL) {
499
396
        tsk_error_reset();
500
396
        tsk_error_set_errno(TSK_ERR_FS_ARG);
501
396
        tsk_error_set_errstr
502
396
            ("tsk_fs_attr_add_run: Error, a_data_run_new is NULL (%"
503
396
            PRIuINUM ")", a_fs_attr->fs_file->meta->addr);
504
396
        return 1;
505
396
    }
506
507
52.0k
    run_len = 0;
508
52.0k
    data_run_cur = a_data_run_new;
509
135k
    while (data_run_cur) {
510
83.2k
        run_len += data_run_cur->len;
511
83.2k
        data_run_cur = data_run_cur->next;
512
83.2k
    }
513
514
    /* First thing, is to check if we can just add it to the end */
515
52.0k
    if ((a_fs_attr->nrd.run_end)
516
52.0k
        && (a_fs_attr->nrd.run_end->offset + a_fs_attr->nrd.run_end->len ==
517
40.1k
            a_data_run_new->offset)) {
518
519
2.77k
        a_fs_attr->nrd.run_end->next = a_data_run_new;
520
        // update the pointer to the end of the list
521
6.20k
        while (a_fs_attr->nrd.run_end->next)
522
3.43k
            a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next;
523
524
        /* return head of a_fs_attr list */
525
2.77k
        return 0;
526
2.77k
    }
527
528
    // cycle through existing runs and see if we can add this into a filler spot
529
49.2k
    data_run_cur = a_fs_attr->nrd.run;
530
49.2k
    data_run_prev = NULL;
531
305k
    while (data_run_cur) {
532
533
278k
        if (tsk_verbose)
534
0
            tsk_fprintf(stderr,
535
0
                "tsk_fs_attr_add: %" PRIuDADDR "@%" PRIuDADDR
536
0
                " (Filler: %s)\n", data_run_cur->offset, data_run_cur->len,
537
0
                (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ? "Yes"
538
0
                : "No");
539
540
        /* Do we replace this filler spot? */
541
278k
        if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
542
543
            /* This should never happen because we always add
544
             * the filler to start from VCN 0 */
545
49.5k
            if (data_run_cur->offset > a_data_run_new->offset) {
546
935
                tsk_error_reset();
547
935
                tsk_error_set_errno(TSK_ERR_FS_GENFS);
548
935
                tsk_error_set_errstr
549
935
                    ("tsk_fs_attr_add_run: could not add data_run b.c. offset (%"
550
935
            PRIuDADDR ") is larger than FILLER (%" PRIuDADDR ") (%"
551
935
                    PRIuINUM ")", a_data_run_new->offset,
552
935
                    data_run_cur->offset, a_fs_attr->fs_file->meta->addr);
553
935
                if (tsk_verbose)
554
0
                    dump_attr(a_fs_attr);
555
935
                return 1;
556
935
            }
557
558
            /* Check if the new run starts inside of this filler. */
559
48.5k
            if (data_run_cur->offset + data_run_cur->len >
560
48.5k
                a_data_run_new->offset) {
561
21.0k
                TSK_FS_ATTR_RUN *endrun;
562
563
                /* if the new starts at the same as the filler,
564
                 * replace the pointer */
565
21.0k
                if (data_run_cur->offset == a_data_run_new->offset) {
566
12.5k
                    if (data_run_prev)
567
6.28k
                        data_run_prev->next = a_data_run_new;
568
6.26k
                    else
569
6.26k
                        a_fs_attr->nrd.run = a_data_run_new;
570
12.5k
                }
571
572
                /* The new run does not start at the beginning of
573
                 * the filler, so make a new start filler
574
                 */
575
8.53k
                else {
576
8.53k
                    TSK_FS_ATTR_RUN *newfill = tsk_fs_attr_run_alloc();
577
8.53k
                    if (newfill == NULL)
578
0
                        return 1;
579
580
8.53k
                    if (data_run_prev)
581
6.21k
                        data_run_prev->next = newfill;
582
2.32k
                    else
583
2.32k
                        a_fs_attr->nrd.run = newfill;
584
585
8.53k
                    newfill->next = a_data_run_new;
586
8.53k
                    newfill->len =
587
8.53k
                        a_data_run_new->offset - data_run_cur->offset;
588
8.53k
                    newfill->offset = data_run_cur->offset;
589
8.53k
                    newfill->flags = TSK_FS_ATTR_RUN_FLAG_FILLER;
590
591
8.53k
                    data_run_cur->len -= newfill->len;
592
8.53k
                }
593
594
                /* get to the end of the run that we are trying to add */
595
21.0k
                endrun = a_data_run_new;
596
28.6k
                while (endrun->next)
597
7.59k
                    endrun = endrun->next;
598
599
                /* if the filler is the same size as the
600
                 * new one, replace it
601
                 */
602
21.0k
                if (run_len == data_run_cur->len) {
603
689
                    endrun->next = data_run_cur->next;
604
605
                    // update the pointer to the end of the list (if we are the end)
606
689
                    if (endrun->next == NULL)
607
0
                        a_fs_attr->nrd.run_end = endrun;
608
609
689
                    free(data_run_cur);
610
689
                }
611
                /* else adjust the last filler entry */
612
20.3k
                else {
613
20.3k
                    endrun->next = data_run_cur;
614
20.3k
                    data_run_cur->len -= run_len;
615
20.3k
                    data_run_cur->offset =
616
20.3k
                        a_data_run_new->offset + a_data_run_new->len;
617
20.3k
                }
618
619
21.0k
                return 0;
620
21.0k
            }
621
48.5k
        }
622
623
256k
        data_run_prev = data_run_cur;
624
256k
        data_run_cur = data_run_cur->next;
625
256k
    }
626
627
628
    /*
629
     * There is no filler holding the location of this run, so
630
     * we will add it to the end of the list
631
     *
632
     * we got here because it did not fit in the current list or
633
     * because the current list is NULL
634
     *
635
     * At this point data_run_prev is the end of the existing list or
636
     * 0 if there is no list
637
     */
638
    /* This is an error condition.
639
     * It means that we cycled through the existing runs,
640
     * ended at a VCN that is larger than what we are adding,
641
     * and never found a filler entry to insert it into...
642
     */
643
27.2k
    if ((data_run_prev)
644
27.2k
        && (data_run_prev->offset + data_run_prev->len >
645
15.3k
            a_data_run_new->offset)) {
646
647
        /* MAYBE this is because of a duplicate entry .. */
648
5.11k
        if ((data_run_prev->addr == a_data_run_new->addr) &&
649
5.11k
            (data_run_prev->len == a_data_run_new->len)) {
650
            // @@@ Sould be we freeing this....?  What if the caller tries to write to it?
651
2.73k
            tsk_fs_attr_run_free(a_data_run_new);
652
2.73k
            return 0;
653
2.73k
        }
654
655
2.38k
        tsk_error_reset();
656
2.38k
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
657
2.38k
        tsk_error_set_errstr
658
2.38k
            ("fs_attr_add_run: error adding additional run (%" PRIuINUM
659
2.38k
            "): No filler entry for %" PRIuDADDR ". Final: %" PRIuDADDR,
660
2.38k
            a_fs_attr->fs_file->meta->addr, a_data_run_new->offset,
661
2.38k
            data_run_prev->offset + data_run_prev->len);
662
2.38k
        if (tsk_verbose)
663
0
            dump_attr(a_fs_attr);
664
2.38k
        return 1;
665
5.11k
    }
666
667
    /* we should add it right here */
668
22.1k
    else if (((data_run_prev)
669
22.1k
            && (data_run_prev->offset + data_run_prev->len ==
670
10.2k
                a_data_run_new->offset))
671
22.1k
        || (a_data_run_new->offset == 0)) {
672
3.43k
        if (data_run_prev)
673
0
            data_run_prev->next = a_data_run_new;
674
3.43k
        else
675
3.43k
            a_fs_attr->nrd.run = a_data_run_new;
676
3.43k
    }
677
    /* we need to make a filler before it */
678
18.7k
    else {
679
18.7k
        TSK_FS_ATTR_RUN *tmprun = tsk_fs_attr_run_alloc();
680
18.7k
        if (tmprun == NULL)
681
0
            return 1;
682
683
18.7k
        if (data_run_prev) {
684
10.2k
            data_run_prev->next = tmprun;
685
10.2k
            tmprun->offset = data_run_prev->offset + data_run_prev->len;
686
10.2k
        }
687
8.44k
        else {
688
8.44k
            a_fs_attr->nrd.run = tmprun;
689
8.44k
        }
690
691
18.7k
        tmprun->len = a_data_run_new->offset - tmprun->offset;
692
18.7k
        tmprun->flags = TSK_FS_ATTR_RUN_FLAG_FILLER;
693
18.7k
        tmprun->next = a_data_run_new;
694
18.7k
    }
695
696
    // update the pointer to the end of the list
697
22.1k
    a_fs_attr->nrd.run_end = a_data_run_new;
698
28.7k
    while (a_fs_attr->nrd.run_end->next)
699
6.64k
        a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next;
700
701
22.1k
    return 0;
702
27.2k
}
703
704
705
/**
706
 * Append a data run to the end of the attribute and update its offset
707
 * value.  This ignores the offset in the data run and blindly appends.
708
 *
709
 * @param a_fs File system run is from
710
 * @param a_fs_attr Data attribute to append to
711
 * @param a_data_run Data run to append.
712
 */
713
void
714
tsk_fs_attr_append_run(
715
  [[maybe_unused]] TSK_FS_INFO * a_fs,
716
  TSK_FS_ATTR * a_fs_attr,
717
  TSK_FS_ATTR_RUN * a_data_run)
718
2.74M
{
719
2.74M
    TSK_FS_ATTR_RUN *data_run_cur;
720
721
2.74M
    if ((a_fs_attr == NULL) || (a_data_run == NULL)) {
722
0
        return;
723
0
    }
724
725
2.74M
    if (a_fs_attr->nrd.run == NULL) {
726
233k
        a_fs_attr->nrd.run = a_data_run;
727
233k
        a_data_run->offset = 0;
728
233k
    }
729
2.51M
    else {
730
        // just in case this was not updated
731
2.51M
        if ((a_fs_attr->nrd.run_end == NULL)
732
2.51M
            || (a_fs_attr->nrd.run_end->next != NULL)) {
733
734
2.51M
            int counter = 0;
735
736
2.51M
            data_run_cur = a_fs_attr->nrd.run;
737
913M
            while (data_run_cur->next) {
738
911M
                data_run_cur = data_run_cur->next;
739
                /* bugfix issue 2268
740
                 * "Infinite loop when processing a Linux disk image (tsk_fs_file_attr_getsize) #2268"
741
                 * This establishes a counter that breaks out of the loop after a large number of iterations.
742
                 */
743
911M
                if (++counter > LOGICAL_MAX_ATTR_RUN) {
744
0
                    break;
745
0
                }
746
911M
            }
747
2.51M
            a_fs_attr->nrd.run_end = data_run_cur;
748
2.51M
        }
749
2.51M
        a_fs_attr->nrd.run_end->next = a_data_run;
750
2.51M
        a_data_run->offset =
751
2.51M
            a_fs_attr->nrd.run_end->offset + a_fs_attr->nrd.run_end->len;
752
2.51M
    }
753
754
    // update the rest of the offsets in the run (if any exist)
755
2.74M
    data_run_cur = a_data_run;
756
2.74M
    while (data_run_cur->next) {
757
0
        data_run_cur->next->offset =
758
0
            data_run_cur->offset + data_run_cur->len;
759
0
        a_fs_attr->nrd.run_end = data_run_cur->next;
760
0
        data_run_cur = data_run_cur->next;
761
0
    }
762
2.74M
}
763
764
/** \internal
765
 * Processes a resident TSK_FS_ATTR structure and calls the callback with the associated
766
 * data. The size of the buffer in the callback will be block_size at max.
767
 *
768
 * @param a_fs File system being analyzed
769
 * @param fs_attr Resident data structure to be walked
770
 * @param a_flags Flags for walking
771
 * @param a_action Callback action
772
 * @param a_ptr Pointer to data that is passed to callback
773
 * @returns 1 on error or 0 on success
774
 */
775
static uint8_t
776
tsk_fs_attr_walk_res(const TSK_FS_ATTR * fs_attr,
777
    TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action,
778
    void *a_ptr)
779
3.26k
{
780
3.26k
    char *buf = NULL;
781
3.26k
    int myflags;
782
3.26k
    int retval;
783
3.26k
    size_t buf_len = 0;
784
3.26k
    TSK_OFF_T off;
785
3.26k
    size_t read_len;
786
3.26k
    TSK_FS_INFO *fs;
787
788
3.26k
    fs = fs_attr->fs_file->fs_info;
789
790
3.26k
    if ((fs_attr->flags & TSK_FS_ATTR_RES) == 0) {
791
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
792
0
        tsk_error_set_errstr
793
0
            ("tsk_fs_file_walk_res: called with non-resident data");
794
0
        return 1;
795
0
    }
796
797
    /* Allocate a buffer that is at most a block size in length */
798
3.26k
    buf_len = (size_t) fs_attr->size;
799
3.26k
    if (buf_len > fs->block_size)
800
158
        buf_len = fs->block_size;
801
802
3.26k
    if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) {
803
3.26k
        if ((buf = (char*) tsk_malloc(buf_len)) == NULL) {
804
0
            return 1;
805
0
        }
806
3.26k
    }
807
808
3.26k
    myflags =
809
3.26k
        TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC |
810
3.26k
        TSK_FS_BLOCK_FLAG_RES;
811
812
    // Call the callback in (at max) block-sized chunks.
813
3.26k
    retval = TSK_WALK_CONT;
814
7.55k
    for (off = 0; off < fs_attr->size; off += read_len) {
815
816
7.45k
        if ((uint64_t) (fs_attr->size - off) > buf_len)
817
4.28k
            read_len = buf_len;
818
3.17k
        else
819
3.17k
            read_len = (size_t) (fs_attr->size - off);
820
821
7.45k
        if (buf) {
822
            // wipe rest of buffer if we are not going to read into all of it
823
7.45k
            if (read_len != buf_len)
824
122
                memset(&buf[read_len], 0, buf_len - read_len);
825
826
7.45k
            memcpy(buf, &fs_attr->rd.buf[off], read_len);
827
7.45k
        }
828
7.45k
        retval =
829
7.45k
            a_action(fs_attr->fs_file, off, 0, buf, read_len,
830
7.45k
              (TSK_FS_BLOCK_FLAG_ENUM) myflags, a_ptr);
831
832
7.45k
        if (retval != TSK_WALK_CONT)
833
3.17k
            break;
834
7.45k
    }
835
836
3.26k
    free(buf);
837
838
3.26k
    if (retval == TSK_WALK_ERROR)
839
0
        return 1;
840
3.26k
    else
841
3.26k
        return 0;
842
3.26k
}
843
844
845
/** \internal
846
 * Processes a non-resident TSK_FS_ATTR structure and calls the callback with the associated
847
 * data.
848
 *
849
 * @param fs_attr Resident data structure to be walked
850
 * @param a_flags Flags for walking
851
 * @param a_action Callback action
852
 * @param a_ptr Pointer to data that is passed to callback
853
 * @returns 1 on error or 0 on success
854
 */
855
static uint8_t
856
tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr,
857
    TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action,
858
    void *a_ptr)
859
53.4k
{
860
53.4k
    char *buf = NULL;
861
53.4k
    TSK_OFF_T tot_size;
862
53.4k
    TSK_OFF_T off = 0;
863
53.4k
    TSK_FS_ATTR_RUN *fs_attr_run;
864
53.4k
    int retval;
865
53.4k
    uint32_t skip_remain;
866
53.4k
    TSK_FS_INFO *fs = fs_attr->fs_file->fs_info;
867
53.4k
    uint8_t stop_loop = 0;
868
869
53.4k
    if ((fs_attr->flags & TSK_FS_ATTR_NONRES) == 0) {
870
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
871
0
        tsk_error_set_errstr
872
0
            ("tsk_fs_file_walk_nonres: called with non-non-resident data");
873
0
        return 1;
874
0
    }
875
876
    /* if we want the slack space too, then use the allocsize  */
877
53.4k
    if (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK)
878
26.4k
        tot_size = fs_attr->nrd.allocsize;
879
27.0k
    else
880
27.0k
        tot_size = fs_attr->size;
881
882
53.4k
    skip_remain = fs_attr->nrd.skiplen;
883
884
53.4k
    if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) {
885
47.3k
        if ((buf = (char *) tsk_malloc(fs->block_size)) == NULL) {
886
0
            return 1;
887
0
        }
888
47.3k
    }
889
890
    /* cycle through the number of runs we have */
891
53.4k
    retval = TSK_WALK_CONT;
892
59.9k
    for (fs_attr_run = fs_attr->nrd.run; fs_attr_run;
893
53.4k
        fs_attr_run = fs_attr_run->next) {
894
53.3k
        TSK_DADDR_T addr, len_idx;
895
896
53.3k
        addr = fs_attr_run->addr;
897
898
        /* cycle through each block in the run */
899
36.7M
        for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) {
900
901
36.7M
            TSK_FS_BLOCK_FLAG_ENUM myflags;
902
903
            /* If the address is too large then give an error */
904
36.7M
            if (addr + len_idx > fs->last_block) {
905
449
                if (fs_attr->fs_file->
906
449
                    meta->flags & TSK_FS_META_FLAG_UNALLOC)
907
247
                    tsk_error_set_errno(TSK_ERR_FS_RECOVER);
908
202
                else
909
202
                    tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
910
449
                tsk_error_set_errstr
911
449
                    ("Invalid address in run (too large): %" PRIuDADDR "",
912
449
                    addr + len_idx);
913
449
                free(buf);
914
449
                return 1;
915
449
            }
916
917
            // load the buffer if they want more than just the address
918
36.7M
            if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) {
919
920
                /* sparse files just get 0s */
921
36.7M
                if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) {
922
1.29M
                    memset(buf, 0, fs->block_size);
923
1.29M
                }
924
                /* FILLER entries exist when the source file system can store run
925
                 * info out of order and we did not get all of the run info.  We
926
                 * return 0s if data is read from this type of run. */
927
35.4M
                else if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
928
32.5M
                    memset(buf, 0, fs->block_size);
929
32.5M
                    if (tsk_verbose)
930
0
                        fprintf(stderr,
931
0
                            "tsk_fs_attr_walk_nonres: File %" PRIuINUM
932
0
                            " has FILLER entry, using 0s\n",
933
0
                            fs_attr->fs_file->meta->addr);
934
32.5M
                }
935
936
                // we return 0s for reads past the initsize
937
2.89M
                else if (off >= fs_attr->nrd.initsize
938
2.89M
                    && (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK) == 0) {
939
1.75M
                    memset(buf, 0, fs->block_size);
940
1.75M
                }
941
1.14M
                else {
942
1.14M
                    ssize_t cnt;
943
1.14M
          if (fs->ftype == TSK_FS_TYPE_LOGICAL) {
944
            // We can't read logical files directly from the image.
945
0
            cnt = logicalfs_read_block(fs, fs_attr->fs_file, addr + len_idx, buf);
946
0
          }
947
1.14M
          else {
948
1.14M
            cnt = tsk_fs_read_block_decrypt
949
1.14M
            (fs, addr + len_idx, buf, fs->block_size, fs_attr_run->crypto_id + len_idx);
950
1.14M
          }
951
1.14M
                    if (cnt != fs->block_size) {
952
1.79k
                        if (cnt >= 0) {
953
0
                            tsk_error_reset();
954
0
                            tsk_error_set_errno(TSK_ERR_FS_READ);
955
0
                        }
956
1.79k
                        tsk_error_set_errstr2
957
1.79k
                            ("tsk_fs_file_walk: Error reading block at %"
958
1.79k
                            PRIuDADDR, addr + len_idx);
959
1.79k
                        free(buf);
960
1.79k
                        return 1;
961
1.79k
                    }
962
1.13M
                    if (off + fs->block_size > fs_attr->nrd.initsize
963
1.13M
                        && (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK) == 0) {
964
24.7k
                        memset(&buf[fs_attr->nrd.initsize - off], 0,
965
24.7k
                            fs->block_size -
966
24.7k
                            (size_t) (fs_attr->nrd.initsize - off));
967
24.7k
                    }
968
1.13M
                }
969
36.7M
            }
970
971
            /* Need to account for the skip length, which is the number of bytes
972
             * in the start of the attribute that are skipped and that are not
973
             * included in the overall length.  We will seek past those and not
974
             * return those in the action.  We just read a block size so check
975
             * if there is data to be returned in this buffer. */
976
36.7M
            if (skip_remain >= fs->block_size) {
977
0
                skip_remain -= fs->block_size;
978
0
            }
979
36.7M
            else {
980
36.7M
                size_t ret_len;
981
982
                /* Do we want to return a full block, or just the end? */
983
36.7M
                if ((TSK_OFF_T) fs->block_size - skip_remain <
984
36.7M
                    tot_size - off)
985
36.7M
                    ret_len = fs->block_size - skip_remain;
986
44.6k
                else
987
44.6k
                    ret_len = (size_t) (tot_size - off);
988
989
                /* Only do sparse or FILLER clusters if NOSPARSE is not set */
990
36.7M
                if ((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) ||
991
36.7M
                    (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ||
992
36.7M
                    (off > fs_attr->nrd.initsize)) {
993
36.6M
                    myflags = fs->block_getflags(fs, 0);
994
36.6M
                    myflags = (TSK_FS_BLOCK_FLAG_ENUM) (myflags | TSK_FS_BLOCK_FLAG_SPARSE);
995
36.6M
                    if ((a_flags & TSK_FS_FILE_WALK_FLAG_NOSPARSE) == 0) {
996
36.6M
                        retval =
997
36.6M
                            a_action(fs_attr->fs_file, off, 0,
998
36.6M
                            &buf[skip_remain], ret_len, myflags, a_ptr);
999
36.6M
                    }
1000
36.6M
                }
1001
155k
                else {
1002
155k
                    myflags = fs->block_getflags(fs, addr + len_idx);
1003
155k
                    myflags = (TSK_FS_BLOCK_FLAG_ENUM) (myflags | TSK_FS_BLOCK_FLAG_RAW);
1004
1005
155k
                    retval =
1006
155k
                        a_action(fs_attr->fs_file, off, addr + len_idx,
1007
155k
                        &buf[skip_remain], ret_len, myflags, a_ptr);
1008
155k
                }
1009
36.7M
                off += ret_len;
1010
36.7M
                skip_remain = 0;
1011
1012
36.7M
                if (retval != TSK_WALK_CONT) {
1013
44.4k
                    stop_loop = 1;
1014
44.4k
                    break;
1015
44.4k
                }
1016
1017
36.7M
                if (off >= tot_size) {
1018
151
                    stop_loop = 1;
1019
151
                    break;
1020
151
                }
1021
36.7M
            }
1022
36.7M
        }
1023
51.0k
        if (stop_loop)
1024
44.6k
            break;
1025
51.0k
    }
1026
1027
51.2k
    free(buf);
1028
1029
51.2k
    if (retval == TSK_WALK_ERROR)
1030
1
        return 1;
1031
51.2k
    else
1032
51.2k
        return 0;
1033
51.2k
}
1034
1035
1036
/**
1037
 * \ingroup fslib
1038
 * Process an attribute and call a callback function with its contents. The callback will be
1039
 * called with chunks of data that are fs->block_size or less.  The address given in the callback
1040
 * will be correct only for raw files (when the raw file contents were stored in the block).  For
1041
 * compressed and sparse attributes, the address may be zero.
1042
 *
1043
 * @param a_fs_attr Attribute to process
1044
 * @param a_flags Flags to use while processing attribute
1045
 * @param a_action Callback action to call with content
1046
 * @param a_ptr Pointer that will passed to callback
1047
 * @returns 1 on error and 0 on success.
1048
 */
1049
uint8_t
1050
tsk_fs_attr_walk(const TSK_FS_ATTR * a_fs_attr,
1051
    TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action,
1052
    void *a_ptr)
1053
58.0k
{
1054
58.0k
    TSK_FS_INFO *fs;
1055
1056
    // clean up any error messages that are lying around
1057
58.0k
    tsk_error_reset();
1058
1059
    // check the FS_INFO, FS_FILE structures
1060
58.0k
    if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
1061
58.0k
        || (a_fs_attr->fs_file->meta == NULL)
1062
58.0k
        || (a_fs_attr->fs_file->fs_info == NULL)) {
1063
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1064
0
        tsk_error_set_errstr
1065
0
            ("tsk_fs_attr_walk: called with NULL pointers");
1066
0
        return 1;
1067
0
    }
1068
58.0k
    fs = a_fs_attr->fs_file->fs_info;
1069
1070
58.0k
    if (fs->tag != TSK_FS_INFO_TAG) {
1071
//        || (a_fs_attr->id != TSK_FS_ATTR_ID)) {
1072
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1073
0
        tsk_error_set_errstr
1074
0
            ("tsk_fs_attr_walk: called with unallocated structures");
1075
0
        return 1;
1076
0
    }
1077
58.0k
    if (a_fs_attr->flags & TSK_FS_ATTR_COMP) {
1078
1.31k
        if (a_fs_attr->w == NULL) {
1079
0
            tsk_error_set_errno(TSK_ERR_FS_ARG);
1080
0
            tsk_error_set_errstr
1081
0
                ("tsk_fs_attr_walk: compressed attribute found, but special function not defined");
1082
0
            return 1;
1083
0
        }
1084
1.31k
        return a_fs_attr->w(a_fs_attr, a_flags, a_action, a_ptr);
1085
1.31k
    }
1086
    // resident data
1087
56.7k
    if (a_fs_attr->flags & TSK_FS_ATTR_RES) {
1088
3.26k
    fflush(stderr);
1089
3.26k
        return tsk_fs_attr_walk_res(a_fs_attr, a_flags, a_action, a_ptr);
1090
3.26k
    }
1091
    // non-resident data
1092
53.4k
    else if (a_fs_attr->flags & TSK_FS_ATTR_NONRES) {
1093
53.4k
        return tsk_fs_attr_walk_nonres(a_fs_attr, a_flags, a_action,
1094
53.4k
            a_ptr);
1095
53.4k
    }
1096
1097
0
    tsk_error_set_errno(TSK_ERR_FS_ARG);
1098
0
    tsk_error_set_errstr
1099
0
        ("tsk_fs_attr_walk: called with unknown attribute type: %x",
1100
0
        a_fs_attr->flags);
1101
0
    return 1;
1102
56.7k
}
1103
1104
1105
1106
/**
1107
 * \ingroup fslib
1108
 * Read the contents of a given attribute using a typical read() type interface.
1109
 * 0s are returned for missing runs.
1110
 *
1111
 * @param a_fs_attr The attribute to read.
1112
 * @param a_offset The byte offset to start reading from.
1113
 * @param a_buf The buffer to read the data into.
1114
 * @param a_len The number of bytes to read from the file.
1115
 * @param a_flags Flags to use while reading
1116
 * @returns The number of bytes read or -1 on error (incl if offset is past end of file).
1117
 */
1118
ssize_t
1119
tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
1120
    char *a_buf, size_t a_len, TSK_FS_FILE_READ_FLAG_ENUM a_flags)
1121
8.57M
{
1122
8.57M
    TSK_FS_INFO *fs;
1123
1124
8.57M
    if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
1125
8.57M
        || (a_fs_attr->fs_file->fs_info == NULL) || (a_buf == NULL)) {
1126
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1127
0
        tsk_error_set_errstr
1128
0
            ("tsk_fs_attr_read: Attribute has null pointers.");
1129
0
        return -1;
1130
0
    }
1131
8.57M
    fs = a_fs_attr->fs_file->fs_info;
1132
1133
  // Handle logical directories separately
1134
8.57M
  if (fs->ftype == TSK_FS_TYPE_LOGICAL) {
1135
0
    return logicalfs_read(fs, a_fs_attr->fs_file, a_offset, a_len, a_buf);
1136
0
  }
1137
1138
    /* for compressed data, call the specialized function */
1139
8.57M
    if (a_fs_attr->flags & TSK_FS_ATTR_COMP) {
1140
0
        if (a_fs_attr->r == NULL) {
1141
0
            tsk_error_set_errno(TSK_ERR_FS_ARG);
1142
0
            tsk_error_set_errstr
1143
0
                ("tsk_fs_attr_read: Attribute has compressed type set, but no compressed read function defined");
1144
0
            return -1;
1145
0
        }
1146
0
        return a_fs_attr->r(a_fs_attr, a_offset, a_buf, a_len);
1147
0
    }
1148
1149
    /* For resident data, copy data from the local buffer */
1150
8.57M
    else if (a_fs_attr->flags & TSK_FS_ATTR_RES) {
1151
22.5k
        size_t len_toread;
1152
1153
22.5k
        if (a_offset >= a_fs_attr->size) {
1154
1
            tsk_error_reset();
1155
1
            tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
1156
1
            tsk_error_set_errstr("tsk_fs_attr_read - %" PRIdOFF, a_offset);
1157
1
            return -1;
1158
1
        }
1159
1160
22.5k
        len_toread = a_len;
1161
22.5k
        if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->size) {
1162
2
            len_toread = (size_t) (a_fs_attr->size - a_offset);
1163
2
            memset(&a_buf[len_toread], 0, a_len - len_toread);
1164
2
        }
1165
1166
22.5k
        memcpy(a_buf, &a_fs_attr->rd.buf[a_offset], len_toread);
1167
1168
22.5k
        return (ssize_t) len_toread;
1169
22.5k
    }
1170
1171
    /* For non-resident data, load the needed block and copy the data */
1172
8.55M
    else if (a_fs_attr->flags & TSK_FS_ATTR_NONRES) {
1173
8.55M
        TSK_FS_ATTR_RUN *data_run_cur;
1174
8.55M
        TSK_DADDR_T blkoffset_toread;   // block offset of where we want to start reading from
1175
8.55M
        size_t byteoffset_toread;       // byte offset in blkoffset_toread of where we want to start reading from
1176
8.55M
        ssize_t len_remain;      // length remaining to copy
1177
8.55M
        size_t len_toread;      // length total to copy
1178
1179
8.55M
        if (((a_flags & TSK_FS_FILE_READ_FLAG_SLACK)
1180
8.55M
                && (a_offset >= a_fs_attr->nrd.allocsize))
1181
8.55M
            || (!(a_flags & TSK_FS_FILE_READ_FLAG_SLACK)
1182
8.55M
                && (a_offset >= a_fs_attr->size))) {
1183
5.36k
            tsk_error_reset();
1184
5.36k
            tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
1185
5.36k
            tsk_error_set_errstr("tsk_fs_attr_read - %" PRIdOFF, a_offset);
1186
5.36k
            return -1;
1187
5.36k
        }
1188
1189
8.54M
        blkoffset_toread = a_offset / fs->block_size;
1190
8.54M
        byteoffset_toread = (size_t) (a_offset % fs->block_size);
1191
1192
        // determine how many bytes we can copy
1193
8.54M
        len_toread = a_len;
1194
8.54M
        if (a_flags & TSK_FS_FILE_READ_FLAG_SLACK) {
1195
0
            if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->nrd.allocsize)
1196
0
                len_toread =
1197
0
                    (size_t) (a_fs_attr->nrd.allocsize - a_offset);
1198
0
        }
1199
8.54M
        else {
1200
8.54M
            if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->size)
1201
4.67k
                len_toread = (size_t) (a_fs_attr->size - a_offset);
1202
8.54M
        }
1203
1204
1205
        // wipe the buffer we won't read into
1206
8.54M
        if (len_toread < a_len)
1207
4.67k
            memset(&a_buf[len_toread], 0, a_len - len_toread);
1208
1209
8.54M
        len_remain = len_toread;
1210
1211
        // cycle through the runs until we find the one where our offset starts
1212
127M
        for (data_run_cur = a_fs_attr->nrd.run; data_run_cur && len_remain > 0;
1213
119M
            data_run_cur = data_run_cur->next) {
1214
1215
119M
            TSK_DADDR_T blkoffset_inrun;
1216
119M
            size_t len_inrun;
1217
1218
            // See if this run contains the starting offset they requested
1219
119M
            if (data_run_cur->offset + data_run_cur->len <=
1220
119M
                blkoffset_toread)
1221
110M
                continue;
1222
1223
            // We want this run, so find out the offset that we want
1224
            // we'll start at 0 if we already read data in the last run.
1225
8.54M
            if (data_run_cur->offset < blkoffset_toread)
1226
7.53M
                blkoffset_inrun = blkoffset_toread - data_run_cur->offset;
1227
1.00M
            else
1228
1.00M
                blkoffset_inrun = 0;
1229
1230
            // see if we need to read the rest of this run and into the next or if it is all here
1231
8.54M
            len_inrun = len_remain;
1232
8.54M
            if ((data_run_cur->len - blkoffset_inrun) * fs->block_size -
1233
8.54M
                byteoffset_toread < (size_t)len_remain) {
1234
3.27k
                len_inrun =
1235
3.27k
                    (size_t) ((data_run_cur->len -
1236
3.27k
                        blkoffset_inrun) * fs->block_size -
1237
3.27k
                    byteoffset_toread);
1238
3.27k
            }
1239
1240
            /* sparse files/runs just get 0s */
1241
8.54M
            if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) {
1242
5.40M
                memset(&a_buf[len_toread - len_remain], 0, len_inrun);
1243
5.40M
            }
1244
1245
            /* FILLER entries exist when the source file system can store run
1246
             * info out of order and we did not get all of the run info.  We
1247
             * return 0s if data is read from this type of run. */
1248
3.14M
            else if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
1249
2.17M
                if (a_buf == NULL) {
1250
0
                    tsk_error_reset();
1251
0
                    tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
1252
0
                    tsk_error_set_errstr("tsk_fs_attr_read - missing a_buf");
1253
0
                    return -1;
1254
0
                }
1255
2.17M
                memset(&a_buf[len_toread - len_remain], 0, len_inrun);
1256
2.17M
                if (tsk_verbose)
1257
0
                    fprintf(stderr,
1258
0
                        "tsk_fs_attr_read_type: File %" PRIuINUM
1259
0
                        " has FILLER entry, using 0s\n",
1260
0
                        (a_fs_attr->fs_file->meta) ? a_fs_attr->
1261
0
                        fs_file->meta->addr : 0);
1262
2.17M
            }
1263
1264
            // we return 0s for reads past the initsize (unless they want slack space)
1265
973k
            else if (((TSK_OFF_T) ((data_run_cur->offset +
1266
973k
                            blkoffset_inrun) * fs->block_size +
1267
973k
                        byteoffset_toread) >= a_fs_attr->nrd.initsize)
1268
973k
                && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) {
1269
214
                memset(&a_buf[len_toread - len_remain], 0, len_inrun);
1270
214
                if (tsk_verbose)
1271
0
                    fprintf(stderr,
1272
0
                        "tsk_fs_attr_read: Returning 0s for read past end of initsize (%"
1273
0
                        PRIuINUM ")\n", ((a_fs_attr->fs_file)
1274
0
                            && (a_fs_attr->fs_file->
1275
0
                                meta)) ? a_fs_attr->fs_file->meta->
1276
0
                        addr : 0);
1277
214
            }
1278
1279
            // we are going to read some data
1280
973k
            else {
1281
973k
                TSK_OFF_T fs_offset_b;
1282
973k
                ssize_t cnt;
1283
1284
                // calculate the byte offset in the file system that we want to read from
1285
973k
                fs_offset_b =
1286
973k
                    (data_run_cur->addr +
1287
973k
                    blkoffset_inrun) * fs->block_size;
1288
1289
                // add the byte offset in the block
1290
973k
                fs_offset_b += byteoffset_toread;
1291
973k
        cnt =
1292
973k
          tsk_fs_read_decrypt(fs, fs_offset_b,
1293
973k
            &a_buf[len_toread - len_remain], len_inrun,
1294
973k
            data_run_cur->crypto_id + blkoffset_inrun);
1295
1296
973k
                if (cnt != (ssize_t)len_inrun) {
1297
80.9k
                    if (cnt >= 0) {
1298
59.9k
                        tsk_error_reset();
1299
59.9k
                        tsk_error_set_errno(TSK_ERR_FS_READ);
1300
59.9k
                    }
1301
80.9k
                    tsk_error_set_errstr2
1302
80.9k
                        ("tsk_fs_attr_read_type: offset: %" PRIdOFF
1303
80.9k
                        "  Len: %" PRIuSIZE "", fs_offset_b, len_inrun);
1304
80.9k
                    return cnt;
1305
80.9k
                }
1306
1307
                // see if part of the data is in the non-initialized space
1308
892k
                if (((TSK_OFF_T) ((data_run_cur->offset +
1309
892k
                                blkoffset_inrun) * fs->block_size +
1310
892k
                            byteoffset_toread + len_inrun) >
1311
892k
                        a_fs_attr->nrd.initsize)
1312
892k
                    && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) {
1313
1314
56
                    size_t uninit_off = (size_t) (a_fs_attr->nrd.initsize -
1315
56
                        ((data_run_cur->offset +
1316
56
                                blkoffset_inrun) * fs->block_size +
1317
56
                            byteoffset_toread));
1318
1319
56
                    memset(&a_buf[len_toread - len_remain + uninit_off], 0,
1320
56
                        len_inrun - uninit_off);
1321
56
                }
1322
1323
892k
            }
1324
1325
8.46M
            len_remain -= len_inrun;
1326
1327
            // reset this in case we need to also read from the next run
1328
8.46M
            byteoffset_toread = 0;
1329
8.46M
        }
1330
8.46M
        return (ssize_t) (len_toread - len_remain);
1331
8.54M
    }
1332
1333
0
    tsk_error_set_errno(TSK_ERR_FS_ARG);
1334
0
    tsk_error_set_errstr("tsk_fs_attr_read: Unknown attribute type: %x",
1335
0
        a_fs_attr->flags);
1336
0
    return -1;
1337
8.57M
}