Coverage Report

Created: 2025-10-12 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/fs/ext2fs.cpp
Line
Count
Source
1
/*
2
** The Sleuth Kit
3
**
4
** Brian Carrier [carrier <at> sleuthkit [dot] org]
5
** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
6
** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
7
**
8
** TASK
9
** Copyright (c) 2002-2003 Brian Carrier, @stake Inc.  All rights reserved
10
**
11
** Copyright (c) 1997,1998,1999, International Business Machines
12
** Corporation and others. All Rights Reserved.
13
*/
14
15
/**
16
 *\file ext2fs.c
17
 * Contains the internal TSK ext2/ext3/ext4 file system functions.
18
 */
19
20
/* TCT
21
 * LICENSE
22
 *  This software is distributed under the IBM Public License.
23
 * AUTHOR(S)
24
 *  Wietse Venema
25
 *  IBM T.J. Watson Research
26
 *  P.O. Box 704
27
 *  Yorktown Heights, NY 10598, USA
28
 --*/
29
30
#include "tsk_fs_i.h"
31
#include "tsk_ext2fs.h"
32
#include "tsk/base/crc.h"
33
34
#include <stddef.h>
35
36
#include <memory>
37
38
//#define Ext4_DBG 1
39
//#define EXT4_CHECKSUMS 1
40
41
42
#ifdef Ext4_DBG
43
static uint8_t
44
debug_print_buf(unsigned char *buf, int len)
45
{
46
    int i = 0;
47
    for (i = 0; i < len; i++) {
48
        if (i % 8 == 0)
49
            printf("%08X:\t", i);
50
        printf("0x%02X ", buf[i]);
51
        if ((i + 1) % 8 == 0)
52
            printf("\n");
53
    }
54
    printf("\n");
55
    return 0;
56
}
57
#endif
58
59
60
/** \internal
61
    test_root - tests to see if a is power of b
62
    @param a the number being investigated
63
    @param b the root
64
    @return 1 if a is a power of b, otherwise 0
65
*/
66
static uint8_t
67
test_root(uint32_t a, uint32_t b)
68
0
{
69
0
    if (a == 0) {
70
0
        return b == 0;
71
0
    }
72
0
    else if (b == 0) {
73
0
        return 0;
74
0
    }
75
0
    else if (a == 1) {
76
        // anything to power of 0 is 1
77
0
        return 1;
78
0
    }
79
0
    else if (b == 1) {
80
0
        return 0;
81
0
    }
82
83
    // keep on multiplying b by itself
84
0
    uint32_t b2;
85
0
    for (b2 = b; b2 < a; b2 *= b) {}
86
87
    // was it an exact match?
88
0
    return b2 == a;
89
0
}
90
91
/** \internal
92
 * Test if block group has a super block in it.
93
 *
94
 * @return 1 if block group has superblock, otherwise 0
95
*/
96
static uint32_t
97
ext2fs_is_super_bg(uint32_t feature_ro_compat, uint32_t group_block)
98
0
{
99
    // if no sparse feature, then it has super block
100
0
    if (!(feature_ro_compat & EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER))
101
0
        return 1;
102
103
    // group 0 always has super block
104
0
    if (group_block == 0)
105
0
        return 1;
106
107
    // Sparse FS put super blocks in groups that are powers of 3, 5, 7
108
0
    if (test_root(group_block, 3) ||
109
0
            (test_root(group_block, 5)) ||
110
0
            (test_root(group_block, 7))) {
111
0
        return 1;
112
0
    }
113
114
0
    return 0;
115
0
}
116
117
118
119
120
121
/* ext2fs_group_load - load 32-bit or 64-bit block group descriptor into cache
122
 *
123
 * Note: This routine assumes &ext2fs->lock is locked by the caller.
124
 *
125
 * return 1 on error and 0 on success.  On success one of either ext2fs->grp_buf or ext2fs->ext4_grp_buf will
126
 * be non-null and contain the valid data. Because Ext4 can have 32-bit group descriptors, check which buffer is
127
 * non-null to determine what to read instead of duplicating the logic everywhere.
128
 *
129
 * */
130
static uint8_t
131
    ext2fs_group_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
132
0
{
133
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) ext2fs;
134
0
    size_t gd_size = tsk_getu16(fs->endian, ext2fs->fs->s_desc_size);
135
136
    /*
137
    * Sanity check
138
    */
139
0
    if (grp_num >= ext2fs->groups_count) {
140
0
        tsk_error_reset();
141
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
142
0
        tsk_error_set_errstr
143
0
            ("ext2fs_group_load: invalid cylinder group number: %"
144
0
            PRI_EXT2GRP "", grp_num);
145
0
        return 1;
146
0
    }
147
    // already loaded
148
0
    else if (ext2fs->grp_num == grp_num) {
149
0
        return 0;
150
0
    }
151
152
    // 64-bit version.
153
0
    if (((fs->ftype == TSK_FS_TYPE_EXT4)) && (EXT2FS_HAS_INCOMPAT_FEATURE(fs, ext2fs->fs,
154
0
        EXT2FS_FEATURE_INCOMPAT_64BIT)
155
0
        && (tsk_getu16(fs->endian, ext2fs->fs->s_desc_size) >= 64))) {
156
0
            TSK_OFF_T offs;
157
0
            ssize_t cnt;
158
159
0
            if (gd_size < (int)sizeof(ext4fs_gd))
160
0
                gd_size = sizeof(ext4fs_gd);
161
162
0
            if (ext2fs->ext4_grp_buf == NULL) {
163
0
                if ((ext2fs->ext4_grp_buf = (ext4fs_gd *) tsk_malloc(gd_size)) == NULL)
164
0
                    return 1;
165
0
            }
166
0
            offs = ext2fs->groups_offset + grp_num * gd_size;
167
168
0
            cnt = tsk_fs_read(&ext2fs->fs_info, offs, (char *) ext2fs->ext4_grp_buf, gd_size);
169
170
#ifdef Ext4_DBG
171
            debug_print_buf((char *) ext2fs->ext4_grp_buf, gd_size);
172
#endif
173
0
            if (cnt != (ssize_t) gd_size) {
174
0
                if (cnt >= 0) {
175
0
                    tsk_error_reset();
176
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
177
0
                }
178
0
                tsk_error_set_errstr2("ext2fs_group_load: Group descriptor %"
179
0
                    PRI_EXT2GRP " at %" PRIdOFF, grp_num, offs);
180
0
                return 1;
181
0
            }
182
183
            // sanity checks
184
0
            if ((ext4_getu64(fs->endian,
185
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_hi,
186
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_lo) > fs->last_block) ||
187
0
                (ext4_getu64(fs->endian,
188
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_hi,
189
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_lo) > fs->last_block) ||
190
0
                (ext4_getu64(fs->endian,
191
0
                ext2fs->ext4_grp_buf->bg_inode_table_hi,
192
0
                ext2fs->ext4_grp_buf->bg_inode_table_lo) > fs->last_block)) {
193
0
                    tsk_error_reset();
194
0
                    tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
195
0
                    tsk_error_set_errstr("extXfs_group_load: Ext4 Group %" PRI_EXT2GRP
196
0
                        " descriptor block locations too large at byte offset %"
197
0
                        PRIuDADDR, grp_num, offs);
198
0
                    return 1;
199
0
            }
200
0
    }
201
0
    else {
202
0
        TSK_OFF_T offs;
203
0
        ssize_t cnt;
204
0
        if (gd_size < (int)sizeof(ext2fs_gd))
205
0
            gd_size = sizeof(ext2fs_gd);
206
207
0
        if (ext2fs->grp_buf == NULL) {
208
0
            if ((ext2fs->grp_buf = (ext2fs_gd *) tsk_malloc(gd_size)) == NULL)
209
0
                return 1;
210
0
        }
211
0
        offs = ext2fs->groups_offset + grp_num * gd_size;
212
213
0
        cnt = tsk_fs_read(&ext2fs->fs_info, offs, (char *) ext2fs->grp_buf, gd_size);
214
215
0
        if (cnt != (ssize_t) gd_size) {
216
0
            if (cnt >= 0) {
217
0
                tsk_error_reset();
218
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
219
0
            }
220
0
            tsk_error_set_errstr2("ext2fs_group_load: Group descriptor %"
221
0
                PRI_EXT2GRP " at %" PRIdOFF, grp_num, offs);
222
0
            return 1;
223
0
        }
224
225
        // sanity checks
226
0
        if ((tsk_getu32(fs->endian,
227
0
            ext2fs->grp_buf->bg_block_bitmap) > fs->last_block) ||
228
0
            (tsk_getu32(fs->endian,
229
0
            ext2fs->grp_buf->bg_inode_bitmap) > fs->last_block) ||
230
0
            (tsk_getu32(fs->endian,
231
0
            ext2fs->grp_buf->bg_inode_table) > fs->last_block)) {
232
0
                tsk_error_reset();
233
0
                tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
234
0
                tsk_error_set_errstr("extXfs_group_load: Group %" PRI_EXT2GRP
235
0
                    " descriptor block locations too large at byte offset %"
236
0
                    PRIuDADDR, grp_num, offs);
237
0
                return 1;
238
0
        }
239
240
0
        if (tsk_verbose) {
241
0
            TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
242
0
            tsk_fprintf(stderr,
243
0
                "\tgroup %" PRI_EXT2GRP ": %" PRIu16 "/%" PRIu16
244
0
                " free blocks/inodes\n", grp_num, tsk_getu16(fs->endian,
245
0
                ext2fs->grp_buf->bg_free_blocks_count),
246
0
                tsk_getu16(fs->endian, ext2fs->grp_buf->bg_free_inodes_count));
247
0
        }
248
0
    }
249
0
    ext2fs->grp_num = grp_num;
250
251
0
    return 0;
252
0
}
253
254
#ifdef EXT4_CHECKSUMS
255
/**
256
 * ext4_group_desc_csum - Calculates the checksum of a group descriptor
257
 * Ported from linux/fs/ext4/super.c
258
 * @ext4_sb:       pointer to ext2 super block structure
259
 * @block_group:   group descriptor number
260
 * @gdp:           pointer to group descriptor to calculate checksum for
261
 * returns the checksum value
262
 */
263
static uint16_t
264
ext4_group_desc_csum(ext2fs_sb * ext4_sb, uint32_t block_group,
265
    struct ext4fs_gd *gdp)
266
{
267
    cm_t CRC16_CTX;
268
    CRC16_CTX.cm_width = 16;
269
    CRC16_CTX.cm_poly = 0x8005L;
270
    CRC16_CTX.cm_init = 0xFFFFL;
271
    CRC16_CTX.cm_refin = TRUE;
272
    CRC16_CTX.cm_refot = TRUE;
273
    CRC16_CTX.cm_xorot = 0x0000L;
274
    cm_ini(&CRC16_CTX);
275
    if (*ext4_sb->s_feature_ro_compat & EXT2FS_FEATURE_RO_COMPAT_GDT_CSUM) {
276
        int offset = offsetof(struct ext4fs_gd, bg_checksum);
277
        uint32_t le_group = tsk_getu32(TSK_LIT_ENDIAN, &block_group);
278
        crc16(&CRC16_CTX, ext4_sb->s_uuid, sizeof(ext4_sb->s_uuid));
279
        crc16(&CRC16_CTX, (uint8_t *) & le_group, sizeof(le_group));
280
        crc16(&CRC16_CTX, (uint8_t *) gdp, offset);
281
        offset += sizeof(gdp->bg_checksum);     /* skip checksum */
282
        /* for checksum of struct ext4_group_desc do the rest... */
283
        if ((*ext4_sb->s_feature_incompat &
284
                EXT2FS_FEATURE_INCOMPAT_64BIT) &&
285
            offset < *ext4_sb->s_desc_size) {
286
            crc16(&CRC16_CTX, (uint8_t *) gdp + offset,
287
                *ext4_sb->s_desc_size - offset);
288
        }
289
    }
290
291
    return cm_crc(&CRC16_CTX);
292
}
293
#endif
294
295
296
/* ext2fs_print_map - print a bitmap */
297
298
static void
299
ext2fs_print_map(uint8_t * map, int len)
300
0
{
301
0
    int i;
302
303
0
    for (i = 0; i < len; i++) {
304
0
        if (i > 0 && i % 10 == 0)
305
0
            putc('|', stderr);
306
0
        putc(isset(map, i) ? '1' : '.', stderr);
307
0
    }
308
0
    putc('\n', stderr);
309
0
}
310
311
#define INODE_TABLE_SIZE(ext2fs) \
312
0
    ((tsk_getu32(ext2fs->fs_info.endian, ext2fs->fs->s_inodes_per_group) * ext2fs->inode_size - 1) \
313
0
           / ext2fs->fs_info.block_size + 1)
314
315
/* ext2fs_bmap_load - look up block bitmap & load into cache
316
 *
317
 * Note: This routine assumes &ext2fs->lock is locked by the caller.
318
 *
319
 * return 1 on error and 0 on success
320
 * */
321
static uint8_t
322
ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
323
0
{
324
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
325
0
    ssize_t cnt;
326
0
    TSK_DADDR_T addr;
327
328
    /*
329
     * Look up the group descriptor info.  The load will do the sanity check.
330
     */
331
0
    if (ext2fs_group_load(ext2fs, grp_num)) {
332
0
        return 1;
333
0
    }
334
335
0
    if (ext2fs->bmap_buf == NULL) {
336
0
        if ((ext2fs->bmap_buf =
337
0
                (uint8_t *) tsk_malloc(fs->block_size)) == NULL) {
338
0
            return 1;
339
0
        }
340
0
    }
341
0
    else if (ext2fs->bmap_grp_num == grp_num) {
342
0
        return 0;
343
0
    }
344
345
0
    if (ext2fs->ext4_grp_buf != NULL) {
346
0
        addr = ext4_getu64(fs->endian,
347
0
            ext2fs->ext4_grp_buf->bg_block_bitmap_hi,
348
0
            ext2fs->ext4_grp_buf->bg_block_bitmap_lo);
349
0
    }
350
0
    else {
351
0
        addr = (TSK_DADDR_T) tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap);
352
0
    }
353
354
0
    if (addr > fs->last_block) {
355
0
        tsk_error_reset();
356
0
        tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
357
0
        tsk_error_set_errstr
358
0
            ("ext2fs_bmap_load: Block too large for image: %" PRIu64, addr);
359
0
        return 1;
360
0
    }
361
362
0
    cnt = tsk_fs_read(fs, addr * fs->block_size,
363
0
        (char *) ext2fs->bmap_buf, ext2fs->fs_info.block_size);
364
365
0
    if (cnt != ext2fs->fs_info.block_size) {
366
0
        if (cnt >= 0) {
367
0
            tsk_error_reset();
368
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
369
0
        }
370
0
        tsk_error_set_errstr2("ext2fs_bmap_load: block bitmap %"
371
0
            PRI_EXT2GRP " at %" PRIu64, grp_num, addr);
372
0
        return 1;
373
0
    }
374
375
0
    ext2fs->bmap_grp_num = grp_num;
376
0
    if (tsk_verbose > 1)
377
0
        ext2fs_print_map(ext2fs->bmap_buf,
378
0
            tsk_getu32(fs->endian, ext2fs->fs->s_blocks_per_group));
379
0
    return 0;
380
0
}
381
382
383
/* ext2fs_imap_load - look up inode bitmap & load into cache
384
 *
385
 * Note: This routine assumes &ext2fs->lock is locked by the caller.
386
 *
387
 * return 0 on success and 1 on error
388
 * */
389
static uint8_t
390
    ext2fs_imap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
391
0
{
392
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
393
0
    ssize_t cnt;
394
0
    TSK_DADDR_T addr;
395
396
    /*
397
    * Look up the group descriptor info.
398
    */
399
0
    if (ext2fs_group_load(ext2fs, grp_num)) {
400
0
        return 1;
401
0
    }
402
403
    /* Allocate the cache buffer and exit if map is already loaded */
404
0
    if (ext2fs->imap_buf == NULL) {
405
0
        if ((ext2fs->imap_buf =
406
0
            (uint8_t *) tsk_malloc(fs->block_size)) == NULL) {
407
0
                return 1;
408
0
        }
409
0
    }
410
0
    else if (ext2fs->imap_grp_num == grp_num) {
411
0
        return 0;
412
0
    }
413
414
    /*
415
    * Look up the inode allocation bitmap.
416
    */
417
0
    if (ext2fs->ext4_grp_buf != NULL) {
418
0
        addr = ext4_getu64(fs->endian,
419
0
            ext2fs->ext4_grp_buf->bg_inode_bitmap_hi,
420
0
            ext2fs->ext4_grp_buf->bg_inode_bitmap_lo);
421
0
    }
422
0
    else {
423
0
        addr = (TSK_DADDR_T) tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap);
424
0
    }
425
426
0
    if (addr > fs->last_block) {
427
0
        tsk_error_reset();
428
0
        tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
429
0
        tsk_error_set_errstr
430
0
            ("ext2fs_imap_load: Block too large for image: %" PRIu64, addr);
431
0
        return 1;
432
0
    }
433
434
    // Ensure the bitmap buffer is initialized.
435
0
    memset(ext2fs->imap_buf, 0, fs->block_size);
436
437
0
    cnt = tsk_fs_read(fs, addr * fs->block_size,
438
0
        (char *) ext2fs->imap_buf, ext2fs->fs_info.block_size);
439
440
0
    if (cnt != ext2fs->fs_info.block_size) {
441
0
        if (cnt >= 0) {
442
0
            tsk_error_reset();
443
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
444
0
        }
445
0
        tsk_error_set_errstr2("ext2fs_imap_load: Inode bitmap %"
446
0
            PRI_EXT2GRP " at %" PRIu64, grp_num, addr);
447
0
        return 1;
448
0
    }
449
450
0
    ext2fs->imap_grp_num = grp_num;
451
0
    if (tsk_verbose > 1)
452
0
        ext2fs_print_map(ext2fs->imap_buf,
453
0
        tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group));
454
455
0
    return 0;
456
0
}
457
458
/* ext2fs_dinode_load - look up disk inode & load into ext2fs_inode structure
459
 * @param ext2fs A ext2fs file system information structure
460
 * @param dino_inum Metadata address
461
 * @param dino_buf The buffer to store the block in (must be size of ext2fs->inode_size or larger)
462
 * @param ea_buf The buffer to hold the extended attribute data
463
 *
464
 * return 1 on error and 0 on success
465
 * */
466
467
static uint8_t
468
ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T dino_inum,
469
    ext2fs_inode * dino_buf, uint8_t ** ea_buf, size_t * ea_buf_len)
470
0
{
471
0
    EXT2_GRPNUM_T grp_num;
472
0
    TSK_OFF_T addr;
473
0
    ssize_t cnt;
474
0
    TSK_INUM_T rel_inum;
475
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
476
477
    /*
478
     * Sanity check.
479
     * Use last_num-1 to account for virtual Orphan directory in last_inum.
480
     */
481
0
    if ((dino_inum < fs->first_inum) || (dino_inum > fs->last_inum - 1)) {
482
0
        tsk_error_reset();
483
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
484
0
        tsk_error_set_errstr("ext2fs_dinode_load: address: %" PRIuINUM,
485
0
            dino_inum);
486
0
        return 1;
487
0
    }
488
489
0
    if (dino_buf == NULL) {
490
0
        tsk_error_reset();
491
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
492
0
        tsk_error_set_errstr("ext2fs_dinode_load: dino_buf is NULL");
493
0
        return 1;
494
0
    }
495
496
    /*
497
     * Look up the group descriptor for this inode.
498
     */
499
0
    grp_num = (EXT2_GRPNUM_T) ((dino_inum - fs->first_inum) /
500
0
        tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group));
501
502
    /* lock access to grp_buf */
503
0
    tsk_take_lock(&ext2fs->lock);
504
505
0
    if (ext2fs_group_load(ext2fs, grp_num)) {
506
0
        tsk_release_lock(&ext2fs->lock);
507
0
        return 1;
508
0
    }
509
510
    /*
511
     * Look up the inode table block for this inode.
512
     */
513
0
    rel_inum =
514
0
        (dino_inum - 1) - tsk_getu32(fs->endian,
515
0
        ext2fs->fs->s_inodes_per_group) * grp_num;
516
0
    if (ext2fs->ext4_grp_buf != NULL) {
517
#ifdef Ext4_DBG
518
        printf("DEBUG: d_inode_load 64bit gd_size=%d\n",
519
            tsk_getu16(fs->endian, ext2fs->fs->s_desc_size));
520
#endif
521
        /* Test for possible overflow */
522
0
        if (ext4_getu64(fs->endian, ext2fs->ext4_grp_buf->bg_inode_table_hi, ext2fs->ext4_grp_buf->bg_inode_table_lo)
523
0
                >= LLONG_MAX / fs->block_size) {
524
0
            tsk_release_lock(&ext2fs->lock);
525
526
0
            tsk_error_reset();
527
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
528
0
            tsk_error_set_errstr
529
0
            ("ext2fs_dinode_load: Overflow when calculating address");
530
0
            return 1;
531
0
        }
532
533
0
        addr =
534
0
            (TSK_OFF_T) ext4_getu64(fs->endian,
535
0
            ext2fs->ext4_grp_buf->bg_inode_table_hi,
536
0
            ext2fs->ext4_grp_buf->bg_inode_table_lo)
537
0
            * (TSK_OFF_T) fs->block_size +
538
0
            rel_inum * (TSK_OFF_T) ext2fs->inode_size;
539
0
    }
