Coverage Report

Created: 2025-07-18 07:03

/src/sleuthkit/tsk/fs/ffs.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
** The Sleuth Kit
3
**
4
** Brian Carrier [carrier <at> sleuthkit [dot] org]
5
** Copyright (c) 2006-2011 Brian Carrier, 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
/* TCT
16
 * LICENSE
17
 *  This software is distributed under the IBM Public License.
18
 * AUTHOR(S)
19
 *  Wietse Venema
20
 *  IBM T.J. Watson Research
21
 *  P.O. Box 704
22
 *  Yorktown Heights, NY 10598, USA
23
 --*/
24
25
/**
26
 * \file ffs.c
27
 * Contains the internal TSK UFS / FFS file system functions
28
 */
29
30
#include "tsk_fs_i.h"
31
#include "tsk_ffs.h"
32
33
#include <memory>
34
35
/* ffs_group_load - load cylinder group descriptor info into cache
36
 *
37
 * Note: This routine assumes &ffs->lock is locked by the caller.
38
 *
39
 * return 1 on error and 0 on success
40
 * */
41
static uint8_t
42
ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num)
43
0
{
44
0
    TSK_DADDR_T addr;
45
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ffs->fs_info;
46
47
    /*
48
     * Sanity check
49
     */
50
0
    if (grp_num >= ffs->groups_count) {
51
0
        tsk_error_reset();
52
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
53
0
        tsk_error_set_errstr
54
0
            ("ffs_group_load: invalid cylinder group number: %" PRI_FFSGRP
55
0
            "", grp_num);
56
0
        return 1;
57
0
    }
58
59
    /*
60
     * Allocate/read cylinder group info on the fly. Trust that a cylinder
61
     * group always fits within a logical disk block (as promised in the
62
     * 4.4BSD <ufs/ffs/fs.h> include file).
63
     */
64
0
    if (ffs->grp_buf == NULL) {
65
0
        if ((ffs->grp_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) {
66
0
            return 1;
67
0
        }
68
0
    }
69
70
0
    addr = cgtod_lcl(fs, ffs->fs.sb1, grp_num);
71
0
    if (ffs->grp_addr != addr) {
72
0
        ffs_cgd *cg;
73
0
        ssize_t cnt;
74
0
        cnt = tsk_fs_read_block(fs, addr, ffs->grp_buf, ffs->ffsbsize_b);
75
0
        if (cnt != ffs->ffsbsize_b) {
76
0
            if (cnt >= 0) {
77
0
                tsk_error_reset();
78
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
79
0
            }
80
0
            tsk_error_set_errstr2("ffs_group_load: Group %" PRI_FFSGRP
81
0
                " at %" PRIuDADDR, grp_num, addr);
82
0
            return 1;
83
0
        }
84
0
        ffs->grp_addr = addr;
85
86
        /* Perform a sanity check on the data to make sure offsets are in range */
87
0
        cg = (ffs_cgd *) ffs->grp_buf;
88
0
        if ((tsk_gets32(fs->endian, cg->cg_iusedoff) > (int)ffs->ffsbsize_b)
89
0
            || (tsk_gets32(fs->endian, cg->cg_freeoff) > (int)ffs->ffsbsize_b)) {
90
0
            tsk_error_reset();
91
0
            tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
92
0
            tsk_error_set_errstr2("ffs_group_load: Group %" PRI_FFSGRP
93
0
                " descriptor offsets too large at %" PRIuDADDR, grp_num,
94
0
                addr);
95
0
            return 1;
96
0
        }
97
0
    }
98
99
0
    ffs->grp_num = grp_num;
100
0
    return 0;
101
0
}
102
103
104
/*
105
 * ffs_dinode_load - read disk inode and load the data into ffs_inode structure
106
 *
107
 * Return 0 on success and 1 on error
108
 */
109
static uint8_t
110
ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum, ffs_inode * dino_buf)
111
0
{
112
0
    TSK_DADDR_T addr;
113
0
    TSK_OFF_T offs;
114
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ffs->fs_info;
115
116
    /*
117
     * Sanity check.
118
     * Use last_num-1 to account for virtual Orphan directory in last_inum.
119
     */
120
0
    if (inum < fs->first_inum || inum > fs->last_inum - 1) {
121
0
        tsk_error_reset();
122
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
123
0
        tsk_error_set_errstr("ffs_dinode_load: address: %" PRIuINUM, inum);
124
0
        return 1;
125
0
    }
126
127
    /*
128
     * Allocate/read the inode table buffer on the fly.
129
     */
130
131
    /* lock access to itbl_buf */
132
0
    tsk_take_lock(&ffs->lock);
133
134
0
    if (ffs->itbl_buf == NULL) {
135
0
        if ((ffs->itbl_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) {
136
0
            tsk_release_lock(&ffs->lock);
137
0
            return 1;
138
0
        }
139
0
    }
140
141
142
    /* UFS2 is different because it does not initialize all inodes
143
     * when the file system is created.  Therefore we need to check
144
     * the group descriptor to find out if this is in the valid
145
     * range
146
     */
147
0
    if (fs->ftype == TSK_FS_TYPE_FFS2) {
148
0
        ffs_cgd2 *cg2;
149
0
        FFS_GRPNUM_T grp_num;
150
151
0
        if (dino_buf == NULL) {
152
0
            tsk_release_lock(&ffs->lock);
153
0
            return 1;
154
0
        }
155
156
        /* Lookup the cylinder group descriptor if it isn't
157
         * cached
158
         */
159
0
        grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, inum);
160
0
        if (ffs_group_load(ffs, grp_num)) {
161
0
            tsk_release_lock(&ffs->lock);
162
0
            return 1;
163
0
        }
164
165
0
        cg2 = (ffs_cgd2 *) ffs->grp_buf;
166
167
        /* If the inode is not init, then do not worry about it */
168
0
        if ((inum - grp_num * tsk_getu32(fs->endian,
169
0
                    ffs->fs.sb2->cg_inode_num)) >= tsk_getu32(fs->endian,
170
0
                cg2->cg_initediblk)) {
171
0
            memset((char *) dino_buf, 0, sizeof(ffs_inode2));
172
0
        }
173
174
0
        else {
175
            /* Get the base and offset addr for the inode in the tbl */
176
0
            addr = itod_lcl(fs, ffs->fs.sb1, inum);
177
178
0
            if (ffs->itbl_addr != addr) {
179
0
                ssize_t cnt;
180
0
                cnt = tsk_fs_read_block
181
0
                    (fs, addr, ffs->itbl_buf, ffs->ffsbsize_b);
182
0
                if (cnt != ffs->ffsbsize_b) {
183
0
                    tsk_release_lock(&ffs->lock);
184
0
                    if (cnt >= 0) {
185
0
                        tsk_error_reset();
186
0
                        tsk_error_set_errno(TSK_ERR_FS_READ);
187
0
                    }
188
0
                    tsk_error_set_errstr2
189
0
                        ("ffs_dinode_load: FFS2 inode table at %"
190
0
                        PRIuDADDR, addr);
191
0
                    return 1;
192
0
                }
193
0
                ffs->itbl_addr = addr;
194
0
            }
195
196
0
            offs = itoo_lcl(fs, ffs->fs.sb2, inum) * sizeof(ffs_inode2);
197
198
0
            memcpy((char *) dino_buf, ffs->itbl_buf + offs,
199
0
                sizeof(ffs_inode2));
200
0
        }
201
0
    }
202
0
    else {
203
0
        if (dino_buf == NULL) {
204
0
            tsk_release_lock(&ffs->lock);
205
0
            return 1;
206
0
        }
207
208
0
        addr = itod_lcl(fs, ffs->fs.sb1, inum);
209
0
        if (ffs->itbl_addr != addr) {
210
0
            ssize_t cnt;
211
0
            cnt =
212
0
                tsk_fs_read_block(fs, addr, ffs->itbl_buf,
213
0
                ffs->ffsbsize_b);
214
0
            if (cnt != ffs->ffsbsize_b) {
215
0
                tsk_release_lock(&ffs->lock);
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
221
0
                    ("ffs_dinode_load: FFS1 inode table at %" PRIuDADDR,
222
0
                    addr);
223
0
                return 1;
224
0
            }
225
0
            ffs->itbl_addr = addr;
226
0
        }
227
228
0
        offs = itoo_lcl(fs, ffs->fs.sb1, inum) * sizeof(ffs_inode1);
229
230
0
        memcpy((char *) dino_buf, ffs->itbl_buf + offs,
231
0
            sizeof(ffs_inode1));
232
0
    }
233
234
0
    tsk_release_lock(&ffs->lock);
235
236
0
    return 0;
237
0
}
238
239
240
static TSK_FS_META_TYPE_ENUM
241
ffsmode2tsktype(uint16_t a_mode)
242
0
{
243
0
    switch (a_mode & FFS_IN_FMT) {
244
0
    case FFS_IN_REG:
245
0
        return TSK_FS_META_TYPE_REG;
246
0
    case FFS_IN_DIR:
247
0
        return TSK_FS_META_TYPE_DIR;
248
0
    case FFS_IN_SOCK:
249
0
        return TSK_FS_META_TYPE_SOCK;
250
0
    case FFS_IN_LNK:
251
0
        return TSK_FS_META_TYPE_LNK;
252
0
    case FFS_IN_BLK:
253
0
        return TSK_FS_META_TYPE_BLK;
254
0
    case FFS_IN_CHR:
255
0
        return TSK_FS_META_TYPE_CHR;
256
0
    case FFS_IN_FIFO:
257
0
        return TSK_FS_META_TYPE_FIFO;
258
0
    case FFS_IN_SHAD:
259
0
        return TSK_FS_META_TYPE_SHAD;
260
0
    case FFS_IN_WHT:
261
0
        return TSK_FS_META_TYPE_WHT;
262
0
    default:
263
0
        return TSK_FS_META_TYPE_UNDEF;
264
0
    }
265
0
}
266
267
static TSK_FS_META_MODE_ENUM
268
ffsmode2tskmode(uint16_t a_mode)
269
0
{
270
0
    uint16_t mode = 0;
271
272
0
    if (a_mode & FFS_IN_ISUID)
273
0
        mode |= TSK_FS_META_MODE_ISUID;
274
0
    if (a_mode & FFS_IN_ISGID)
275
0
        mode |= TSK_FS_META_MODE_ISGID;
276
0
    if (a_mode & FFS_IN_ISVTX)
277
0
        mode |= TSK_FS_META_MODE_ISVTX;
278
279
0
    if (a_mode & FFS_IN_IRUSR)
280
0
        mode |= TSK_FS_META_MODE_IRUSR;
281
0
    if (a_mode & FFS_IN_IWUSR)
282
0
        mode |= TSK_FS_META_MODE_IWUSR;
283
0
    if (a_mode & FFS_IN_IXUSR)
284
0
        mode |= TSK_FS_META_MODE_IXUSR;
285
286
0
    if (a_mode & FFS_IN_IRGRP)
287
0
        mode |= TSK_FS_META_MODE_IRGRP;
288
0
    if (a_mode & FFS_IN_IWGRP)
289
0
        mode |= TSK_FS_META_MODE_IWGRP;
290
0
    if (a_mode & FFS_IN_IXGRP)
291
0
        mode |= TSK_FS_META_MODE_IXGRP;
292
293
0
    if (a_mode & FFS_IN_IROTH)
294
0
        mode |= TSK_FS_META_MODE_IROTH;
295
0
    if (a_mode & FFS_IN_IWOTH)
296
0
        mode |= TSK_FS_META_MODE_IWOTH;
297
0
    if (a_mode & FFS_IN_IXOTH)
298
0
        mode |= TSK_FS_META_MODE_IXOTH;
299
300
0
    return (TSK_FS_META_MODE_ENUM) mode;
301
0
}
302
303
/* ffs_dinode_copy - copy cached disk inode to generic inode
304
 *
305
 * Return 1 on error and 0 on success
306
 */
307
static uint8_t
308
ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta,
309
    TSK_INUM_T dino_inum, const ffs_inode * dino_buf)
