Coverage Report

Created: 2026-02-26 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/fs/fatxxfs_dent.c
Line
Count
Source
1
/*
2
** fatfs_dent
3
** The Sleuth Kit
4
**
5
** file name layer support for the FAT file system
6
**
7
** Brian Carrier [carrier <at> sleuthkit [dot] org]
8
** Copyright (c) 2006-2013 Brian Carrier, Basis Technology.  All Rights reserved
9
** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
10
**
11
** TASK
12
** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
13
**
14
**
15
** This software is distributed under the Common Public License 1.0
16
**
17
** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
18
**
19
*/
20
21
/**
22
* \file fatfs_dent.cpp
23
* Contains the internal TSK FAT file name processing code.
24
*/
25
26
#include "tsk_fs_i.h"
27
#include "tsk_fatxxfs.h"
28
#include <assert.h>
29
30
/* Special data structure allocated for each directory to hold the long
31
* file name entries until all entries have been found */
32
typedef struct {
33
    uint8_t name[FATFS_MAXNAMLEN_UTF8]; /* buffer for lfn - in reverse order */
34
    uint16_t start;             /* current start of name */
35
    uint8_t chk;                /* current checksum */
36
    uint8_t seq;                /* seq of first entry in lfn */
37
} FATXXFS_LFN;
38
39
/**
40
 * /internal
41
 * Parse a buffer containing the contents of a directory and add TSK_FS_NAME
42
 * objects for each named file found to the TSK_FS_DIR representation of the
43
 * directory.
44
 *
45
 * @param fatfs File system information structure for file system that
46
 * contains the directory.
47
 * @param a_fs_dir Directory structure into to which parsed file metadata will
48
 * be added.
49
 * @param buf Buffer that contains the directory contents.
50
 * @param len Length of buffer in bytes (must be a multiple of sector
51
 *  size).
52
 * @param addrs Array where each element is the original address of
53
 * the corresponding sector in a_buf (size of array is number of sectors in
54
 * the directory).
55
 * @param recursion_depth Recursion depth to limit the number of self-calls
56
 * @return TSK_RETVAL_ENUM
57
*/
58
TSK_RETVAL_ENUM
59
fatxxfs_dent_parse_buf(FATFS_INFO *fatfs, TSK_FS_DIR *a_fs_dir, char *buf,
60
    TSK_OFF_T len, TSK_DADDR_T *addrs, int recursion_depth)
61
824
{
62
824
    const char *func_name = "fatxxfs_dent_parse_buf";
63
824
    unsigned int idx = 0;
64
824
    unsigned int sidx = 0;
65
824
    int a = 0;
66
824
    int b = 0;
67
824
    TSK_INUM_T ibase = 0;
68
824
    FATXXFS_DENTRY *dep = NULL;
69
824
    TSK_FS_INFO *fs = (TSK_FS_INFO*)&fatfs->fs_info;
70
824
    int sectalloc = 0;
71
824
    TSK_FS_NAME *fs_name = NULL;
72
824
    FATXXFS_LFN lfninfo;
73
824
    int entrySeenCount = 0;
74
824
    int entryInvalidCount = 0;
75
824
    uint8_t isCorruptDir = 0;
76
77
824
    tsk_error_reset();
78
824
    if (fatfs_ptr_arg_is_null(fatfs, "fatfs", func_name) ||
79
824
        fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) ||
80
824
        fatfs_ptr_arg_is_null(buf, "buf", func_name) ||
81
824
        fatfs_ptr_arg_is_null(addrs, "addrs", func_name)) {
82
0
        return TSK_ERR;
83
0
    }
84
85
824
    assert(len > 0);
86
824
    if (len < 0) {
87
0
        tsk_error_reset();
88
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
89
0
        tsk_error_set_errstr("%s: invalid buffer length", func_name);
90
0
        return TSK_ERR;
91
0
    }
92
93
824
    dep = (FATXXFS_DENTRY*)buf;
94
95
824
    if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) {
96
0
        return TSK_ERR;
97
0
    }
98
99
824
    memset(&lfninfo, 0, sizeof(FATXXFS_LFN));
100
824
    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
101
102
    /* Loop through the sectors in the buffer. */
103
35.9k
    for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) {
104
105
        /* Get the base inode for the current sector */
106
35.1k
        ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]);
107
35.1k
        if (ibase > fs->last_inum) {
108
2
            tsk_error_reset();
109
2
            tsk_error_set_errno(TSK_ERR_FS_ARG);
110
2
            tsk_error_set_errstr
111
2
                ("fatfs_parse: inode address is too large");
112
2
            tsk_fs_name_free(fs_name);
113
2
            return TSK_COR;
114
2
        }
