Coverage Report

Created: 2025-08-26 06:11

/src/sleuthkit/tsk/fs/fatfs_dent.cpp
Line
Count
Source (jump to first uncovered line)
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 "tsk_exfatfs.h"
29
#include "tsk_fatfs.h"
30
31
#include <map>
32
33
/*
34
* DESIGN NOTES
35
*
36
* the basic goal of this code is to parse directory entry structures for
37
* file names.  The main function is fatfs_parse_buf, which parses
38
* a buffer and stores the entries in FS_DIR.  That structure is then
39
* used by dir_get() or dir_walk() to provide the data back to the user.
40
*
41
* One of the odd aspects of this code is that the 'inode' values are
42
* the 'slot-address'.  Refer to the document on how FAT was implemented
43
* for more details. This means that we need to search for the actual
44
* 'inode' address for the '.' and '..' entries though!  The search
45
* for '..' is quite painful if this code is called from a random
46
* location.  It does save what the parent is though, so the search
47
* only has to be done once per session.
48
*/
49
50
/*
51
* name_walk callback used when finding the parent directory.  It
52
* forces the walking process to stop when we hit a target directory.
53
* A list of directory to parent directory mappings is built up during
54
* the walk and this function is used to stop that building process.
55
*/
56
TSK_WALK_RET_ENUM
57
    fatfs_find_parent_act(TSK_FS_FILE * fs_file, const char * /*a_path*/, void *ptr)
58
0
{
59
0
    TSK_INUM_T par_inum = *(TSK_INUM_T *) ptr;
60
61
0
    if ((fs_file->meta == NULL)
62
0
        || ( ! TSK_FS_IS_DIR_META(fs_file->meta->type)))
63
0
        return TSK_WALK_CONT;
64
65
0
    if (fs_file->meta->addr == par_inum)
66
0
        return TSK_WALK_STOP;
67
68
0
    return TSK_WALK_CONT;
69
0
}
70
71
/** \internal
72
* Casts the void * to a map.  This obfuscation is done so that the rest of the library
73
* can remain as C and only this code needs to be C++.
74
*
75
* Assumes that you already have the lock
76
*/
77
117k
static std::map<TSK_INUM_T, TSK_INUM_T> * getParentMap(FATFS_INFO *fatfs) {
78
    // allocate it if it hasn't already been
79
117k
    if (fatfs->inum2par == NULL) {
80
24
        fatfs->inum2par = new std::map<TSK_INUM_T, TSK_INUM_T>;
81
24
    }
82
117k
    return (std::map<TSK_INUM_T, TSK_INUM_T> *)fatfs->inum2par;
83
117k
}
84
85
/**
86
* Adds an entry to the parent directory map.  Used to make further processing
87
* faster.
88
* @param fatfs File system
89
* @param par_inum Parent folder meta data address.
90
* @param dir_inum Sub-folder meta data address.
91
* @returns 0
92
*/
93
uint8_t
94
    fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum,
95
    TSK_INUM_T dir_inum)
96
116k
{
97
116k
    tsk_take_lock(&fatfs->dir_lock);
98
116k
    std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs);
99
116k
    (*tmpMap)[dir_inum] = par_inum;
100
116k
    tsk_release_lock(&fatfs->dir_lock);
101
102
116k
    return 0;
103
116k
}
104
105
/**
106
* Looks up the parent meta address for a child from the cached list.
107
* @param fatfs File system
108
* @param dir_inum Inode of sub-directory to look up
109
* @param par_inum [out] Result of lookup
110
* @returns 0 if found and 1 if not.
111
*/
112
uint8_t
113
    fatfs_dir_buf_get(FATFS_INFO * fatfs, TSK_INUM_T dir_inum,
114
    TSK_INUM_T *par_inum)
115
109
{
116
109
    uint8_t retval = 1;
117
109
    tsk_take_lock(&fatfs->dir_lock);
118
109
    std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs);
119
109
    if (tmpMap->count( dir_inum) > 0) {
120
93
        *par_inum = (*tmpMap)[dir_inum];
121
93
        retval = 0;
122
93
    }
123
109
    tsk_release_lock(&fatfs->dir_lock);
124
125
109
    return retval;
126
109
}
127
128
/**
129
* Frees the memory associated with the parent map
130
*/
131
28
void fatfs_dir_buf_free(FATFS_INFO *fatfs) {
132
28
    tsk_take_lock(&fatfs->dir_lock);
133
28
    if (fatfs->inum2par != NULL) {
134
24
        std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs);
135
24
        delete tmpMap;
136
24
        fatfs->inum2par = NULL;
137
24
    }
138
28
    tsk_release_lock(&fatfs->dir_lock);