310
0
{
311
0
    int i, j;
312
0
    unsigned int count;
313
0
    TSK_FS_INFO *fs = &(ffs->fs_info);
314
0
    FFS_GRPNUM_T grp_num;
315
0
    ffs_cgd *cg;
316
0
    unsigned char *inosused = NULL;
317
0
    TSK_INUM_T ibase;
318
319
0
    if (dino_buf == NULL) {
320
0
        tsk_error_reset();
321
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
322
0
        tsk_error_set_errstr("ffs_dinode_copy: dino_buf is NULL");
323
0
        return 1;
324
0
    }
325
326
0
    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
327
0
    if (fs_meta->attr) {
328
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
329
0
    }
330
331
0
    fs_meta->flags = (TSK_FS_META_FLAG_ENUM) 0;
332
0
    fs_meta->seq = 0;
333
334
    /* If the symlink field is set from a previous run, then free it */
335
0
    if (fs_meta->link) {
336
0
        free(fs_meta->link);
337
0
        fs_meta->link = NULL;
338
0
    }
339
340
0
    fs_meta->addr = dino_inum;
341
342
    /* OpenBSD and FreeBSD style */
343
0
    if (fs->ftype == TSK_FS_TYPE_FFS1) {
344
0
        ffs_inode1 *in = (ffs_inode1 *) dino_buf;
345
0
        TSK_DADDR_T *addr_ptr;
346
347
0
        fs_meta->mode =
348
0
            ffsmode2tskmode(tsk_getu16(fs->endian, in->di_mode));
349
0
        fs_meta->type =
350
0
            ffsmode2tsktype(tsk_getu16(fs->endian, in->di_mode));
351
352
0
        fs_meta->nlink = tsk_gets16(fs->endian, in->di_nlink);
353
0
        fs_meta->size = tsk_getu64(fs->endian, in->di_size);
354
0
        fs_meta->uid = tsk_getu32(fs->endian, in->di_uid);
355
0
        fs_meta->gid = tsk_getu32(fs->endian, in->di_gid);
356
357
0
        fs_meta->mtime = tsk_gets32(fs->endian, in->di_mtime);
358
0
        fs_meta->atime = tsk_gets32(fs->endian, in->di_atime);
359
0
        fs_meta->ctime = tsk_gets32(fs->endian, in->di_ctime);
360
0
        fs_meta->crtime = 0;
361
0
        fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
362
0
            fs_meta->crtime_nano = 0;
363
364
0
        if (fs_meta->content_len < FFS_FILE_CONTENT_LEN) {
365
0
            if ((fs_meta =
366
0
                    tsk_fs_meta_realloc(fs_meta,
367
0
                        FFS_FILE_CONTENT_LEN)) == NULL) {
368
0
                return 1;
369
0
            }
370
0
        }
371
0
        addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
372
373
0
        for (i = 0; i < FFS_NDADDR; i++)
374
0
            addr_ptr[i] = tsk_gets32(fs->endian, in->di_db[i]);
375
376
0
        for (i = 0; i < FFS_NIADDR; i++)
377
0
            addr_ptr[FFS_NDADDR + i] =
378
0
                tsk_gets32(fs->endian, in->di_ib[i]);
379
380
381
        /* set the link string (if the file is a link)
382
         * The size check is a sanity check so that we don't try and allocate
383
         * a huge amount of memory for a bad inode value
384
         */
385
0
        if ((fs_meta->type == TSK_FS_META_TYPE_LNK)
386
0
            && (fs_meta->size < FFS_MAXPATHLEN)
387
0
            && (fs_meta->size >= 0)) {
388
0
            int i;
389
390
0
            fs_meta->link = (char*) tsk_malloc((size_t) fs_meta->size + 1);
391
0
            if (fs_meta->link == NULL) {
392
0
                return 1;
393
0
            }
394
395
0
            count = 0;          /* index into the link array */
396
397
            /* it is located directly in the pointers   */
398
0
            if (fs_meta->size < 4 * (FFS_NDADDR + FFS_NIADDR)) {
399
0
                char *ptr;
400
401
                /* Direct block pointer locations */
402
0
                for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) {
403
0
                    ptr = (char *) &in->di_db[i];
404
0
                    for (j = 0; j < 4 && count < fs_meta->size; j++)
405
0
                        fs_meta->link[count++] = ptr[j];
406
0
                }
407
408
                /* indirect block pointers */
409
0
                for (i = 0; i < FFS_NIADDR && count < fs_meta->size; i++) {
410
0
                    ptr = (char *) &in->di_ib[i];
411
0
                    for (j = 0; j < 4 && count < fs_meta->size; j++)
412
0
                        fs_meta->link[count++] = ptr[j];
413
0
                }
414
415
0
                fs_meta->link[count] = '\0';
416
417
                /* clear the values to avoid other code from reading them */
418
0
                memset(fs_meta->content_ptr, 0, fs_meta->content_len);
419
0
            }
420
421
            /* it is in blocks (the regular way) */
422
0
            else {
423
0
                char *buf;
424
0
                char *ptr = fs_meta->link;
425
426
0
                if ((buf = (char *)
427
0
                        tsk_malloc(fs->block_size)) == NULL) {
428
0
                    return 1;
429
0
                }
430
431
                /* there is a max link length of 1000, so we should never
432
                 * need the indirect blocks
433
                 */
434
0
                for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) {
435
0
                    ssize_t cnt;
436
0
                    TSK_DADDR_T *addr_ptr =
437
0
                        (TSK_DADDR_T *) fs_meta->content_ptr;
438
439
                    /* Do we need the entire block, or just part of it? */
440
0
                    int read_count =
441
0
                        (fs_meta->size - count <
442
0
                        fs->block_size) ? (int) fs_meta->size -
443
0
                        count : fs->block_size;
444
445
0
                    cnt =
446
0
                        tsk_fs_read_block(fs, addr_ptr[i],
447
0
                        buf, fs->block_size);
448
0
                    if (cnt != fs->block_size) {
449
0
                        if (cnt >= 0) {
450
0
                            tsk_error_reset();
451
0
                            tsk_error_set_errno(TSK_ERR_FS_READ);
452
0
                        }
453
0
                        tsk_error_set_errstr2
454
0
                            ("ffs_dinode_copy: FFS1A symlink dest at %"
455
0
                            PRIuDADDR, addr_ptr[i]);
456
0
                        free(buf);
457
0
                        return 1;
458
0
                    }
459
460
0
                    memcpy(ptr, buf, read_count);
461
0
                    count += read_count;
462
0
                    ptr = (char *) ((uintptr_t) ptr + read_count);
463
0
                }
464
                /* terminate the string */
465
0
                *ptr = '\0';
466
467
                /* Clean up name */
468
0
                i = 0;
469
0
                while (fs_meta->link[i] != '\0') {
470
0
                    if (TSK_IS_CNTRL(fs_meta->link[i]))
471
0
                        fs_meta->link[i] = '^';
472
0
                    i++;
473
0
                }
474
475
0
                free(buf);
476
0
            }
477
0
        }                       /* end of symlink */
478
0
    }
479
    /* TSK_FS_TYPE_FFS1B - Solaris */
480
0
    else if (fs->ftype == TSK_FS_TYPE_FFS1B) {
481
0
        ffs_inode1b *in = (ffs_inode1b *) dino_buf;
482
0
        TSK_DADDR_T *addr_ptr;
483
484
0
        fs_meta->mode =
485
0
            ffsmode2tskmode(tsk_getu16(fs->endian, in->di_mode));
486
0
        fs_meta->type =
487
0
            ffsmode2tsktype(tsk_getu16(fs->endian, in->di_mode));
488
489
0
        fs_meta->nlink = tsk_gets16(fs->endian, in->di_nlink);
490
0
        fs_meta->size = tsk_getu64(fs->endian, in->di_size);
491
0
        fs_meta->uid = tsk_getu32(fs->endian, in->di_uid);
492
0
        fs_meta->gid = tsk_getu32(fs->endian, in->di_gid);
493
494
0
        fs_meta->mtime = tsk_gets32(fs->endian, in->di_mtime);
495
0
        fs_meta->atime = tsk_gets32(fs->endian, in->di_atime);
496
0
        fs_meta->ctime = tsk_gets32(fs->endian, in->di_ctime);
497
0
        fs_meta->crtime = 0;
498
0
        fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
499
0
            fs_meta->crtime_nano = 0;
500
501
0
        if (fs_meta->content_len < FFS_FILE_CONTENT_LEN) {
502
0
            if ((fs_meta =
503
0
                    tsk_fs_meta_realloc(fs_meta,
504
0
                        FFS_FILE_CONTENT_LEN)) == NULL) {
505
0
                return 1;
506
0
            }
507
0
        }
508
0
        addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
509
510
0
        for (i = 0; i < FFS_NDADDR; i++)
511
0
            addr_ptr[i] = tsk_gets32(fs->endian, in->di_db[i]);
512
513
0
        for (i = 0; i < FFS_NIADDR; i++)
514
0
            addr_ptr[FFS_NDADDR + i] =
515
0
                tsk_gets32(fs->endian, in->di_ib[i]);
516
517
0
        if ((fs_meta->type == TSK_FS_META_TYPE_LNK)
518
0
            && (fs_meta->size < FFS_MAXPATHLEN)
519
0
            && (fs_meta->size >= 0)) {
520
521
0
            count = 0;          /* index into the link array */
522
523
            /* it is located directly in the pointers   */
524
0
            if (fs_meta->size < 4 * (FFS_NDADDR + FFS_NIADDR)) {
525
0
                char *ptr;
526
527
                /* Direct block pointer locations */
528
0
                for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) {
529
0
                    ptr = (char *) &in->di_db[i];
530
0
                    for (j = 0; j < 4 && count < fs_meta->size; j++)
531
0
                        fs_meta->link[count++] = ptr[j];
532
0
                }
533
534
                /* indirect block pointers */
535
0
                for (i = 0; i < FFS_NIADDR && count < fs_meta->size; i++) {
536
0
                    ptr = (char *) &in->di_ib[i];
537
0
                    for (j = 0; j < 4 && count < fs_meta->size; j++)
538
0
                        fs_meta->link[count++] = ptr[j];
539
0
                }
540
541
0
                fs_meta->link[count] = '\0';
542
543
                /* clear the values to avoid other code from reading them */
544
0
                memset(fs_meta->content_ptr, 0, fs_meta->content_len);
545
0
            }
546
547
            /* it is in blocks (the regular way) */
548
0
            else {
549
0
                char *buf;
550
0
                char *ptr;
551
552
0
                if ((buf = (char *)
553
0
                        tsk_malloc(fs->block_size)) == NULL)
554
0
                    return 1;
555
556
0
                fs_meta->link = ptr =
557
0
                    (char*) tsk_malloc((size_t) fs_meta->size + 1);
558
0
                if (fs_meta->link == NULL) {
559
0
                    free(buf);
560
0
                    return 1;
561
0
                }
562
563
                /* there is a max link length of 1000, so we should never
564
                 * need the indirect blocks
565
                 */
566
0
                for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) {
567
0
                    ssize_t cnt;
568
0
                    TSK_DADDR_T *addr_ptr =
569
0
                        (TSK_DADDR_T *) fs_meta->content_ptr;
570
571
                    /* Do we need the entire block, or just part of it? */
572
0
                    int read_count =
573
0
                        (fs_meta->size - count <
574
0
                        fs->block_size) ? (int) fs_meta->size -
575
0
                        count : fs->block_size;
576
577
0
                    cnt =
578
0
                        tsk_fs_read_block(fs, addr_ptr[i],
579
0
                        buf, fs->block_size);
580
0
                    if (cnt != fs->block_size) {
581
0
                        if (cnt >= 0) {
582
0
                            tsk_error_reset();
583
0
                            tsk_error_set_errno(TSK_ERR_FS_READ);
584
0
                        }
585
0
                        tsk_error_set_errstr2
586
0
                            ("ffs_dinode_copy: FFS1B symlink dest at %"
587
0
                            PRIuDADDR, addr_ptr[i]);
588
0
                        free(buf);
589
0
                        return 1;
590
0
                    }
591
592
0
                    memcpy(ptr, buf, read_count);
593
0
                    count += read_count;
594
0
                    ptr = (char *) ((uintptr_t) ptr + read_count);
595
0
                }
596
597
                /* terminate the string */
598
0
                *ptr = '\0';
599
600
0
                free(buf);
601
0
            }
602
0
        }
603
0
    }