115
116
35.1k
        if (tsk_verbose)
117
0
            tsk_fprintf(stderr,
118
0
            "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR
119
0
            " for dir %" PRIuINUM "\n", addrs[sidx], a_fs_dir->addr);
120
121
        /* Get the allocation status of the current sector. */
122
35.1k
        if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) {
123
0
            if (tsk_verbose) {
124
0
                tsk_fprintf(stderr,
125
0
                    "fatfs_dent_parse_buf: Error looking up sector allocation: %"
126
0
                    PRIuDADDR "\n", addrs[sidx]);
127
0
                tsk_error_print(stderr);
128
0
            }
129
0
            tsk_error_reset();
130
0
            continue;
131
0
        }
132
133
        /* Loop through the putative directory entries in the current sector. */
134
1.00M
        for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) {
135
964k
            FATXXFS_DENTRY *dir;
136
964k
            TSK_INUM_T inode;
137
138
964k
            entrySeenCount++;
139
140
            /* Is the current entry a valid entry? */
141
964k
            if (0 == fatxxfs_is_dentry(fatfs, (FATFS_DENTRY*)dep,
142
964k
                (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sectalloc,
143
964k
                ((isCorruptDir == 0) && (sectalloc)) ? 1 : 0)) {
144
579k
                    if (tsk_verbose)
145
0
                        tsk_fprintf(stderr,
146
0
                        "fatfs_dent_parse_buf: Entry %u is invalid\n",
147
0
                        idx);
148
579k
                    entryInvalidCount++;
149
                    /* If we have seen four entries and all of them are corrupt,
150
                    * then test every remaining entry in this folder --
151
                    * even if the sector is allocated. The scenario is one
152
                    * where we are processing a cluster that is allocated
153
                    * to a file and we happen to get some data that matches
154
                    * every now and then. */
155
579k
                    if ((entrySeenCount == 4) && (entryInvalidCount == 4)) {
156
82
                        isCorruptDir = 1;
157
82
                    }
158
579k
                    continue;
159
579k
            }
160
161
385k
            dir = dep;
162
163
            /* Compute the inode address corresponding to this directory entry. */
164
385k
            inode = ibase + idx;
165
166
385k
            if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
167
                /* The current entry is a long file name entry. */
168
8.86k
                FATXXFS_DENTRY_LFN *dirl = (FATXXFS_DENTRY_LFN *) dir;
169
170
                /* Store the name in dinfo until we get the 8.3 name
171
                 * Use the checksum to identify a new sequence. */
172
8.86k
                if (((dirl->seq & FATXXFS_LFN_SEQ_FIRST) && (dirl->seq != FATXXFS_SLOT_DELETED)) ||
173
8.66k
                    (dirl->chksum != lfninfo.chk)) {
174
                    // @@@ Do a partial output here
175
176
                    /* This is the last long file name entry in a sequence.
177
                     * Reset the sequence number, check sum, and next char
178
                     * address. */
179
5.03k
                    lfninfo.seq = dirl->seq & FATXXFS_LFN_SEQ_MASK;
180
5.03k
                    lfninfo.chk = dirl->chksum;
181
5.03k
                    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
182
5.03k
                }
183
3.83k
                else if (dirl->seq != lfninfo.seq - 1) {
184
                    // @@@ Check the sequence number - the checksum is correct though...
185
3.81k
                }
186
187
                /* Copy the UTF16 values starting at end of buffer */
188
44.3k
                for (a = 3; a >= 0; a--) {
189
35.4k
                    if ((lfninfo.start > 0))
190
35.4k
                        lfninfo.name[lfninfo.start--] = dirl->part3[a];
191
35.4k
                }
192
115k
                for (a = 11; a >= 0; a--) {
193
106k
                    if ((lfninfo.start > 0))
194
106k
                        lfninfo.name[lfninfo.start--] = dirl->part2[a];
195
106k
                }
196
97.5k
                for (a = 9; a >= 0; a--) {
197
88.6k
                    if ((lfninfo.start > 0))
198
88.6k
                        lfninfo.name[lfninfo.start--] = dirl->part1[a];
199
88.6k
                }
200
201
                // Skip ahead until we get a new sequence num or the 8.3 name
202
8.86k
                continue;
203
8.86k
            }
204
376k
            else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) {
205
                /* Special case for volume label: name does not have an
206
                * extension and we add a note at the end that it is a label */
207
39.0k
                a = 0;
208
209
351k
                for (b = 0; b < 8; b++) {
210
312k
                    if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) {
211
54.8k
                        fs_name->name[a++] = dir->name[b];
212
54.8k
                    }
213
257k
                    else {
214
257k
                        fs_name->name[a++] = '^';
215
257k
                    }
216
312k
                }
