Coverage Report

Created: 2025-11-11 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/fs/xfs_dent.cpp
Line
Count
Source
1
/*
2
** The Sleuth Kit
3
**
4
** Brian Carrier [carrier <at> sleuthkit [dot] org]
5
** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
6
**
7
** ICS Laboratory [515lab.ics <at> gmail [dot] com]
8
** Copyright (c) 2019 ICS Laboratory.  All rights reserved.
9
**
10
** This software is distributed under the Common Public License 1.0
11
*/
12
13
#include "tsk_fs_i.h"
14
#include "tsk_xfs.h"
15
16
/*
17
 * Inode numbers in short-form directories can come in two versions,
18
 * either 4 bytes or 8 bytes wide.  These helpers deal with the
19
 * two forms transparently by looking at the headers i8count field.
20
 *
21
 * For 64-bit inode number the most significant byte must be zero.
22
 */
23
static xfs_ino_t
24
xfs_dir2_sf_get_ino(
25
    struct xfs_dir2_sf_hdr  *hdr,
26
    uint8_t         *from)
27
0
{
28
0
    if (hdr->i8count)
29
0
        return get_unaligned_be64(from) & 0x00ffffffffffffffULL;
30
0
    else
31
0
        return get_unaligned_be32(from);
32
0
}
33
34
static xfs_ino_t
35
xfs_dir3_sfe_get_ino(
36
    struct xfs_dir2_sf_hdr  *hdr,
37
    struct xfs_dir2_sf_entry *sfep)
38
0
{
39
0
    return xfs_dir2_sf_get_ino(hdr, &sfep->name[sfep->namelen + 1]);
40
0
}
41
42
static uint8_t
43
xfs_dir3_sfe_get_ftype(
44
  struct xfs_dir2_sf_entry *sfep)
45
0
{
46
0
    uint8_t ftype;
47
0
  ftype = sfep->name[sfep->namelen];
48
0
  if (ftype >= XFS_DIR3_FT_MAX)
49
0
    return XFS_DIR3_FT_UNKNOWN;
50
0
  return ftype;
51
0
}
52
53
static uint8_t
54
xfs_dir3_blockentry_get_ftype(
55
    struct xfs_dir2_data_entry *daen) // inumber namelen name ftype tag
56
0
{
57
0
    uint8_t ftype;
58
0
    ftype = daen->name[daen->namelen];
59
0
    if (ftype >= XFS_DIR3_FT_MAX)
60
0
        return XFS_DIR3_FT_UNKNOWN;
61
0
    return ftype;
62
0
}
63
64
static uint8_t
65
xfs_dent_copy(XFS_INFO * xfs,
66
    char *xfs_dent, TSK_FS_NAME *fs_name, TSK_FS_FILE *fs_file)