604
0
    else if (fs->ftype == TSK_FS_TYPE_FFS2) {
605
0
        ffs_inode2 *in = (ffs_inode2 *) dino_buf;
606
0
        TSK_DADDR_T *addr_ptr;
607
608
0
        fs_meta->mode =
609
0
            ffsmode2tskmode(tsk_getu16(fs->endian, in->di_mode));
610
0
        fs_meta->type =
611
0
            ffsmode2tsktype(tsk_getu16(fs->endian, in->di_mode));
612
613
0
        fs_meta->nlink = tsk_gets16(fs->endian, in->di_nlink);
614
0
        fs_meta->size = tsk_getu64(fs->endian, in->di_size);
615
0
        fs_meta->uid = tsk_getu32(fs->endian, in->di_uid);
616
0
        fs_meta->gid = tsk_getu32(fs->endian, in->di_gid);
617
618
0
        fs_meta->mtime = (time_t) tsk_gets64(fs->endian, in->di_mtime);
619
0
        fs_meta->atime = (time_t) tsk_gets64(fs->endian, in->di_atime);
620
0
        fs_meta->ctime = (time_t) tsk_gets64(fs->endian, in->di_ctime);
621
0
        fs_meta->crtime = 0;
622
0
        fs_meta->mtime_nano = tsk_getu32(fs->endian, in->di_mtimensec);
623
0
        fs_meta->atime_nano = tsk_getu32(fs->endian, in->di_atimensec);
624
0
        fs_meta->ctime_nano = tsk_getu32(fs->endian, in->di_ctimensec);
625
0
        fs_meta->crtime_nano = tsk_getu32(fs->endian, in->di_crtimensec);
626
627
0
        if (fs_meta->content_len < FFS_FILE_CONTENT_LEN) {
628
0
            if ((fs_meta =
629
0
                    tsk_fs_meta_realloc(fs_meta,
630
0
                        FFS_FILE_CONTENT_LEN)) == NULL) {
631
0
                return 1;
632
0
            }
633
0
        }
634
0
        addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
635
636
0
        for (i = 0; i < FFS_NDADDR; i++)
637
0
            addr_ptr[i] = tsk_gets64(fs->endian, in->di_db[i]);
638
639
0
        for (i = 0; i < FFS_NIADDR; i++)
640
0
            addr_ptr[FFS_NDADDR + i] =
641
0
                tsk_gets64(fs->endian, in->di_ib[i]);
642
643
644
        /* set the link string (if the file is a link)
645
         * The size check is a sanity check so that we don't try and allocate
646
         * a huge amount of memory for a bad inode value
647
         */
648
0
        if ((fs_meta->type == TSK_FS_META_TYPE_LNK)
649
0
            && (fs_meta->size < FFS_MAXPATHLEN)
650
0
            && (fs_meta->size >= 0)) {
651
652
0
            fs_meta->link = (char*) tsk_malloc((size_t) fs_meta->size + 1);
653
0
            if (fs_meta->link == NULL) {
654
0
                return 1;
655
0
            }
656
657
0
            count = 0;          /* index into the link array */
658
659
            /* it is located directly in the pointers
660
             * Only the new style inode has this "fast link"
661
             */
662
0
            if (fs_meta->size < 8 * (FFS_NDADDR + FFS_NIADDR)) {
663
0
                char *ptr;
664
665
                /* Direct block pointer locations */
666
0
                for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) {
667
0
                    ptr = (char *) &in->di_db[i];
668
0
                    for (j = 0; j < 8 && count < fs_meta->size; j++)
669
0
                        fs_meta->link[count++] = ptr[j];
670
0
                }
671
672
                /* indirect block pointers */
673
0
                for (i = 0; i < FFS_NIADDR && count < fs_meta->size; i++) {
674
0
                    ptr = (char *) &in->di_ib[i];
675
0
                    for (j = 0; j < 8 && count < fs_meta->size; j++)
676
0
                        fs_meta->link[count++] = ptr[j];
677
0
                }
678
679
0
                fs_meta->link[count] = '\0';
680
681
                /* clear the values to avoid other code from reading them */
682
0
                memset(fs_meta->content_ptr, 0, fs_meta->content_len);
683
0
            }
684
685
            /* it is in blocks (the regular way) */
686
0
            else {
687
0
                char *buf;
688
0
                char *ptr = fs_meta->link;
689
690
0
                if ((buf = (char *)
691
0
                        tsk_malloc(fs->block_size)) == NULL) {
692
0
                    return 1;
693
0
                }
694
695
                /* there is a max link length of 1000, so we should never
696
                 * need the indirect blocks
697
                 */
698
0
                for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) {
699
0
                    ssize_t cnt;
700
0
                    TSK_DADDR_T *addr_ptr = (TSK_DADDR_T*) fs_meta->content_ptr;
701
702
                    /* Do we need the entire block, or just part of it? */
703
0
                    int read_count =
704
0
                        (fs_meta->size - count <
705
0
                        fs->block_size) ? (int) fs_meta->size -
706
0
                        count : fs->block_size;
707
708
0
                    cnt = tsk_fs_read_block(fs,
709
0
                        addr_ptr[i], buf, fs->block_size);
710
0
                    if (cnt != fs->block_size) {
711
0
                        if (cnt >= 0) {
712
0
                            tsk_error_reset();
713
0
                            tsk_error_set_errno(TSK_ERR_FS_READ);
714
0
                        }
715
0
                        tsk_error_set_errstr2
716
0
                            ("ffs_dinode_copy: FFS2 symlink dest at %"
717
0
                            PRIuDADDR, addr_ptr[i]);
718
0
                        free(buf);
719
0
                        return 1;
720
0
                    }
721
722
0
                    memcpy(ptr, buf, read_count);
723
0
                    count += read_count;
724
0
                    ptr = (char *) ((uintptr_t) ptr + read_count);
725
0
                }
726
                /* terminate the string */
727
0
                *ptr = '\0';
728
729
0
                free(buf);
730
0
            }
731
0
        }                       /* end of symlink */
732
0
    }
733
0
    else {
734
0
        tsk_error_reset();
735
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
736
0
        tsk_error_set_errstr("ffs_dinode_copy: Unknown FFS Type");
737
0
        return 1;
738
0
    }
739
740
    /* set the flags */
741
0
    grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, dino_inum);
742
743
0
    tsk_take_lock(&ffs->lock);
744
0
    if (ffs_group_load(ffs, grp_num)) {
745
0
        tsk_release_lock(&ffs->lock);
746
0
        return 1;
747
0
    }
748
749
0
    cg = (ffs_cgd *) ffs->grp_buf;
750
751
0
    inosused = (unsigned char *) cg_inosused_lcl(fs, cg);
752
0
    ibase = grp_num * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num);
753
754
    /* get the alloc flag */
755
0
    fs_meta->flags = (isset(inosused, dino_inum - ibase) ?
756
0
        TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
757
758
0
    tsk_release_lock(&ffs->lock);
759
760
    /* used/unused */
761
0
    fs_meta->flags = (TSK_FS_META_FLAG_ENUM) (fs_meta->flags | (fs_meta->ctime ?
762
0
        TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED));
763
764
0
    return 0;
765
0
}
766
767
768
769
770
/* ffs_inode_lookup - lookup inode, external interface
771
 *
772
 * Return 1 on error
773
 *
774
 * */
775
static uint8_t
776
ffs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
777
    TSK_INUM_T inum)
778
0
{
779
0
    ffs_inode *dino_buf;
780
0
    FFS_INFO *ffs = (FFS_INFO *) fs;
781
782
0
    if (a_fs_file == NULL) {
783
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
784
0
        tsk_error_set_errstr("ffs_inode_lookup: fs_file is NULL");
785
0
        return 1;
786
0
    }
787
788
    /* copy it to the TSK_FS_META structure */
789
0
    if (a_fs_file->meta == NULL) {
790
0
        a_fs_file->meta = tsk_fs_meta_alloc(FFS_FILE_CONTENT_LEN);
791
0
        if (a_fs_file->meta == NULL)
792
0
            return 1;
793
0
    }
794
0
    else {
795
0
        tsk_fs_meta_reset(a_fs_file->meta);
796
0
    }
797
798
    // see if they are looking for the special "orphans" directory
799
0
    if (inum == TSK_FS_ORPHANDIR_INUM(fs)) {
800
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta))
801
0
            return 1;
802
0
        else
803
0
            return 0;
804
0
    }
805
806
    /* Lookup the inode and store it in ffs */
807
0
    if ((dino_buf = (ffs_inode *) tsk_malloc(sizeof(ffs_inode2))) == NULL)
808
0
        return 1;
809
810
0
    if (ffs_dinode_load(ffs, inum, dino_buf)) {
811
0
        free(dino_buf);
812
0
        return 1;
813
0
    }
814
815
0
    if (ffs_dinode_copy(ffs, a_fs_file->meta, inum, dino_buf)) {
816
0
        free(dino_buf);
817
0
        return 1;
818
0
    }
819
820
0
    free(dino_buf);
821
822
0
    return 0;
823
0
}
824
825
826
827
/**************************************************************************
828
 *
829
 * INODE WALKING
830
 *
831
 **************************************************************************/
832
833
834
835
/* ffs_inode_walk - inode iterator
836
 *
837
 * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED,
838
 *  TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN
839
 *
840
 *  return 1 on error and 0 on success
841
 */
842
uint8_t
843
ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
844
    TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM a_flags,
845
    TSK_FS_META_WALK_CB action, void *ptr)
846
0
{
847
0
    const char *myname = "ffs_inode_walk";
848
0
    FFS_INFO *ffs = (FFS_INFO *) fs;
849
0
    ffs_cgd *cg = NULL;
850
0
    TSK_INUM_T inum;
851
0
    unsigned char *inosused = NULL;
852
0
    unsigned int myflags;
853
0
    TSK_INUM_T ibase = 0;
854
0
    TSK_INUM_T end_inum_tmp;
855
0
    ffs_inode *dino_buf;
856
857
    // clean up any error messages that are lying around
858
0
    tsk_error_reset();
859
860
    /*
861
     * Sanity checks.
862
     */
863
0
    if (start_inum < fs->first_inum || start_inum > fs->last_inum) {
864
0
        tsk_error_reset();
865
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
866
0
        tsk_error_set_errstr("%s: Start inode: %" PRIuINUM "", myname,
867
0
            start_inum);
868
0
        return 1;
869
0
    }
870
0
    else if (end_inum < fs->first_inum || end_inum > fs->last_inum
871
0
        || end_inum < start_inum) {
872
0
        tsk_error_reset();
873
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
874
0
        tsk_error_set_errstr("%s: End inode: %" PRIuINUM "", myname,
875
0
            end_inum);
876
0
        return 1;
877
0
    }
878
879
    /* If ORPHAN is wanted, then make sure that the flags are correct */
880
0
    if (a_flags & TSK_FS_META_FLAG_ORPHAN) {
881
0
        a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_UNALLOC);
882
0
        a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags & ~TSK_FS_META_FLAG_ALLOC);
883
0
        a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_USED);
884
0
        a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags & ~TSK_FS_META_FLAG_UNUSED);
885
0
    }