540
0
    else {
541
0
        addr =
542
0
            (TSK_OFF_T) tsk_getu32(fs->endian,
543
0
            ext2fs->grp_buf->bg_inode_table) * (TSK_OFF_T) fs->block_size +
544
0
            rel_inum * (TSK_OFF_T) ext2fs->inode_size;
545
0
    }
546
0
    tsk_release_lock(&ext2fs->lock);
547
548
0
    cnt = tsk_fs_read(fs, addr, (char *) dino_buf, ext2fs->inode_size);
549
550
0
    if (cnt != ext2fs->inode_size) {
551
0
        if (cnt >= 0) {
552
0
            tsk_error_reset();
553
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
554
0
        }
555
0
        tsk_error_set_errstr2("ext2fs_dinode_load: Inode %" PRIuINUM
556
0
            " from %" PRIdOFF, dino_inum, addr);
557
0
        return 1;
558
0
    }
559
//DEBUG    printf("Inode Size: %d, %d, %d, %d\n", sizeof(ext2fs_inode), *ext2fs->fs->s_inode_size, ext2fs->inode_size, *ext2fs->fs->s_want_extra_isize);
560
//DEBUG    debug_print_buf((char *)dino_buf, ext2fs->inode_size);
561
562
    // Check if we have an extended attribute in the inode
563
0
    if (ext2fs->inode_size > EXT2_EA_INODE_OFFSET) {
564
        // The extended attribute data immediatly follows the standard inode data
565
0
        *ea_buf = (uint8_t*)dino_buf + EXT2_EA_INODE_OFFSET;
566
0
        *ea_buf_len = ext2fs->inode_size - EXT2_EA_INODE_OFFSET;
567
0
    }
568
0
    else {
569
0
        *ea_buf = NULL;
570
0
    }
571
572
0
    if (tsk_verbose) {
573
0
        tsk_fprintf(stderr,
574
0
            "%" PRIuINUM " m/l/s=%o/%d/%" PRIu32
575
0
            " u/g=%d/%d macd=%" PRIu32 "/%" PRIu32 "/%" PRIu32 "/%" PRIu32
576
0
            "\n", dino_inum, tsk_getu16(fs->endian, dino_buf->i_mode),
577
0
            tsk_getu16(fs->endian, dino_buf->i_nlink),
578
0
            (tsk_getu32(fs->endian,
579
0
                    dino_buf->i_size) + (tsk_getu16(fs->endian,
580
0
                        dino_buf->i_mode) & EXT2_IN_REG) ? (uint64_t)
581
0
                tsk_getu32(fs->endian, dino_buf->i_size_high) << 32 : 0),
582
0
            tsk_getu16(fs->endian,
583
0
                dino_buf->i_uid) + (tsk_getu16(fs->endian,
584
0
                    dino_buf->i_uid_high) << 16), tsk_getu16(fs->endian,
585
0
                dino_buf->i_gid) + (tsk_getu16(fs->endian,
586
0
                    dino_buf->i_gid_high) << 16), tsk_getu32(fs->endian,
587
0
                dino_buf->i_mtime), tsk_getu32(fs->endian,
588
0
                dino_buf->i_atime), tsk_getu32(fs->endian,
589
0
                dino_buf->i_ctime), tsk_getu32(fs->endian,
590
0
                dino_buf->i_dtime));
591
0
    }
592
593
0
    return 0;
594
0
}
595
596
/**
597
* \internal
598
* Loads attribute for Ext4 inline storage method.
599
* @param fs_file File to load attrs
600
* @param ea_buf  Extended attribute buffer
601
* @param ea_buf_len Extended attribute buffer length
602
* @returns 0 on success, 1 otherwise
603
*/
604
static uint8_t
605
ext4_load_attrs_inline(TSK_FS_FILE *fs_file, const uint8_t * ea_buf, size_t ea_buf_len)
606
0
{
607
0
    TSK_FS_META *fs_meta = fs_file->meta;
608
0
    TSK_FS_ATTR *fs_attr;
609
610
    // see if we have already loaded the attr
611
0
    if ((fs_meta->attr != NULL)
612
0
        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
613
0
        return 0;
614
0
    }
615
616
0
    if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) {
617
0
        return 1;
618
0
    }
619
620
    // First load the data from the extended attr (if present)
621
0
    const char *ea_inline_data = NULL;
622
0
    uint32_t ea_inline_data_len = 0;
623
0
    if ((ea_buf != NULL) && (ea_buf_len > 4 + sizeof(ext2fs_ea_entry))
624
0
        && (tsk_getu32(fs_file->fs_info->endian, ea_buf) == EXT2_EA_MAGIC)) {
625
626
        // First entry starts after the four byte header
627
0
        size_t index = 4;
628
0
        ext2fs_ea_entry *ea_entry = (ext2fs_ea_entry*) &(ea_buf[index]);
629
630
        // The end of the list of entries is marked by two null bytes
631
0
        while ((ea_entry->nlen != 0) || (ea_entry->nidx != 0)) {
632
633
            // It looks like the continuation of inline data is stored in system.data.
634
            // Make sure we have room to read the attr name 'data'.
635
0
            if ((ea_entry->nidx == EXT2_EA_IDX_SYSTEM)
636
0
                && (ea_entry->nlen == 4)
637
0
                && (index + sizeof(ext2fs_ea_entry) + strlen("data") < ea_buf_len)
638
0
                && (strncmp((const char*) &(ea_entry->name), "data", 4)) == 0) {
639
640
                // This is the right attribute. Check that the length and offset are valid.
641
                // The offset is from the beginning of the entries, i.e., four bytes into the buffer.
642
0
                uint16_t offset = tsk_getu16(fs_file->fs_info->endian, ea_entry->val_off);
643
0
                uint32_t size = tsk_getu32(fs_file->fs_info->endian, ea_entry->val_size);
644
0
                if ((ea_buf_len >= 4) && (offset < ea_buf_len - 4) && (size <= ea_buf_len - 4 - offset)) {
645
0
                    ea_inline_data = (const char*) &(ea_buf[4 + offset]);
646
0
                    ea_inline_data_len = size;
647
0
                    break;
648
0
                }
649
0
            }
650
651
            // Prepare to load the next entry.
652
            // The entry size is the size of the struct plus the length of the name, minus one
653
            // because the struct contains the first character of the name.
654
0
            index += sizeof(ext2fs_ea_entry) + ea_entry->nlen - 1;
655
656
            // Make sure there's room for the next entry plus the 'data' name we're looking for.
657
0
            if (index + sizeof(ext2fs_ea_entry) + strlen("data") > ea_buf_len) {
658
0
                break;
659
0
            }
660
0
            ea_entry = (ext2fs_ea_entry*) &(ea_buf[index]);
661
0
        }
662
0
    }
663
664
    // Check if resident data size exceeds maximum inode size (1024) - ext4 inode resident data offset (156)
665
0
    if ((fs_meta->size == 0) || (fs_meta->size > (1024 - 156))) {
666
0
        return 1;
667
0
    }
668
669
    // Combine the two parts of the inline data for the resident attribute. For now, make a
670
    // buffer for the full file size - this may be different than the length of the data
671
    // from the inode if we have sparse data.
672
0
    uint8_t *resident_data = (uint8_t*)tsk_malloc(fs_meta->size);
673
0
    if (resident_data == NULL) {
674
0
        return 1;
675
0
    }
676
0
    memset(resident_data, 0, fs_meta->size);
677
678
    // Copy the data from the inode.
679
0
    size_t inode_data_len = (fs_meta->size < EXT2_INLINE_MAX_DATA_LEN) ? fs_meta->size : EXT2_INLINE_MAX_DATA_LEN;
680
0
    memcpy(resident_data, fs_meta->content_ptr, inode_data_len);
681
682
    // If we need more data and found an extended attribute, append that data
683
0
    if ((fs_meta->size > EXT2_INLINE_MAX_DATA_LEN) && (ea_inline_data_len > 0)) {
684
        // Don't go beyond the size of the file
685
0
        size_t ea_data_len = (ea_inline_data_len < (uint64_t)fs_meta->size - inode_data_len) ? ea_inline_data_len : fs_meta->size - inode_data_len;
686
0
        memcpy(resident_data + inode_data_len, ea_inline_data, ea_data_len);
687
0
    }
688
689
0
    if (fs_meta->attr == NULL) {
690
0
        fs_meta->attr = tsk_fs_attrlist_alloc();
691
0
    }
692
0
    if ((fs_attr =
693
0
        tsk_fs_attrlist_getnew(fs_meta->attr,
694
0
            TSK_FS_ATTR_RES)) == NULL) {
695
0
        free(resident_data);
696
0
        return 1;
697
0
    }
698
699
    // Set the details in the fs_attr structure
700
0
    if (tsk_fs_attr_set_str(fs_file, fs_attr, "DATA",
701
0
        TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
702
0
        (void*)resident_data,
703
0
        fs_meta->size)) {
704
0
        free(resident_data);
705
0
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
706
0
        return 1;
707
0
    }
708
709
0
    free(resident_data);
710
0
    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
711
0
    return 0;
712
0
}
713
714
/* ext2fs_dinode_copy - copy cached disk inode into generic inode
715
 *
716
 * returns 1 on error and 0 on success
717
 * */
718
static uint8_t
719
ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_FILE * fs_file,
720
    TSK_INUM_T inum, const ext2fs_inode * dino_buf, const uint8_t * ea_buf, size_t ea_buf_len)
721
0
{
722
0
    int i;
723
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
724
0
    TSK_FS_META * fs_meta = fs_file->meta;
725
0
    ext2fs_sb *sb = ext2fs->fs;
726
0
    EXT2_GRPNUM_T grp_num;
727
0
    TSK_INUM_T ibase = 0;
728
729
0
    if (dino_buf == NULL) {
730
0
        tsk_error_reset();
731
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
732
0
        tsk_error_set_errstr("ext2fs_dinode_copy: dino_buf is NULL");
733
0
        return 1;
734
0
    }
735
736
0
    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
737
0
    if (fs_meta->attr) {
738
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
739
0
    }
740
741
    // set the type
742
0
    switch (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_FMT) {
743
0
    case EXT2_IN_REG:
744
0
        fs_meta->type = TSK_FS_META_TYPE_REG;
745
0
        break;
746
0
    case EXT2_IN_DIR:
747
0
        fs_meta->type = TSK_FS_META_TYPE_DIR;
748
0
        break;
749
0
    case EXT2_IN_SOCK:
750
0
        fs_meta->type = TSK_FS_META_TYPE_SOCK;
751
0
        break;
752
0
    case EXT2_IN_LNK:
753
0
        fs_meta->type = TSK_FS_META_TYPE_LNK;
754
0
        break;
755
0
    case EXT2_IN_BLK:
756
0
        fs_meta->type = TSK_FS_META_TYPE_BLK;
757
0
        break;
758
0
    case EXT2_IN_CHR:
759
0
        fs_meta->type = TSK_FS_META_TYPE_CHR;
760
0
        break;
761
0
    case EXT2_IN_FIFO:
762
0
        fs_meta->type = TSK_FS_META_TYPE_FIFO;
763
0
        break;
764
0
    default:
765
0
        fs_meta->type = TSK_FS_META_TYPE_UNDEF;
766
0
        break;
767
0
    }
768
769
    // set the mode
770
0
    fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
771
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISUID)
772
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_ISUID);
773
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISGID)
774
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_ISGID);
775
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISVTX)
776
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_ISVTX);
777
778
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IRUSR)
779
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IRUSR);
780
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWUSR)
781
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IWUSR);
782
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXUSR)
783
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IXUSR);
784
785
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IRGRP)
786
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IRGRP);
787
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWGRP)
788
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IWGRP);
789
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXGRP)
790
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IXGRP);
791
792
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IROTH)
793
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IROTH);
794
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWOTH)
795
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IWOTH);
796
0
    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXOTH)
797
0
        fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IXOTH);
798
799
0
    fs_meta->nlink = tsk_getu16(fs->endian, dino_buf->i_nlink);
800
801
0
    fs_meta->size = tsk_getu32(fs->endian, dino_buf->i_size);
802
803
0
    fs_meta->addr = inum;
804
805
    /* the general size value in the inode is only 32-bits,
806
     * but the i_dir_acl value is used for regular files to
807
     * hold the upper 32-bits
808
     *
809
     * The RO_COMPAT_LARGE_FILE flag in the super block will identify
810
     * if there are any large files in the file system
811
     */
812
0
    if ((fs_meta->type == TSK_FS_META_TYPE_REG) &&
813
0
        (tsk_getu32(fs->endian, sb->s_feature_ro_compat) &
814
0
            EXT2FS_FEATURE_RO_COMPAT_LARGE_FILE)) {
815
0
        fs_meta->size +=
816
0
            ((uint64_t) tsk_getu32(fs->endian,
817
0
                dino_buf->i_size_high) << 32);
818
0
    }
819
820
0
    fs_meta->uid =
821
0
        tsk_getu16(fs->endian, dino_buf->i_uid) + ((TSK_UID_T)tsk_getu16(fs->endian,
822
0
            dino_buf->i_uid_high) << 16);
823
0
    fs_meta->gid =
824
0
        tsk_getu16(fs->endian, dino_buf->i_gid) + ((TSK_GID_T)tsk_getu16(fs->endian,
825
0
            dino_buf->i_gid_high) << 16);
826
0
    fs_meta->mtime = tsk_getu32(fs->endian, dino_buf->i_mtime);
827
0
    fs_meta->atime = tsk_getu32(fs->endian, dino_buf->i_atime);
828
0
    fs_meta->ctime = tsk_getu32(fs->endian, dino_buf->i_ctime);
829
0
    fs_meta->time2.ext2.dtime = tsk_getu32(fs->endian, dino_buf->i_dtime);
830
0
    if (fs->ftype == TSK_FS_TYPE_EXT4) {
831
0
        fs_meta->mtime_nano =
832
0
            tsk_getu32(fs->endian, dino_buf->i_mtime_extra) >> 2;
833
0
        fs_meta->atime_nano =
834
0
            tsk_getu32(fs->endian, dino_buf->i_atime_extra) >> 2;
835
0
        fs_meta->ctime_nano =
836
0
            tsk_getu32(fs->endian, dino_buf->i_ctime_extra) >> 2;
837
0
        fs_meta->crtime = tsk_getu32(fs->endian, dino_buf->i_crtime);
838
0
        fs_meta->crtime_nano =
839
0
            tsk_getu32(fs->endian, dino_buf->i_crtime_extra) >> 2;
840
0
    }
841
0
    else {
842
0
        fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = 0;
843
0
        fs_meta->crtime = 0;
844
0
    }
845
0
    fs_meta->time2.ext2.dtime_nano = 0;
846
0
    fs_meta->seq = 0;
847
848
0
    if (fs_meta->link) {
849
0
        free(fs_meta->link);
850
0
        fs_meta->link = NULL;
851
0
    }
852
853
0
    if (fs_meta->content_len != EXT2FS_FILE_CONTENT_LEN) {
854
0
        if ((fs_meta =
855
0
                tsk_fs_meta_realloc(fs_meta,
856
0
                    EXT2FS_FILE_CONTENT_LEN)) == NULL) {
857
0
            return 1;
858
0
        }
859
0
    }
860
861
0
    if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EXTENTS) {
862
0
        uint32_t *addr_ptr;
863
0
        fs_meta->content_type = TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS;
864
        /* NOTE TSK_DADDR_T != uint32_t, so lets make sure we use uint32_t */
865
0
        addr_ptr = (uint32_t *) fs_meta->content_ptr;
866
0
        for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) {
867
0
            addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]);
868
0
        }
869
0
    }
870
0
    else if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_INLINE_DATA) {
871
0
        uint32_t *addr_ptr;
872
0
        fs_meta->content_type = TSK_FS_META_CONTENT_TYPE_EXT4_INLINE;
873
0
        addr_ptr = (uint32_t *)fs_meta->content_ptr;
874
0
        for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) {
875
0
            addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]);
876
0
        }
877
878
        // For inline data we create the default attribute now
879
0
        ext4_load_attrs_inline(fs_file, ea_buf, ea_buf_len);
880
0
    }
881
0
    else {
882
0
        TSK_DADDR_T *addr_ptr;
883
0
        addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
884
0
        for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++)
885
0
            addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]);
886
887
        /* set the link string
888
         * the size check prevents us from trying to allocate a huge amount of
889
         * memory for a bad inode value
890
         */
891
0
        if ((fs_meta->type == TSK_FS_META_TYPE_LNK)
892
0
            && (fs_meta->size < EXT2FS_MAXPATHLEN) && (fs_meta->size >= 0)) {
893
0
            int i;
894
895
0
            if ((fs_meta->link =
896
0
                    (char*) tsk_malloc((size_t) (fs_meta->size + 1))) == NULL)
897
0
                return 1;
898
899
            /* it is located directly in the pointers */
900
0
            if (fs_meta->size < 4 * (EXT2FS_NDADDR + EXT2FS_NIADDR)) {
901
0
                unsigned int j;
902
0
                unsigned int count = 0;
903
904
0
                for (i = 0; i < (EXT2FS_NDADDR + EXT2FS_NIADDR) &&
905
0
                    count < fs_meta->size; i++) {
906
0
                    char *a_ptr = (char *) &dino_buf->i_block[i];
907
0
                    for (j = 0; j < 4 && count < fs_meta->size; j++) {
908
0
                        fs_meta->link[count++] = a_ptr[j];
909
0
                    }
910
0
                }
911
0
                fs_meta->link[count] = '\0';
912
913
                /* clear the content pointer data to avoid the prog from reading them */
914
0
                memset(fs_meta->content_ptr, 0, fs_meta->content_len);
915
0
            }
916
917
            /* it is in blocks */
918
0
            else {
919
0
                TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
920
0
                char *data_buf = NULL;
921
0
                char *a_ptr = fs_meta->link;
922
0
                unsigned int total_read = 0;
923
0
                TSK_DADDR_T *addr_ptr = (TSK_DADDR_T*) fs_meta->content_ptr;;
924
925
0
                if ((data_buf = (char*) tsk_malloc(fs->block_size)) == NULL) {
926
0
                    return 1;
927
0
                }
928
929
                /* we only need to do the direct blocks due to the limit
930
                 * on path length */
931
0
                for (i = 0; i < EXT2FS_NDADDR && total_read < fs_meta->size;
932
0
                    i++) {
933
0
                    ssize_t cnt;
934
935
0
                    cnt = tsk_fs_read_block(fs,
936
0
                        addr_ptr[i], data_buf, fs->block_size);
937
938
0
                    if (cnt != fs->block_size) {
939
0
                        if (cnt >= 0) {
940
0
                            tsk_error_reset();
941
0
                            tsk_error_set_errno(TSK_ERR_FS_READ);
942
0
                        }
943
0
                        tsk_error_set_errstr2
944
0
                            ("ext2fs_dinode_copy: symlink destination from %"
945
0
                            PRIuDADDR, addr_ptr[i]);
946
0
                        free(data_buf);
947
0
                        return 1;
948
0
                    }
949
950
0
                    int copy_len =
951
0
                        (fs_meta->size - total_read <
952
0
                        fs->block_size) ? (int) (fs_meta->size -
953
0
                        total_read) : (int) (fs->block_size);
954
955
0
                    memcpy(a_ptr, data_buf, copy_len);
956
0
                    total_read += copy_len;
957
0
                    a_ptr = (char *) ((uintptr_t) a_ptr + copy_len);
958
0
                }
959
960
                /* terminate the string */
961
0
                *a_ptr = '\0';
962
0
                free(data_buf);
963
0
            }
964
965
            /* Clean up name */
966
0
            i = 0;
967
0
            while (fs_meta->link[i] != '\0') {
968
0
                if (TSK_IS_CNTRL(fs_meta->link[i]))
969
0
                    fs_meta->link[i] = '^';
970
0
                i++;
971
0
            }
972
0
        }
973
0
    }
974
975
    /* Fill in the flags value */
976
0
    grp_num = (EXT2_GRPNUM_T) ((inum - fs->first_inum) /
977
0
        tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group));
978
979
980
0
    tsk_take_lock(&ext2fs->lock);
981
982
0
    if (ext2fs_imap_load(ext2fs, grp_num)) {
983
0
        tsk_release_lock(&ext2fs->lock);
984
0
        return 1;
985
0
    }
986
987
0
    ibase =
988
0
        grp_num * tsk_getu32(fs->endian,
989
0
        ext2fs->fs->s_inodes_per_group) + fs->first_inum;
990
991
992
    /*
993
     * Ensure that inum - ibase refers to a valid bit offset in imap_buf.
994
     */