67
0
{
68
0
    if (fs_file->meta->content_type == 
69
0
        TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_SHORTFORM)
70
0
    {
71
0
        xfs_dir2_sf_t *dir2_sf = (xfs_dir2_sf_t*)xfs_dent;
72
0
        xfs_dir2_sf_hdr_t *hdr = (xfs_dir2_sf_hdr_t*)dir2_sf->hdr;
73
0
        xfs_dir2_sf_entry_t *ent = (xfs_dir2_sf_entry_t*)dir2_sf->entry;
74
75
0
        strncpy(fs_name->name, (char*)ent->name, ent->namelen);
76
0
        fs_name->name[ent->namelen] = '\0';
77
0
        fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
78
0
        fs_name->meta_addr = (TSK_INUM_T)xfs_dir3_sfe_get_ino(hdr, ent);
79
80
0
        if (ent->namelen >= fs_name->name_size){
81
0
            tsk_error_reset();
82
0
            tsk_error_set_errno(TSK_ERR_FS_ARG);
83
0
            tsk_error_set_errstr
84
0
                ("xfs_dent_copy: Name Space too Small %d %" PRIuSIZE "",
85
0
               ent->namelen, fs_name->name_size);
86
0
            return 1;
87
0
        }
88
89
0
        switch (xfs_dir3_sfe_get_ftype(ent)) {
90
0
            case XFS_DE_REG:
91
0
                fs_name->type = TSK_FS_NAME_TYPE_REG;
92
0
                break;
93
0
            case XFS_DE_DIR:
94
0
                fs_name->type = TSK_FS_NAME_TYPE_DIR;
95
0
                break;
96
0
            case XFS_DE_CHR:
97
0
                fs_name->type = TSK_FS_NAME_TYPE_CHR;
98
0
                break;
99
0
            case XFS_DE_BLK:
100
0
                fs_name->type = TSK_FS_NAME_TYPE_BLK;
101
0
                break;
102
0
            case XFS_DE_FIFO:
103
0
                fs_name->type = TSK_FS_NAME_TYPE_FIFO;
104
0
                break;
105
0
            case XFS_DE_SOCK:
106
0
                fs_name->type = TSK_FS_NAME_TYPE_SOCK;
107
0
                break;
108
0
            case XFS_DE_LNK:
109
0
                fs_name->type = TSK_FS_NAME_TYPE_LNK;
110
0
                break;
111
0
            case XFS_DE_UNKNOWN:
112
0
            default:
113
0
                fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
114
0
                break;
115
0
        }
116
0
    }
117
0
    else if (fs_file->meta->content_type ==
118
0
        TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_EXTENTS)
119
0
    {
120
0
        xfs_dir2_data_entry_t *ent = (xfs_dir2_data_entry_t*)xfs_dent;
121
122
0
        strncpy(fs_name->name, (char*)ent->name, ent->namelen);
123
0
        fs_name->name[ent->namelen] = '\0';
124
0
        fs_name->meta_addr = tsk_getu64(xfs->fs_info.endian, ent->inumber);
125
0
        fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
126
127
0
        if (ent->namelen >= fs_name->name_size){
128
0
            tsk_error_reset();
129
0
            tsk_error_set_errno(TSK_ERR_FS_ARG);
130
0
            tsk_error_set_errstr
131
0
                ("xfs_dent_copy: Name Space too Small %d %" PRIuSIZE "",
132
0
               ent->namelen, fs_name->name_size);
133
0
            return 1;
134
0
        }
135
136
0
        switch (xfs_dir3_blockentry_get_ftype(ent)) {
137
0
            case XFS_DE_REG:
138
0
                fs_name->type = TSK_FS_NAME_TYPE_REG;
139
0
                break;
140
0
            case XFS_DE_DIR:
141
0
                fs_name->type = TSK_FS_NAME_TYPE_DIR;
142
0
                break;
143
0
            case XFS_DE_CHR:
144
0
                fs_name->type = TSK_FS_NAME_TYPE_CHR;
145
0
                break;
146
0
            case XFS_DE_BLK:
147
0
                fs_name->type = TSK_FS_NAME_TYPE_BLK;
148
0
                break;
149
0
            case XFS_DE_FIFO:
150
0
                fs_name->type = TSK_FS_NAME_TYPE_FIFO;
151
0
                break;
152
0
            case XFS_DE_SOCK:
153
0
                fs_name->type = TSK_FS_NAME_TYPE_SOCK;
154
0
                break;
155
0
            case XFS_DE_LNK:
156
0
                fs_name->type = TSK_FS_NAME_TYPE_LNK;
157
0
                break;
158
0
            case XFS_DE_UNKNOWN:
159
0
            default:
160
0
                fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
161
0
                break;
162
0
        }
163
0
    }
164
0
    else fprintf(stderr, "[i] xfs_dent_copy: xfs.cpp: %d - unsupported metadata type detected\n", __LINE__);
165
166
0
    fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
167
168
0
    return 0;
169
0
}
170
171
/*
172
 * Shortform directory ops
173
 */
174
static int
175
xfs_dir2_sf_entsize(
176
  struct xfs_dir2_sf_hdr  *hdr,
177
  int     len)