886
0
    else {
887
0
        if (((a_flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
888
0
            ((a_flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
889
0
            a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
890
0
        }
891
892
        /* If neither of the USED or UNUSED flags are set, then set them
893
         * both
894
         */
895
0
        if (((a_flags & TSK_FS_META_FLAG_USED) == 0) &&
896
0
            ((a_flags & TSK_FS_META_FLAG_UNUSED) == 0)) {
897
0
            a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
898
0
        }
899
0
    }
900
901
    /* If we are looking for orphan files and have not yet filled
902
     * in the list of unalloc inodes that are pointed to, then fill
903
     * in the list
904
     * */
905
0
    if ((a_flags & TSK_FS_META_FLAG_ORPHAN)) {
906
0
        if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
907
0
            tsk_error_errstr2_concat
908
0
                ("- ffs_inode_walk: identifying inodes allocated by file names");
909
0
            return 1;
910
0
        }
911
0
    }
912
913
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
914
0
        tsk_fs_file_alloc(fs),
915
0
        tsk_fs_file_close
916
0
    };
917
918
0
    if (!fs_file) {
919
0
        return 1;
920
0
    }
921
922
0
    if ((fs_file->meta = tsk_fs_meta_alloc(FFS_FILE_CONTENT_LEN)) == NULL)
923
0
        return 1;
924
925
    // we need to handle fs->last_inum specially because it is for the
926
    // virtual ORPHANS directory.  Handle it outside of the loop.
927
0
    if (end_inum == TSK_FS_ORPHANDIR_INUM(fs))
928
0
        end_inum_tmp = end_inum - 1;
929
0
    else
930
0
        end_inum_tmp = end_inum;
931
932
0
    if ((dino_buf = (ffs_inode *) tsk_malloc(sizeof(ffs_inode2))) == NULL)
933
0
        return 1;
934
935
    /*
936
     * Iterate. This is easy because inode numbers are contiguous, unlike
937
     * data blocks which are interleaved with cylinder group blocks.
938
     */
939
0
    for (inum = start_inum; inum <= end_inum_tmp; inum++) {
940
0
        int retval;
941
0
        FFS_GRPNUM_T grp_num;
942
943
        /*
944
         * Be sure to use the proper cylinder group data.
945
         */
946
0
        grp_num = itog_lcl(fs, ffs->fs.sb1, inum);
947
948
0
        tsk_take_lock(&ffs->lock);
949
0
        if (ffs_group_load(ffs, grp_num)) {
950
0
            tsk_release_lock(&ffs->lock);
951
0
            free(dino_buf);
952
0
            return 1;
953
0
        }
954
0
        cg = (ffs_cgd *) ffs->grp_buf;
955
0
        inosused = (unsigned char *) cg_inosused_lcl(fs, cg);
956
0
        ibase =
957
0
            grp_num * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num);
958
959
        /*
960
         * Apply the allocated/unallocated restriction.
961
         */
962
0
        myflags = (isset(inosused, inum - ibase) ?
963
0
            TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
964
965
0
        tsk_release_lock(&ffs->lock);
966
967
0
        if ((a_flags & myflags) != myflags)
968
0
            continue;
969
970
971
0
        if (ffs_dinode_load(ffs, inum, dino_buf)) {
972
0
            free(dino_buf);
973
0
            return 1;
974
0
        }
975
976
0
        if (fs->ftype == TSK_FS_TYPE_FFS1
977
0
            || fs->ftype == TSK_FS_TYPE_FFS1B) {
978
            /* both inode forms are the same for the required fields */
979
0
            ffs_inode1 *in1 = (ffs_inode1 *) dino_buf;
980
981
            /*
982
             * Apply the used/unused restriction.
983
             */
984
0
            myflags |= (tsk_gets32(fs->endian, in1->di_ctime) ?
985
0
                TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED);
986
0
            if ((a_flags & myflags) != myflags)
987
0
                continue;
988
0
        }
989
0
        else {
990
0
            ffs_inode2 *in2 = (ffs_inode2 *) dino_buf;
991
992
            /*
993
             * Apply the used/unused restriction.
994
             */
995
0
            myflags |= (tsk_gets64(fs->endian, in2->di_ctime) ?
996
0
                TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED);
997
0
            if ((a_flags & myflags) != myflags)
998
0
                continue;
999
0
        }
1000
1001
        /* If we want only orphans, then check if this
1002
         * inode is in the seen list
1003
         */
1004
0
        if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
1005
0
            (a_flags & TSK_FS_META_FLAG_ORPHAN) &&
1006
0
            (tsk_fs_dir_find_inum_named(fs, inum))) {
1007
0
            continue;
1008
0
        }
1009
1010
1011
        /*
1012
         * Fill in a file system-independent inode structure and pass control
1013
         * to the application.
1014
         */
1015
0
        if (ffs_dinode_copy(ffs, fs_file->meta, inum, dino_buf)) {
1016
0
            free(dino_buf);
1017
0
            return 1;
1018
0
        }
1019
1020
0
        retval = action(fs_file.get(), ptr);
1021
0
        if (retval == TSK_WALK_STOP) {
1022
0
            free(dino_buf);
1023
0
            return 0;
1024
0
        }
1025
0
        else if (retval == TSK_WALK_ERROR) {
1026
0
            free(dino_buf);
1027
0
            return 1;
1028
0
        }
1029
0
    }
1030
1031
    // handle the virtual orphans folder if they asked for it
1032
0
    if ((end_inum == TSK_FS_ORPHANDIR_INUM(fs))
1033
0
        && (a_flags & TSK_FS_META_FLAG_ALLOC)
1034
0
        && (a_flags & TSK_FS_META_FLAG_USED)) {
1035
0
        int retval;
1036
1037
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
1038
0
            free(dino_buf);
1039
0
            return 1;
1040
0
        }
1041
        /* call action */
1042
0
        retval = action(fs_file.get(), ptr);
1043
0
        if (retval == TSK_WALK_STOP) {
1044
0
            free(dino_buf);
1045
0
            return 0;
1046
0
        }
1047
0
        else if (retval == TSK_WALK_ERROR) {
1048
0
            free(dino_buf);
1049
0
            return 1;
1050
0
        }
1051
0
    }
1052
1053
    /*
1054
     * Cleanup.
1055
     */
1056
0
    free(dino_buf);
1057
1058
0
    return 0;
1059
0
}
1060
1061
TSK_FS_BLOCK_FLAG_ENUM
1062
ffs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
1063
0
{
1064
0
    FFS_INFO *ffs = (FFS_INFO *) a_fs;
1065
0
    FFS_GRPNUM_T grp_num;
1066
0
    ffs_cgd *cg = 0;
1067
0
    TSK_DADDR_T frag_base = 0;
1068
0
    TSK_DADDR_T dblock_addr = 0;        /* first data block in group */
1069
0
    TSK_DADDR_T sblock_addr = 0;        /* super block in group */
1070
0
    unsigned char *freeblocks = NULL;
1071
0
    int flags;
1072
1073
    // sparse
1074
0
    if (a_addr == 0)
1075
0
        return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC);
1076
1077
0
    grp_num = dtog_lcl(a_fs, ffs->fs.sb1, a_addr);
1078
1079
0
    tsk_take_lock(&ffs->lock);
1080
0
    if (ffs_group_load(ffs, grp_num)) {
1081
0
        tsk_release_lock(&ffs->lock);
1082
0
        return TSK_FS_BLOCK_FLAG_UNUSED;
1083
0
    }
1084
1085
0
    cg = (ffs_cgd *) ffs->grp_buf;
1086
0
    freeblocks = (unsigned char *) cg_blksfree_lcl(a_fs, cg);
1087
1088
    // get the base fragment for the group
1089
0
    frag_base = cgbase_lcl(a_fs, ffs->fs.sb1, grp_num);
1090
1091
    // address of first data block in group
1092
0
    dblock_addr = cgdmin_lcl(a_fs, ffs->fs.sb1, grp_num);
1093
1094
    // address of super block in group
1095
0
    sblock_addr = cgsblock_lcl(a_fs, ffs->fs.sb1, grp_num);
1096
1097
    /* get the flags for this fragment
1098
     *
1099
     * Beware: FFS stores file data in the blocks between the start of a
1100
     * cylinder group and the start of its super block.
1101
     */
1102
0
    flags = (isset(freeblocks, a_addr - frag_base) ?
1103
0
        TSK_FS_BLOCK_FLAG_UNALLOC : TSK_FS_BLOCK_FLAG_ALLOC);
1104
1105
0
    tsk_release_lock(&ffs->lock);
1106
1107
0
    if (a_addr >= sblock_addr && a_addr < dblock_addr)
1108
0
        flags |= TSK_FS_BLOCK_FLAG_META;
1109
0
    else
1110
0
        flags |= TSK_FS_BLOCK_FLAG_CONT;
1111
1112
0
    return (TSK_FS_BLOCK_FLAG_ENUM) flags;
1113
0
}
1114
1115
/**************************************************************************
1116
 *
1117
 * BLOCK WALKING
1118
 *
1119
 **************************************************************************/
1120
1121
/* ffs_block_walk - block iterator
1122
 *
1123
 * flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT,
1124
 *  TSK_FS_BLOCK_FLAG_META
1125
 *
1126
 *  return 1 on error and 0 on success
1127
 */
1128
1129
uint8_t
1130
ffs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
1131
    TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags,
1132
    TSK_FS_BLOCK_WALK_CB action, void *ptr)
1133
0
{
1134
0
    const char *myname = "ffs_block_walk";
1135
0
    FFS_INFO *ffs = (FFS_INFO *) fs;
1136
0
    TSK_FS_BLOCK *fs_block;
1137
0
    TSK_DADDR_T addr;
1138
1139
0
    char *cache_blk_buf;        // buffer used for local read cache
1140
0
    TSK_DADDR_T cache_addr;     // base address in local cache
1141
0
    int cache_len_f;            // amount of data read into cache (in fragments)
1142
1143
    // clean up any error messages that are lying around
1144
0
    tsk_error_reset();
1145
1146
    /*
1147
     * Sanity checks on input bounds
1148
     */
1149
0
    if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
1150
0
        tsk_error_reset();
1151
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1152
0
        tsk_error_set_errstr("%s: Start block: %" PRIuDADDR "", myname,
1153
0
            a_start_blk);
1154
0
        return 1;
1155
0
    }
1156
1157
0
    if (a_end_blk < fs->first_block || a_end_blk > fs->last_block
1158
0
        || a_end_blk < a_start_blk) {
1159
0
        tsk_error_reset();
1160
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1161
0
        tsk_error_set_errstr("%s: End block: %" PRIuDADDR "", myname,
1162
0
            a_end_blk);
1163
0
        return 1;
1164
0
    }
1165
1166
    /* Sanity check on flags -- make sure at least one ALLOC is set */
1167
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
1168
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
1169
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
1170
0
            (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC |
1171
0
            TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
1172
0
    }
1173
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
1174
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
1175
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
1176
0
            (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
1177
0
    }
1178
1179
1180
    /* Other initialization */
1181
0
    if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) {
1182
0
        return 1;
1183
0
    }
1184
0
    if ((cache_blk_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) {
1185
0
        return 1;
1186
0
    }
1187
0
    cache_len_f = 0;
1188
0
    cache_addr = 0;
1189
1190
    /* Cycle through the fragment range requested */
1191
0
    for (addr = a_start_blk; addr <= a_end_blk; addr++) {
1192
0
        int retval;
1193
0
        size_t cache_offset = 0;
1194
0
        int myflags = ffs_block_getflags(fs, addr);
1195
1196
0
        if ((tsk_verbose) && (myflags & TSK_FS_BLOCK_FLAG_META)
1197
0
            && (myflags & TSK_FS_BLOCK_FLAG_UNALLOC))
1198
0
            tsk_fprintf(stderr,
1199
0
                "impossible: unallocated meta block %" PRIuDADDR, addr);
1200
1201
        // test if we should call the callback with this one
1202
0
        if ((myflags & TSK_FS_BLOCK_FLAG_META)
1203
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META)))
1204
0
            continue;
1205
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
1206
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
1207
0
            continue;
1208
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
1209
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
1210
0
            continue;
1211
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
1212
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
1213
0
            continue;
1214
1215
1216
0
        if ((a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) == 0) {
1217
            /* we read in block-sized chunks and cache the result for later
1218
             * calls.  See if this fragment is in our cache */
1219
0
            if ((cache_len_f == 0) || (addr >= cache_addr + cache_len_f)) {
1220
0
                ssize_t cnt;
1221
0
                int frags;
1222
1223
                /* Ideally, we want to read in block sized chunks, verify we can do that */
1224
0
                frags = a_end_blk > addr + ffs->ffsbsize_f - 1 ?
1225
0
                    ffs->ffsbsize_f : a_end_blk + 1 - addr;
1226
1227
0
                cnt =
1228
0
                    tsk_fs_read_block(fs, addr, cache_blk_buf,
1229
0
                    fs->block_size * frags);
1230
0
                if (cnt != fs->block_size * frags) {
1231
0
                    if (cnt >= 0) {
1232
0
                        tsk_error_reset();
1233
0
                        tsk_error_set_errno(TSK_ERR_FS_READ);
1234
0
                    }
1235
0
                    tsk_error_set_errstr2("ffs_block_walk: Block %"
1236
0
                        PRIuDADDR, addr);
1237
0
                    tsk_fs_block_free(fs_block);
1238
0
                    free(cache_blk_buf);
1239
0
                    return 1;
1240
0
                }
1241
0
                cache_len_f = frags;
1242
0
                cache_addr = addr;
1243
0
            }
1244
0
            cache_offset = (size_t) ((addr - cache_addr) * fs->block_size);
1245
0
        }
1246
1247
0
        if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
1248
0
            myflags |= TSK_FS_BLOCK_FLAG_AONLY;