995
0
    if ((ibase > inum) || (inum - ibase) >= (fs->block_size * 8)) {
996
0
        tsk_release_lock(&ext2fs->lock);
997
0
        tsk_error_reset();
998
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
999
0
        tsk_error_set_errstr("ext2fs_dinode_copy: Invalid offset into imap_buf (inum %" PRIuINUM " - ibase %" PRIuINUM ")",
1000
0
            inum, ibase);
1001
0
        return 1;
1002
0
    }
1003
1004
1005
    /*
1006
     * Apply the allocated/unallocated restriction.
1007
     */
1008
0
    fs_meta->flags = (isset(ext2fs->imap_buf, inum - ibase) ?
1009
0
        TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
1010
1011
0
    tsk_release_lock(&ext2fs->lock);
1012
1013
1014
    /*
1015
     * Apply the used/unused restriction.
1016
     */
1017
0
    fs_meta->flags = (TSK_FS_META_FLAG_ENUM) (fs_meta->flags | (fs_meta->ctime ?
1018
0
        TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED));
1019
1020
0
    return 0;
1021
0
}
1022
1023
1024
1025
/* ext2fs_inode_lookup - lookup inode, external interface
1026
 *
1027
 * Returns 1 on error and 0 on success
1028
 *
1029
 */
1030
1031
static uint8_t
1032
ext2fs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
1033
    TSK_INUM_T inum)
1034
0
{
1035
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
1036
0
    ext2fs_inode *dino_buf = NULL;
1037
0
    uint8_t *ea_buf = NULL;
1038
0
    size_t ea_buf_len = 0;
1039
0
    unsigned int size = 0;
1040
1041
0
    if (a_fs_file == NULL) {
1042
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1043
0
        tsk_error_set_errstr("ext2fs_inode_lookup: fs_file is NULL");
1044
0
        return 1;
1045
0
    }
1046
1047
0
    if (a_fs_file->meta == NULL) {
1048
0
        if ((a_fs_file->meta =
1049
0
                tsk_fs_meta_alloc(EXT2FS_FILE_CONTENT_LEN)) == NULL)
1050
0
            return 1;
1051
0
    }
1052
0
    else {
1053
0
        tsk_fs_meta_reset(a_fs_file->meta);
1054
0
    }
1055
1056
    // see if they are looking for the special "orphans" directory
1057
0
    if (inum == TSK_FS_ORPHANDIR_INUM(fs)) {
1058
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta))
1059
0
            return 1;
1060
0
        else
1061
0
            return 0;
1062
0
    }
1063
1064
0
    size =
1065
0
        ext2fs->inode_size >
1066
0
        sizeof(ext2fs_inode) ? ext2fs->inode_size : sizeof(ext2fs_inode);
1067
0
    if ((dino_buf = (ext2fs_inode *) tsk_malloc(size)) == NULL) {
1068
0
        return 1;
1069
0
    }
1070
1071
0
    if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) {
1072
0
        free(dino_buf);
1073
0
        return 1;
1074
0
    }
1075
1076
0
    if (ext2fs_dinode_copy(ext2fs, a_fs_file, inum, dino_buf, ea_buf, ea_buf_len)) {
1077
0
        free(dino_buf);
1078
0
        return 1;
1079
0
    }
1080
1081
0
    free(dino_buf);
1082
0
    return 0;
1083
0
}
1084
1085
1086
1087
/* ext2fs_inode_walk - inode iterator
1088
 *
1089
 * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED,
1090
 *  TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN
1091
 *
1092
 *  Return 1 on error and 0 on success
1093
*/
1094
1095
uint8_t
1096
ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
1097
    TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags,
1098
    TSK_FS_META_WALK_CB a_action, void *a_ptr)
1099
0
{
1100
0
    const char *myname = "extXfs_inode_walk";
1101
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
1102
0
    TSK_INUM_T inum;
1103
0
    TSK_INUM_T end_inum_tmp;
1104
0
    TSK_INUM_T ibase = 0;
1105
0
    unsigned int myflags;
1106
0
    ext2fs_inode *dino_buf = NULL;
1107
0
    uint8_t *ea_buf = NULL;
1108
0
    size_t ea_buf_len = 0;
1109
0
    unsigned int size = 0;
1110
1111
    // clean up any error messages that are lying around
1112
0
    tsk_error_reset();
1113
1114
    /*
1115
     * Sanity checks.
1116
     */
1117
0
    if (start_inum < fs->first_inum || start_inum > fs->last_inum) {
1118
0
        tsk_error_reset();
1119
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1120
0
        tsk_error_set_errstr("%s: start inode: %" PRIuINUM "", myname,
1121
0
            start_inum);
1122
0
        return 1;
1123
0
    }
1124
1125
0
    if (end_inum < fs->first_inum || end_inum > fs->last_inum
1126
0
        || end_inum < start_inum) {
1127
0
        tsk_error_reset();
1128
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1129
0
        tsk_error_set_errstr("%s: end inode: %" PRIuINUM "", myname,
1130
0
            end_inum);
1131
0
        return 1;
1132
0
    }
1133
1134
    /* If ORPHAN is wanted, then make sure that the flags are correct */
1135
0
    if (flags & TSK_FS_META_FLAG_ORPHAN) {
1136
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_UNALLOC);
1137
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_ALLOC);
1138
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED);
1139
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_UNUSED);
1140
0
    }
1141
0
    else {
1142
0
        if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
1143
0
            ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
1144
0
            flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
1145
0
        }
1146
1147
        /* If neither of the USED or UNUSED flags are set, then set them
1148
         * both
1149
         */
1150
0
        if (((flags & TSK_FS_META_FLAG_USED) == 0) &&
1151
0
            ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) {
1152
0
            flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
1153
0
        }
1154
0
    }
1155
1156
1157
1158
    /* If we are looking for orphan files and have not yet filled
1159
     * in the list of unalloc inodes that are pointed to, then fill
1160
     * in the list
1161
     */
1162
0
    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
1163
0
        if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
1164
0
            tsk_error_errstr2_concat
1165
0
                ("- ext2fs_inode_walk: identifying inodes allocated by file names");
1166
0
            return 1;
1167
0
        }
1168
1169
0
    }
1170
1171
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
1172
0
        tsk_fs_file_alloc(fs),
1173
0
        tsk_fs_file_close
1174
0
    };
1175
1176
0
    if (!fs_file)
1177
0
        return 1;
1178
1179
0
    fs_file->meta = tsk_fs_meta_alloc(EXT2FS_FILE_CONTENT_LEN);
1180
0
    if (fs_file->meta == NULL) {
1181
0
        return 1;
1182
0
    }
1183
1184
    // we need to handle fs->last_inum specially because it is for the
1185
    // virtual ORPHANS directory.  Handle it outside of the loop.
1186
0
    if (end_inum == TSK_FS_ORPHANDIR_INUM(fs))
1187
0
        end_inum_tmp = end_inum - 1;
1188
0
    else
1189
0
        end_inum_tmp = end_inum;
1190
1191
    /*
1192
     * Iterate.
1193
     */
1194
0
    size =
1195
0
        ext2fs->inode_size >
1196
0
        sizeof(ext2fs_inode) ? ext2fs->inode_size : sizeof(ext2fs_inode);
1197
1198
0
    dino_buf = (ext2fs_inode *) tsk_malloc(size);
1199
0
    if (dino_buf == NULL) {
1200
0
        return 1;
1201
0
    }
1202
1203
0
    for (inum = start_inum; inum <= end_inum_tmp; inum++) {
1204
0
        int retval;
1205
0
        EXT2_GRPNUM_T grp_num;
1206
1207
        /*
1208
         * Be sure to use the proper group descriptor data. XXX Linux inodes
1209
         * start at 1, as in Fortran.
1210
         */
1211
0
        grp_num =
1212
0
            (EXT2_GRPNUM_T) ((inum - 1) / tsk_getu32(fs->endian,
1213
0
                ext2fs->fs->s_inodes_per_group));
1214
1215
        /* lock access to imap_buf */
1216
0
        tsk_take_lock(&ext2fs->lock);
1217
1218
0
        if (ext2fs_imap_load(ext2fs, grp_num)) {
1219
0
            tsk_release_lock(&ext2fs->lock);
1220
0
            free(dino_buf);
1221
0
            return 1;
1222
0
        }
1223
0
        ibase =
1224
0
            grp_num * tsk_getu32(fs->endian,
1225
0
            ext2fs->fs->s_inodes_per_group) + 1;
1226
1227
        /*
1228
         * Ensure that inum - ibase refers to a valid bit offset in imap_buf.
1229
         */
1230
0
        if ((ibase > inum) || (inum - ibase) >= (fs->block_size * 8)) {
1231
0
            tsk_release_lock(&ext2fs->lock);
1232
0
            free(dino_buf);
1233
1234
0
            tsk_error_reset();
1235
0
            tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1236
0
            tsk_error_set_errstr("%s: Invalid offset into imap_buf (inum %" PRIuINUM " - ibase %" PRIuINUM ")",
1237
0
                myname, inum, ibase);
1238
0
            return 1;
1239
0
        }
1240
1241
        /*
1242
         * Apply the allocated/unallocated restriction.
1243
         */
1244
0
        myflags = (isset(ext2fs->imap_buf, inum - ibase) ?
1245
0
            TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
1246
1247
0
        tsk_release_lock(&ext2fs->lock);
1248
1249
0
        if ((flags & myflags) != myflags)
1250
0
            continue;
1251
1252
0
        if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) {
1253
0
            free(dino_buf);
1254
0
            return 1;
1255
0
        }
1256
1257
1258
        /*
1259
         * Apply the used/unused restriction.
1260
         */
1261
0
        myflags |= (tsk_getu32(fs->endian, dino_buf->i_ctime) ?
1262
0
            TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED);
1263
1264
0
        if ((flags & myflags) != myflags)
1265
0
            continue;
1266
1267
        /* If we want only orphans, then check if this
1268
         * inode is in the seen list
1269
         */
1270
0
        if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
1271
0
            (flags & TSK_FS_META_FLAG_ORPHAN) &&
1272
0
            (tsk_fs_dir_find_inum_named(fs, inum))) {
1273
0
            continue;
1274
0
        }
1275
1276
        /*
1277
         * Fill in a file system-independent inode structure and pass control
1278
         * to the application.
1279
         */
1280
0
        if (ext2fs_dinode_copy(ext2fs, fs_file.get(), inum, dino_buf, ea_buf, ea_buf_len)) {
1281
0
            free(dino_buf);
1282
0
            return 1;
1283
0
        }
1284
1285
0
        retval = a_action(fs_file.get(), a_ptr);
1286
0
        if (retval == TSK_WALK_STOP) {
1287
0
            free(dino_buf);
1288
0
            return 0;
1289
0
        }
1290
0
        else if (retval == TSK_WALK_ERROR) {
1291
0
            free(dino_buf);
1292
0
            return 1;
1293
0
        }
1294
0
    }
1295
1296
    // handle the virtual orphans folder if they asked for it
1297
0
    if ((end_inum == TSK_FS_ORPHANDIR_INUM(fs))
1298
0
        && (flags & TSK_FS_META_FLAG_ALLOC)
1299
0
        && (flags & TSK_FS_META_FLAG_USED)) {
1300
0
        int retval;
1301
1302
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
1303
0
            free(dino_buf);
1304
0
            return 1;
1305
0
        }
1306
        /* call action */
1307
0
        retval = a_action(fs_file.get(), a_ptr);
1308
0
        if (retval == TSK_WALK_STOP) {
1309
0
            free(dino_buf);
1310
0
            return 0;
1311
0
        }
1312
0
        else if (retval == TSK_WALK_ERROR) {
1313
0
            free(dino_buf);
1314
0
            return 1;
1315
0
        }
1316
0
    }
1317
1318
    /*
1319
     * Cleanup.
1320
     */
1321
0
    free(dino_buf);
1322
1323
0
    return 0;
1324
0
}
1325
1326
1327
1328
TSK_FS_BLOCK_FLAG_ENUM
1329
ext2fs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
1330
0
{
1331
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) a_fs;
1332
0
    int flags;
1333
0
    EXT2_GRPNUM_T grp_num;
1334
0
    TSK_DADDR_T dbase = 0;      /* first block number in group */
1335
0
    TSK_DADDR_T dmin = 0;       /* first block after inodes */
1336
1337
    // these blocks are not described in the group descriptors
1338
    // sparse
1339
0
    if (a_addr == 0)
1340
0
        return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC);
1341
0
    if (a_addr < ext2fs->first_data_block)
1342
0
        return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_META | TSK_FS_BLOCK_FLAG_ALLOC);
1343
1344
0
    grp_num = ext2_dtog_lcl(a_fs, ext2fs->fs, a_addr);
1345
1346
    /* lock access to bmap_buf */
1347
0
    tsk_take_lock(&ext2fs->lock);
1348
1349
    /* Lookup bitmap if not loaded */
1350
0
    if (ext2fs_bmap_load(ext2fs, grp_num)) {
1351
0
        tsk_release_lock(&ext2fs->lock);
1352
0
        return TSK_FS_BLOCK_FLAG_UNUSED;
1353
0
    }
1354
1355
    /*
1356
     * Be sure to use the right group descriptor information. XXX There
1357
     * appears to be an off-by-one discrepancy between bitmap offsets and
1358
     * disk block numbers.
1359
     *
1360
     * Addendum: this offset is controlled by the super block's
1361
     * s_first_data_block field.
1362
     */
1363
0
    dbase = ext2_cgbase_lcl(a_fs, ext2fs->fs, grp_num);
1364
0
    flags = (isset(ext2fs->bmap_buf, a_addr - dbase) ?
1365
0
        TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC);
1366
1367
    /*
1368
     *  Identify meta blocks
1369
     * (any blocks that can't be allocated for file/directory data).
1370
     *
1371
     * XXX With sparse superblock placement, most block groups have the
1372
     * block and inode bitmaps where one would otherwise find the backup
1373
     * superblock and the backup group descriptor blocks. The inode
1374
     * blocks are in the normal place, though. This leaves little gaps
1375
     * between the bitmaps and the inode table - and ext2fs will use
1376
     * those blocks for file/directory data blocks. So we must properly
1377
     * account for those gaps between meta blocks.
1378
     *
1379
     * Thus, superblocks and group descriptor blocks are sometimes overlaid
1380
     * by bitmap blocks. This means that one can still assume that the
1381
     * locations of superblocks and group descriptor blocks are reserved.
1382
     * They just happen to be reserved for something else :-)
1383
     */
1384
1385
0
    if (ext2fs->ext4_grp_buf != NULL) {
1386
0
        dmin = ext4_getu64(a_fs->endian, ext2fs->ext4_grp_buf->bg_inode_table_hi,
1387
0
                    ext2fs->ext4_grp_buf->bg_inode_table_lo) + + INODE_TABLE_SIZE(ext2fs);
1388
1389
0
        if ((a_addr >= dbase
1390
0
                && a_addr < ext4_getu64(a_fs->endian,
1391
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_hi,
1392
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_lo))
1393
0
            || (a_addr == ext4_getu64(a_fs->endian,
1394
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_hi,
1395
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_lo))
1396
0
            || (a_addr == ext4_getu64(a_fs->endian,
1397
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_hi,
1398
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_lo))
1399
0
            || (a_addr >= ext4_getu64(a_fs->endian,
1400
0
                ext2fs->ext4_grp_buf->bg_inode_table_hi,
1401
0
                ext2fs->ext4_grp_buf->bg_inode_table_lo)
1402
0
                && a_addr < dmin))
1403
0
            flags |= TSK_FS_BLOCK_FLAG_META;
1404
0
        else
1405
0
            flags |= TSK_FS_BLOCK_FLAG_CONT;
1406
1407
0
    }
1408
0
    else {
1409
0
        dmin =
1410
0
            tsk_getu32(a_fs->endian,
1411
0
            ext2fs->grp_buf->bg_inode_table) + INODE_TABLE_SIZE(ext2fs);
1412
1413
0
        if ((a_addr >= dbase
1414
0
                && a_addr < tsk_getu32(a_fs->endian,
1415
0
                    ext2fs->grp_buf->bg_block_bitmap))
1416
0
            || (a_addr == tsk_getu32(a_fs->endian,
1417
0
                    ext2fs->grp_buf->bg_block_bitmap))
1418
0
            || (a_addr == tsk_getu32(a_fs->endian,
1419
0
                    ext2fs->grp_buf->bg_inode_bitmap))
1420
0
            || (a_addr >= tsk_getu32(a_fs->endian,
1421
0
                    ext2fs->grp_buf->bg_inode_table)
1422
0
                && a_addr < dmin))
1423
0
            flags |= TSK_FS_BLOCK_FLAG_META;
1424
0
        else
1425
0
            flags |= TSK_FS_BLOCK_FLAG_CONT;
1426
0
    }
1427
1428
0
    tsk_release_lock(&ext2fs->lock);
1429
0
    return (TSK_FS_BLOCK_FLAG_ENUM)flags;
1430
0
}
1431
1432
1433
/* ext2fs_block_walk - block iterator
1434
 *
1435
 * flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT,
1436
 *  TSK_FS_BLOCK_FLAG_META
1437
 *
1438
 *  Return 1 on error and 0 on success
1439
*/
1440
1441
uint8_t
1442
ext2fs_block_walk(TSK_FS_INFO * a_fs, TSK_DADDR_T a_start_blk,
1443
    TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags,
1444
    TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr)
1445
0
{
1446
0
    const char *myname = "extXfs_block_walk";
1447
0
    TSK_FS_BLOCK *fs_block;
1448
0
    TSK_DADDR_T addr;
1449
1450
    // clean up any error messages that are lying around
1451
0
    tsk_error_reset();
1452
1453
    /*
1454
     * Sanity checks.
1455
     */
1456
0
    if (a_start_blk < a_fs->first_block || a_start_blk > a_fs->last_block) {
1457
0
        tsk_error_reset();
1458
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1459
0
        tsk_error_set_errstr("%s: start block: %" PRIuDADDR, myname,
1460
0
            a_start_blk);
1461
0
        return 1;
1462
0
    }
1463
0
    if (a_end_blk < a_fs->first_block || a_end_blk > a_fs->last_block
1464
0
        || a_end_blk < a_start_blk) {
1465
0
        tsk_error_reset();
1466
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1467
0
        tsk_error_set_errstr("%s: end block: %" PRIuDADDR, myname,
1468
0
            a_end_blk);
1469
0
        return 1;
1470
0
    }
1471
1472
    /* Sanity check on a_flags -- make sure at least one ALLOC is set */
1473
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
1474
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
1475
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
1476
0
            (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC |
1477
0
            TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
1478
0
    }
1479
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
1480
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
1481
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
1482
0
            (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
1483
0
    }
1484
1485
1486
0
    if ((fs_block = tsk_fs_block_alloc(a_fs)) == NULL) {
1487
0
        return 1;
1488
0
    }
1489
1490
    /*
1491
     * Iterate. This is not as tricky as it could be, because the free list
1492
     * map covers the entire disk partition, including blocks occupied by
1493
     * group descriptor blocks, bit maps, and other non-data blocks.
1494
     */
1495
0
    for (addr = a_start_blk; addr <= a_end_blk; addr++) {
1496
0
        int retval;
1497
0
        int myflags;
1498
1499
0
        myflags = ext2fs_block_getflags(a_fs, addr);
1500
1501
        // test if we should call the callback with this one
1502
0
        if ((myflags & TSK_FS_BLOCK_FLAG_META)
1503
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META)))
1504
0
            continue;
1505
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
1506
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
1507
0
            continue;
1508
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
1509
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
1510
0
            continue;
1511
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
1512
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
1513
0
            continue;
1514
1515
0
        if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
1516
0
            myflags |= TSK_FS_BLOCK_FLAG_AONLY;
1517
1518
0
        if (tsk_fs_block_get_flag(a_fs, fs_block, addr, (TSK_FS_BLOCK_FLAG_ENUM) myflags) == NULL) {
1519
0
            tsk_error_set_errstr2("ext2fs_block_walk: block %" PRIuDADDR,
1520
0
                addr);
1521
0
            tsk_fs_block_free(fs_block);
1522
0
            return 1;
1523
0
        }
1524
1525
0
        retval = a_action(fs_block, a_ptr);
1526
0
        if (retval == TSK_WALK_STOP) {
1527
0
            break;
1528
0
        }
1529
0
        else if (retval == TSK_WALK_ERROR) {
1530
0
            tsk_fs_block_free(fs_block);
1531
0
            return 1;
1532
0
        }
1533
0
    }
1534
1535
    /*
1536
     * Cleanup.
1537
     */
1538
0
    tsk_fs_block_free(fs_block);
1539
0
    return 0;
1540
0
}
1541
1542
static uint8_t
1543
ext2fs_fscheck(
1544
  [[maybe_unused]] TSK_FS_INFO * fs,
1545
  [[maybe_unused]] FILE * hFile)
1546
0
{
1547
0
    tsk_error_reset();
1548
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1549
0
    tsk_error_set_errstr("fscheck not implemented yet for Ext3");
1550
0
    return 1;
1551
0
}
1552
1553
/** \internal
1554
 * Add a single extent -- that is, a single data ran -- to the file data attribute.
1555
 * @return 0 on success, 1 on error.
1556
 */