178
0
{
179
0
  int count = sizeof(struct xfs_dir2_sf_entry); /* namelen + offset */
180
181
0
  count += len;         /* name */
182
0
  count += hdr->i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE; /* ino # */
183
0
  return count;
184
0
}
185
186
static int
187
xfs_dir3_sf_entsize(
188
  struct xfs_dir2_sf_hdr  *hdr,
189
  int     len)
190
0
{
191
0
  return xfs_dir2_sf_entsize(hdr, len) + sizeof(uint8_t);
192
0
}
193
194
static struct xfs_dir2_sf_entry *
195
xfs_dir3_sf_nextentry(
196
  struct xfs_dir2_sf_hdr  *hdr,
197
  struct xfs_dir2_sf_entry *sfep)
198
0
{
199
0
  return (struct xfs_dir2_sf_entry *)
200
0
    ((char *)sfep + xfs_dir3_sf_entsize(hdr, sfep->namelen));
201
0
}
202
203
static TSK_RETVAL_ENUM
204
xfs_dent_parse_shortform(XFS_INFO * xfs, TSK_FS_DIR * a_fs_dir, char *buf)
205
0
{
206
0
    TSK_FS_INFO *fs = &(xfs->fs_info);
207
    
208
0
    TSK_FS_NAME *fs_name;
209
0
    TSK_FS_FILE *fs_file = a_fs_dir->fs_file;
210
0
    xfs_dir2_sf_hdr_t *hdr;
211
0
    xfs_dir2_sf_entry_t *ent; 
212
    
213
0
    xfs_dir2_sf_t * dir2_sf = (xfs_dir2_sf_t *)tsk_malloc(sizeof(xfs_dir2_sf_t));
214
0
    hdr = (xfs_dir2_sf_hdr_t*)buf;
215
0
    dir2_sf->hdr = hdr;
216
217
0
    if ((fs_name = tsk_fs_name_alloc(XFS_MAXNAMELEN + 1, 0)) == NULL)
218
0
        return TSK_ERR;
219
220
0
    ent = (xfs_dir2_sf_entry_t*)((char*)(hdr + 1) - (hdr->i8count == 0) * 4); // code of miracle
221
    
222
0
    while (1)
223
0
    {
224
0
        uint8_t namelen;
225
0
        uint64_t inode;
226
        
227
0
        dir2_sf->entry = ent;
228
0
        namelen = ent->namelen;
229
0
        inode = xfs_dir2_sf_get_ino(hdr, (uint8_t*)ent);
230
        
231
0
        if (inode > fs->last_inum || namelen == 0) {
232
0
            break;
233
0
        }
234
235
0
        if (xfs_dent_copy(xfs, (char*)dir2_sf, fs_name, fs_file)) {
236
0
            tsk_fs_name_free(fs_name);
237
0
            return TSK_ERR;
238
0
        }
239
240
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
241
242
0
        if (tsk_fs_dir_add(a_fs_dir, fs_name)) {
243
0
            tsk_fs_name_free(fs_name);
244
0
            return TSK_ERR;
245
0
        }
246
247
0
        ent = xfs_dir3_sf_nextentry(hdr, ent);
248
0
    }
249
0
    free(dir2_sf);
250
0
    tsk_fs_name_free(fs_name);
251
0
    return TSK_OK;
252
0
}
253
254
/*
255
 * @param a_is_del Set to 1 if block is from a deleted directory
256
 * a_fs_dir = 채워야 할 것, 나머지는 채워져 있는 것
257
 * parse_block = 최종목표: a_fs_dir 채우기
258
 * inode format = local -> shortform
259
 *              = block -> block
260
 *                      or leaf
261
 */