1249
1250
        // call the callback
1251
0
        tsk_fs_block_set(fs, fs_block, addr,
1252
0
            (TSK_FS_BLOCK_FLAG_ENUM) (myflags | TSK_FS_BLOCK_FLAG_RAW),
1253
0
            &cache_blk_buf[cache_offset]);
1254
0
        retval = action(fs_block, ptr);
1255
0
        if (retval == TSK_WALK_STOP) {
1256
0
            break;
1257
0
        }
1258
0
        else if (retval == TSK_WALK_ERROR) {
1259
0
            tsk_fs_block_free(fs_block);
1260
0
            free(cache_blk_buf);
1261
0
            return 1;
1262
0
        }
1263
0
    }
1264
1265
    /* Cleanup */
1266
0
    tsk_fs_block_free(fs_block);
1267
0
    free(cache_blk_buf);
1268
0
    return 0;
1269
0
}
1270
1271
1272
1273
/*
1274
 * return 1 on error and 0 on success
1275
 */
1276
static uint8_t
1277
ffs_fscheck(
1278
  [[maybe_unused]] TSK_FS_INFO * fs,
1279
  [[maybe_unused]] FILE * hFile)
1280
0
{
1281
0
    tsk_error_reset();
1282
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1283
0
    tsk_error_set_errstr("fscheck not implemented for ffs yet");
1284
0
    return 1;
1285
0
}
1286
1287
1288
/**
1289
 * Print details about the file system to a file handle.
1290
 *
1291
 * @param fs File system to print details on
1292
 * @param hFile File handle to print text to
1293
 *
1294
 * @returns 1 on error and 0 on success
1295
 */
1296
static uint8_t
1297
ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
1298
0
{
1299
0
    unsigned int i;
1300
0
    time_t tmptime;
1301
0
    ffs_csum1 *csum1 = NULL;
1302
0
    ffs_cgd *cgd = NULL;
1303
1304
0
    FFS_INFO *ffs = (FFS_INFO *) fs;
1305
0
    ffs_sb1 *sb1 = ffs->fs.sb1;
1306
0
    ffs_sb2 *sb2 = ffs->fs.sb2;
1307
0
    int flags;
1308
0
    char timeBuf[128];
1309
1310
    // clean up any error messages that are lying around
1311
0
    tsk_error_reset();
1312
1313
0
    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
1314
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
1315
1316
0
    if ((fs->ftype == TSK_FS_TYPE_FFS1)
1317
0
        || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
1318
0
        tsk_fprintf(hFile, "File System Type: UFS 1\n");
1319
0
        tmptime = tsk_getu32(fs->endian, sb1->wtime);
1320
0
        tsk_fprintf(hFile, "Last Written: %s\n",
1321
0
            (tmptime > 0) ? tsk_fs_time_to_str(tmptime,
1322
0
                timeBuf) : "empty");
1323
0
        tsk_fprintf(hFile, "Last Mount Point: %s\n", sb1->last_mnt);
1324
1325
0
        flags = sb1->fs_flags;
1326
0
    }
1327
0
    else {
1328
0
        tsk_fprintf(hFile, "File System Type: UFS 2\n");
1329
0
        tmptime = tsk_getu32(fs->endian, sb2->wtime);
1330
0
        tsk_fprintf(hFile, "Last Written: %s\n",
1331
0
            (tmptime > 0) ? tsk_fs_time_to_str(tmptime,
1332
0
                timeBuf) : "empty");
1333
0
        tsk_fprintf(hFile, "Last Mount Point: %s\n", sb2->last_mnt);
1334
0
        tsk_fprintf(hFile, "Volume Name: %s\n", sb2->volname);
1335
0
        tsk_fprintf(hFile, "System UID: %" PRIu64 "\n",
1336
0
            tsk_getu64(fs->endian, sb2->swuid));
1337
0
        flags = tsk_getu32(fs->endian, sb2->fs_flags);
1338
0
    }
1339
1340
0
    if (flags) {
1341
0
        int cnt = 0;
1342
1343
0
        tsk_fprintf(hFile, "Flags: ");
1344
1345
0
        if (flags & FFS_SB_FLAG_UNCLEAN)
1346
0
            tsk_fprintf(hFile, "%s Unclean", (cnt++ == 0 ? "" : ","));
1347
1348
0
        if (flags & FFS_SB_FLAG_SOFTDEP)
1349
0
            tsk_fprintf(hFile, "%s Soft Dependencies",
1350
0
                (cnt++ == 0 ? "" : ","));
1351
1352
0
        if (flags & FFS_SB_FLAG_NEEDFSCK)
1353
0
            tsk_fprintf(hFile, "%s Needs fsck", (cnt++ == 0 ? "" : ","));
1354
1355
0
        if (flags & FFS_SB_FLAG_INDEXDIR)
1356
0
            tsk_fprintf(hFile, "%s Index directories",
1357
0
                (cnt++ == 0 ? "" : ","));
1358
1359
0
        if (flags & FFS_SB_FLAG_ACL)
1360
0
            tsk_fprintf(hFile, "%s ACLs", (cnt++ == 0 ? "" : ","));
1361
1362
0
        if (flags & FFS_SB_FLAG_MULTILABEL)
1363
0
            tsk_fprintf(hFile, "%s TrustedBSD MAC Multi-label",
1364
0
                (cnt++ == 0 ? "" : ","));
1365
1366
0
        if (flags & FFS_SB_FLAG_UPDATED)
1367
0
            tsk_fprintf(hFile, "%s Updated Flag Location",
1368
0
                (cnt++ == 0 ? "" : ","));
1369
1370
0
        tsk_fprintf(hFile, "\n");
1371
0
    }
1372
1373
1374
1375
0
    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
1376
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
1377
1378
0
    tsk_fprintf(hFile, "Inode Range: %" PRIuINUM " - %" PRIuINUM "\n",
1379
0
        fs->first_inum, fs->last_inum);
1380
0
    tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum);
1381
0
    if ((fs->ftype == TSK_FS_TYPE_FFS1)
1382
0
        || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
1383
0
        tsk_fprintf(hFile, "Num of Avail Inodes: %" PRIu32 "\n",
1384
0
            tsk_getu32(fs->endian, sb1->cstotal.ino_free));
1385
0
        tsk_fprintf(hFile, "Num of Directories: %" PRIu32 "\n",
1386
0
            tsk_getu32(fs->endian, sb1->cstotal.dir_num));
1387
0
    }
1388
0
    else {
1389
0
        tsk_fprintf(hFile, "Num of Avail Inodes: %" PRIu64 "\n",
1390
0
            tsk_getu64(fs->endian, sb2->cstotal.ino_free));
1391
0
        tsk_fprintf(hFile, "Num of Directories: %" PRIu64 "\n",
1392
0
            tsk_getu64(fs->endian, sb2->cstotal.dir_num));
1393
0
    }
1394
1395
1396
0
    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
1397
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
1398
1399
0
    tsk_fprintf(hFile, "Fragment Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
1400
0
        fs->first_block, fs->last_block);
1401
1402
0
    if (fs->last_block != fs->last_block_act)
1403
0
        tsk_fprintf(hFile,
1404
0
            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
1405
0
            fs->first_block, fs->last_block_act);
1406
1407
0
    tsk_fprintf(hFile, "Block Size: %u\n", ffs->ffsbsize_b);
1408
0
    tsk_fprintf(hFile, "Fragment Size: %u\n", fs->block_size);
1409
1410
0
    if ((fs->ftype == TSK_FS_TYPE_FFS1)
1411
0
        || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
1412
0
        tsk_fprintf(hFile, "Num of Avail Full Blocks: %" PRIu32 "\n",
1413
0
            tsk_getu32(fs->endian, sb1->cstotal.blk_free));
1414
0
        tsk_fprintf(hFile, "Num of Avail Fragments: %" PRIu32 "\n",
1415
0
            tsk_getu32(fs->endian, sb1->cstotal.frag_free));
1416
0
    }
1417
0
    else {
1418
0
        tsk_fprintf(hFile, "Num of Avail Full Blocks: %" PRIu64 "\n",
1419
0
            tsk_getu64(fs->endian, sb2->cstotal.blk_free));
1420
0
        tsk_fprintf(hFile, "Num of Avail Fragments: %" PRIu64 "\n",
1421
0
            tsk_getu64(fs->endian, sb2->cstotal.frag_free));
1422
0
    }
1423
1424
0
    tsk_fprintf(hFile, "\nCYLINDER GROUP INFORMATION\n");
1425
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
1426
1427
0
    tsk_fprintf(hFile, "Number of Cylinder Groups: %" PRIu32 "\n",
1428
0
        ffs->groups_count);
1429
0
    tsk_fprintf(hFile, "Inodes per group: %" PRId32 "\n",
1430
0
        tsk_gets32(fs->endian, sb1->cg_inode_num));
1431
0
    tsk_fprintf(hFile, "Fragments per group: %" PRId32 "\n",
1432
0
        tsk_gets32(fs->endian, sb1->cg_frag_num));
1433
1434
1435
    /* UFS 1 and 2 use the same ssize field  and use the same csum1 */
1436
0
    if (tsk_getu32(fs->endian, sb1->cg_ssize_b)) {
1437
0
        ssize_t cnt;
1438
0
        csum1 =
1439
0
            (ffs_csum1 *) tsk_malloc(tsk_getu32(fs->endian,
1440
0
                sb1->cg_ssize_b));
1441
0
        if (csum1 == NULL)
1442
0
            return 1;
1443
1444
0
        if ((fs->ftype == TSK_FS_TYPE_FFS1)
1445
0
            || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
1446
0
            cnt =
1447
0
                tsk_fs_read_block(fs, (TSK_DADDR_T) tsk_getu32(fs->endian,
1448
0
                    sb1->cg_saddr), (char *) csum1, tsk_getu32(fs->endian,
1449
0
                    sb1->cg_ssize_b));
1450
1451
0
            if (cnt != tsk_getu32(fs->endian, sb1->cg_ssize_b)) {
1452
0
                if (cnt >= 0) {
1453
0
                    tsk_error_reset();
1454
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
1455
0
                }
1456
0
                tsk_error_set_errstr2
1457
0
                    ("ffs_fsstat: FFS1 group descriptor at %" PRIu32,
1458
0
                    tsk_getu32(fs->endian, sb1->cg_saddr));
1459
0
                return 1;
1460
0
            }
1461
0
        }
1462
0
        else {
1463
0
            cnt = tsk_fs_read_block
1464
0
                (fs, (TSK_DADDR_T) tsk_getu64(fs->endian,
1465
0
                    sb2->cg_saddr), (char *) csum1, tsk_getu32(fs->endian,
1466
0
                    sb2->cg_ssize_b));
1467
0
            if (cnt != tsk_getu32(fs->endian, sb2->cg_ssize_b)) {
1468
0
                if (cnt >= 0) {
1469
0
                    tsk_error_reset();
1470
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
1471
0
                }
1472
0
                tsk_error_set_errstr2
1473
0
                    ("ffs_fsstat: FFS2 group descriptor at %" PRIu64,
1474
0
                    tsk_getu64(fs->endian, sb2->cg_saddr));
1475
0
                return 1;
1476
0
            }
1477
0
        }
1478
0
    }