139
28
}
140
141
/**************************************************************************
142
*
143
* dent_walk
144
*
145
*************************************************************************/
146
147
/* values used to copy the directory contents into a buffer */
148
149
150
typedef struct {
151
    /* ptr to the current location in a local buffer */
152
    char *curdirptr;
153
154
    /* number of bytes left in curdirptr */
155
    size_t dirleft;
156
157
    /* ptr to a local buffer for the stack of sector addresses */
158
    TSK_DADDR_T *addrbuf;
159
160
    /* num of entries allocated to addrbuf */
161
    size_t addrsize;
162
163
    /* The current index in the addrbuf stack */
164
    size_t addridx;
165
166
} FATFS_LOAD_DIR;
167
168
169
170
/**
171
* file walk callback that is used to load directory contents
172
* into a buffer
173
*/
174
static TSK_WALK_RET_ENUM
175
    fatfs_dent_action(TSK_FS_FILE * /*fs_file*/, TSK_OFF_T /*a_off*/,
176
    TSK_DADDR_T addr, char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM /*flags*/,
177
    void *ptr)
178
51.5k
{
179
51.5k
    FATFS_LOAD_DIR *load = (FATFS_LOAD_DIR *) ptr;
180
181
    /* how much of the buffer are we copying */
182
51.5k
    size_t len = (load->dirleft < size) ? load->dirleft : size;
183
184
    /* Copy the sector into a buffer and increment the pointers */
185
51.5k
    memcpy(load->curdirptr, buf, len);
186
51.5k
    load->curdirptr = (char *) ((uintptr_t) load->curdirptr + len);
187
51.5k
    load->dirleft -= len;
188
189
    /* fill in the stack of addresses of sectors
190
    *
191
    * if we are at the last entry, then realloc more */
192
51.5k
    if (load->addridx == load->addrsize) {
193
0
        tsk_error_reset();
194
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
195
0
        tsk_error_set_errstr
196
0
            ("fatfs_dent_walk: Trying to put more sector address in stack than were allocated (%lu)",
197
0
            (long) load->addridx);
198
0
        return TSK_WALK_ERROR;
199
0
    }
200
201
    /* Add this sector to the stack */
202
51.5k
    load->addrbuf[load->addridx++] = addr;
203
204
51.5k
    if (load->dirleft)
205
51.0k
        return TSK_WALK_CONT;
206
462
    else
207
462
        return TSK_WALK_STOP;
208
51.5k
}
209
210
211
/** \internal
212
* Process a directory and load up FS_DIR with the entries. If a pointer to
213
* an already allocated FS_DIR structure is given, it will be cleared.  If no existing
214
* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
215
* value is error or corruption, then the FS_DIR structure could
216
* have entries (depending on when the error occurred).
217
*
218
* @param a_fs File system to analyze
219
* @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
220
* structure or a new structure.
221
* @param a_addr Address of directory to process.
222
* @param recursion_depth Recursion depth to limit the number of self-calls
223
* @returns error, corruption, ok etc.
224
*/
225
226
TSK_RETVAL_ENUM
227
    fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
228
    TSK_INUM_T a_addr, int recursion_depth)
229
21.3k
{
230
21.3k
    const char *func_name = "fatfs_dir_open_meta";
231
21.3k
    TSK_OFF_T size, len;
232
21.3k
    FATFS_INFO *fatfs = (FATFS_INFO *) a_fs;
233
21.3k
    char *dirbuf;
234
21.3k
    TSK_DADDR_T *addrbuf;
235
21.3k
    FATFS_LOAD_DIR load;
236
21.3k
    TSK_RETVAL_ENUM retval;
237
238
21.3k
    TSK_FS_DIR *fs_dir;
239
240
21.3k
    if ((a_addr < a_fs->first_inum) || (a_addr > a_fs->last_inum)) {
241
0
        tsk_error_reset();
242
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
243
0
        tsk_error_set_errstr("%s: invalid a_addr value: %"
244
0
            PRIuINUM "\n", func_name, a_addr);
245
0
        return TSK_ERR;
246
0
    }
247
21.3k
    else if (a_fs_dir == NULL) {
248
0
        tsk_error_reset();
249
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
250
0
        tsk_error_set_errstr
251
0
            ("%s: NULL fs_attr argument given", func_name);
252
0
        return TSK_ERR;
253
0
    }
254
255
21.3k
    fs_dir = *a_fs_dir;
256
21.3k
    if (fs_dir) {
257
0
        tsk_fs_dir_reset(fs_dir);
258
0
        fs_dir->addr = a_addr;
259
0
    }
260
21.3k
    else {
261
21.3k
        if ((*a_fs_dir = fs_dir =
262
21.3k
            tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) {
263
0
                return TSK_ERR;
264
0
        }
265
21.3k
    }
266
267
    //  handle the orphan directory if its contents were requested
268
21.3k
    if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) {
269
27
        return tsk_fs_dir_find_orphans(a_fs, fs_dir);
270
27
    }
271
272
21.3k
    fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr);
273
21.3k
    if (fs_dir->fs_file == NULL) {
274
0
        tsk_error_reset();
275
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
276
0
        tsk_error_set_errstr("%s: %" PRIuINUM
277
0
            " is not a valid inode", func_name, a_addr);
278
0
        return TSK_COR;
279
0
    }
280
281
21.3k
    size = fs_dir->fs_file->meta->size;
282
21.3k
    len = roundup(size, fatfs->ssize);
283
284
21.3k
    if (tsk_verbose) {
285
0
        tsk_fprintf(stderr,
286
0
        "%s: Processing directory %" PRIuINUM "\n",
287
0
        func_name, a_addr);
288
0
    }