217
156k
                for (b = 0; b < 3; b++) {
218
117k
                    if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) {
219
5.28k
                        fs_name->name[a++] = dir->ext[b];
220
5.28k
                    }
221
111k
                    else {
222
111k
                        fs_name->name[a++] = '^';
223
111k
                    }
224
117k
                }
225
226
39.0k
                fs_name->name[a] = '\0';
227
                /* Append a string to show it is a label */
228
39.0k
                if (a + 22 < FATFS_MAXNAMLEN_UTF8) {
229
39.0k
                    const char *volstr = " (Volume Label Entry)";
230
39.0k
                    strncat(fs_name->name, volstr,
231
39.0k
                        FATFS_MAXNAMLEN_UTF8 - a);
232
39.0k
                }
233
39.0k
            }
234
337k
            else {
235
                /* A short (8.3) entry */
236
337k
                char *name_ptr; // The dest location for the short name
237
238
                /* if we have a lfn, copy it into fs_name->name
239
                * and put the short name in fs_name->shrt_name */
240
337k
                if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) {
241
3.67k
                    int retVal;
242
243
                    /* @@@ Check the checksum */
244
245
                    /* Convert the UTF16 to UTF8 */
246
3.67k
                    UTF16 *name16 =
247
3.67k
                        (UTF16 *) ((uintptr_t) & lfninfo.
248
3.67k
                        name[lfninfo.start + 1]);
249
3.67k
                    UTF8 *name8 = (UTF8 *) fs_name->name;
250
251
3.67k
                    retVal =
252
3.67k
                        tsk_UTF16toUTF8(fs->endian,
253
3.67k
                        (const UTF16 **) &name16,
254
3.67k
                        (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8],
255
3.67k
                        &name8,
256
3.67k
                        (UTF8 *) ((uintptr_t) name8 +
257
3.67k
                        FATFS_MAXNAMLEN_UTF8), TSKlenientConversion);
258
259
3.67k
                    if (retVal != TSKconversionOK) {
260
560
                        tsk_error_reset();
261
560
                        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
262
560
                        tsk_error_set_errstr
263
560
                            ("fatfs_parse: Error converting FAT LFN to UTF8: %d",
264
560
                            retVal);
265
560
                        continue;
266
560
                    }
267
268
                    /* Make sure it is NULL Terminated */
269
3.11k
                    if ((uintptr_t) name8 >
270
3.11k
                        (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8)
271
0
                        fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0';
272
3.11k
                    else
273
3.11k
                        *name8 = '\0';
274
275
3.11k
                    lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
276
3.11k
                    name_ptr = fs_name->shrt_name;      // put 8.3 into shrt_name
277
3.11k
                }
278
                /* We don't have a LFN, so put the short name in
279
                * fs_name->name */
280
333k
                else {
281
333k
                    fs_name->shrt_name[0] = '\0';
282
333k
                    name_ptr = fs_name->name;   // put 8.3 into normal location
283
333k
                }
284
285
                /* copy in the short name into the place specified above.
286
                * Skip spaces and put in the . */
287
336k
                a = 0;
288
3.03M
                for (b = 0; b < 8; b++) {
289
2.69M
                    if ((dir->name[b] != 0) && (dir->name[b] != 0xff) &&
290
678k
                        (dir->name[b] != 0x20)) {
291
292
678k
                            if ((b == 0)
293
105k
                                && (dir->name[0] == FATXXFS_SLOT_DELETED)) {
294
19
                                    name_ptr[a++] = '_';
295
19
                            }
296
678k
                            else if ((dir->ntbyte & FATXXFS_CASE_LOWER_BASE)
297
82.4k
                                && (dir->name[b] >= 'A')
298
7.95k
                                && (dir->name[b] <= 'Z')) {
299
312
                                    name_ptr[a++] = dir->name[b] + 32;
300
312
                            }
301
677k
                            else {
302
677k
                                name_ptr[a++] = dir->name[b];
303
677k
                            }
304
678k
                    }
305
2.69M
                }
306
307
1.34M
                for (b = 0; b < 3; b++) {
308
1.01M
                    if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) &&
309
170k
                        (dir->ext[b] != 0x20)) {
310
170k
                            if (b == 0)
311
60.5k
                                name_ptr[a++] = '.';
312
170k
                            if ((dir->ntbyte & FATXXFS_CASE_LOWER_EXT) &&
313
18.7k
                                (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z'))
314
547
                                name_ptr[a++] = dir->ext[b] + 32;
315
170k
                            else
316
170k
                                name_ptr[a++] = dir->ext[b];
317
170k
                    }
318
1.01M
                }
319
336k
                name_ptr[a] = '\0';
320
321
                // make sure that only ASCII is in the short name
322
336k
                fatfs_cleanup_ascii(name_ptr);
323
336k
            }