262
static TSK_RETVAL_ENUM
263
xfs_dent_parse_block(XFS_INFO * xfs, TSK_FS_DIR * a_fs_dir, [[maybe_unused]]uint8_t a_is_del, [[maybe_unused]]TSK_LIST ** list_seen, char *buf,[[maybe_unused]] TSK_OFF_T offset)
264
0
{
265
0
    TSK_FS_NAME *fs_name;
266
267
0
    if ((fs_name = tsk_fs_name_alloc(XFS_MAXNAMELEN + 1, 0)) == NULL)
268
0
        return TSK_ERR;
269
270
0
    xfs_bmbt_rec_t *rec;
271
0
    xfs_bmbt_irec_t *irec;
272
273
0
    rec = (xfs_bmbt_rec_t*)buf;
274
0
    irec = (xfs_bmbt_irec_t*)tsk_malloc(sizeof(xfs_bmbt_irec_t));
275
276
0
    xfs_bmbt_disk_get_all(xfs, rec, irec);
277
278
0
    ssize_t len = irec->br_blockcount * tsk_getu32(xfs->fs_info.endian, xfs->fs->sb_blocksize);
279
280
0
    char *fbuf = (char*)tsk_malloc(sizeof(char) * len);
281
282
0
    struct xfs_dir3_data_hdr *hdr = (struct xfs_dir3_data_hdr*)fbuf;
283
284
    // sanity check
285
0
    if (hdr->hdr.magic != 0x33424458) { // XDB3
286
        // Trick to explore unalloc dent
287
0
        a_fs_dir->fs_file->meta->content_type = TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_SHORTFORM;
288
0
        if (xfs_dent_parse_shortform(xfs, a_fs_dir, buf) == TSK_OK){
289
0
            return TSK_OK;
290
0
        }else{
291
0
            fprintf(stderr, "[i] xfs_dent_parse_block: xfs_dent.cpp: %d - not a dir2_data_hdr: %8x\n",
292
0
            __LINE__, hdr->hdr.magic);            
293
0
            return TSK_ERR;
294
0
        }
295
0
    }
296
297
0
    xfs_dir2_data_entry_t *ent = (xfs_dir2_data_entry_t*)((char*)(hdr + 1) + 32); // magically should be happened
298
299
0
    while (true)
300
0
    {
301
0
        if (ent->namelen == 0)
302
0
            break;
303
  
304
0
        if (xfs_dent_copy(xfs, (char*)ent, fs_name, a_fs_dir->fs_file)) {
305
0
            tsk_fs_name_free(fs_name);
306
0
            return TSK_ERR;
307
0
        }
308
309
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
310
311
0
        if (tsk_fs_dir_add(a_fs_dir, fs_name)) {
312
0
            tsk_fs_name_free(fs_name);
313
0
            return TSK_ERR;
314
0
        }
315
316
0
        ent = xfs_dir2_data_nextentry(ent);
317
0
    }
318
319
0
    return TSK_OK;
320
0
}
321
322
static TSK_RETVAL_ENUM
323
xfs_dent_parse(XFS_INFO * xfs, TSK_FS_DIR * a_fs_dir, uint8_t a_is_del, TSK_LIST ** list_seen, char *buf, TSK_OFF_T offset)
324
0
{
325
0
    switch(a_fs_dir->fs_file->meta->content_type){
326
0
        case TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_SHORTFORM:
327
0
            xfs_dent_parse_shortform(xfs, a_fs_dir, buf);
328
0
            break;
329
330
0
        case TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_EXTENTS:
331
0
            xfs_dent_parse_block(xfs, a_fs_dir, a_is_del, list_seen, buf, offset);
332
0
            break;
333
0
        default:
334
0
            return TSK_ERR;
335
0
    }
336
0
    return TSK_OK;
337
0
}
338
339
/** \internal
340
* Process a directory and load up FS_DIR with the entries. If a pointer to
341
* an already allocated FS_DIR structure is given, it will be cleared.  If no existing
342
* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
343
* value is error or corruption, then the FS_DIR structure could
344
* have entries (depending on when the error occurred).
345
*
346
* @param a_fs File system to analyze
347
* @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
348
* structure or a new structure.
349
* @param a_addr Address of directory to process.
350
* @returns error, corruption, ok etc.
351
*/
352
TSK_RETVAL_ENUM 
353
xfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
354
    TSK_INUM_T a_addr,[[maybe_unused]] int recursion_depth)