1557
static TSK_OFF_T
1558
ext2fs_make_data_run_extent(TSK_FS_INFO * fs_info, TSK_FS_ATTR * fs_attr,
1559
    ext2fs_extent * extent)
1560
0
{
1561
0
    TSK_FS_ATTR_RUN *data_run;
1562
0
    data_run = tsk_fs_attr_run_alloc();
1563
0
    if (data_run == NULL) {
1564
0
        return 1;
1565
0
    }
1566
1567
0
    data_run->offset = tsk_getu32(fs_info->endian, extent->ee_block);
1568
1569
    // Check if the extent is initialized or uninitialized
1570
0
    if (tsk_getu16(fs_info->endian, extent->ee_len) <= EXT2_MAX_INIT_EXTENT_LENGTH) {
1571
        // Extent is initalized - process normally
1572
0
        data_run->addr =
1573
0
            (((uint32_t)tsk_getu16(fs_info->endian,
1574
0
                extent->ee_start_hi)) << 16) | tsk_getu32(fs_info->endian,
1575
0
                    extent->ee_start_lo);
1576
0
        data_run->len = tsk_getu16(fs_info->endian, extent->ee_len);
1577
0
    }
1578
0
    else {
1579
        // Extent is uninitialized - make a sparse run
1580
0
        data_run->len = tsk_getu16(fs_info->endian, extent->ee_len) - EXT2_MAX_INIT_EXTENT_LENGTH;
1581
0
        data_run->addr = 0;
1582
0
        data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
1583
0
    }
1584
1585
    // save the run
1586
0
    if (tsk_fs_attr_add_run(fs_info, fs_attr, data_run)) {
1587
0
        tsk_fs_attr_run_free(data_run);
1588
0
        return 1;
1589
0
    }
1590
1591
0
    return 0;
1592
0
}
1593
1594
1595
/** \internal
1596
 * Given a block that contains an extent node (which starts with extent_header),
1597
 * walk it, and add everything encountered to the appropriate attributes.
1598
 * @return 0 on success, 1 on error.
1599
 */
1600
static TSK_OFF_T
1601
ext2fs_make_data_run_extent_index(TSK_FS_INFO * fs_info,
1602
    TSK_FS_ATTR * fs_attr, TSK_FS_ATTR * fs_attr_extent,
1603
    TSK_DADDR_T idx_block, TSK_DADDR_T * idx_offset)
1604
0
{
1605
0
    ext2fs_extent_header *header;
1606
0
    TSK_FS_ATTR_RUN *data_run;
1607
0
    uint8_t *buf;
1608
0
    ssize_t cnt;
1609
0
    unsigned int i;
1610
1611
    /* first, read the block specified by the parameter */
1612
0
    int fs_blocksize = fs_info->block_size;
1613
0
    if ((buf = (uint8_t *) tsk_malloc(fs_blocksize)) == NULL) {
1614
0
        return 1;
1615
0
    }
1616
1617
0
    cnt =
1618
0
        tsk_fs_read_block(fs_info, idx_block, (char *) buf, fs_blocksize);
1619
0
    if (cnt != fs_blocksize) {
1620
0
        if (cnt >= 0) {
1621
0
            tsk_error_reset();
1622
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
1623
0
        }
1624
0
        tsk_error_set_errstr("ext2fs_make_data_run_extent_index: Block %"
1625
0
            PRIuDADDR, idx_block);
1626
0
        free(buf);
1627
0
        return 1;
1628
0
    }
1629
0
    header = (ext2fs_extent_header *) buf;
1630
1631
    /* add it to the extent attribute */
1632
0
    if (tsk_getu16(fs_info->endian, header->eh_magic) != 0xF30A) {
1633
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1634
0
        tsk_error_set_errstr
1635
0
            ("ext2fs_make_data_run_extent_index: extent header magic valid incorrect!");
1636
0
        free(buf);
1637
0
        return 1;
1638
0
    }
1639
1640
0
    data_run = tsk_fs_attr_run_alloc();
1641
0
    if (data_run == NULL) {
1642
0
        free(buf);
1643
0
        return 1;
1644
0
    }
1645
0
    data_run->offset = *idx_offset;
1646
0
    ++*idx_offset;
1647
0
    data_run->addr = idx_block;
1648
0
    data_run->len = 1;
1649
1650
0
    if (tsk_fs_attr_add_run(fs_info, fs_attr_extent, data_run)) {
1651
0
        tsk_fs_attr_run_free(data_run);
1652
0
        free(buf);
1653
0
        return 1;
1654
0
    }
1655
1656
    /* process leaf nodes */
1657
0
    if (tsk_getu16(fs_info->endian, header->eh_depth) == 0) {
1658
0
        uint16_t num_entries = tsk_getu16(fs_info->endian, header->eh_entries);
1659
1660
        // Ensure buf is sufficiently large
1661
        // Otherwise extents[i] below can cause an OOB read
1662
0
        if (((unsigned long)fs_blocksize < sizeof(ext2fs_extent_header)) || (num_entries > (fs_blocksize - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent))) {
1663
0
            free(buf);
1664
0
            return 1;
1665
0
        }
1666
0
        ext2fs_extent *extents = (ext2fs_extent *) (header + 1);
1667
0
        for (i = 0; i < num_entries; i++) {
1668
0
            ext2fs_extent extent = extents[i];
1669
0
            if (ext2fs_make_data_run_extent(fs_info, fs_attr, &extent)) {
1670
0
                free(buf);
1671
0
                return 1;
1672
0
            }
1673
0
        }
1674
0
    }
1675
    /* recurse on interior nodes */
1676
0
    else {
1677
0
        uint16_t num_entries = tsk_getu16(fs_info->endian, header->eh_entries);
1678
1679
        // Ensure buf is sufficiently large
1680
        // Otherwise indices[i] below can cause an OOB read
1681
0
        if (((unsigned long)fs_blocksize < sizeof(ext2fs_extent_header)) || (num_entries > (fs_blocksize - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent_idx))) {
1682
0
            free(buf);
1683
0
            return 1;
1684
0
        }
1685
0
        ext2fs_extent_idx *indices = (ext2fs_extent_idx *) (header + 1);
1686
0
        for (i = 0; i < num_entries; i++) {
1687
0
            ext2fs_extent_idx *index = &indices[i];
1688
0
            TSK_DADDR_T child_block =
1689
0
                (((uint32_t) tsk_getu16(fs_info->endian,
1690
0
                        index->ei_leaf_hi)) << 16) | tsk_getu32(fs_info->
1691
0
                endian, index->ei_leaf_lo);
1692
0
            if (ext2fs_make_data_run_extent_index(fs_info, fs_attr,
1693
0
                    fs_attr_extent, child_block, idx_offset)) {
1694
0
                free(buf);
1695
0
                return 1;
1696
0
            }
1697
0
        }
1698
0
    }
1699
1700
0
    free(buf);
1701
0
    return 0;
1702
0
}
1703
1704
/** \internal
1705
 * Get the number of extent blocks rooted at the given extent_header.
1706
 * The count does not include the extent header passed as a parameter.
1707
 *
1708
 * @return the number of extent blocks, or -1 on error.
1709
 */
1710
static int32_t
1711
ext2fs_extent_tree_index_count(TSK_FS_INFO * fs_info,
1712
    TSK_FS_META * fs_meta, ext2fs_extent_header * header, int recursion_depth)
1713
0
{
1714
0
    int fs_blocksize = fs_info->block_size;
1715
0
    ext2fs_extent_idx *indices;
1716
0
    int count = 0;
1717
0
    uint8_t *buf;
1718
0
    int i;
1719
1720
    // 32 is an arbitrary chosen value.
1721
0
    if (recursion_depth > 32) {
1722
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1723
0
        tsk_error_set_errstr
1724
0
            ("ext2fs_load_attrs: exceeded maximum recursion depth!");
1725
0
        return -1;
1726
0
    }
1727
0
    if (tsk_getu16(fs_info->endian, header->eh_magic) != 0xF30A) {
1728
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1729
0
        tsk_error_set_errstr
1730
0
            ("ext2fs_load_attrs: extent header magic valid incorrect!");
1731
0
        return -1;
1732
0
    }
1733
1734
0
    if (tsk_getu16(fs_info->endian, header->eh_depth) == 0) {
1735
0
        return 0;
1736
0
    }
1737
1738
0
    buf = (uint8_t *) tsk_malloc(fs_blocksize);
1739
0
    if (buf == NULL) {
1740
0
        return -1;
1741
0
    }
1742
1743
0
    indices = (ext2fs_extent_idx *) (header + 1);
1744
0
    for (i = 0; i < tsk_getu16(fs_info->endian, header->eh_entries); i++) {
1745
0
        ext2fs_extent_idx *index = &indices[i];
1746
0
        TSK_DADDR_T block =
1747
0
            (((uint32_t) tsk_getu16(fs_info->endian,
1748
0
                    index->ei_leaf_hi)) << 16) | tsk_getu32(fs_info->
1749
0
            endian, index->ei_leaf_lo);
1750
0
        ssize_t cnt =
1751
0
            tsk_fs_read_block(fs_info, block, (char *) buf, fs_blocksize);
1752
0
        int ret;
1753
1754
0
        if (cnt != fs_blocksize) {
1755
0
            if (cnt >= 0) {
1756
0
                tsk_error_reset();
1757
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
1758
0
            }
1759
0
            tsk_error_set_errstr2("ext2fs_extent_tree_index_count: Block %"
1760
0
                PRIuDADDR, block);
1761
0
            free(buf);
1762
0
            return -1;
1763
0
        }
1764
1765
0
        if ((ret =
1766
0
                ext2fs_extent_tree_index_count(fs_info, fs_meta,
1767
0
                    (ext2fs_extent_header *) buf, recursion_depth + 1)) < 0) {
1768
0
            free(buf);
1769
0
            return -1;
1770
0
        }
1771
0
        count += ret;
1772
0
        count++;
1773
0
    }
1774
1775
0
    free(buf);
1776
0
    return count;
1777
0
}
1778
1779
/** \internal
1780
* If the file length is longer than what is in the attr runs, add a sparse
1781
* data run to cover the rest of the file.
1782
*
1783
* @return 0 if successful or 1 on error.
1784
*/
1785
static uint8_t
1786
0
ext2fs_handle_implicit_sparse_data_run(TSK_FS_INFO * fs_info, TSK_FS_ATTR * fs_attr) {
1787
0
    TSK_FS_FILE *fs_file = fs_attr->fs_file;
1788
1789
0
    if (fs_file == NULL) {
1790
0
        return 1;
1791
0
    }
1792
1793
0
    TSK_DADDR_T end_of_runs;
1794
0
    TSK_DADDR_T total_file_blocks = roundup(fs_file->meta->size, fs_info->block_size) / fs_info->block_size;
1795
1796
0
    if (fs_attr->nrd.run_end) {
1797
0
        end_of_runs = fs_attr->nrd.run_end->offset + fs_attr->nrd.run_end->len;
1798
0
    }
1799
0
    else {
1800
0
        end_of_runs = 0;
1801
0
    }
1802
1803
0
    if (end_of_runs < total_file_blocks) {
1804
        // Make sparse run.
1805
0
        TSK_FS_ATTR_RUN *data_run;
1806
0
        data_run = tsk_fs_attr_run_alloc();
1807
0
        if (data_run == NULL) {
1808
0
            return 1;
1809
0
        }
1810
0
        data_run->offset = end_of_runs;
1811
0
        data_run->addr = 0;
1812
0
        data_run->len = total_file_blocks - end_of_runs;
1813
0
        data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
1814
1815
        // Save the run.
1816
0
        if (tsk_fs_attr_add_run(fs_info, fs_attr, data_run)) {
1817
1818
0
            return 1;
1819
0
        }
1820
0
    }
1821
0
    return 0;
1822
0
}
1823
1824
/**
1825
 * \internal
1826
 * Loads attribute for Ext4 Extents-based storage method.
1827
 * @param fs_file File system to analyze
1828
 * @returns 0 on success, 1 otherwise
1829
 */
1830
static uint8_t
1831
ext4_load_attrs_extents(TSK_FS_FILE *fs_file)
1832
0
{
1833
0
    TSK_FS_META *fs_meta = fs_file->meta;
1834
0
    TSK_FS_INFO *fs_info = fs_file->fs_info;
1835
0
    TSK_OFF_T length = 0;
1836
0
    TSK_FS_ATTR *fs_attr;
1837
0
    int i;
1838
0
    ext2fs_extent *extents = NULL;
1839
0
    ext2fs_extent_idx *indices = NULL;
1840
1841
0
    ext2fs_extent_header *header = (ext2fs_extent_header *) fs_meta->content_ptr;
1842
0
    uint16_t num_entries = tsk_getu16(fs_info->endian, header->eh_entries);
1843
0
    uint16_t depth = tsk_getu16(fs_info->endian, header->eh_depth);
1844
1845
0
    if (tsk_getu16(fs_info->endian, header->eh_magic) != 0xF30A) {
1846
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1847
0
        tsk_error_set_errstr
1848
0
        ("ext2fs_load_attrs: extent header magic valid incorrect!");
1849
0
        return 1;
1850
0
    }
1851
1852
0
    if ((fs_meta->attr != NULL)
1853
0
        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
1854
0
        return 0;
1855
0
    }
1856
0
    else if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) {
1857
0
        return 1;
1858
0
    }
1859
1860
0
    if (fs_meta->attr != NULL) {
1861
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
1862
0
    }
1863
0
    else {
1864
0
        fs_meta->attr = tsk_fs_attrlist_alloc();
1865
0
    }
1866
1867
0
    if (TSK_FS_TYPE_ISEXT(fs_info->ftype) == 0) {
1868
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1869
0
        tsk_error_set_errstr
1870
0
        ("ext2fs_load_attr: Called with non-ExtX file system: %x",
1871
0
         fs_info->ftype);
1872
0
        return 1;
1873
0
    }
1874
1875
0
    length = roundup(fs_meta->size, fs_info->block_size);
1876
1877
0
    if ((fs_attr =
1878
0
         tsk_fs_attrlist_getnew(fs_meta->attr,
1879
0
                                TSK_FS_ATTR_NONRES)) == NULL) {
1880
0
        return 1;
1881
0
    }
1882
1883
0
    if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL,
1884
0
                            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
1885
0
                            fs_meta->size, fs_meta->size, length, TSK_FS_ATTR_FLAG_NONE, 0)) {
1886
0
        return 1;
1887
0
    }
1888
1889
0
    if (num_entries == 0) {
1890
0
        if (fs_meta->size == 0) {
1891
            // Empty file
1892
0
            fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
1893
0
            return 0;
1894
0
        }
1895
1896
        // The entire file is sparse
1897
0
        if (ext2fs_handle_implicit_sparse_data_run(fs_info, fs_attr)) {
1898
0
            return 1;
1899
0
        }
1900
1901
0
        fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
1902
0
        return 0;
1903
0
    }
1904
1905
0
    if (depth == 0) {       /* leaf node */
1906
        // Ensure fs_meta->content_ptr is sufficiently large
1907
        // Otherwise extents[i] below can cause an OOB read
1908
0
        if ((fs_meta->content_len < sizeof(ext2fs_extent_header)) || (num_entries > (fs_meta->content_len - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent))) {
1909
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1910
0
            tsk_error_set_errstr
1911
0
            ("ext2fs_load_attr: Inode reports too many extents");
1912
0
            return 1;
1913
0
        }
1914
1915
0
        extents = (ext2fs_extent *) (header + 1);
1916
0
        for (i = 0; i < num_entries; i++) {
1917
0
            ext2fs_extent extent = extents[i];
1918
0
            if (ext2fs_make_data_run_extent(fs_info, fs_attr, &extent)) {
1919
0
                return 1;
1920
0
            }
1921
0
        }
1922
0
    }
1923
0
    else {                  /* interior node */
1924
0
        TSK_FS_ATTR *fs_attr_extent;
1925
0
        int32_t extent_index_size;
1926
0
        TSK_DADDR_T idx_offset;
1927
1928
        // Ensure fs_meta->content_ptr is sufficiently large
1929
        // Otherwise indices[i] below can cause an OOB read
1930
0
        if ((fs_meta->content_len < sizeof(ext2fs_extent_header)) || (num_entries > (fs_meta->content_len - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent_idx))) {
1931
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1932
0
            tsk_error_set_errstr
1933
0
            ("ext2fs_load_attr: Inode reports too many extent indices");
1934
0
            return 1;
1935
0
        }
1936
1937
0
        if ((fs_attr_extent =
1938
0
             tsk_fs_attrlist_getnew(fs_meta->attr,
1939
0
                                    TSK_FS_ATTR_NONRES)) == NULL) {
1940
0
             return 1;
1941
0
         }
1942
1943
0
        extent_index_size =
1944
0
        ext2fs_extent_tree_index_count(fs_info, fs_meta, header, 0);
1945
0
        if (extent_index_size < 0) {
1946
0
            return 1;
1947
0
        }
1948
1949
0
        if (tsk_fs_attr_set_run(fs_file, fs_attr_extent, NULL, NULL,
1950
0
                                TSK_FS_ATTR_TYPE_UNIX_EXTENT, TSK_FS_ATTR_ID_DEFAULT,
1951
0
                                fs_info->block_size * extent_index_size,
1952
0
                                fs_info->block_size * extent_index_size,
1953
0
                                fs_info->block_size * extent_index_size, TSK_FS_ATTR_FLAG_NONE, 0)) {
1954
0
            return 1;
1955
0
        }
1956
1957
0
        indices = (ext2fs_extent_idx *) (header + 1);
1958
0
        idx_offset = 0;
1959
0
        for (i = 0; i < num_entries; i++) {
1960
0
            ext2fs_extent_idx *index = &indices[i];
1961
0
            TSK_DADDR_T child_block =
1962
0
            (((uint32_t) tsk_getu16(fs_info->endian,
1963
0
                                    index->
1964
0
                                    ei_leaf_hi)) << 16) | tsk_getu32(fs_info->
1965
0
                                                                     endian, index->ei_leaf_lo);
1966
0
            if (ext2fs_make_data_run_extent_index(fs_info, fs_attr,
1967
0
                                                  fs_attr_extent, child_block, &idx_offset)) {
1968
0
                return 1;
1969
0
            }
1970
0
        }
1971
0
    }
1972
1973
    // There may be implicit sparse blocks at the end of the file
1974
0
    if (ext2fs_handle_implicit_sparse_data_run(fs_info, fs_attr)) {
1975
0
        return 1;
1976
0
    }
1977
1978
0
    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
1979
1980
0
    return 0;
1981
0
}
1982
1983
/** \internal
1984
 * Add the data runs and extents to the file attributes.
1985
 *
1986
 * @param fs_file File system to analyze
1987
 * @returns 0 on success, 1 otherwise
1988
 */
1989
static uint8_t
1990
ext2fs_load_attrs(TSK_FS_FILE * fs_file)
1991
0
{
1992
    /* EXT4 extents-based storage is dealt with differently than
1993
     * the traditional pointer lists. */
1994
0
    if (fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) {
1995
0
        return ext4_load_attrs_extents(fs_file);
1996
0
    }
1997
0
    else if (fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) {
1998
        // Inline attributes are loaded in dinode_copy
1999
0
        return 0;
2000
0
    }
2001
0
    else {
2002
0
        return tsk_fs_unix_make_data_run(fs_file);
2003
0
    }
2004
0
}
2005
2006
2007
static void
2008
ext4_fsstat_datablock_helper(TSK_FS_INFO * fs, FILE * hFile,
2009
    unsigned int i, TSK_DADDR_T cg_base, int gd_size)