1479
1480
0
    for (i = 0; i < ffs->groups_count; i++) {
1481
1482
0
        tsk_take_lock(&ffs->lock);
1483
0
        if (ffs_group_load(ffs, i)) {
1484
0
            tsk_release_lock(&ffs->lock);
1485
0
            return 1;
1486
0
        }
1487
0
        cgd = (ffs_cgd *) ffs->grp_buf;
1488
1489
0
        tsk_fprintf(hFile, "\nGroup %d:\n", i);
1490
0
        if (cgd) {
1491
0
            if ((fs->ftype == TSK_FS_TYPE_FFS1)
1492
0
                || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
1493
0
                tmptime = tsk_getu32(fs->endian, cgd->wtime);
1494
0
            }
1495
0
            else {
1496
0
                ffs_cgd2 *cgd2 = (ffs_cgd2 *) cgd;
1497
0
                tmptime = (uint32_t) tsk_getu64(fs->endian, cgd2->wtime);
1498
0
            }
1499
0
            tsk_fprintf(hFile, "  Last Written: %s\n",
1500
0
                (tmptime > 0) ? tsk_fs_time_to_str(tmptime,
1501
0
                    timeBuf) : "empty");
1502
0
        }
1503
0
        tsk_release_lock(&ffs->lock);
1504
1505
0
        tsk_fprintf(hFile, "  Inode Range: %" PRIu32 " - %" PRIu32 "\n",
1506
0
            (tsk_gets32(fs->endian, sb1->cg_inode_num) * i),
1507
0
            ((uint32_t) ((tsk_gets32(fs->endian,
1508
0
                            sb1->cg_inode_num) * (i + 1)) - 1)
1509
0
                < fs->last_inum) ? (uint32_t) ((tsk_gets32(fs->endian,
1510
0
                        sb1->cg_inode_num) * (i + 1)) -
1511
0
                1) : (uint32_t) fs->last_inum);
1512
1513
0
        tsk_fprintf(hFile,
1514
0
            "  Fragment Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
1515
0
            cgbase_lcl(fs, sb1, i),
1516
0
            ((cgbase_lcl(fs, sb1, i + 1) - 1) <
1517
0
                fs->last_block) ? (cgbase_lcl(fs, sb1,
1518
0
                    i + 1) - 1) : fs->last_block);
1519
1520
        /* The first group is special because the first 16 sectors are
1521
         * reserved for the boot block.
1522
         * the next contains the primary Super Block
1523
         */
1524
0
        if (!i) {
1525
0
            tsk_fprintf(hFile, "    Boot Block: 0 - %" PRIu32 "\n",
1526
0
                (uint32_t) (15 * 512 / fs->block_size));
1527
1528
1529
0
            tsk_fprintf(hFile,
1530
0
                "    Super Block: %" PRIu32 " - %" PRIu32 "\n",
1531
0
                (uint32_t) (16 * 512 / fs->block_size),
1532
0
                (uint32_t) ((16 * 512 / fs->block_size) + ffs->ffsbsize_f -
1533
0
                    1));
1534
0
        }
1535
1536
0
        tsk_fprintf(hFile,
1537
0
            "    Super Block: %" PRIuDADDR " - %" PRIuDADDR "\n",
1538
0
            cgsblock_lcl(fs, sb1, i),
1539
0
            (cgsblock_lcl(fs, sb1, i) + ffs->ffsbsize_f - 1));
1540
1541
1542
0
        tsk_fprintf(hFile,
1543
0
            "    Group Desc: %" PRIuDADDR " - %" PRIuDADDR "\n",
1544
0
            cgtod_lcl(fs, sb1, i), (cgtod_lcl(fs, sb1,
1545
0
                    i) + ffs->ffsbsize_f - 1));
1546
1547
1548
0
        if (fs->ftype == TSK_FS_TYPE_FFS2) {
1549
0
            tsk_fprintf(hFile,
1550
0
                "    Inode Table: %" PRIuDADDR " - %" PRIuDADDR "\n",
1551
0
                cgimin_lcl(fs, sb1, i),
1552
0
                (cgimin_lcl(fs, sb1, i) +
1553
0
                    ((roundup
1554
0
                            (tsk_gets32(fs->endian,
1555
0
                                    sb1->cg_inode_num) *
1556
0
                                sizeof(ffs_inode2), fs->block_size)
1557
0
                            / fs->block_size) - 1)));
1558
0
        }
1559
0
        else {
1560
0
            tsk_fprintf(hFile,
1561
0
                "    Inode Table: %" PRIuDADDR " - %" PRIuDADDR "\n",
1562
0
                cgimin_lcl(fs, sb1, i),
1563
0
                (cgimin_lcl(fs, sb1, i) +
1564
0
                    ((roundup
1565
0
                            (tsk_gets32(fs->endian,
1566
0
                                    sb1->cg_inode_num) *
1567
0
                                sizeof(ffs_inode1), fs->block_size)
1568
0
                            / fs->block_size) - 1)));
1569
0
        }
1570
1571
0
        tsk_fprintf(hFile, "    Data Fragments: ");
1572
1573
        /* For all groups besides the first, the space before the
1574
         * super block is also used for data
1575
         */
1576
0
        if (i)
1577
0
            tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR ", ",
1578
0
                cgbase_lcl(fs, sb1, i), cgsblock_lcl(fs, sb1, i) - 1);
1579
1580
0
        tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR "\n",
1581
0
            cgdmin_lcl(fs, sb1, i),
1582
0
            ((cgbase_lcl(fs, sb1, i + 1) - 1) < fs->last_block) ?
1583
0
            (cgbase_lcl(fs, sb1, i + 1) - 1) : fs->last_block);
1584
1585
1586
0
        if ((csum1)
1587
0
            && ((i + 1) * sizeof(ffs_csum1) < tsk_getu32(fs->endian,
1588
0
                    sb1->cg_ssize_b))) {
1589
0
            tsk_fprintf(hFile,
1590
0
                "  Global Summary (from the superblock summary area):\n");
1591
0
            tsk_fprintf(hFile, "    Num of Dirs: %" PRIu32 "\n",
1592
0
                tsk_getu32(fs->endian, &csum1[i].dir_num));
1593
0
            tsk_fprintf(hFile, "    Num of Avail Blocks: %" PRIu32 "\n",
1594
0
                tsk_getu32(fs->endian, &csum1[i].blk_free));
1595
0
            tsk_fprintf(hFile, "    Num of Avail Inodes: %" PRIu32 "\n",
1596
0
                tsk_getu32(fs->endian, &csum1[i].ino_free));
1597
0
            tsk_fprintf(hFile, "    Num of Avail Frags: %" PRIu32 "\n",
1598
0
                tsk_getu32(fs->endian, &csum1[i].frag_free));
1599
0
        }
1600
1601
0
        if (cgd) {
1602
0
            tsk_fprintf(hFile,
1603
0
                "  Local Summary (from the group descriptor):\n");
1604
0
            tsk_fprintf(hFile, "    Num of Dirs: %" PRIu32 "\n",
1605
0
                tsk_getu32(fs->endian, &cgd->cs.dir_num));
1606
0
            tsk_fprintf(hFile, "    Num of Avail Blocks: %" PRIu32 "\n",
1607
0
                tsk_getu32(fs->endian, &cgd->cs.blk_free));
1608
0
            tsk_fprintf(hFile, "    Num of Avail Inodes: %" PRIu32 "\n",
1609
0
                tsk_getu32(fs->endian, &cgd->cs.ino_free));
1610
0
            tsk_fprintf(hFile, "    Num of Avail Frags: %" PRIu32 "\n",
1611
0
                tsk_getu32(fs->endian, &cgd->cs.frag_free));
1612
0
            tsk_fprintf(hFile,
1613
0
                "    Last Block Allocated: %" PRIuDADDR "\n",
1614
0
                tsk_getu32(fs->endian,
1615
0
                    &cgd->last_alloc_blk) + cgbase_lcl(fs, sb1, i));
1616
0
            tsk_fprintf(hFile,
1617
0
                "    Last Fragment Allocated: %" PRIuDADDR "\n",
1618
0
                tsk_getu32(fs->endian,
1619
0
                    &cgd->last_alloc_frag) + cgbase_lcl(fs, sb1, i));
1620
0
            tsk_fprintf(hFile, "    Last Inode Allocated: %" PRIu32 "\n",
1621
0
                tsk_getu32(fs->endian,
1622
0
                    &cgd->last_alloc_ino) + (tsk_gets32(fs->endian,
1623
0
                        sb1->cg_inode_num) * i));
1624
0
        }
1625
0
    }
1626
0
    return 0;
1627
0
}
1628
1629
1630
1631
/************************* istat *******************************/
1632
1633
typedef struct {
1634
    FILE *hFile;
1635
    int idx;
1636
} FFS_PRINT_ADDR;
1637
1638
1639
static TSK_WALK_RET_ENUM
1640
print_addr_act(
1641
  [[maybe_unused]] TSK_FS_FILE * fs_file,
1642
  [[maybe_unused]] TSK_OFF_T a_off,
1643
  TSK_DADDR_T addr,
1644
  [[maybe_unused]] char *buf,
1645
  size_t size,
1646
  TSK_FS_BLOCK_FLAG_ENUM a_flags,
1647
  void *ptr)
1648
0
{
1649
0
    TSK_FS_INFO *fs = fs_file->fs_info;
1650
0
    FFS_PRINT_ADDR *print = (FFS_PRINT_ADDR *) ptr;
1651
1652
0
    if (a_flags & TSK_FS_BLOCK_FLAG_CONT) {
1653
0
        int i, s;
1654
        /* cycle through the fragments if they exist */
1655
0
        for (i = 0, s = (int) size; s > 0; s -= fs->block_size, i++) {
1656
1657
            /* sparse file */
1658
0
            if (addr)
1659
0
                tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr + i);
1660
0
            else
1661
0
                tsk_fprintf(print->hFile, "0 ");
1662
1663
0
            if (++(print->idx) == 8) {
1664
0
                tsk_fprintf(print->hFile, "\n");
1665
0
                print->idx = 0;
1666
0
            }
1667
0
        }
1668
0
    }
1669
1670
0
    return TSK_WALK_CONT;
1671
0
}
1672
1673
1674
1675
/**
1676
 * Print details on a specific file to a file handle.
1677
 *
1678
 * @param fs File system file is located in
1679
 * @param hFile File handle to print text to
1680
 * @param inum Address of file in file system
1681
 * @param numblock The number of blocks in file to force print (can go beyond file size)
1682
 * @param sec_skew Clock skew in seconds to also print times in
1683
 *
1684
 * @returns 1 on error and 0 on success
1685
 */
1686
static uint8_t
1687
ffs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, TSK_INUM_T inum,
1688
    TSK_DADDR_T numblock, int32_t sec_skew)
1689
0
{
1690
0
    FFS_INFO *ffs = (FFS_INFO *) fs;
1691
0
    TSK_FS_META *fs_meta;
1692
0
    char ls[12];
1693
0
    FFS_PRINT_ADDR print;
1694
0
    const TSK_FS_ATTR *fs_attr_indir;
1695
0
    char *dino_buf;
1696
0
    char timeBuf[128];
1697
1698
    // clean up any error messages that are lying around
1699
0
    tsk_error_reset();
1700
1701
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
1702
0
        tsk_fs_file_open_meta(fs, NULL, inum),
1703
0
        tsk_fs_file_close
1704
0
    };
1705
1706
0
    if (!fs_file) {
1707
0
        return 1;
1708
0
    }
1709
0
    fs_meta = fs_file->meta;
1710
1711
0
    tsk_fprintf(hFile, "inode: %" PRIuINUM "\n", inum);
1712
0
    tsk_fprintf(hFile, "%sAllocated\n",
1713
0
        (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : "Not ");
1714
1715
0
    tsk_take_lock(&ffs->lock);
1716
0
    tsk_fprintf(hFile, "Group: %" PRI_FFSGRP "\n", ffs->grp_num);
1717
0
    tsk_release_lock(&ffs->lock);
1718
1719
0
    if (fs_meta->link)
1720
0
        tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link);
1721
1722
0
    tsk_fprintf(hFile, "uid / gid: %" PRIuUID " / %" PRIuGID "\n",
1723
0
        fs_meta->uid, fs_meta->gid);
1724
1725
1726
0
    tsk_fs_meta_make_ls(fs_meta, ls, sizeof(ls));
1727
0
    tsk_fprintf(hFile, "mode: %s\n", ls);
1728
1729
0
    tsk_fprintf(hFile, "size: %" PRIdOFF "\n", fs_meta->size);
1730
0
    tsk_fprintf(hFile, "num of links: %u\n", fs_meta->nlink);
1731
1732
1733
0
    if (sec_skew != 0) {
1734
0
        tsk_fprintf(hFile, "\nAdjusted Inode Times:\n");
1735
0
        if (fs_meta->mtime)
1736
0
            fs_meta->mtime -= sec_skew;
1737
0
        if (fs_meta->atime)
1738
0
            fs_meta->atime -= sec_skew;
1739
0
        if (fs_meta->ctime)
1740
0
            fs_meta->ctime -= sec_skew;
1741
1742
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
1743
0
            tsk_fs_time_to_str(fs_meta->atime, timeBuf));