355
0
{
356
0
    XFS_INFO * xfs = (XFS_INFO *) a_fs;
357
0
    TSK_FS_DIR * fs_dir;
358
0
    TSK_LIST *list_seen = NULL;
359
360
0
    char *dirbuf;
361
    
362
0
    TSK_RETVAL_ENUM retval_tmp;
363
0
    TSK_RETVAL_ENUM retval_final = TSK_OK;
364
365
0
    if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
366
0
        tsk_error_reset();
367
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
368
0
        tsk_error_set_errstr("xfs_dir_open_meta: inode value: %" PRIuINUM
369
0
            "\n", a_addr);
370
0
        return TSK_ERR;
371
0
    }
372
0
    else if (a_fs_dir == NULL) {
373
0
        tsk_error_reset();
374
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
375
0
        tsk_error_set_errstr
376
0
            ("xfs_dir_open_meta: NULL fs_attr argument given");
377
0
        return TSK_ERR;
378
0
    }
379
380
0
    if (tsk_verbose) {
381
0
        tsk_fprintf(stderr,
382
0
            "xfs_dir_open_meta: Processing directory %" PRIuINUM
383
0
            "\n", a_addr);
384
0
    }
385
386
0
    fs_dir = *a_fs_dir;
387
388
0
    if (fs_dir) {
389
0
        tsk_fs_dir_reset(fs_dir);
390
0
        fs_dir->addr = a_addr;
391
0
    }
392
0
    else {
393
0
        if((*a_fs_dir = fs_dir =
394
0
                tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) {
395
0
            return TSK_ERR;
396
0
        }
397
0
    }
398
399
0
    if ((fs_dir->fs_file =
400
0
        tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { // inode_lookup -> content_ptr 채움
401
0
        fprintf(stderr, "xfs_fs_dir_open_meta: failed to obtain fs_file meta info\n");
402
0
        tsk_error_errstr2_concat("- xfs_dir_open_meta");
403
0
        return TSK_COR;
404
0
    }
405
406
    // We only read in and process a single block at a time
407
0
    if ((dirbuf = (char*)tsk_malloc((size_t)a_fs->block_size)) == NULL) {
408
0
        fprintf(stderr, "[i] xfs_load_attr_block: xfs.cpp: %d - failed to malloc\n", __LINE__);
409
0
        return TSK_ERR;
410
0
    }
411
412
0
    memcpy(dirbuf, fs_dir->fs_file->meta->content_ptr, XFS_CONTENT_LEN_V5(xfs));
413
414
0
    retval_tmp =
415
0
        xfs_dent_parse(xfs, fs_dir, (fs_dir->fs_file->meta->
416
0
            flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, &list_seen,
417
0
        dirbuf, XFS_CONTENT_LEN_V5(xfs));
418
 
419
0
    if (retval_tmp == TSK_ERR)
420
0
        retval_final = TSK_ERR;
421
0
    else if (retval_tmp == TSK_COR)
422
0
        retval_final = TSK_COR;
423
424
0
    free(dirbuf);
425
426
0
    return retval_final;
427
0
}
428
429
uint8_t xfs_jentry_walk([[maybe_unused]]TSK_FS_INFO *info, [[maybe_unused]]int a,
430
        [[maybe_unused]]TSK_FS_JENTRY_WALK_CB c, [[maybe_unused]]void *b)
431
0
{
432
0
    return -1;
433
0
}
434
435
uint8_t xfs_jblk_walk([[maybe_unused]]TSK_FS_INFO *a, [[maybe_unused]]TSK_DADDR_T b,
436
        [[maybe_unused]]TSK_DADDR_T c, [[maybe_unused]]int d, [[maybe_unused]]TSK_FS_JBLK_WALK_CB e, [[maybe_unused]]void *f)
437
0
{
438
0
    return -1;
439
0
}
440
441
uint8_t xfs_jopen([[maybe_unused]]TSK_FS_INFO *a, [[maybe_unused]]TSK_INUM_T b)
442
0
{
443
0
    return -1;
444
0
}