2010
0
{
2011
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
2012
0
    ext2fs_sb *sb = ext2fs->fs;
2013
0
    unsigned int gpfbg = (1 << sb->s_log_groups_per_flex);
2014
0
    unsigned int ibpg, gd_blocks;
2015
0
    unsigned int num_flex_bg, curr_flex_bg;
2016
0
    uint64_t last_block;
2017
0
    ext4fs_gd *ext4_gd = ext2fs->ext4_grp_buf;
2018
0
    uint64_t db_offset = 0;
2019
2020
0
    if (ext4_gd == NULL) {
2021
0
        return;
2022
0
    }
2023
2024
#ifdef Ext4_DBG
2025
    printf("\nDEBUG 64bit:%d, gd_size %d, combined %d\n",
2026
        EXT2FS_HAS_INCOMPAT_FEATURE(fs, sb, EXT2FS_FEATURE_INCOMPAT_64BIT),
2027
        gd_size >= 64,
2028
        EXT2FS_HAS_INCOMPAT_FEATURE(fs, sb, EXT2FS_FEATURE_INCOMPAT_64BIT)
2029
        && gd_size >= 64);
2030
#endif
2031
    /* number of blocks the inodes consume */
2032
0
    ibpg =
2033
0
        (tsk_getu32(fs->endian,
2034
0
            sb->s_inodes_per_group) * ext2fs->inode_size + fs->block_size -
2035
0
        1) / fs->block_size;
2036
    /* number of blocks group descriptors consume */
2037
0
    gd_blocks =
2038
0
        (unsigned int)((gd_size * ext2fs->groups_count + fs->block_size -
2039
0
        1) / fs->block_size);
2040
0
    num_flex_bg = (unsigned int)(ext2fs->groups_count / gpfbg);
2041
0
    if (ext2fs->groups_count % gpfbg)
2042
0
        num_flex_bg++;
2043
0
    curr_flex_bg = i / gpfbg;
2044
2045
0
    last_block =
2046
0
        cg_base + tsk_getu32(fs->endian, sb->s_blocks_per_group) - 1;
2047
0
    if (last_block > fs->last_block) {
2048
0
        last_block = fs->last_block;
2049
0
    }
2050
2051
//DEBUG printf("ibpg %d  cur_flex: %d, flex_bgs: %d : %d, %d",ibpg, i/gpfbg, num_flex_bg, ext2fs->groups_count/gpfbg, ext2fs->groups_count%gpfbg);
2052
2053
#ifdef Ext4_DBG
2054
    printf("\nDEBUG: Flex BG PROCESSING cg_base: %" PRIuDADDR
2055
        ", gpfbg: %d, ibpg: %d \n", cg_base, gpfbg, ibpg);
2056
#endif
2057
    /*If this is the 1st bg in a flex bg then it contains the bitmaps and inode tables */
2058
0
    if (i % gpfbg == 0) {
2059
0
        if (curr_flex_bg == (num_flex_bg - 1)) {
2060
0
            unsigned int num_groups = 0;
2061
0
            unsigned int left_over = 0;
2062
2063
0
            num_groups = (unsigned int)
2064
0
                (fs->last_block / tsk_getu32(fs->endian,
2065
0
                sb->s_blocks_per_group));
2066
0
            if (num_groups % tsk_getu32(fs->endian,
2067
0
                    sb->s_blocks_per_group))
2068
0
                num_groups++;
2069
0
            left_over = (num_groups % gpfbg);
2070
2071
0
            tsk_fprintf(hFile, "    Uninit Data Bitmaps: ");
2072
0
            tsk_fprintf(hFile, "%" PRIu64 " - %" PRIu64 "\n",
2073
0
                ext4_getu64(fs->endian, ext4_gd->bg_block_bitmap_hi,
2074
0
                    ext2fs->ext4_grp_buf->bg_block_bitmap_lo)
2075
0
                + (left_over), ext4_getu64(fs->endian,
2076
0
                    ext4_gd->bg_block_bitmap_hi,
2077
0
                    ext2fs->ext4_grp_buf->bg_block_bitmap_lo)
2078
0
                + gpfbg - 1);
2079
0
            tsk_fprintf(hFile, "    Uninit Inode Bitmaps: ");
2080
0
            tsk_fprintf(hFile, "%" PRIu64 " - %" PRIu64 "\n",
2081
0
                ext4_getu64(fs->endian, ext4_gd->bg_inode_bitmap_hi,
2082
0
                    ext2fs->ext4_grp_buf->bg_inode_bitmap_lo)
2083
0
                + (left_over), ext4_getu64(fs->endian,
2084
0
                    ext4_gd->bg_inode_bitmap_hi,
2085
0
                    ext2fs->ext4_grp_buf->bg_inode_bitmap_lo)
2086
0
                + gpfbg - 1);
2087
0
            tsk_fprintf(hFile, "    Uninit Inode Table: ");
2088
0
            tsk_fprintf(hFile, "%" PRIu64 " - %" PRIu64 "\n",
2089
0
                ext4_getu64(fs->endian, ext4_gd->bg_inode_table_hi,
2090
0
                    ext2fs->ext4_grp_buf->bg_inode_table_lo)
2091
0
                + ((left_over) * ibpg), ext4_getu64(fs->endian,
2092
0
                    ext4_gd->bg_inode_table_hi,
2093
0
                    ext2fs->ext4_grp_buf->bg_inode_table_lo)
2094
0
                + (gpfbg * ibpg) - 1);
2095
2096
0
        }
2097
0
        tsk_fprintf(hFile, "    Data Blocks: ");
2098
0
        db_offset = 0;
2099
0
        if (ext2fs_is_super_bg(tsk_getu32(fs->endian,
2100
0
                    sb->s_feature_ro_compat), i)) {
2101
0
            db_offset = cg_base + (gpfbg * 2)   //To account for the bitmaps
2102
0
                + (ibpg * gpfbg)        //Combined inode tables
2103
0
                + tsk_getu16(fs->endian, ext2fs->fs->pad_or_gdt.s_reserved_gdt_blocks) + gd_blocks      //group descriptors
2104
0
                + 1;            //superblock
2105
0
        }
2106
0
        else {
2107
0
            db_offset = cg_base + (gpfbg * 2)   //To account for the bitmaps
2108
0
                + (ibpg * gpfbg);       //Combined inode tables
2109
0
        }
2110
0
        tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR "\n",
2111
0
            db_offset, last_block);
2112
0
    }
2113
0
    else {
2114
0
        tsk_fprintf(hFile, "    Data Blocks: ");
2115
0
        db_offset = 0;
2116
0
        if (ext2fs_is_super_bg(tsk_getu32(fs->endian,
2117
0
                    sb->s_feature_ro_compat), i)) {
2118
0
            db_offset = cg_base + tsk_getu16(fs->endian, ext2fs->fs->pad_or_gdt.s_reserved_gdt_blocks) + gd_blocks      //group descriptors
2119
0
                + 1;            //superblock
2120
0
        }
2121
0
        else {
2122
0
            db_offset = cg_base;
2123
0
        }
2124
0
        tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR "\n",
2125
0
            db_offset, last_block);
2126
0
    }
2127
2128
0
}
2129
2130
/**
2131
 * Print details about the file system to a file handle.
2132
 *
2133
 * @param fs File system to print details on
2134
 * @param hFile File handle to print text to
2135
 *
2136
 * @returns 1 on error and 0 on success
2137
 */
2138
static uint8_t
2139
ext2fs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
2140
0
{
2141
0
    unsigned int i;
2142
//    unsigned int gpfbg;
2143
//    unsigned int gd_blocks;
2144
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
2145
0
    ext2fs_sb *sb = ext2fs->fs;
2146
0
    int ibpg;
2147
0
    int gd_size;
2148
0
    time_t tmptime;
2149
0
    char timeBuf[128];
2150
0
    const char *tmptypename;
2151
2152
2153
    // clean up any error messages that are lying around
2154
0
    tsk_error_reset();
2155
2156
0
    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
2157
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
2158
2159
0
    switch (fs->ftype) {
2160
0
    case TSK_FS_TYPE_EXT3:
2161
0
        tmptypename = "Ext3";
2162
0
        gd_size = sizeof(ext2fs_gd);
2163
0
        break;
2164
0
    case TSK_FS_TYPE_EXT4:
2165
0
        tmptypename = "Ext4";
2166
0
        if (EXT2FS_HAS_INCOMPAT_FEATURE(fs, sb,
2167
0
                EXT2FS_FEATURE_INCOMPAT_64BIT))
2168
0
            gd_size = sizeof(ext4fs_gd);
2169
0
        else
2170
0
            gd_size = sizeof(ext2fs_gd);
2171
0
        break;
2172
0
    default:
2173
0
        tmptypename = "Ext2";
2174
0
        gd_size = sizeof(ext2fs_gd);
2175
0
    }
2176
0
    tsk_fprintf(hFile, "File System Type: %s\n", tmptypename);
2177
0
    tsk_fprintf(hFile, "Volume Name: %s\n", sb->s_volume_name);
2178
0
    tsk_fprintf(hFile, "Volume ID: %" PRIx64 "%" PRIx64 "\n",
2179
0
        tsk_getu64(fs->endian, &sb->s_uuid[8]), tsk_getu64(fs->endian,
2180
0
            &sb->s_uuid[0]));
2181
2182
0
    tmptime = tsk_getu32(fs->endian, sb->s_wtime);
2183
0
    tsk_fprintf(hFile, "\nLast Written at: %s\n",
2184
0
        (tmptime > 0) ? tsk_fs_time_to_str(tmptime, timeBuf) : "empty");
2185
0
    tmptime = tsk_getu32(fs->endian, sb->s_lastcheck);
2186
0
    tsk_fprintf(hFile, "Last Checked at: %s\n",
2187
0
        (tmptime > 0) ? tsk_fs_time_to_str(tmptime, timeBuf) : "empty");
2188
2189
0
    tmptime = tsk_getu32(fs->endian, sb->s_mtime);
2190
0
    tsk_fprintf(hFile, "\nLast Mounted at: %s\n",
2191
0
        (tmptime > 0) ? tsk_fs_time_to_str(tmptime, timeBuf) : "empty");
2192
2193
    /* State of the file system */
2194
0
    if (tsk_getu16(fs->endian, sb->s_state) & EXT2FS_STATE_VALID)
2195
0
        tsk_fprintf(hFile, "Unmounted properly\n");
2196
0
    else
2197
0
        tsk_fprintf(hFile, "Unmounted Improperly\n");
2198
2199
0
    if (sb->s_last_mounted[0] != '\0')
2200
0
        tsk_fprintf(hFile, "Last mounted on: %s\n", sb->s_last_mounted);
2201
2202
0
    tsk_fprintf(hFile, "\nSource OS: ");
2203
0
    switch (tsk_getu32(fs->endian, sb->s_creator_os)) {
2204
0
    case EXT2FS_OS_LINUX:
2205
0
        tsk_fprintf(hFile, "Linux\n");
2206
0
        break;
2207
0
    case EXT2FS_OS_HURD:
2208
0
        tsk_fprintf(hFile, "HURD\n");
2209
0
        break;
2210
0
    case EXT2FS_OS_MASIX:
2211
0
        tsk_fprintf(hFile, "MASIX\n");
2212
0
        break;
2213
0
    case EXT2FS_OS_FREEBSD:
2214
0
        tsk_fprintf(hFile, "FreeBSD\n");
2215
0
        break;
2216
0
    case EXT2FS_OS_LITES:
2217
0
        tsk_fprintf(hFile, "LITES\n");
2218
0
        break;
2219
0
    default:
2220
0
        tsk_fprintf(hFile, "%" PRIx32 "\n", tsk_getu32(fs->endian,
2221
0
                sb->s_creator_os));
2222
0
        break;
2223
0
    }
2224
2225
0
    if (tsk_getu32(fs->endian, sb->s_rev_level) == EXT2FS_REV_ORIG)
2226
0
        tsk_fprintf(hFile, "Static Structure\n");
2227
0
    else
2228
0
        tsk_fprintf(hFile, "Dynamic Structure\n");
2229
2230
2231
    /* add features */
2232
0
    if (tsk_getu32(fs->endian, sb->s_feature_compat)) {
2233
0
        tsk_fprintf(hFile, "Compat Features: ");
2234
2235
0
        if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2236
0
            EXT2FS_FEATURE_COMPAT_DIR_PREALLOC)
2237
0
            tsk_fprintf(hFile, "Dir Prealloc, ");
2238
0
        if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2239
0
            EXT2FS_FEATURE_COMPAT_IMAGIC_INODES)
2240
0
            tsk_fprintf(hFile, "iMagic inodes, ");
2241
0
        if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2242
0
            EXT2FS_FEATURE_COMPAT_HAS_JOURNAL)
2243
0
            tsk_fprintf(hFile, "Journal, ");
2244
0
        if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2245
0
            EXT2FS_FEATURE_COMPAT_EXT_ATTR)
2246
0
            tsk_fprintf(hFile, "Ext Attributes, ");
2247
0
        if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2248
0
            EXT2FS_FEATURE_COMPAT_RESIZE_INO)
2249
0
            tsk_fprintf(hFile, "Resize Inode, ");
2250
0
        if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2251
0
            EXT2FS_FEATURE_COMPAT_DIR_INDEX)
2252
0
            tsk_fprintf(hFile, "Dir Index");
2253
2254
0
        tsk_fprintf(hFile, "\n");
2255
0
    }
2256
2257
0
    if (tsk_getu32(fs->endian, sb->s_feature_incompat)) {
2258
0
        tsk_fprintf(hFile, "InCompat Features: ");
2259
2260
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2261
0
            EXT2FS_FEATURE_INCOMPAT_COMPRESSION)
2262
0
            tsk_fprintf(hFile, "Compression, ");
2263
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2264
0
            EXT2FS_FEATURE_INCOMPAT_FILETYPE)
2265
0
            tsk_fprintf(hFile, "Filetype, ");
2266
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2267
0
            EXT2FS_FEATURE_INCOMPAT_RECOVER)
2268
0
            tsk_fprintf(hFile, "Needs Recovery, ");
2269
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2270
0
            EXT2FS_FEATURE_INCOMPAT_JOURNAL_DEV)
2271
0
            tsk_fprintf(hFile, "Journal Dev");
2272
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2273
0
            EXT2FS_FEATURE_INCOMPAT_META_BG)
2274
0
            tsk_fprintf(hFile, "Meta Block Groups, ");
2275
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2276
0
            EXT2FS_FEATURE_INCOMPAT_EXTENTS)
2277
0
            tsk_fprintf(hFile, "Extents, ");
2278
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2279
0
            EXT2FS_FEATURE_INCOMPAT_64BIT)
2280
0
            tsk_fprintf(hFile, "64bit, ");
2281
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2282
0
            EXT2FS_FEATURE_INCOMPAT_MMP)
2283
0
            tsk_fprintf(hFile, "Multiple Mount Protection, ");
2284
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2285
0
            EXT2FS_FEATURE_INCOMPAT_FLEX_BG)
2286
0
            tsk_fprintf(hFile, "Flexible Block Groups, ");
2287
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2288
0
            EXT2FS_FEATURE_INCOMPAT_EA_INODE)
2289
0
            tsk_fprintf(hFile, "Extended Attributes, ");
2290
0
        if (tsk_getu32(fs->endian, sb->s_feature_incompat) &
2291
0
            EXT2FS_FEATURE_INCOMPAT_DIRDATA)
2292
0
            tsk_fprintf(hFile, "Directory Entry Data");
2293
2294
0
        tsk_fprintf(hFile, "\n");
2295
0
    }
2296
2297
0
    if (tsk_getu32(fs->endian, sb->s_feature_ro_compat)) {
2298
0
        tsk_fprintf(hFile, "Read Only Compat Features: ");
2299
2300
0
        if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) &
2301
0
            EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER)
2302
0
            tsk_fprintf(hFile, "Sparse Super, ");
2303
0
        if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) &
2304
0
            EXT2FS_FEATURE_RO_COMPAT_LARGE_FILE)
2305
0
            tsk_fprintf(hFile, "Large File, ");
2306
0
        if (EXT2FS_HAS_RO_COMPAT_FEATURE(fs, sb,
2307
0
                EXT2FS_FEATURE_RO_COMPAT_HUGE_FILE))
2308
0
            tsk_fprintf(hFile, "Huge File, ");
2309
0
        if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) &
2310
0
            EXT2FS_FEATURE_RO_COMPAT_BTREE_DIR)
2311
0
            tsk_fprintf(hFile, "Btree Dir, ");
2312
0
        if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) &
2313
0
            EXT2FS_FEATURE_RO_COMPAT_EXTRA_ISIZE)
2314
0
            tsk_fprintf(hFile, "Extra Inode Size");
2315
2316
0
        tsk_fprintf(hFile, "\n");
2317
0
    }
2318
2319
    /* Print journal information */
2320
0
    if (tsk_getu32(fs->endian, sb->s_feature_compat) &
2321
0
        EXT2FS_FEATURE_COMPAT_HAS_JOURNAL) {
2322
2323
0
        tsk_fprintf(hFile, "\nJournal ID: %" PRIx64 "%" PRIx64 "\n",
2324
0
            tsk_getu64(fs->endian, &sb->s_journal_uuid[8]),
2325
0
            tsk_getu64(fs->endian, &sb->s_journal_uuid[0]));
2326
2327
0
        if (tsk_getu32(fs->endian, sb->s_journal_inum) != 0)
2328
0
            tsk_fprintf(hFile, "Journal Inode: %" PRIu32 "\n",
2329
0
                tsk_getu32(fs->endian, sb->s_journal_inum));
2330
2331
0
        if (tsk_getu32(fs->endian, sb->s_journal_dev) != 0)
2332
0
            tsk_fprintf(hFile, "Journal Device: %" PRIu32 "\n",
2333
0
                tsk_getu32(fs->endian, sb->s_journal_dev));
2334
2335
2336
0
    }
2337
2338
0
    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
2339
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
2340
2341
0
    tsk_fprintf(hFile, "Inode Range: %" PRIuINUM " - %" PRIuINUM "\n",
2342
0
        fs->first_inum, fs->last_inum);
2343
0
    tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum);
2344
2345
0
    tsk_fprintf(hFile, "Free Inodes: %" PRIu32 "\n",
2346
0
        tsk_getu32(fs->endian, sb->s_free_inode_count));
2347
    /*
2348
       Only print size of inode for Ext4
2349
       This determines if you will get nanosecs and crtime
2350
     */
2351
0
    if (!strcmp(tmptypename, "Ext4")) {
2352
0
        tsk_fprintf(hFile, "Inode Size: %" PRIu16 "\n",
2353
0
            tsk_getu16(fs->endian, sb->s_inode_size));
2354
0
    }
2355
2356
2357
0
    if (tsk_getu32(fs->endian, sb->s_last_orphan)) {
2358
0
        uint32_t or_in;
2359
0
        tsk_fprintf(hFile, "Orphan Inodes: ");
2360
0
        or_in = tsk_getu32(fs->endian, sb->s_last_orphan);
2361
2362
0
        while (or_in) {
2363
0
            if ((or_in > fs->last_inum) || (or_in < fs->first_inum))
2364
0
                break;
2365
2366
0
            tsk_fprintf(hFile, "%" PRIu32 ", ", or_in);
2367
2368
0
            std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
2369
0
                tsk_fs_file_alloc(fs),
2370
0
                tsk_fs_file_close
2371
0
            };
2372
2373
0
            if (!fs_file) {
2374
                /* Ignore this error */
2375
0
                tsk_error_reset();
2376
0
                break;
2377
0
            }
2378
2379
            /* Get the next one */
2380
0
            if (ext2fs_inode_lookup(fs, fs_file.get(), or_in)) {
2381
                /* Ignore this error */
2382
0
                tsk_error_reset();
2383
0
                break;
2384
0
            }
2385
2386
0
            or_in = (uint32_t) fs_file->meta->time2.ext2.dtime;
2387
0
        }
2388
0
        tsk_fprintf(hFile, "\n");
2389
0
    }
2390
2391
0
    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
2392
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
2393
2394
0
    if (fs->ftype == TSK_FS_TYPE_EXT4) {
2395
0
        tsk_fprintf(hFile, "Block Groups Per Flex Group: %" PRIu32 "\n",
2396
0
            (1 << sb->s_log_groups_per_flex));
2397
//        gpfbg = (1 << sb->s_log_groups_per_flex);
2398
0
    }
2399
2400
0
    tsk_fprintf(hFile, "Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
2401
0
        fs->first_block, fs->last_block);
2402
2403
0
    if (fs->last_block != fs->last_block_act)
2404
0
        tsk_fprintf(hFile,
2405
0
            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
2406
0
            fs->first_block, fs->last_block_act);
2407
2408
0
    tsk_fprintf(hFile, "Block Size: %u\n", fs->block_size);
2409
2410
0
    if (tsk_getu32(fs->endian, sb->s_first_data_block))
2411
0
        tsk_fprintf(hFile,
2412
0
            "Reserved Blocks Before Block Groups: %" PRIu32 "\n",
2413
0
            tsk_getu32(fs->endian, sb->s_first_data_block));
2414
2415
0
    tsk_fprintf(hFile, "Free Blocks: %" PRIu32 "\n",
2416
0
        tsk_getu32(fs->endian, sb->s_free_blocks_count));
2417
2418
0
    tsk_fprintf(hFile, "\nBLOCK GROUP INFORMATION\n");
2419
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
2420
2421
0
    tsk_fprintf(hFile, "Number of Block Groups: %" PRI_EXT2GRP "\n",
2422
0
        ext2fs->groups_count);
2423
2424
0
    tsk_fprintf(hFile, "Inodes per group: %" PRIu32 "\n",
2425
0
        tsk_getu32(fs->endian, sb->s_inodes_per_group));
2426
0
    tsk_fprintf(hFile, "Blocks per group: %" PRIu32 "\n",
2427
0
        tsk_getu32(fs->endian, sb->s_blocks_per_group));
2428
2429
2430
    /* number of blocks the inodes consume */
2431
0
    ibpg =
2432
0
        (tsk_getu32(fs->endian,
2433
0
            sb->s_inodes_per_group) * ext2fs->inode_size + fs->block_size -
2434
0
        1) / fs->block_size;
2435
    /* number of blocks group descriptors consume */
2436
//    gd_blocks =
2437
//        (unsigned int)((gd_size * ext2fs->groups_count + fs->block_size -
2438
//        1) / fs->block_size);
2439
2440
#ifdef Ext4_DBG
2441
    tsk_fprintf(hFile, "\n\tDEBUG: Group Descriptor Size: %d\n", gd_size);      //DEBUG
2442
    tsk_fprintf(hFile, "\n\tDEBUG: Group Descriptor Size: %d\n", *sb->s_desc_size);     //DEBUG