1744
0
        tsk_fprintf(hFile, "File Modified:\t%s\n",
1745
0
            tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
1746
0
        tsk_fprintf(hFile, "Inode Modified:\t%s\n",
1747
0
            tsk_fs_time_to_str(fs_meta->ctime, timeBuf));
1748
1749
0
        if (fs_meta->mtime)
1750
0
            fs_meta->mtime += sec_skew;
1751
0
        if (fs_meta->atime)
1752
0
            fs_meta->atime += sec_skew;
1753
0
        if (fs_meta->ctime)
1754
0
            fs_meta->ctime += sec_skew;
1755
1756
0
        tsk_fprintf(hFile, "\nOriginal Inode Times:\n");
1757
0
    }
1758
0
    else {
1759
0
        tsk_fprintf(hFile, "\nInode Times:\n");
1760
0
    }
1761
1762
0
    tsk_fprintf(hFile, "Accessed:\t%s\n",
1763
0
        tsk_fs_time_to_str(fs_meta->atime, timeBuf));
1764
0
    tsk_fprintf(hFile, "File Modified:\t%s\n",
1765
0
        tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
1766
0
    tsk_fprintf(hFile, "Inode Modified:\t%s\n",
1767
0
        tsk_fs_time_to_str(fs_meta->ctime, timeBuf));
1768
1769
0
    if ((dino_buf = (char *) tsk_malloc(sizeof(ffs_inode2))) == NULL)
1770
0
        return 1;
1771
    // we won't have dino_buf for "virtual" files
1772
0
    if ((fs->ftype == TSK_FS_TYPE_FFS2) && (dino_buf)) {
1773
0
        ffs_inode2 *in = (ffs_inode2 *) dino_buf;
1774
        /* Are there extended attributes */
1775
0
        if (tsk_getu32(fs->endian, in->di_extsize) > 0) {
1776
0
            ffs_extattr *ea;
1777
0
            uint32_t size;
1778
0
            char name[257];
1779
0
            char *blk_buf;
1780
1781
0
            if ((blk_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) {
1782
0
                free(dino_buf);
1783
0
                return 1;
1784
0
            }
1785
1786
0
            size = tsk_getu32(fs->endian, in->di_extsize);
1787
0
            tsk_fprintf(hFile, "\nExtended Attributes:\n");
1788
0
            tsk_fprintf(hFile,
1789
0
                "Size: %" PRIu32 " (%" PRIu64 ", %" PRIu64 ")\n", size,
1790
0
                tsk_getu64(fs->endian, in->di_extb[0]),
1791
0
                tsk_getu64(fs->endian, in->di_extb[1]));
1792
1793
1794
            /* Process first block */
1795
            // @@@ Incorporate values into this as well
1796
0
            if ((tsk_getu64(fs->endian, in->di_extb[0]) >= fs->first_block)
1797
0
                && (tsk_getu64(fs->endian,
1798
0
                        in->di_extb[0]) <= fs->last_block)) {
1799
0
                uintptr_t end;
1800
0
                ssize_t cnt;
1801
1802
0
                cnt =
1803
0
                    tsk_fs_read_block(fs, tsk_getu64(fs->endian,
1804
0
                        in->di_extb[0]), blk_buf, ffs->ffsbsize_b);
1805
0
                if (cnt != ffs->ffsbsize_b) {
1806
0
                    if (cnt >= 0) {
1807
0
                        tsk_error_reset();
1808
0
                        tsk_error_set_errno(TSK_ERR_FS_READ);
1809
0
                    }
1810
0
                    tsk_error_set_errstr2
1811
0
                        ("ffs_istat: FFS2 extended attribute 0 at %"
1812
0
                        PRIu64, tsk_getu64(fs->endian, in->di_extb[0]));
1813
0
                    free(blk_buf);
1814
0
                    free(dino_buf);
1815
0
                    return 1;
1816
0
                }
1817
1818
0
                ea = (ffs_extattr *) blk_buf;
1819
1820
0
                if (size > ffs->ffsbsize_b) {
1821
0
                    end = (uintptr_t) ea + ffs->ffsbsize_b;
1822
0
                    size -= ffs->ffsbsize_b;
1823
0
                }
1824
0
                else {
1825
0
                    end = (uintptr_t) ea + size;
1826
0
                    size = 0;
1827
0
                }
1828
1829
0
                for (; (uintptr_t) ea < end;
1830
0
                    ea =
1831
0
                    (ffs_extattr *) ((uintptr_t) ea +
1832
0
                        tsk_getu32(fs->endian, ea->reclen))) {
1833
0
                    memcpy(name, ea->name, ea->nlen);
1834
0
                    name[ea->nlen] = '\0';
1835
0
                    tsk_fprintf(hFile, "%s\n", name);
1836
0
                }
1837
0
            }
1838
0
            if ((tsk_getu64(fs->endian, in->di_extb[1]) >= fs->first_block)
1839
0
                && (tsk_getu64(fs->endian,
1840
0
                        in->di_extb[1]) <= fs->last_block)) {
1841
0
                uintptr_t end;
1842
0
                ssize_t cnt;
1843
1844
0
                cnt =
1845
0
                    tsk_fs_read_block(fs, tsk_getu64(fs->endian,
1846
0
                        in->di_extb[1]), blk_buf, ffs->ffsbsize_b);
1847
0
                if (cnt != ffs->ffsbsize_b) {
1848
0
                    if (cnt >= 0) {
1849
0
                        tsk_error_reset();
1850
0
                        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1851
0
                    }
1852
0
                    tsk_error_set_errstr2
1853
0
                        ("ffs_istat: FFS2 extended attribute 1 at %"
1854
0
                        PRIu64, tsk_getu64(fs->endian, in->di_extb[1]));
1855
0
                    free(blk_buf);
1856
0
                    free(dino_buf);
1857
0
                    return 1;
1858
0
                }
1859
1860
0
                ea = (ffs_extattr *) blk_buf;
1861
1862
0
                if (size > ffs->ffsbsize_b)
1863
0
                    end = (uintptr_t) ea + ffs->ffsbsize_b;
1864
0
                else
1865
0
                    end = (uintptr_t) ea + size;
1866
1867
0
                for (; (uintptr_t) ea < end;
1868
0
                    ea =
1869
0
                    (ffs_extattr *) ((uintptr_t) ea +
1870
0
                        tsk_getu32(fs->endian, ea->reclen))) {
1871
0
                    memcpy(name, ea->name, ea->nlen);
1872
0
                    name[ea->nlen] = '\0';
1873
0
                    tsk_fprintf(hFile, "%s\n", name);
1874
0
                }
1875
0
            }
1876
0
            free(blk_buf);
1877
0
        }
1878
0
        free(dino_buf);
1879
0
    }
1880
1881
1882
    /* A bad hack to force a specified number of blocks */
1883
0
    if (numblock > 0)
1884
0
        fs_meta->size = numblock * ffs->ffsbsize_b;
1885
1886
0
    tsk_fprintf(hFile, "\nDirect Blocks:\n");
1887
1888
0
    if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
1889
0
        const TSK_FS_ATTR * fs_attr_direct = tsk_fs_file_attr_get_type(fs_file.get(),
1890
0
            TSK_FS_ATTR_TYPE_DEFAULT, 0, 0);
1891
0
        if (fs_attr_direct && (fs_attr_direct->flags & TSK_FS_ATTR_NONRES)) {
1892
0
            if (tsk_fs_attr_print(fs_attr_direct, hFile)) {
1893
0
                tsk_fprintf(hFile, "\nError creating run lists\n");
1894
0
                tsk_error_print(hFile);
1895
0
                tsk_error_reset();
1896
0
            }
1897
0
        }
1898
0
    }
1899
0
    else {
1900
0
        print.idx = 0;
1901
0
        print.hFile = hFile;
1902
1903
0
        if (tsk_fs_file_walk(fs_file.get(), TSK_FS_FILE_WALK_FLAG_AONLY,
1904
0
            print_addr_act, (void *)&print)) {
1905
0
            tsk_fprintf(hFile, "\nError reading blocks in file\n");
1906
0
            tsk_error_print(hFile);
1907
0
            return 1;
1908
0
        }
1909
1910
0
        if (print.idx != 0)
1911
0
            tsk_fprintf(hFile, "\n");
1912
0
    }
1913
1914
0
    fs_attr_indir = tsk_fs_file_attr_get_type(fs_file.get(),
1915
0
        TSK_FS_ATTR_TYPE_UNIX_INDIR, 0, 0);
1916
0
    if (fs_attr_indir) {
1917
0
        tsk_fprintf(hFile, "\nIndirect Blocks:\n");
1918
0
        if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
1919
0
            if (tsk_fs_attr_print(fs_attr_indir, hFile)) {
1920
0
                tsk_fprintf(hFile, "\nError creating run lists\n");
1921
0
                tsk_error_print(hFile);
1922
0
                tsk_error_reset();
1923
0
            }
1924
0
        }
1925
0
        else {
1926
0
            print.idx = 0;
1927
1928
0
            if (tsk_fs_attr_walk(fs_attr_indir, TSK_FS_FILE_WALK_FLAG_AONLY,
1929
0
                print_addr_act, (void *)&print)) {
1930
0
                tsk_fprintf(hFile, "\nError reading indirect attribute:  ");
1931
0
                tsk_error_print(hFile);
1932
0
                tsk_error_reset();
1933
0
            }
1934
0
            else if (print.idx != 0) {
1935
0
                tsk_fprintf(hFile, "\n");
1936
0
            }
1937
0
        }
1938
0
    }
1939
1940
0
    return 0;
1941
0
}
1942
1943
/* Return 1 on error and 0 on success */
1944
uint8_t
1945
ffs_jopen(
1946
  [[maybe_unused]] TSK_FS_INFO * fs,
1947
  [[maybe_unused]] TSK_INUM_T inum)
1948
0
{
1949
0
    tsk_error_reset();
1950
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1951
0
    tsk_error_set_errstr("UFS does not have a journal");
1952
0
    return 1;
1953
0
}
1954
1955
uint8_t
1956
ffs_jentry_walk(
1957
  [[maybe_unused]] TSK_FS_INFO * fs,
1958
  [[maybe_unused]] int a_flags,
1959
  [[maybe_unused]] TSK_FS_JENTRY_WALK_CB action,
1960
  [[maybe_unused]] void *ptr)
1961
0
{
1962
0
    tsk_error_reset();
1963
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1964
0
    tsk_error_set_errstr("UFS does not have a journal");
1965
0
    return 1;
1966
0
}
1967
1968
1969
uint8_t
1970
ffs_jblk_walk(
1971
  [[maybe_unused]] TSK_FS_INFO * fs,
1972
  [[maybe_unused]] TSK_DADDR_T start,
1973
  [[maybe_unused]] TSK_DADDR_T end,
1974
  [[maybe_unused]] int a_flags,
1975
  [[maybe_unused]] TSK_FS_JBLK_WALK_CB action,
1976
  [[maybe_unused]] void *ptr)
1977
0
{
1978
0
    tsk_error_reset();
1979
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1980
0
    tsk_error_set_errstr("UFS does not have a journal");
1981
0
    return 1;
1982
0
}
1983
1984
1985
/* ffs_close - close a fast file system */
1986
static void
1987
ffs_close(TSK_FS_INFO * fs)
1988
0
{
1989
0
    FFS_INFO *ffs = (FFS_INFO *) fs;
1990
1991
0
    fs->tag = 0;
1992
1993
0
    free(ffs->grp_buf);
1994
0
    free(ffs->itbl_buf);
1995
1996
0
    tsk_deinit_lock(&ffs->lock);
1997
1998
0
    free(ffs->fs.sb1);
1999
0
    tsk_fs_free(fs);
2000
0
}
2001
2002
/**
2003
 * \internal
2004
 * Open part of a disk image as a FFS/UFS file system.
2005
 *
2006
 * @param img_info Disk image to analyze
2007
 * @param offset Byte offset where file system starts
2008
 * @param ftype Specific type of file system
2009
 * @param a_pass NOT USED
2010
 * @param test NOT USED
2011
 * @returns NULL on error or if data is not a FFS file system
2012
 */
2013
TSK_FS_INFO *
2014
ffs_open(
2015
  TSK_IMG_INFO * img_info,
2016
  TSK_OFF_T offset,
2017
  TSK_FS_TYPE_ENUM ftype,
2018
  [[maybe_unused]] const char* a_pass,
2019
  [[maybe_unused]] uint8_t test)