324
325
            /* file type: FAT only knows DIR and FILE */
326
375k
            if ((dir->attrib & FATFS_ATTR_DIRECTORY) ==
327
375k
                FATFS_ATTR_DIRECTORY)
328
20.2k
                fs_name->type = TSK_FS_NAME_TYPE_DIR;
329
355k
            else
330
355k
                fs_name->type = TSK_FS_NAME_TYPE_REG;
331
332
            /* set the inode */
333
375k
            fs_name->meta_addr = inode;
334
375k
            inode = 0;  // so that we don't use it anymore -- use only fs_name->meta_addr
335
336
            /* Handle the . and .. entries specially
337
            * The current inode 'address' they have is for the current
338
            * slot in the cluster, but it needs to refer to the original
339
            * slot
340
            */
341
375k
            if (TSK_FS_ISDOT(fs_name->name)
342
264
                    && (fs_name->type == TSK_FS_NAME_TYPE_DIR)
343
250
                    && idx < 2) {
344
239
                if (fs_name->name[1] == '\0') {
345
                    /* Current directory - "." */
346
21
                    fs_name->meta_addr =
347
21
                        a_fs_dir->fs_file->meta->addr;
348
21
                }
349
                /* for the parent directory, look up in the list that
350
                * is maintained in fafs_info */
351
218
                else if (fs_name->name[1] == '.') {
352
                    /* Parent directory - ".." */
353
218
                    uint8_t dir_found = 0;
354
355
218
                    if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0)  {
356
200
                        dir_found = 1;
357
200
                    }
358
359
218
                    if ((dir_found == 0)
360
18
                        && (addrs[0] == fatfs->firstdatasect)) {
361
                            /* if we are currently in the root directory, we aren't going to find
362
                            * a parent.  This shouldn't happen, but could result in an infinite loop. */
363
18
                            fs_name->meta_addr = 0;
364
18
                            dir_found = 1;
365
18
                    }
366
218
                    if (dir_found == 0) {
367
0
                        if (tsk_verbose)
368
0
                            fprintf(stderr,
369
0
                            "fatfs_dent_parse_buf: Walking directory to find parent\n");
370
371
                        /* The parent directory is not in the list.  We are going to walk
372
                        * the directory until we hit this directory. This process will
373
                        * populate the buffer table and we will then rescan it */
374
0
                        if (tsk_fs_dir_walk_internal(fs, fs->root_inum,
375
0
                            (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC |
376
0
                            TSK_FS_DIR_WALK_FLAG_UNALLOC |
377
0
                            TSK_FS_DIR_WALK_FLAG_RECURSE),
378
0
                            fatfs_find_parent_act,
379
0
                            (void *) &a_fs_dir->fs_file->meta->addr, recursion_depth)) {
380
0
                                return TSK_OK;
381
0
                        }
382
383
0
                        if (tsk_verbose)
384
0
                            fprintf(stderr,
385
0
                            "fatfs_dent_parse_buf: Finished walking directory to find parent\n");
386
387
0
                        if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) {
388
0
                            dir_found = 1;
389
0
                        }
390
391
                        // if we did not find it, then it was probably
392
                        // from the orphan directory...
393
0
                        if (dir_found == 0)
394
0
                            fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs);
395
0
                    }
396
218
                }
397
239
            }
398
375k
            else {
399
                /* Save the (non-. or ..) directory to parent directory info to local
400
                * structures so that we can later fill into the inode
401
                * info for '..' entries */
402
375k
                if (fs_name->type == TSK_FS_NAME_TYPE_DIR) {
403
20.0k
                    if (fatfs_dir_buf_add(fatfs,
404
20.0k
                        a_fs_dir->fs_file->meta->addr, fs_name->meta_addr))
405
0
                        return TSK_ERR;
406
20.0k
                }
407
375k
            }
408
409
410
            /* The allocation status of an entry is based on the allocation
411
            * status of the sector it is in and the flag.  Deleted directories
412
            * do not always clear the flags of each entry
413
            */
414
375k
            if (sectalloc == 1) {
415
375k
        if(FATXXFS_IS_DELETED(dep->name, fatfs)){
416
227k
            fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
417
227k
        }
418
148k
        else{
419
148k
          fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
420
148k
        }
421
375k
            }
422
194
            else {
423
194
                fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
424
194
            }
425
426
375k
            tsk_fs_dir_add(a_fs_dir, fs_name);
427
375k
        }
428
35.1k
    }
429
822
    tsk_fs_name_free(fs_name);
430
431
822
    return TSK_OK;
432
824
}