2443
    debug_print_buf((unsigned char *) &sb->pad_or_gdt, 16);
2444
    printf("\n\tDEBUG: gdt_growth: %d\n", tsk_getu16(fs->endian,
2445
        sb->pad_or_gdt.s_reserved_gdt_blocks));
2446
#endif
2447
2448
0
    for (i = 0; i < ext2fs->groups_count; i++) {
2449
0
        TSK_DADDR_T cg_base;
2450
0
        TSK_INUM_T inum;
2451
2452
        /* lock access to grp_buf */
2453
0
        tsk_take_lock(&ext2fs->lock);
2454
2455
0
        if (ext2fs_group_load(ext2fs, i)) {
2456
0
            tsk_release_lock(&ext2fs->lock);
2457
0
            return 1;
2458
0
        }
2459
0
        tsk_fprintf(hFile, "\nGroup: %d:\n", i);
2460
0
        if (ext2fs->ext4_grp_buf != NULL) {
2461
0
            tsk_fprintf(hFile, "  Block Group Flags: [");
2462
0
            if (EXT4BG_HAS_FLAG(fs, ext2fs->ext4_grp_buf,
2463
0
                EXT4_BG_INODE_UNINIT))
2464
0
                tsk_fprintf(hFile, "INODE_UNINIT, ");
2465
0
            if (EXT4BG_HAS_FLAG(fs, ext2fs->ext4_grp_buf,
2466
0
                EXT4_BG_BLOCK_UNINIT))
2467
0
                tsk_fprintf(hFile, "BLOCK_UNINIT, ");
2468
0
            if (EXT4BG_HAS_FLAG(fs, ext2fs->ext4_grp_buf,
2469
0
                EXT4_BG_INODE_ZEROED))
2470
0
                tsk_fprintf(hFile, "INODE_ZEROED, ");
2471
0
            tsk_fprintf(hFile, "\b\b]\n");
2472
0
        }
2473
0
        inum =
2474
0
            fs->first_inum + tsk_gets32(fs->endian,
2475
0
            sb->s_inodes_per_group) * i;
2476
0
        tsk_fprintf(hFile, "  Inode Range: %" PRIuINUM " - ", inum);
2477
2478
0
        if ((inum + tsk_gets32(fs->endian, sb->s_inodes_per_group) - 1) <
2479
0
            fs->last_inum)
2480
0
            tsk_fprintf(hFile, "%" PRIuINUM "\n",
2481
0
            inum + tsk_gets32(fs->endian, sb->s_inodes_per_group) - 1);
2482
0
        else
2483
0
            tsk_fprintf(hFile, "%" PRIuINUM "\n", fs->last_inum);
2484
2485
0
        if (tsk_getu32(fs->endian,
2486
0
            ext2fs->fs->
2487
0
            s_feature_incompat) & EXT2FS_FEATURE_INCOMPAT_64BIT) {
2488
0
                cg_base = ext4_cgbase_lcl(fs, sb, i);
2489
#ifdef Ext4_DBG
2490
                printf("DEBUG64: ext2_cgbase_lcl %" PRIuDADDR "\n", cg_base);
2491
                printf("DEBUG64: fs->s_first_data_block %" PRIuDADDR "\n",
2492
                    tsk_getu32(fs->endian, sb->s_first_data_block));
2493
                printf("DEBUG64: blocks_per_group %" PRIuDADDR "\n",
2494
                    tsk_getu32(fs->endian, sb->s_blocks_per_group));
2495
                printf("DEBUG64: i %" PRIuDADDR " %" PRIuDADDR " %" PRIuDADDR
2496
                    "\n", i, tsk_getu32(fs->endian, sb->s_blocks_per_group),
2497
                    (uint64_t) i * (uint64_t) tsk_getu32(fs->endian,
2498
                    sb->s_blocks_per_group));
2499
                //printf("DEBUG: calculated %"PRIuDADDR"\n", )
2500
#endif
2501
0
                tsk_fprintf(hFile,
2502
0
                    "  Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
2503
0
                    cg_base, ((ext4_cgbase_lcl(fs, sb,
2504
0
                    i + 1) - 1) <
2505
0
                    fs->last_block) ? (ext4_cgbase_lcl(fs, sb,
2506
0
                    i + 1) - 1) : fs->last_block);
2507
0
        }
2508
0
        else {
2509
0
            cg_base = ext2_cgbase_lcl(fs, sb, i);
2510
#ifdef Ext4_DBG
2511
            debug_print_buf(sb, 100);
2512
            printf("DEBUG32: ext2_cgbase_lcl %" PRIuDADDR "\n", cg_base);
2513
            printf("DEBUG32: fs->s_first_data_block %" PRIu32 "\n",
2514
                tsk_getu32(fs->endian, sb->s_first_data_block));
2515
            printf("DEBUG32: blocks_per_group %" PRIu32 "\n",
2516
                tsk_getu32(fs->endian, sb->s_blocks_per_group));
2517
            printf("DEBUG32: i: %" PRIu32 " blocks per group: %" PRIu32
2518
                " i*blocks_per_group: %" PRIu32 "\n",
2519
                i, tsk_getu32(fs->endian, sb->s_blocks_per_group),
2520
                (uint64_t) i * (uint64_t) tsk_getu32(fs->endian,
2521
                sb->s_blocks_per_group));
2522
            //printf("DEBUG: calculated %"PRIuDADDR"\n", )
2523
#endif
2524
0
            tsk_fprintf(hFile,
2525
0
                "  Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
2526
0
                cg_base, ((ext2_cgbase_lcl(fs, sb,
2527
0
                i + 1) - 1) <
2528
0
                fs->last_block) ? (ext2_cgbase_lcl(fs, sb,
2529
0
                i + 1) - 1) : fs->last_block);
2530
0
        }
2531
2532
2533
2534
0
        tsk_fprintf(hFile, "  Layout:\n");
2535
2536
        /* only print the super block data if we are not in a sparse
2537
        * group
2538
        */
2539
#ifdef Ext4_DBG
2540
        printf("DEBUG: ext2fs_super: %d\n",
2541
            ext2fs_is_super_bg(tsk_getu32(fs->endian,
2542
            sb->s_feature_ro_compat), i));
2543
#endif
2544
        /*        if (((tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) &
2545
        EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) &&
2546
        (cg_base != tsk_getu32(fs->endian,
2547
        ext2fs->grp_buf->bg_block_bitmap)))
2548
        || ((tsk_getu32(fs->endian,
2549
        ext2fs->fs->s_feature_ro_compat) &
2550
        EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) == 0)) {
2551
        */
2552
0
        if (ext2fs_is_super_bg(tsk_getu32(fs->endian,
2553
0
            sb->s_feature_ro_compat), i)) {
2554
0
                TSK_OFF_T boff;
2555
2556
                /* the super block is the first 1024 bytes */
2557
0
                tsk_fprintf(hFile,
2558
0
                    "    Super Block: %" PRIuDADDR " - %" PRIuDADDR "\n",
2559
0
                    cg_base,
2560
0
                    cg_base +
2561
0
                    ((sizeof(ext2fs_sb) + fs->block_size -
2562
0
                    1) / fs->block_size) - 1);
2563
2564
0
                boff = roundup(sizeof(ext2fs_sb), fs->block_size);
2565
2566
                /* Group Descriptors */
2567
0
                tsk_fprintf(hFile,
2568
0
                    "    Group Descriptor Table: %" PRIuDADDR " - ",
2569
0
                    (cg_base + (boff + fs->block_size - 1) / fs->block_size));
2570
2571
                //            printf("DEBUG: Groups Count: %u * gd_size: %u = %u\n", ext2fs->groups_count, gd_size, ext2fs->groups_count * gd_size);
2572
0
                boff += (ext2fs->groups_count * gd_size);
2573
0
                tsk_fprintf(hFile, "%" PRIuDADDR "\n",
2574
0
                    ((cg_base +
2575
0
                    (boff + fs->block_size - 1) / fs->block_size) -
2576
0
                    1));
2577
0
                if (fs->ftype == TSK_FS_TYPE_EXT4) {
2578
0
                    tsk_fprintf(hFile,
2579
0
                        "    Group Descriptor Growth Blocks: %" PRIuDADDR
2580
0
                        " - ",
2581
0
                        cg_base + (boff + fs->block_size -
2582
0
                        1) / fs->block_size);
2583
0
                    boff +=
2584
0
                        tsk_getu16(fs->endian,
2585
0
                        ext2fs->fs->pad_or_gdt.s_reserved_gdt_blocks) *
2586
0
                        fs->block_size;
2587
0
                    tsk_fprintf(hFile, "%" PRIuDADDR "\n",
2588
0
                        ((cg_base + (boff + fs->block_size -
2589
0
                        1) / fs->block_size) - 1));
2590
0
                }
2591
0
        }
2592
2593
2594
0
        if (ext2fs->ext4_grp_buf != NULL) {
2595
            /* The block bitmap is a full block */
2596
0
            tsk_fprintf(hFile,
2597
0
                "    Data bitmap: %" PRIu64 " - %" PRIu64 "\n",
2598
0
                ext4_getu64(fs->endian,
2599
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_hi,
2600
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_lo),
2601
0
                ext4_getu64(fs->endian,
2602
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_hi,
2603
0
                ext2fs->ext4_grp_buf->bg_block_bitmap_lo));
2604
2605
2606
            /* The inode bitmap is a full block */
2607
0
            tsk_fprintf(hFile,
2608
0
                "    Inode bitmap: %" PRIu64 " - %" PRIu64 "\n",
2609
0
                ext4_getu64(fs->endian,
2610
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_hi,
2611
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_lo),
2612
0
                ext4_getu64(fs->endian,
2613
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_hi,
2614
0
                ext2fs->ext4_grp_buf->bg_inode_bitmap_lo));
2615
2616
2617
0
            tsk_fprintf(hFile,
2618
0
                "    Inode Table: %" PRIu64 " - %" PRIu64 "\n",
2619
0
                ext4_getu64(fs->endian,
2620
0
                ext2fs->ext4_grp_buf->bg_inode_table_hi,
2621
0
                ext2fs->ext4_grp_buf->bg_inode_table_lo),
2622
0
                ext4_getu64(fs->endian,
2623
0
                ext2fs->ext4_grp_buf->bg_inode_table_hi,
2624
0
                ext2fs->ext4_grp_buf->bg_inode_table_lo)
2625
0
                + ibpg - 1);
2626
2627
0
            ext4_fsstat_datablock_helper(fs, hFile, i, cg_base, gd_size);
2628
0
        }
2629
0
        else {
2630
            /* The block bitmap is a full block */
2631
0
            tsk_fprintf(hFile,
2632
0
                "    Data bitmap: %" PRIu32 " - %" PRIu32 "\n",
2633
0
                tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap),
2634
0
                tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap));
2635
2636
2637
            /* The inode bitmap is a full block */
2638
0
            tsk_fprintf(hFile,
2639
0
                "    Inode bitmap: %" PRIu32 " - %" PRIu32 "\n",
2640
0
                tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap),
2641
0
                tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap));
2642
2643
2644
0
            tsk_fprintf(hFile,
2645
0
                "    Inode Table: %" PRIu32 " - %" PRIu32 "\n",
2646
0
                tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_table),
2647
0
                tsk_getu32(fs->endian,
2648
0
                ext2fs->grp_buf->bg_inode_table) + ibpg - 1);
2649
2650
0
            tsk_fprintf(hFile, "    Data Blocks: ");
2651
            // BC: Commented out from Ext4 commit because it produced
2652
            // bad data on Ext2 test image.
2653
            //if (ext2fs_is_super_bg(tsk_getu32(fs->endian,
2654
            //            sb->s_feature_ro_compat), i)) {
2655
0
            if ((tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) &
2656
0
                EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) &&
2657
0
                (cg_base == tsk_getu32(fs->endian,
2658
0
                ext2fs->grp_buf->bg_block_bitmap))) {
2659
2660
                    /* it goes from the end of the inode bitmap to before the
2661
                    * table
2662
                    *
2663
                    * This hard coded aspect does not scale ...
2664
                    */
2665
2666
0
                    tsk_fprintf(hFile, "%" PRIu32 " - %" PRIu32 ", ",
2667
0
                        tsk_getu32(fs->endian,
2668
0
                        ext2fs->grp_buf->bg_inode_bitmap) + 1,
2669
0
                        tsk_getu32(fs->endian,
2670
0
                        ext2fs->grp_buf->bg_inode_table) - 1);
2671
0
            }
2672
2673
0
            tsk_fprintf(hFile, "%" PRIu32 " - %" PRIu32 "\n",
2674
0
                (uint64_t) tsk_getu32(fs->endian,
2675
0
                ext2fs->grp_buf->bg_inode_table) + ibpg,
2676
0
                ((ext2_cgbase_lcl(fs, sb, i + 1) - 1) <
2677
0
                fs->last_block) ? (ext2_cgbase_lcl(fs, sb,
2678
0
                i + 1) - 1) : fs->last_block);
2679
0
        }
2680
2681
        /* Print the free info */
2682
2683
        /* The last group may not have a full number of blocks */
2684
0
        if (i != (ext2fs->groups_count - 1)) {
2685
0
            uint64_t tmpInt;
2686
2687
0
            if (ext2fs->ext4_grp_buf != NULL)
2688
                // @@@ Should be 32-bit
2689
0
                tmpInt = tsk_getu16(fs->endian,
2690
0
                    ext2fs->ext4_grp_buf->bg_free_inodes_count_lo);
2691
0
            else
2692
0
                tmpInt = tsk_getu16(fs->endian,
2693
0
                    ext2fs->grp_buf->bg_free_inodes_count);
2694
2695
0
            tsk_fprintf(hFile,
2696
0
                "  Free Inodes: %" PRIu32 " (%" PRIu32 "%%)\n",
2697
0
                tmpInt, (100 * tmpInt) /
2698
0
                tsk_getu32(fs->endian, sb->s_inodes_per_group));
2699
2700
2701
0
            if (ext2fs->ext4_grp_buf != NULL)
2702
                // @@@ Should be 32-bit
2703
0
                tmpInt = tsk_getu16(fs->endian,
2704
0
                    ext2fs->ext4_grp_buf->bg_free_blocks_count_lo);
2705
0
            else
2706
0
                tmpInt = tsk_getu16(fs->endian,
2707
0
                    ext2fs->grp_buf->bg_free_blocks_count);
2708
2709
0
            tsk_fprintf(hFile,
2710
0
                "  Free Blocks: %" PRIu32 " (%" PRIu32 "%%)\n",
2711
0
                tmpInt,
2712
0
                (100 * tmpInt) /
2713
0
                tsk_getu32(fs->endian, sb->s_blocks_per_group));
2714
0
        }
2715
0
        else {
2716
0
            TSK_INUM_T inum_left;
2717
0
            TSK_DADDR_T blk_left;
2718
0
            uint64_t tmpInt;
2719
2720
0
            inum_left =
2721
0
                (fs->last_inum % tsk_gets32(fs->endian,
2722
0
                sb->s_inodes_per_group)) - 1;
2723
2724
0
            if (inum_left == 0)
2725
0
                inum_left = tsk_getu32(fs->endian, sb->s_inodes_per_group);
2726
2727
0
            if (ext2fs->ext4_grp_buf != NULL)
2728
                // @@@ Should be 32-bit
2729
0
                tmpInt = tsk_getu16(fs->endian,
2730
0
                    ext2fs->ext4_grp_buf->bg_free_inodes_count_lo);
2731
0
            else
2732
0
                tmpInt = tsk_getu16(fs->endian,
2733
0
                    ext2fs->grp_buf->bg_free_inodes_count);
2734
2735
0
            tsk_fprintf(hFile, "  Free Inodes: %" PRIu32 " (%d%%)\n",
2736
0
                tmpInt, 100 * tmpInt / inum_left);
2737
2738
            /* Now blocks */
2739
0
            blk_left =
2740
0
                fs->block_count % tsk_getu32(fs->endian,
2741
0
                sb->s_blocks_per_group);
2742
0
            if (blk_left == 0)
2743
0
                blk_left = tsk_getu32(fs->endian, sb->s_blocks_per_group);
2744
2745
0
            if (ext2fs->ext4_grp_buf != NULL)
2746
                // @@@ Should be 32-bit
2747
0
                tmpInt = tsk_getu16(fs->endian,
2748
0
                    ext2fs->ext4_grp_buf->bg_free_blocks_count_lo);
2749
0
            else
2750
0
                tmpInt = tsk_getu16(fs->endian,
2751
0
                    ext2fs->grp_buf->bg_free_blocks_count);
2752
2753
0
            tsk_fprintf(hFile, "  Free Blocks: %" PRIu32 " (%d%%)\n",
2754
0
                tmpInt, 100 * tmpInt / blk_left);
2755
0
        }
2756
2757
2758
0
        if (ext2fs->ext4_grp_buf != NULL) {
2759
            // @@@@ Sould be 32-bit
2760
0
            tsk_fprintf(hFile, "  Total Directories: %" PRIu16 "\n",
2761
0
                tsk_getu16(fs->endian, ext2fs->ext4_grp_buf->bg_used_dirs_count_lo));
2762
2763
0
            tsk_fprintf(hFile, "  Stored Checksum: 0x%04" PRIX16 "\n",
2764
0
                tsk_getu16(fs->endian, ext2fs->ext4_grp_buf->bg_checksum));
2765
#ifdef EXT4_CHECKSUMS
2766
            //Need Non-GPL CRC16
2767
            tsk_fprintf(hFile, "  Calculated Checksum: 0x%04" PRIX16 "\n",
2768
                ext4_group_desc_csum(ext2fs->fs, i, ext2fs->ext4_grp_buf));
2769
#endif
2770
0
        }
2771
0
        else {
2772
0
            tsk_fprintf(hFile, "  Total Directories: %" PRIu16 "\n",
2773
0
               tsk_getu16(fs->endian, ext2fs->grp_buf->bg_used_dirs_count));
2774
0
        }
2775
2776
0
        tsk_release_lock(&ext2fs->lock);
2777
0
    }
2778
2779
0
    return 0;
2780
0
}
2781
2782
2783
/************************* istat *******************************/
2784
2785
static void
2786
ext2fs_make_acl_str(char *str, int len, uint16_t perm)
2787
0
{
2788
0
    int i = 0;
2789
2790
0
    if (perm & EXT2_PACL_PERM_READ) {
2791
0
        snprintf(&str[i], len - 1, "Read");
2792
0
        i += 4;
2793
0
    }
2794
0
    if (perm & EXT2_PACL_PERM_WRITE) {
2795
0
        if (i) {
2796
0
            snprintf(&str[i], len - i - 1, ", ");
2797
0
            i += 2;
2798
0
        }
2799
0
        snprintf(&str[i], len - i - 1, "Write");
2800
0
        i += 5;
2801
0
    }
2802
0
    if (perm & EXT2_PACL_PERM_EXEC) {
2803
0
        if (i) {
2804
0
            snprintf(&str[i], len - i - 1, ", ");
2805
0
            i += 2;
2806
0
        }
2807
0
        snprintf(&str[i], len - i - 1, "Execute");
2808
0
        i += 7;
2809
0
    }
2810
0
}
2811
2812
2813
typedef struct {
2814
    FILE *hFile;
2815
    int idx;
2816
} EXT2FS_PRINT_ADDR;
2817
2818
2819
/* Callback for istat to print the block addresses */
2820
static TSK_WALK_RET_ENUM
2821
print_addr_act(
2822
  TSK_FS_FILE * fs_file,
2823
  [[maybe_unused]] TSK_OFF_T a_off,
2824
  TSK_DADDR_T addr,
2825
  [[maybe_unused]] char *buf,
2826
  size_t size,
2827
  TSK_FS_BLOCK_FLAG_ENUM flags,
2828
  void *a_ptr)
2829
0
{
2830
0
    TSK_FS_INFO *fs = fs_file->fs_info;
2831
0
    EXT2FS_PRINT_ADDR *print = (EXT2FS_PRINT_ADDR *) a_ptr;
2832
2833
0
    if (flags & TSK_FS_BLOCK_FLAG_CONT) {
2834
0
        int i, s;
2835
        /* cycle through the blocks if they exist */
2836
0
        for (i = 0, s = (int) size; s > 0; s -= fs->block_size, i++) {
2837
2838
            /* sparse file */
2839
0
            if (addr)
2840
0
                tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr + i);
2841
0
            else
2842
0
                tsk_fprintf(print->hFile, "0 ");
2843
2844
0
            if (++(print->idx) == 8) {
2845
0
                tsk_fprintf(print->hFile, "\n");
2846
0
                print->idx = 0;
2847
0
            }
2848
0
        }
2849
0
    }
2850
2851
0
    return TSK_WALK_CONT;
2852
0
}
2853
2854
2855
/**
2856
 * Print details on a specific file to a file handle.
2857
 *
2858
 * @param fs File system file is located in
2859
 * @param hFile File handle to print text to
2860
 * @param inum Address of file in file system
2861
 * @param numblock The number of blocks in file to force print (can go beyond file size)
2862
 * @param sec_skew Clock skew in seconds to also print times in
2863
 *
2864
 * @returns 1 on error and 0 on success
2865
 */