2020
0
{
2021
0
    const char *myname = "ffs_open";
2022
0
    FFS_INFO *ffs;
2023
0
    unsigned int len;
2024
0
    TSK_FS_INFO *fs;
2025
0
    ssize_t cnt;
2026
2027
    // clean up any error messages that are lying around
2028
0
    tsk_error_reset();
2029
2030
0
    if (TSK_FS_TYPE_ISFFS(ftype) == 0) {
2031
0
        tsk_error_reset();
2032
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2033
0
        tsk_error_set_errstr("Invalid FS Type in ffs_open");
2034
0
        return NULL;
2035
0
    }
2036
2037
0
    if (img_info->sector_size == 0) {
2038
0
        tsk_error_reset();
2039
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2040
0
        tsk_error_set_errstr("ffs_open: sector size is 0");
2041
0
        return NULL;
2042
0
    }
2043
2044
0
    if ((ffs = (FFS_INFO *) tsk_fs_malloc(sizeof(*ffs))) == NULL)
2045
0
        return NULL;
2046
2047
0
    fs = &(ffs->fs_info);
2048
2049
0
    fs->ftype = ftype;
2050
0
    fs->flags = TSK_FS_INFO_FLAG_NONE;
2051
0
    fs->duname = "Fragment";
2052
0
    fs->tag = TSK_FS_INFO_TAG;
2053
2054
0
    fs->img_info = img_info;
2055
0
    fs->offset = offset;
2056
2057
    /* Both sbs are the same size */
2058
0
    len = roundup(sizeof(ffs_sb1), img_info->sector_size);
2059
0
    ffs->fs.sb1 = (ffs_sb1 *) tsk_malloc(len);
2060
0
    if (ffs->fs.sb1 == NULL) {
2061
0
        fs->tag = 0;
2062
0
        tsk_fs_free((TSK_FS_INFO *)ffs);
2063
0
        return NULL;
2064
0
    }
2065
2066
    /* check the magic and figure out the endian ordering */
2067
2068
    /* Try UFS2 first - I read somewhere that some upgrades
2069
     * kept the original UFS1 superblock in addition to
2070
     * the new one */
2071
0
    cnt = tsk_fs_read
2072
0
        (fs, (TSK_OFF_T) UFS2_SBOFF, (char *) ffs->fs.sb2,
2073
0
        sizeof(ffs_sb2));
2074
0
    if (cnt != sizeof(ffs_sb2)) {
2075
0
        if (cnt >= 0) {
2076
0
            tsk_error_reset();
2077
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
2078
0
        }
2079
0
        tsk_error_set_errstr("%s: Superblock at %" PRIuDADDR, myname,
2080
0
            (TSK_OFF_T) UFS2_SBOFF);
2081
0
        fs->tag = 0;
2082
0
        free(ffs->fs.sb1);
2083
0
        tsk_fs_free((TSK_FS_INFO *)ffs);
2084
0
        return NULL;
2085
0
    }
2086
2087
    /* If that didn't work, try the 256KB UFS2 location */
2088
0
    if (tsk_fs_guessu32(fs, ffs->fs.sb2->magic, UFS2_FS_MAGIC)) {
2089
0
        if (tsk_verbose)
2090
0
            fprintf(stderr, "ufs_open: Trying 256KB UFS2 location\n");
2091
2092
0
        cnt = tsk_fs_read
2093
0
            (fs, (TSK_OFF_T) UFS2_SBOFF2, (char *) ffs->fs.sb2,
2094
0
            sizeof(ffs_sb2));
2095
0
        if (cnt != sizeof(ffs_sb2)) {
2096
0
            if (cnt >= 0) {
2097
0
                tsk_error_reset();
2098
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
2099
0
            }
2100
0
            tsk_error_set_errstr2("%s: Superblock at %" PRIuDADDR,
2101
0
                myname, (TSK_OFF_T) UFS2_SBOFF2);
2102
0
            fs->tag = 0;
2103
0
            free(ffs->fs.sb1);
2104
0
            tsk_fs_free((TSK_FS_INFO *)ffs);
2105
0
            return NULL;
2106
0
        }
2107
2108
        /* Try UFS1 if that did not work */
2109
0
        if (tsk_fs_guessu32(fs, ffs->fs.sb2->magic, UFS2_FS_MAGIC)) {
2110
0
            if (tsk_verbose)
2111
0
                fprintf(stderr, "ufs_open: Trying UFS1 location\n");
2112
2113
0
            cnt = tsk_fs_read
2114
0
                (fs, (TSK_OFF_T) UFS1_SBOFF, (char *) ffs->fs.sb1, len);
2115
0
            if (cnt != len) {
2116
0
                if (cnt >= 0) {
2117
0
                    tsk_error_reset();
2118
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
2119
0
                }
2120
0
                tsk_error_set_errstr2("%s: Superblock at %" PRIuDADDR,
2121
0
                    myname, (TSK_OFF_T) UFS1_SBOFF);
2122
0
                fs->tag = 0;
2123
0
                free(ffs->fs.sb1);
2124
0
                tsk_fs_free((TSK_FS_INFO *)ffs);
2125
0
                return NULL;
2126
0
            }
2127
0
            if (tsk_fs_guessu32(fs, ffs->fs.sb1->magic, UFS1_FS_MAGIC)) {
2128
0
                tsk_error_reset();
2129
0
                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
2130
0
                tsk_error_set_errstr("No UFS Magic Found");
2131
0
                if (tsk_verbose)
2132
0
                    fprintf(stderr, "ufs_open: No UFS magic found\n");
2133
0
                fs->tag = 0;
2134
0
                free(ffs->fs.sb1);
2135
0
                tsk_fs_free((TSK_FS_INFO *)ffs);
2136
0
                return NULL;
2137
0
            }
2138
0
            else {
2139
                // @@@ NEED TO DIFFERENTIATE BETWEEN A & B - UID/GID location in inode
2140
0
                fs->ftype = TSK_FS_TYPE_FFS1;
2141
0
            }
2142
0
        }
2143
0
        else {
2144
0
            fs->ftype = TSK_FS_TYPE_FFS2;
2145
0
        }
2146
0
    }
2147
0
    else {
2148
0
        fs->ftype = TSK_FS_TYPE_FFS2;
2149
0
    }
2150
2151
2152
    /*
2153
     * Translate some filesystem-specific information to generic form.
2154
     */
2155
2156
0
    if (fs->ftype == TSK_FS_TYPE_FFS2) {
2157
0
        fs->block_count = tsk_gets64(fs->endian, ffs->fs.sb2->frag_num);
2158
0
        fs->block_size = tsk_gets32(fs->endian, ffs->fs.sb2->fsize_b);
2159
0
        ffs->ffsbsize_b = tsk_gets32(fs->endian, ffs->fs.sb2->bsize_b);
2160
0
        ffs->ffsbsize_f = tsk_gets32(fs->endian, ffs->fs.sb2->bsize_frag);
2161
0
        ffs->groups_count = tsk_gets32(fs->endian, ffs->fs.sb2->cg_num);
2162
0
    }
2163
0
    else {
2164
0
        fs->block_count = tsk_gets32(fs->endian, ffs->fs.sb1->frag_num);
2165
0
        fs->block_size = tsk_gets32(fs->endian, ffs->fs.sb1->fsize_b);
2166
0
        ffs->ffsbsize_b = tsk_gets32(fs->endian, ffs->fs.sb1->bsize_b);
2167
0
        ffs->ffsbsize_f = tsk_gets32(fs->endian, ffs->fs.sb1->bsize_frag);
2168
0
        ffs->groups_count = tsk_gets32(fs->endian, ffs->fs.sb1->cg_num);
2169
0
    }
2170
2171
    // apply some sanity checks before we start using these numbers
2172
0
    if ((fs->block_size == 0) || (ffs->ffsbsize_b == 0) || (ffs->ffsbsize_f == 0)
2173
0
        || (fs->block_size % 512) || (ffs->ffsbsize_b % 512)) {
2174
0
        tsk_error_reset();
2175
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
2176
0
        tsk_error_set_errstr
2177
0
        ("Not a UFS FS (invalid fragment or block size)");
2178
0
        if (tsk_verbose)
2179
0
            fprintf(stderr, "ufs_open: invalid fragment or block size\n");
2180
0
        fs->tag = 0;
2181
0
        free(ffs->fs.sb1);
2182
0
        tsk_fs_free((TSK_FS_INFO *)ffs);
2183
0
        return NULL;
2184
0
    }
2185
2186
0
    if ((ffs->ffsbsize_b / fs->block_size) != ffs->ffsbsize_f) {
2187
0
        tsk_error_reset();
2188
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
2189
0
        tsk_error_set_errstr("Not a UFS FS (frag / block size mismatch)");
2190
0
        if (tsk_verbose)
2191
0
            fprintf(stderr, "ufs_open: fragment / block size mismatch\n");
2192
0
        fs->tag = 0;
2193
0
        free(ffs->fs.sb1);
2194
0
        tsk_fs_free((TSK_FS_INFO *)ffs);
2195
0
        return NULL;
2196
0
    }
2197
2198
    /*
2199
     * Block calculations
2200
     */
2201
0
    fs->first_block = 0;
2202
0
    fs->last_block = fs->last_block_act = fs->block_count - 1;
2203
0
    fs->dev_bsize = img_info->sector_size;
2204
2205
    // determine the last block we have in this image
2206
0
    if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) <
2207
0
        fs->block_count)
2208
0
        fs->last_block_act =
2209
0
            (img_info->size - offset) / fs->block_size - 1;
2210
2211
2212
2213
    // Inode / meta data calculations
2214
0
    if (fs->ftype == TSK_FS_TYPE_FFS2) {
2215
0
        fs->inum_count = ffs->groups_count * tsk_gets32(fs->endian, ffs->fs.sb2->cg_inode_num) + 1;     // we are adding 1 in this calc to account for Orphans directory
2216
0
    }
2217
0
    else {
2218
0
        fs->inum_count = ffs->groups_count * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num) + 1;     // we are adding 1 in this calc to account for Orphans directory
2219
0
    }
2220
2221
0
    fs->root_inum = FFS_ROOTINO;
2222
0
    fs->first_inum = FFS_FIRSTINO;
2223
0
    fs->last_inum = fs->inum_count - 1;
2224
2225
    /* Volume ID - in the same place for both types */
2226
0
    for (fs->fs_id_used = 0; fs->fs_id_used < 8; fs->fs_id_used++) {
2227
0
        fs->fs_id[fs->fs_id_used] = ffs->fs.sb1->fs_id[fs->fs_id_used];
2228
0
    }
2229
2230
    // set the function pointers
2231
0
    fs->inode_walk = ffs_inode_walk;
2232
0
    fs->block_walk = ffs_block_walk;
2233
0
    fs->block_getflags = ffs_block_getflags;
2234
2235
0
    fs->get_default_attr_type = tsk_fs_unix_get_default_attr_type;
2236
0
    fs->load_attrs = tsk_fs_unix_make_data_run;
2237
0
    fs->name_cmp = tsk_fs_unix_name_cmp;
2238
2239
0
    fs->file_add_meta = ffs_inode_lookup;
2240
0
    fs->dir_open_meta = ffs_dir_open_meta;
2241
0
    fs->fsstat = ffs_fsstat;
2242
0
    fs->fscheck = ffs_fscheck;
2243
0
    fs->istat = ffs_istat;
2244
0
    fs->close = ffs_close;
2245
0
    fs->jblk_walk = ffs_jblk_walk;
2246
0
    fs->jentry_walk = ffs_jentry_walk;
2247
0
    fs->jopen = ffs_jopen;
2248
0
    fs->journ_inum = 0;
2249
2250
    // initialize caches
2251
0
    ffs->grp_buf = NULL;
2252
0
    ffs->grp_num = 0xffffffff;
2253
0
    ffs->grp_addr = 0;
2254
2255
0
    ffs->itbl_buf = NULL;
2256
0
    ffs->itbl_addr = 0;
2257
2258
    /*
2259
     * Print some stats.
2260
     */
2261
0
    if (tsk_verbose)
2262
0
        tsk_fprintf(stderr,
2263
0
            "inodes %" PRIuINUM " root ino %" PRIuINUM " cyl groups %"
2264
0
            PRId32 " blocks %" PRIuDADDR "\n", fs->inum_count,
2265
0
            fs->root_inum, ffs->groups_count, fs->block_count);
2266
2267
0
    tsk_init_lock(&ffs->lock);
2268
2269
0
    return fs;
2270
0
}