289
290
21.3k
    if (size == 0) {
291
18.7k
        if (tsk_verbose)
292
0
            tsk_fprintf(stderr,
293
0
            "%s: directory has 0 size\n", func_name);
294
18.7k
        return TSK_OK;
295
18.7k
    }
296
297
    /* Make a copy of the directory contents using file_walk */
298
2.59k
    if ((dirbuf = (char *)tsk_malloc((size_t) len)) == NULL) {
299
0
        return TSK_ERR;
300
0
    }
301
2.59k
    load.curdirptr = dirbuf;
302
2.59k
    load.dirleft = (size_t) size;
303
304
    /* We are going to save the address of each sector in the directory
305
    * in a stack - they are needed to determine the inode address.
306
    */
307
2.59k
    load.addrsize = (size_t) (len / fatfs->ssize);
308
2.59k
    addrbuf =
309
2.59k
        (TSK_DADDR_T *) tsk_malloc(load.addrsize * sizeof(TSK_DADDR_T));
310
2.59k
    if (addrbuf == NULL) {
311
0
        free(dirbuf);
312
0
        return TSK_ERR;
313
0
    }
314
315
    /* Set the variables that are used during the copy */
316
2.59k
    load.addridx = 0;
317
2.59k
    load.addrbuf = addrbuf;
318
319
    /* save the directory contents into dirbuf */
320
2.59k
    if (tsk_fs_file_walk(fs_dir->fs_file,
321
2.59k
        TSK_FS_FILE_WALK_FLAG_SLACK,
322
2.59k
        fatfs_dent_action, (void *) &load)) {
323
2.12k
            tsk_error_errstr2_concat("- %s", func_name);
324
2.12k
            free(dirbuf);
325
2.12k
            free(addrbuf);
326
2.12k
            return TSK_COR;
327
2.12k
    }
328
329
    /* We did not copy the entire directory, which occurs if an error occurred */
330
462
    if (load.dirleft > 0) {
331
0
        tsk_error_reset();
332
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
333
0
        tsk_error_set_errstr
334
0
            ("%s: Error reading directory %" PRIuINUM,
335
0
            func_name, a_addr);
336
337
        /* Free the local buffers */
338
0
        free(dirbuf);
339
0
        free(addrbuf);
340
0
        return TSK_COR;
341
0
    }
342
343
462
    if (tsk_verbose)
344
0
        fprintf(stderr,
345
0
        "%s: Parsing directory %" PRIuINUM "\n",
346
0
        func_name, a_addr);
347
348
462
    retval = fatfs->dent_parse_buf(fatfs, fs_dir, dirbuf, len, addrbuf, recursion_depth);
349
350
462
    free(dirbuf);
351
462
    free(addrbuf);
352
353
    // if we are listing the root directory, add the Orphan directory and special FAT file entries
354
462
    if (a_addr == a_fs->root_inum) {
355
102
        TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0);
356
102
        if (fs_name == NULL)
357
0
            return TSK_ERR;
358
359
        // MBR Entry
360
102
        strncpy(fs_name->name, FATFS_MBRNAME, fs_name->name_size);
361
102
        fs_name->meta_addr = fatfs->mbr_virt_inum;
362
102
        fs_name->type = TSK_FS_NAME_TYPE_VIRT;
363
102
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
364
102
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
365
0
            tsk_fs_name_free(fs_name);
366
0
            return TSK_ERR;
367
0
        }
368
369
        // FAT1 Entry
370
102
        strncpy(fs_name->name, FATFS_FAT1NAME, fs_name->name_size);
371
102
        fs_name->meta_addr = fatfs->fat1_virt_inum;
372
102
        fs_name->type = TSK_FS_NAME_TYPE_VIRT;
373
102
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
374
102
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
375
0
            tsk_fs_name_free(fs_name);
376
0
            return TSK_ERR;
377
0
        }
378
379
        // FAT2 Entry
380
102
        if (fatfs->numfat == 2) {
381
13
            strncpy(fs_name->name, FATFS_FAT2NAME, fs_name->name_size);
382
13
            fs_name->meta_addr = fatfs->fat2_virt_inum;
383
13
            fs_name->type = TSK_FS_NAME_TYPE_VIRT;
384
13
            fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
385
13
            if (tsk_fs_dir_add(fs_dir, fs_name)) {
386
0
                tsk_fs_name_free(fs_name);
387
0
                return TSK_ERR;
388
0
            }
389
13
        }
390
391
        // orphan directory
392
102
        if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) {
393
0
            tsk_fs_name_free(fs_name);
394
0
            return TSK_ERR;
395
0
        }
396
102
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
397
0
            tsk_fs_name_free(fs_name);
398
0
            return TSK_ERR;
399
0
        }
400
102
        tsk_fs_name_free(fs_name);
401
102
    }
402
403
462
    return retval;
404
462
}
405
406
int
407
fatfs_name_cmp(TSK_FS_INFO * /*a_fs_info*/, const char *s1, const char *s2)
408
0
{
409
0
    return strcasecmp(s1, s2);
410
0
}