2866
static uint8_t
2867
ext2fs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, TSK_INUM_T inum,
2868
    TSK_DADDR_T numblock, int32_t sec_skew)
2869
0
{
2870
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
2871
0
    TSK_FS_META *fs_meta;
2872
0
    char ls[12];
2873
0
    EXT2FS_PRINT_ADDR print;
2874
0
    const TSK_FS_ATTR *fs_attr_indir;
2875
0
    ext2fs_inode *dino_buf = NULL;
2876
0
    uint8_t *ea_buf = NULL;
2877
0
    size_t ea_buf_len = 0;
2878
0
    char timeBuf[128];
2879
0
    unsigned int size;
2880
0
    unsigned int large_inodes;
2881
2882
    // clean up any error messages that are lying around
2883
0
    tsk_error_reset();
2884
0
    if (ext2fs->inode_size > 128) {
2885
0
        large_inodes = 1;
2886
0
    }
2887
0
    else {
2888
0
        large_inodes = 0;
2889
0
    }
2890
2891
0
    size =
2892
0
        ext2fs->inode_size >
2893
0
        sizeof(ext2fs_inode) ? ext2fs->inode_size : sizeof(ext2fs_inode);
2894
0
    if ((dino_buf = (ext2fs_inode *) tsk_malloc(size)) == NULL) {
2895
0
        return 1;
2896
0
    }
2897
2898
0
    if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) {
2899
0
        free(dino_buf);
2900
0
        return 1;
2901
0
    }
2902
2903
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
2904
0
        tsk_fs_file_open_meta(fs, NULL, inum),
2905
0
        tsk_fs_file_close
2906
0
    };
2907
2908
0
    if (!fs_file) {
2909
0
        free(dino_buf);
2910
0
        return 1;
2911
0
    }
2912
0
    fs_meta = fs_file->meta;
2913
2914
0
    tsk_fprintf(hFile, "inode: %" PRIuINUM "\n", inum);
2915
0
    tsk_fprintf(hFile, "%sAllocated\n",
2916
0
        (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : "Not ");
2917
2918
0
    tsk_fprintf(hFile, "Group: %" PRIuGID "\n", ext2fs->grp_num);
2919
2920
    // Note that if this is a "virtual file", then ext2fs->dino_buf may not be set.
2921
0
    tsk_fprintf(hFile, "Generation Id: %" PRIu32 "\n",
2922
0
        tsk_getu32(fs->endian, dino_buf->i_generation));
2923
2924
0
    if (fs_meta->link)
2925
0
        tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link);
2926
2927
0
    tsk_fprintf(hFile, "uid / gid: %" PRIuUID " / %" PRIuGID "\n",
2928
0
        fs_meta->uid, fs_meta->gid);
2929
2930
0
    tsk_fs_meta_make_ls(fs_meta, ls, sizeof(ls));
2931
0
    tsk_fprintf(hFile, "mode: %s\n", ls);
2932
2933
    /* Print the device ids */
2934
0
    if ((fs_meta->type == TSK_FS_META_TYPE_BLK)
2935
0
        || (fs_meta->type == TSK_FS_META_TYPE_CHR)) {
2936
0
        tsk_fprintf(hFile,
2937
0
            "Device Major: %" PRIu8 "   Minor: %" PRIu8 "\n",
2938
0
            dino_buf->i_block[0][1], dino_buf->i_block[0][0]);
2939
0
    }
2940
2941
0
    if (tsk_getu32(fs->endian, dino_buf->i_flags)) {
2942
0
        tsk_fprintf(hFile, "Flags: ");
2943
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_SECDEL)
2944
0
            tsk_fprintf(hFile, "Secure Delete, ");
2945
2946
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_UNRM)
2947
0
            tsk_fprintf(hFile, "Undelete, ");
2948
2949
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_COMP)
2950
0
            tsk_fprintf(hFile, "Compressed, ");
2951
2952
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_SYNC)
2953
0
            tsk_fprintf(hFile, "Sync Updates, ");
2954
2955
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_IMM)
2956
0
            tsk_fprintf(hFile, "Immutable, ");
2957
2958
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_APPEND)
2959
0
            tsk_fprintf(hFile, "Append Only, ");
2960
2961
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NODUMP)
2962
0
            tsk_fprintf(hFile, "Do Not Dump, ");
2963
2964
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NOA)
2965
0
            tsk_fprintf(hFile, "No A-Time, ");
2966
2967
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_DIRTY)
2968
0
            tsk_fprintf(hFile, "Dirty Compressed File, ");
2969
2970
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_COMPRBLK)
2971
0
            tsk_fprintf(hFile, "Compressed Clusters, ");
2972
2973
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NOCOMPR)
2974
0
            tsk_fprintf(hFile, "Do Not Compress, ");
2975
2976
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_ECOMPR)
2977
0
            tsk_fprintf(hFile, "Compression Error, ");
2978
2979
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_INDEX)
2980
0
            tsk_fprintf(hFile, "Hash Indexed Directory, ");
2981
2982
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_IMAGIC)
2983
0
            tsk_fprintf(hFile, "AFS Magic Directory, ");
2984
2985
0
        if (tsk_getu32(fs->endian,
2986
0
                dino_buf->i_flags) & EXT2_IN_JOURNAL_DATA)
2987
0
            tsk_fprintf(hFile, "Journal Data, ");
2988
2989
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NOTAIL)
2990
0
            tsk_fprintf(hFile, "Do Not Merge Tail, ");
2991
2992
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_DIRSYNC)
2993
0
            tsk_fprintf(hFile, "Directory  Sync, ");
2994
2995
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_TOPDIR)
2996
0
            tsk_fprintf(hFile, "Top Directory, ");
2997
2998
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_HUGE_FILE)
2999
0
            tsk_fprintf(hFile, "Huge File, ");
3000
3001
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EXTENTS)
3002
0
            tsk_fprintf(hFile, "Extents, ");
3003
3004
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EA_INODE)
3005
0
            tsk_fprintf(hFile, "Large Extended Attribute, ");
3006
3007
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EOFBLOCKS)
3008
0
            tsk_fprintf(hFile, "Blocks Allocated Beyond EOF, ");
3009
3010
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE)
3011
0
            tsk_fprintf(hFile, "Snapshot, ");
3012
3013
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE_DELETED)
3014
0
            tsk_fprintf(hFile, "Deleted Snapshot, ");
3015
3016
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE_SHRUNK)
3017
0
            tsk_fprintf(hFile, "Shrunk Snapshot, ");
3018
3019
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_INLINE_DATA)
3020
0
            tsk_fprintf(hFile, "Inline Data, ");
3021
3022
0
        if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_PROJINHERIT)
3023
0
            tsk_fprintf(hFile, "Inherited project ID, ");
3024
3025
3026
0
        tsk_fprintf(hFile, "\n");
3027
0
    }
3028
3029
0
    tsk_fprintf(hFile, "size: %" PRIdOFF "\n", fs_meta->size);
3030
0
    tsk_fprintf(hFile, "num of links: %d\n", fs_meta->nlink);
3031
3032
    /* Ext attribute are stored in a block with a header and a list
3033
     * of entries that are aligned to 4-byte boundaries.  The attr
3034
     * value is stored at the end of the block.  There are 4 null bytes
3035
     * in between the headers and values
3036
     */
3037
0
    if (tsk_getu32(fs->endian, dino_buf->i_file_acl) != 0) {
3038
0
        char *buf;
3039
0
        ext2fs_ea_header *ea_head;
3040
0
        ext2fs_ea_entry *ea_entry;
3041
0
        ssize_t cnt;
3042
3043
0
        if ((buf = (char*) tsk_malloc(fs->block_size)) == NULL) {
3044
0
            free(dino_buf);
3045
0
            return 1;
3046
0
        }
3047
3048
0
        tsk_fprintf(hFile,
3049
0
            "\nExtended Attributes  (Block: %" PRIu32 ")\n",
3050
0
            tsk_getu32(fs->endian, dino_buf->i_file_acl));
3051
3052
        /* Is the value too big? */
3053
0
        if (tsk_getu32(fs->endian, dino_buf->i_file_acl) > fs->last_block) {
3054
0
            tsk_fprintf(hFile,
3055
0
                "Extended Attributes block is larger than file system\n");
3056
0
            goto egress_ea;
3057
0
        }
3058
3059
0
        cnt = tsk_fs_read(fs,
3060
0
            (TSK_DADDR_T) tsk_getu32(fs->endian,
3061
0
                dino_buf->i_file_acl) * fs->block_size,
3062
0
            buf, fs->block_size);
3063
3064
0
        if (cnt != fs->block_size) {
3065
0
            if (cnt >= 0) {
3066
0
                tsk_error_reset();
3067
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
3068
0
            }
3069
0
            tsk_error_set_errstr2("ext2fs_istat: ACL block %" PRIu32,
3070
0
                tsk_getu32(fs->endian, dino_buf->i_file_acl));
3071
0
            free(buf);
3072
0
            free(dino_buf);
3073
0
            return 1;
3074
0
        }
3075
3076
3077
        /* Check the header */
3078
0
        ea_head = (ext2fs_ea_header *) buf;
3079
0
        if (tsk_getu32(fs->endian, ea_head->magic) != EXT2_EA_MAGIC) {
3080
0
            tsk_fprintf(hFile,
3081
0
                "Incorrect extended attribute header: %" PRIx32 "\n",
3082
0
                tsk_getu32(fs->endian, ea_head->magic));
3083
0
        }
3084
3085
3086
        /* Cycle through each entry - at the top of the block */
3087
0
        for (ea_entry = (ext2fs_ea_entry *) & ea_head->entry;
3088
0
            ((uintptr_t) ea_entry <
3089
0
                ((uintptr_t) buf + fs->block_size -
3090
0
                    sizeof(ext2fs_ea_entry)));
3091
0
            ea_entry =
3092
0
            (ext2fs_ea_entry *) ((uintptr_t) ea_entry +
3093
0
                EXT2_EA_LEN(ea_entry->nlen))) {
3094
3095
0
            char name[256];
3096
3097
            /* Stop if the first four bytes are NULL */
3098
0
            if ((ea_entry->nlen == 0) && (ea_entry->nidx == 0) &&
3099
0
                (tsk_getu16(fs->endian, ea_entry->val_off) == 0))
3100
0
                break;
3101
3102
            /* The Linux src does not allow this */
3103
0
            if (tsk_getu32(fs->endian, ea_entry->val_blk) != 0) {
3104
0
                tsk_fprintf(hFile,
3105
0
                    "Attribute has non-zero value block - skipping\n");
3106
0
                continue;
3107
0
            }
3108
3109
3110
            /* Is the value location and size valid? */
3111
            //if ((tsk_getu32(fs->endian,
3112
0
            if ((tsk_getu16(fs->endian,
3113
0
                        ea_entry->val_off) > fs->block_size)
3114
0
                || ((tsk_getu16(fs->endian,
3115
0
                            ea_entry->val_off) + tsk_getu32(fs->endian,
3116
0
                            ea_entry->val_size)) > fs->block_size)) {
3117
0
                continue;
3118
0
            }
3119
3120
3121
            /* Copy the name into a buffer - not NULL term */
3122
0
            strncpy(name, (char *) &ea_entry->name, ea_entry->nlen);
3123
0
            name[ea_entry->nlen] = '\0';
3124
3125
3126
            /* User assigned attributes - setfattr / getfattr */
3127
0
            if ((ea_entry->nidx == EXT2_EA_IDX_USER) ||
3128
0
                (ea_entry->nidx == EXT2_EA_IDX_TRUSTED) ||
3129
0
                (ea_entry->nidx == EXT2_EA_IDX_SECURITY)) {
3130
0
                char val[256];
3131
3132
0
                strncpy(val,
3133
0
                    &buf[tsk_getu16(fs->endian, ea_entry->val_off)],
3134
0
                    tsk_getu32(fs->endian,
3135
0
                        ea_entry->val_size) >
3136
0
                    256 ? 256 : tsk_getu32(fs->endian,
3137
0
                        ea_entry->val_size));
3138
3139
0
                val[tsk_getu32(fs->endian, ea_entry->val_size) > 255 ?
3140
0
                    255 : tsk_getu32(fs->endian, ea_entry->val_size)] =
3141
0
                    '\0';
3142
3143
0
                if (ea_entry->nidx == EXT2_EA_IDX_USER)
3144
0
                    tsk_fprintf(hFile, "user.%s=%s\n", name, val);
3145
0
                else if (ea_entry->nidx == EXT2_EA_IDX_TRUSTED)
3146
0
                    tsk_fprintf(hFile, "trust.%s=%s\n", name, val);
3147
0
                else if (ea_entry->nidx == EXT2_EA_IDX_SECURITY)
3148
0
                    tsk_fprintf(hFile, "security.%s=%s\n", name, val);
3149
3150
0
            }
3151
3152
3153
            /* POSIX ACL - setfacl / getfacl stuff */
3154
0
            else if ((ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_ACCESS)
3155
0
                || (ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_DEFAULT)) {
3156
3157
0
                ext2fs_pos_acl_entry_lo *acl_lo;
3158
0
                ext2fs_pos_acl_head *acl_head;
3159
3160
0
                if (ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_ACCESS)
3161
0
                    tsk_fprintf(hFile,
3162
0
                        "POSIX Access Control List Entries:\n");
3163
0
                else if (ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_DEFAULT)
3164
0
                    tsk_fprintf(hFile,
3165
0
                        "POSIX Default Access Control List Entries:\n");
3166
3167
                /* examine the header */
3168
0
                acl_head =
3169
0
                    (ext2fs_pos_acl_head *) &
3170
0
                    buf[tsk_getu16(fs->endian, ea_entry->val_off)];
3171
3172
0
                if (tsk_getu32(fs->endian, acl_head->ver) != 1) {
3173
0
                    tsk_fprintf(hFile,
3174
0
                        "Invalid ACL Header Version: %" PRIu32 "\n",
3175
0
                        tsk_getu32(fs->endian, acl_head->ver));
3176
0
                    continue;
3177
0
                }
3178
3179
                /* The first entry starts after the header */
3180
0
                acl_lo =
3181
0
                    (ext2fs_pos_acl_entry_lo *) ((uintptr_t) acl_head +
3182
0
                    sizeof(ext2fs_pos_acl_head));
3183
3184
3185
                /* Cycle through the values */
3186
0
                while ((uintptr_t) acl_lo <
3187
0
                    ((uintptr_t) buf +
3188
0
                        tsk_getu16(fs->endian,
3189
0
                            ea_entry->val_off) + tsk_getu32(fs->endian,
3190
0
                            ea_entry->val_size))) {
3191
3192
0
                    char perm[64];
3193
0
                    int len;
3194
3195
                    /* Make a string from the permissions */
3196
0
                    ext2fs_make_acl_str(perm, 64,
3197
0
                        tsk_getu16(fs->endian, acl_lo->perm));
3198
3199
0
                    switch (tsk_getu16(fs->endian, acl_lo->tag)) {
3200
0
                    case EXT2_PACL_TAG_USERO:
3201
0
                        tsk_fprintf(hFile, "  uid: %" PRIuUID ": %s\n",
3202
0
                            fs_meta->uid, perm);
3203
0
                        len = sizeof(ext2fs_pos_acl_entry_sh);
3204
0
                        break;
3205
3206
0
                    case EXT2_PACL_TAG_GRPO:
3207
0
                        tsk_fprintf(hFile, "  gid: %" PRIuGID ": %s\n",
3208
0
                            fs_meta->gid, perm);
3209
0
                        len = sizeof(ext2fs_pos_acl_entry_sh);
3210
0
                        break;
3211
0
                    case EXT2_PACL_TAG_OTHER:
3212
0
                        tsk_fprintf(hFile, "  other: %s\n", perm);
3213
0
                        len = sizeof(ext2fs_pos_acl_entry_sh);
3214
0
                        break;
3215
0
                    case EXT2_PACL_TAG_MASK:
3216
0
                        tsk_fprintf(hFile, "  mask: %s\n", perm);
3217
0
                        len = sizeof(ext2fs_pos_acl_entry_sh);
3218
0
                        break;
3219
3220
3221
0
                    case EXT2_PACL_TAG_GRP:
3222
0
                        tsk_fprintf(hFile, "  gid: %" PRIu32 ": %s\n",
3223
0
                            tsk_getu32(fs->endian, acl_lo->id), perm);
3224
0
                        len = sizeof(ext2fs_pos_acl_entry_lo);
3225
0
                        break;
3226
3227
0
                    case EXT2_PACL_TAG_USER:
3228
0
                        tsk_fprintf(hFile, "  uid: %" PRIu32 ": %s\n",
3229
0
                            tsk_getu32(fs->endian, acl_lo->id), perm);
3230
3231
0
                        len = sizeof(ext2fs_pos_acl_entry_lo);
3232
0
                        break;
3233
3234
0
                    default:
3235
0
                        tsk_fprintf(hFile, "Unknown ACL tag: %d\n",
3236
0
                            tsk_getu16(fs->endian, acl_lo->tag));
3237
0
                        len = sizeof(ext2fs_pos_acl_entry_sh);
3238
0
                        break;
3239
0
                    }
3240
0
                    acl_lo =
3241
0
                        (ext2fs_pos_acl_entry_lo *) ((uintptr_t) acl_lo
3242
0
                        + len);
3243
0
                }
3244
0
            }
3245
0
            else {
3246
0
                tsk_fprintf(hFile,
3247
0
                    "Unsupported Extended Attr Type: %d\n",
3248
0
                    ea_entry->nidx);
3249
0
            }
3250
0
        }
3251
0
      egress_ea:
3252
3253
0
        free(buf);
3254
0
    }
3255
3256
0
    if (sec_skew != 0) {
3257
0
        tsk_fprintf(hFile, "\nAdjusted Inode Times:\n");
3258
0
        if (fs_meta->mtime)
3259
0
            fs_meta->mtime -= sec_skew;
3260
0
        if (fs_meta->atime)
3261
0
            fs_meta->atime -= sec_skew;
3262
0
        if (fs_meta->ctime)
3263
0
            fs_meta->ctime -= sec_skew;
3264
3265
3266
0
        if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) {
3267
0
            tsk_fprintf(hFile, "Accessed:\t%s\n",
3268
0
                tsk_fs_time_to_str_subsecs(fs_meta->atime,
3269
0
                    fs_meta->atime_nano, timeBuf));
3270
0
            tsk_fprintf(hFile, "File Modified:\t%s\n",
3271
0
                tsk_fs_time_to_str_subsecs(fs_meta->mtime,
3272
0
                    fs_meta->mtime_nano, timeBuf));
3273
0
            tsk_fprintf(hFile, "Inode Modified:\t%s\n",
3274
0
                tsk_fs_time_to_str_subsecs(fs_meta->ctime,
3275
0
                    fs_meta->ctime_nano, timeBuf));
3276
0
        }
3277
0
        else {
3278
0
            tsk_fprintf(hFile, "Accessed:\t%s\n",
3279
0
                tsk_fs_time_to_str(fs_meta->atime, timeBuf));
3280
0
            tsk_fprintf(hFile, "File Modified:\t%s\n",
3281
0
                tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
3282
0
            tsk_fprintf(hFile, "Inode Modified:\t%s\n",
3283
0
                tsk_fs_time_to_str(fs_meta->ctime, timeBuf));
3284
0
        }
3285
3286
0
        if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) {
3287
0
            fs_meta->crtime -= sec_skew;
3288
0
            tsk_fprintf(hFile, "File Created:\t%s\n",
3289
0
                tsk_fs_time_to_str(fs_meta->crtime, timeBuf));
3290
0
            fs_meta->crtime += sec_skew;
3291
3292
0
        }
3293
3294
0
        if (fs_meta->time2.ext2.dtime) {
3295
0
            fs_meta->time2.ext2.dtime -= sec_skew;
3296
0
            tsk_fprintf(hFile, "Deleted:\t%s",
3297
0
                tsk_fs_time_to_str(fs_meta->time2.ext2.dtime, timeBuf));
3298
0
            fs_meta->time2.ext2.dtime += sec_skew;
3299
0
        }
3300
3301
0
        if (fs_meta->mtime)
3302
0
            fs_meta->mtime += sec_skew;
3303
0
        if (fs_meta->atime)
3304
0
            fs_meta->atime += sec_skew;
3305
0
        if (fs_meta->ctime)
3306
0
            fs_meta->ctime += sec_skew;
3307
3308
0
        tsk_fprintf(hFile, "\nOriginal Inode Times:\n");
3309
0
    }
3310
0
    else {
3311
0
        tsk_fprintf(hFile, "\nInode Times:\n");
3312
0
    }
3313
3314
0
    if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) {
3315
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
3316
0
            tsk_fs_time_to_str_subsecs(fs_meta->atime, fs_meta->atime_nano,
3317
0
                timeBuf));
3318
0
        tsk_fprintf(hFile, "File Modified:\t%s\n",
3319
0
            tsk_fs_time_to_str_subsecs(fs_meta->mtime, fs_meta->mtime_nano,
3320
0
                timeBuf));
3321
0
        tsk_fprintf(hFile, "Inode Modified:\t%s\n",
3322
0
            tsk_fs_time_to_str_subsecs(fs_meta->ctime, fs_meta->ctime_nano,
3323
0
                timeBuf));
3324
0
    }
3325
0
    else {
3326
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
3327
0
            tsk_fs_time_to_str(fs_meta->atime, timeBuf));
3328
0
        tsk_fprintf(hFile, "File Modified:\t%s\n",
3329
0
            tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
3330
0
        tsk_fprintf(hFile, "Inode Modified:\t%s\n",
3331
0
            tsk_fs_time_to_str(fs_meta->ctime, timeBuf));
3332
0
    }
3333
3334
3335
3336
0
    if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) {
3337
0
        tsk_fprintf(hFile, "File Created:\t%s\n",
3338
0
            tsk_fs_time_to_str_subsecs(fs_meta->crtime,
3339
0
                fs_meta->crtime_nano, timeBuf));
3340
0
    }
3341
0
    if (fs_meta->time2.ext2.dtime)
3342
0
        tsk_fprintf(hFile, "Deleted:\t%s\n",
3343
0
            tsk_fs_time_to_str(fs_meta->time2.ext2.dtime, timeBuf));
3344
3345
0
    if (numblock > 0)
3346
0
        fs_meta->size = numblock * fs->block_size;
3347
3348
0
    if (fs_meta->content_type != TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) {
3349
0
        tsk_fprintf(hFile, "\nDirect Blocks:\n");
3350
3351
0
        if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
3352
0
            const TSK_FS_ATTR *fs_attr_default =
3353
0
                tsk_fs_file_attr_get_type(fs_file.get(),
3354
0
                    TSK_FS_ATTR_TYPE_DEFAULT, 0, 0);
3355
0
            if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) {
3356
0
                if (tsk_fs_attr_print(fs_attr_default, hFile)) {
3357
0
                    tsk_fprintf(hFile, "\nError creating run lists\n");
3358
0
                    tsk_error_print(hFile);
3359
0
                    tsk_error_reset();
3360
0
                }
3361
0
            }
3362
0
        }
3363
0
        else {
3364
0
            print.idx = 0;
3365
0
            print.hFile = hFile;
3366
3367
0
            if (tsk_fs_file_walk(fs_file.get(), TSK_FS_FILE_WALK_FLAG_AONLY,
3368
0
                print_addr_act, (void *)&print)) {
3369
0
                tsk_fprintf(hFile, "\nError reading file:  ");
3370
0
                tsk_error_print(hFile);
3371
0
                tsk_error_reset();
3372
0
            }
3373
0
            else if (print.idx != 0) {
3374
0
                tsk_fprintf(hFile, "\n");
3375
0
            }
3376
0
        }
3377
3378
0
        if (fs_meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) {
3379
0
            const TSK_FS_ATTR *fs_attr_extent =
3380
0
                tsk_fs_file_attr_get_type(fs_file.get(),
3381
0
                    TSK_FS_ATTR_TYPE_UNIX_EXTENT, 0, 0);
3382
0
            if (fs_attr_extent) {
3383
0
                tsk_fprintf(hFile, "\nExtent Blocks:\n");
3384
3385
0
                if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
3386
0
                    if (tsk_fs_attr_print(fs_attr_extent, hFile)) {
3387
0
                        tsk_fprintf(hFile, "\nError creating run lists\n");
3388
0
                        tsk_error_print(hFile);
3389
0
                        tsk_error_reset();
3390
0
                    }
3391
0
                }
3392
0
                else {
3393
0
                    print.idx = 0;
3394
3395
0
                    if (tsk_fs_attr_walk(fs_attr_extent,
3396
0
                        TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act,
3397
0
                        (void *)&print)) {
3398
0
                        tsk_fprintf(hFile,
3399
0
                            "\nError reading indirect attribute:  ");
3400
0
                        tsk_error_print(hFile);
3401
0
                        tsk_error_reset();
3402
0
                    }
3403
0
                    else if (print.idx != 0) {
3404
0
                        tsk_fprintf(hFile, "\n");
3405
0
                    }
3406
0
                }
3407
0
            }
3408
0
        }
3409
0
        else {
3410
0
            fs_attr_indir = tsk_fs_file_attr_get_type(fs_file.get(),
3411
0
                TSK_FS_ATTR_TYPE_UNIX_INDIR, 0, 0);
3412
0
            if (fs_attr_indir) {
3413
0
                tsk_fprintf(hFile, "\nIndirect Blocks:\n");
3414
0
                if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
3415
0
                    tsk_fs_attr_print(fs_attr_indir, hFile);
3416
0
                }
3417
0
                else {
3418
0
                    print.idx = 0;
3419
3420
0
                    if (tsk_fs_attr_walk(fs_attr_indir,
3421
0
                        TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act,
3422
0
                        (void *)&print)) {
3423
0
                        tsk_fprintf(hFile,
3424
0
                            "\nError reading indirect attribute:  ");
3425
0
                        tsk_error_print(hFile);
3426
0
                        tsk_error_reset();
3427
0
                    }
3428
0
                    else if (print.idx != 0) {
3429
0
                        tsk_fprintf(hFile, "\n");
3430
0
                    }
3431
0
                }
3432
0
            }
3433
0
        }
3434
0
    }
3435
3436
0
    free(dino_buf);
3437
0
    return 0;
3438
0
}
3439
3440
3441
/* ext2fs_close - close an ext2fs file system */
3442
3443
static void
3444
ext2fs_close(TSK_FS_INFO * fs)
3445
0
{
3446
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
3447
3448
0
    fs->tag = 0;
3449
0
    free(ext2fs->fs);
3450
0
    free(ext2fs->grp_buf);
3451
0
    free(ext2fs->ext4_grp_buf);
3452
0
    free(ext2fs->bmap_buf);
3453
0
    free(ext2fs->imap_buf);
3454
3455
0
    tsk_deinit_lock(&ext2fs->lock);
3456
3457
0
    tsk_fs_free(fs);
3458
0
}
3459
3460
/**
3461
 * \internal
3462
 * Open part of a disk image as a Ext2/3 file system.
3463
 *
3464
 * @param img_info Disk image to analyze
3465
 * @param offset Byte offset where file system starts
3466
 * @param ftype Specific type of file system
3467
 * @param a_pass NOT USED
3468
 * @param test NOT USED
3469
 * @returns NULL on error or if data is not an Ext2/3 file system
3470
 */
3471
TSK_FS_INFO *
3472
ext2fs_open(
3473
  TSK_IMG_INFO * img_info,
3474
  TSK_OFF_T offset,
3475
  TSK_FS_TYPE_ENUM ftype,
3476
  [[maybe_unused]] const char* a_pass,
3477
  [[maybe_unused]] uint8_t test)
3478
0
{
3479
0
    EXT2FS_INFO *ext2fs;
3480
0
    unsigned int len;
3481
0
    TSK_FS_INFO *fs;
3482
0
    ssize_t cnt;
3483
3484
    // clean up any error messages that are lying around
3485
0
    tsk_error_reset();
3486
3487
0
    if (TSK_FS_TYPE_ISEXT(ftype) == 0) {
3488
0
        tsk_error_reset();
3489
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3490
0
        tsk_error_set_errstr("Invalid FS Type in ext2fs_open");
3491
0
        return NULL;
3492
0
    }
3493
3494
0
    if (img_info->sector_size == 0) {
3495
0
        tsk_error_reset();
3496
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3497
0
        tsk_error_set_errstr("ext2fs_open: sector size is 0");
3498
0
        return NULL;
3499
0
    }
3500
3501
0
    if ((ext2fs = (EXT2FS_INFO *) tsk_fs_malloc(sizeof(*ext2fs))) == NULL)
3502
0
        return NULL;
3503
3504
0
    fs = &(ext2fs->fs_info);
3505
3506
0
    fs->ftype = ftype;
3507
0
    fs->flags = TSK_FS_INFO_FLAG_NONE;
3508
0
    fs->img_info = img_info;
3509
0
    fs->offset = offset;
3510
0
    fs->tag = TSK_FS_INFO_TAG;
3511
3512
    /*
3513
     * Read the superblock.
3514
     */
3515
0
    len = sizeof(ext2fs_sb);
3516
0
    if ((ext2fs->fs = (ext2fs_sb *) tsk_malloc(len)) == NULL) {
3517
0
        fs->tag = 0;
3518
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3519
0
        return NULL;
3520
0
    }
3521
3522
0
    cnt = tsk_fs_read(fs, EXT2FS_SBOFF, (char *) ext2fs->fs, len);
3523
0
    if (cnt != len) {
3524
0
        if (cnt >= 0) {
3525
0
            tsk_error_reset();
3526
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
3527
0
        }
3528
0
        tsk_error_set_errstr2("ext2fs_open: superblock");
3529
0
        fs->tag = 0;
3530
0
        free(ext2fs->fs);
3531
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3532
0
        return NULL;
3533
0
    }
3534
3535
    /*
3536
     * Verify we are looking at an EXTxFS image
3537
     */
3538
0
    if (tsk_fs_guessu16(fs, ext2fs->fs->s_magic, EXT2FS_FS_MAGIC)) {
3539
0
        fs->tag = 0;
3540
0
        free(ext2fs->fs);
3541
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3542
0
        tsk_error_reset();
3543
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3544
0
        tsk_error_set_errstr("not an EXTxFS file system (magic)");
3545
0
        if (tsk_verbose)
3546
0
            fprintf(stderr, "ext2fs_open: invalid magic\n");
3547
0
        return NULL;
3548
0
    }
3549
3550
0
    if (tsk_verbose) {
3551
0
        if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) &
3552
0
            EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER)
3553
0
            tsk_fprintf(stderr, "File system has sparse super blocks\n");
3554
3555
0
        tsk_fprintf(stderr, "First data block is %" PRIu32 "\n",
3556
0
            tsk_getu32(fs->endian, ext2fs->fs->s_first_data_block));
3557
0
    }
3558
3559
    /* If autodetect was given, look for the journal */
3560
0
    if (ftype == TSK_FS_TYPE_EXT_DETECT) {
3561
0
        if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_incompat) &
3562
0
            EXT2FS_FEATURE_INCOMPAT_EXTENTS) {
3563
0
            fs->ftype = TSK_FS_TYPE_EXT4;
3564
0
            fs->flags = (TSK_FS_INFO_FLAG_ENUM) (fs->flags | TSK_FS_INFO_FLAG_HAVE_NANOSEC);
3565
0
        }
3566
0
        else if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_compat) &
3567
0
            EXT2FS_FEATURE_COMPAT_HAS_JOURNAL)
3568
0
            fs->ftype = TSK_FS_TYPE_EXT3;
3569
0
        else
3570
0
            fs->ftype = TSK_FS_TYPE_EXT2;
3571
0
    }
3572
0
    fs->duname = "Fragment";
3573
3574
3575
    /* we need to figure out if dentries are v1 or v2 */
3576
0
    if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_incompat) &
3577
0
        EXT2FS_FEATURE_INCOMPAT_FILETYPE)
3578
0
        ext2fs->deentry_type = EXT2_DE_V2;
3579
0
    else
3580
0
        ext2fs->deentry_type = EXT2_DE_V1;
3581
3582
3583
    /*
3584
     * Calculate the meta data info
3585
     */
3586
0
    fs->inum_count = tsk_getu32(fs->endian, ext2fs->fs->s_inodes_count) + 1;    // we are adding 1 in this calc to account for Orphans directory
3587
0
    fs->last_inum = fs->inum_count;
3588
0
    fs->first_inum = EXT2FS_FIRSTINO;
3589
0
    fs->root_inum = EXT2FS_ROOTINO;
3590
3591
0
    if (fs->inum_count < 10) {
3592
0
        fs->tag = 0;
3593
0
        free(ext2fs->fs);
3594
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3595
0
        tsk_error_reset();
3596
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3597
0
        tsk_error_set_errstr("Not an EXTxFS file system (inum count)");
3598
0
        if (tsk_verbose)
3599
0
            fprintf(stderr, "ext2fs_open: two few inodes\n");
3600
0
        return NULL;
3601
0
    }
3602
3603
3604
    /* Set the size of the inode, but default to our data structure
3605
     * size if it is larger */
3606
0
    ext2fs->inode_size = tsk_getu16(fs->endian, ext2fs->fs->s_inode_size);
3607
0
    if (ext2fs->inode_size < sizeof(ext2fs_inode)) {
3608
0
        if (tsk_verbose)
3609
0
            tsk_fprintf(stderr, "SB inode size is small");
3610
0
    }
3611
3612
3613
    /*
3614
     * Calculate the block info
3615
     */
3616
0
    fs->dev_bsize = img_info->sector_size;
3617
0
    if (tsk_getu32(fs->endian,
3618
0
            ext2fs->fs->
3619
0
            s_feature_incompat) & EXT2FS_FEATURE_INCOMPAT_64BIT) {
3620
//        printf("DEBUG fs_open: 64bit file system\n");
3621
0
        fs->block_count =
3622
0
            ext4_getu64(fs->endian, ext2fs->fs->s_blocks_count_hi,
3623
0
            ext2fs->fs->s_blocks_count);
3624
0
    }
3625
0
    else {
3626
0
        fs->block_count =
3627
0
            tsk_getu32(fs->endian, ext2fs->fs->s_blocks_count);
3628
0
    }
3629
0
    fs->first_block = 0;
3630
0
    fs->last_block_act = fs->last_block = fs->block_count - 1;
3631
0
    ext2fs->first_data_block =
3632
0
        tsk_getu32(fs->endian, ext2fs->fs->s_first_data_block);
3633
3634
0
    if (tsk_getu32(fs->endian, ext2fs->fs->s_log_block_size) !=
3635
0
        tsk_getu32(fs->endian, ext2fs->fs->s_log_frag_size)) {
3636
0
        fs->tag = 0;
3637
0
        free(ext2fs->fs);
3638
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3639
0
        tsk_error_reset();
3640
0
        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
3641
0
        tsk_error_set_errstr
3642
0
            ("This file system has fragments that are a different size than blocks, which is not currently supported\nContact brian with details of the system that created this image");
3643
0
        if (tsk_verbose)
3644
0
            fprintf(stderr,
3645
0
                "ext2fs_open: fragment size not equal to block size\n");
3646
0
        return NULL;
3647
0
    }
3648
3649
0
    if (tsk_getu32(fs->endian, ext2fs->fs->s_log_block_size) >= sizeof(uint32_t) * 8) {
3650
0
        free(ext2fs->fs);
3651
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3652
0
        tsk_error_reset();
3653
0
        tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
3654
0
        tsk_error_set_errstr("Block size too large");
3655
0
        if (tsk_verbose)
3656
0
            fprintf(stderr,
3657
0
                "ext2fs_open: block size too large\n");
3658
0
        return NULL;
3659
0
    }
3660
3661
0
    fs->block_size =
3662
0
        EXT2FS_MIN_BLOCK_SIZE << tsk_getu32(fs->endian,
3663
0
        ext2fs->fs->s_log_block_size);
3664
3665
    // sanity check
3666
0
    if ((fs->block_size == 0) || (fs->block_size % 512)) {
3667
0
        fs->tag = 0;
3668
0
        free(ext2fs->fs);
3669
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3670
0
        tsk_error_reset();
3671
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3672
0
        tsk_error_set_errstr("Not an EXTxFS file system (block size)");
3673
0
        if (tsk_verbose)
3674
0
            fprintf(stderr, "ext2fs_open: invalid block size\n");
3675
0
        return NULL;
3676
0
    }
3677
3678
    // determine the last block we have in this image
3679
0
    if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) <
3680
0
        fs->block_count)
3681
0
        fs->last_block_act =
3682
0
            (img_info->size - offset) / fs->block_size - 1;
3683
3684
3685
    /* The group descriptors are located in the block following the
3686
     * super block */
3687
0
    ext2fs->groups_offset =
3688
0
        roundup((EXT2FS_SBOFF + sizeof(ext2fs_sb)), fs->block_size);
3689
3690
    // sanity check to avoid divide by zero issues
3691
0
    if (tsk_getu32(fs->endian, ext2fs->fs->s_blocks_per_group) == 0) {
3692
0
        fs->tag = 0;
3693
0
        free(ext2fs->fs);
3694
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3695
0
        tsk_error_reset();
3696
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3697
0
        tsk_error_set_errstr("Not an EXTxFS file system (blocks per group)");
3698
0
        if (tsk_verbose)
3699
0
            fprintf(stderr, "ext2fs_open: blocks per group is 0\n");
3700
0
        return NULL;
3701
0
    }
3702
0
    if (tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group) == 0) {
3703
0
        fs->tag = 0;
3704
0
        free(ext2fs->fs);
3705
0
        tsk_fs_free((TSK_FS_INFO *)ext2fs);
3706
0
        tsk_error_reset();
3707
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3708
0
        tsk_error_set_errstr("Not an EXTxFS file system (inodes per group)");
3709
0
        if (tsk_verbose)
3710
0
            fprintf(stderr, "ext2fs_open: inodes per group is 0\n");
3711
0
        return NULL;
3712
0
    }
3713
3714
0
    if (tsk_getu32(fs->endian,
3715
0
            ext2fs->fs->
3716
0
            s_feature_incompat) & EXT2FS_FEATURE_INCOMPAT_64BIT) {
3717
0
        ext2fs->groups_count =
3718
0
            (EXT2_GRPNUM_T) ((ext4_getu64(fs->endian,
3719
0
                    ext2fs->fs->s_blocks_count_hi,
3720
0
                    ext2fs->fs->s_blocks_count)
3721
0
                - ext2fs->first_data_block + tsk_getu32(fs->endian,
3722
0
                    ext2fs->fs->s_blocks_per_group) - 1)
3723
0
            / tsk_getu32(fs->endian, ext2fs->fs->s_blocks_per_group));
3724
0
    }
3725
0
    else {
3726
0
        ext2fs->groups_count =
3727
0
            (EXT2_GRPNUM_T) ((tsk_getu32(fs->endian,
3728
0
                    ext2fs->fs->s_blocks_count) -
3729
0
                ext2fs->first_data_block + tsk_getu32(fs->endian,
3730
0
                    ext2fs->fs->s_blocks_per_group) -
3731
0
                1) / tsk_getu32(fs->endian,
3732
0
                ext2fs->fs->s_blocks_per_group));
3733
0
    }
3734
3735
    /* Volume ID */
3736
0
    for (fs->fs_id_used = 0; fs->fs_id_used < 16; fs->fs_id_used++) {
3737
0
        fs->fs_id[fs->fs_id_used] = ext2fs->fs->s_uuid[fs->fs_id_used];
3738
0
    }
3739
3740
    /* Set the generic function pointers */
3741
0
    fs->inode_walk = ext2fs_inode_walk;
3742
0
    fs->block_walk = ext2fs_block_walk;
3743
0
    fs->block_getflags = ext2fs_block_getflags;
3744
3745
0
    fs->get_default_attr_type = tsk_fs_unix_get_default_attr_type;
3746
    //fs->load_attrs = tsk_fs_unix_make_data_run;
3747
0
    fs->load_attrs = ext2fs_load_attrs;
3748
3749
0
    fs->file_add_meta = ext2fs_inode_lookup;
3750
0
    fs->dir_open_meta = ext2fs_dir_open_meta;
3751
0
    fs->fsstat = ext2fs_fsstat;
3752
0
    fs->fscheck = ext2fs_fscheck;
3753
0
    fs->istat = ext2fs_istat;
3754
0
    fs->name_cmp = tsk_fs_unix_name_cmp;
3755
0
    fs->close = ext2fs_close;
3756
3757
3758
    /* Journal */
3759
0
    fs->journ_inum = tsk_getu32(fs->endian, ext2fs->fs->s_journal_inum);
3760
0
    fs->jblk_walk = ext2fs_jblk_walk;
3761
0
    fs->jentry_walk = ext2fs_jentry_walk;
3762
0
    fs->jopen = ext2fs_jopen;
3763
3764
    /* initialize the caches */
3765
    /* inode map */
3766
0
    ext2fs->imap_buf = NULL;
3767
0
    ext2fs->imap_grp_num = 0xffffffff;
3768
3769
    /* block map */
3770
0
    ext2fs->bmap_buf = NULL;
3771
0
    ext2fs->bmap_grp_num = 0xffffffff;
3772
3773
    /* group descriptor */
3774
0
    ext2fs->grp_buf = NULL;
3775
0
    ext2fs->grp_num = 0xffffffff;
3776
3777
3778
    /*
3779
     * Print some stats.
3780
     */
3781
0
    if (tsk_verbose)
3782
0
        tsk_fprintf(stderr,
3783
0
            "inodes %" PRIu32 " root ino %" PRIuINUM " blocks %" PRIu32
3784
0
            " blocks/group %" PRIu32 "\n", tsk_getu32(fs->endian,
3785
0
                ext2fs->fs->s_inodes_count),
3786
0
            fs->root_inum, tsk_getu32(fs->endian,
3787
0
                ext2fs->fs->s_blocks_count), tsk_getu32(fs->endian,
3788
0
                ext2fs->fs->s_blocks_per_group));
3789
3790
0
    tsk_init_lock(&ext2fs->lock);
3791
3792
0
    return fs;
3793
0
}