Coverage Report

Created: 2025-07-01 06:58

/src/sleuthkit/tsk/fs/exfatfs.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
** The Sleuth Kit
3
**
4
** Copyright (c) 2013 Basis Technology Corp.  All rights reserved
5
** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
6
**
7
** This software is distributed under the Common Public License 1.0
8
**
9
*/
10
11
/*
12
 * This code makes use of research presented in the following paper:
13
 * "Reverse Engineering the exFAT File System" by Robert Shullich
14
 * Retrieved May 2013 from:
15
 * http://www.sans.org/reading_room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system_33274
16
 *
17
 * Some additional details concerning TexFAT were obtained in May 2013
18
 * from:
19
 * http://msdn.microsoft.com/en-us/library/ee490643(v=winembedded.60).aspx
20
 */
21
22
/**
23
 * \file exfatfs.c
24
 * Contains the internal TSK exFAT file system code to "open" an exFAT file
25
 * system found in a device image and do the equivalent of a UNIX "stat" call
26
 * on the file system.
27
 */
28
29
#include "tsk_exfatfs.h"
30
#include "tsk_fs_i.h"
31
#include "tsk_fatfs.h"
32
#include <assert.h>
33
34
#include <memory>
35
36
/**
37
 * \internal
38
 * Parses the MBR of an exFAT file system to obtain file system size
39
 * information - bytes per sector, sectors per cluster, and sectors per FAT -
40
 * to add to a FATFS_INFO object.
41
 *
42
 * @param [in, out] a_fatfs Generic FAT file system info structure.
43
 * @return 0 on success, 1 otherwise, per TSK convention.
44
 */
45
static uint8_t
46
exfatfs_get_fs_size_params(FATFS_INFO *a_fatfs)
47
0
{
48
0
    const char *func_name = "exfatfs_get_fs_size_params";
49
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
50
0
    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
51
52
0
    assert(a_fatfs != NULL);
53
54
0
    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
55
56
    /* Get bytes per sector.
57
     * Bytes per sector is a base 2 logarithm, defining a range of sizes with
58
     * a min of 512 bytes and a max of 4096 bytes. */
59
0
    a_fatfs->ssize_sh = (uint16_t)exfatbs->bytes_per_sector;
60
0
    if ((a_fatfs->ssize_sh < 9) || (a_fatfs->ssize_sh > 12))
61
0
    {
62
0
        tsk_error_reset();
63
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
64
0
        tsk_error_set_errstr("Not an exFAT file system (invalid sector size)");
65
0
        if (tsk_verbose) {
66
0
            fprintf(stderr, "%s: Invalid sector size base 2 logarithm (%d), not in range (9 - 12)\n", func_name, a_fatfs->ssize);
67
0
        }
68
0
        return FATFS_FAIL;
69
0
    }
70
0
    a_fatfs->ssize = (1 << a_fatfs->ssize_sh);
71
72
    /* Get sectors per cluster.
73
     * Sectors per cluster is a base 2 logarithm. The max cluster size is
74
     * 32 MiB, so the sum of the bytes per sector and sectors per cluster
75
     * logs cannot exceed 25. */
76
0
    if ((a_fatfs->ssize_sh + exfatbs->sectors_per_cluster) > 25) {
77
0
        tsk_error_reset();
78
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
79
0
        tsk_error_set_errstr("Not an exFAT file system (invalid cluster size)");
80
0
        if (tsk_verbose) {
81
0
            fprintf(stderr, "%s: Invalid cluster size (%d)\n", func_name, exfatbs->sectors_per_cluster);
82
0
        }
83
0
        return FATFS_FAIL;
84
0
    }
85
0
    a_fatfs->csize = (1 << exfatbs->sectors_per_cluster);
86
87
    /* Get sectors per FAT.
88
     * It will at least be non-zero. */
89
0
    a_fatfs->sectperfat = tsk_getu32(fs->endian, exfatbs->fat_len_in_sectors);
90
0
    if (a_fatfs->sectperfat == 0) {
91
0
        tsk_error_reset();
92
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
93
0
        tsk_error_set_errstr("Not an exFAT file system (invalid sectors per FAT)");
94
0
        if (tsk_verbose) {
95
0
            fprintf(stderr, "%s: Invalid number of sectors per FAT (%d)\n", func_name, a_fatfs->sectperfat);
96
0
        }
97
0
        return FATFS_FAIL;
98
0
    }
99
100
0
    return FATFS_OK;
101
0
}
102
103
/**
104
 * \internal
105
 * Parses the MBR of an exFAT file system to obtain file system layout
106
 * information to add to a FATFS_INFO object.
107
 *
108
 * @param [in, out] a_fatfs Generic FAT file system info structure.
109
 * @return 0 on success, 1 otherwise, per TSK convention.
110
 */
111
static uint8_t
112
exfatfs_get_fs_layout(FATFS_INFO *a_fatfs)
113
0
{
114
0
    const char *func_name = "exfatfs_get_fs_layout";
115
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
116
0
    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
117
0
    uint64_t vol_len_in_sectors = 0;
118
0
    uint64_t last_sector_of_cluster_heap = 0;
119
120
0
    assert(a_fatfs != NULL);
121
122
    /* Get the size of the volume. It should be non-zero. */
123
0
    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
124
0
    vol_len_in_sectors = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors);
125
0
    if (vol_len_in_sectors == 0) {
126
0
        tsk_error_reset();
127
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
128
0
        tsk_error_set_errstr("Not an exFAT file system (invalid volume length)");
129
0
        if (tsk_verbose) {
130
0
            fprintf(stderr, "%s: Invalid volume length in sectors (%" PRIu64 ")\n", func_name, vol_len_in_sectors);
131
0
        }
132
0
        return FATFS_FAIL;
133
0
    }
134
135
    /* Get the number of FATs. There will be one FAT for regular exFAT and two
136
     * FATs for TexFAT (transactional exFAT). */
137
0
    a_fatfs->numfat = exfatbs->num_fats;
138
0
    if ((a_fatfs->numfat != 1) && (a_fatfs->numfat != 2)) {
139
0
        tsk_error_reset();
140
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
141
0
        tsk_error_set_errstr("Not an exFAT file system (number of FATs)");
142
0
        if (tsk_verbose) {
143
0
            fprintf(stderr, "%s: Invalid number of FATs (%d)\n", func_name, a_fatfs->numfat);
144
0
        }
145
0
        return FATFS_FAIL;
146
0
    }
147
148
    /* Get the sector address of the first FAT (FAT0).
149
     * It should be non-zero and within the boundaries of the volume.
150
     * Note that if the file system is TexFAT, FAT1 will be the working copy
151
     * of the FAT and FAT0 will be the stable copy of the last known good FAT.
152
     * Therefore, the Sleuthkit should use FAT0. */
153
0
    a_fatfs->firstfatsect = tsk_getu32(fs->endian, exfatbs->fat_offset);
154
0
    if ((a_fatfs->firstfatsect == 0) || ((uint64_t)a_fatfs->firstfatsect >= vol_len_in_sectors)) {
155
0
        tsk_error_reset();
156
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
157
0
        tsk_error_set_errstr("Not an exFAT file system (invalid first FAT sector)");
158
0
        if (tsk_verbose) {
159
0
            fprintf(stderr, "%s: Invalid first FAT sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstfatsect);
160
0
        }
161
0
        return FATFS_FAIL;
162
0
    }
163
164
    /* Get the sector address of the cluster heap (data area). It should be
165
     * after the FATs and within the boundaries of the volume. */
166
0
    a_fatfs->firstdatasect = tsk_getu32(fs->endian, exfatbs->cluster_heap_offset);
167
0
    if ((a_fatfs->firstdatasect <= (a_fatfs->firstfatsect + (a_fatfs->sectperfat * a_fatfs->numfat) - 1)) ||
168
0
        ((uint64_t)a_fatfs->firstdatasect >= vol_len_in_sectors)) {
169
0
        tsk_error_reset();
170
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
171
0
        tsk_error_set_errstr("Not an exFAT file system (invalid first data sector");
172
0
        if (tsk_verbose) {
173
0
            fprintf(stderr, "%s: Invalid first data sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstdatasect);
174
0
        }
175
0
        return FATFS_FAIL;
176
0
    }
177
178
    /* Unlike FAT12 and FAT16, but like FAT32, the sector address of the first
179
     * cluster (cluster #2, there is no cluster #0 or cluster #1) is the same
180
     * as the sector address of the cluster heap (data area). */
181
0
    a_fatfs->firstclustsect = a_fatfs->firstdatasect;
182
183
    /* Get the total number of clusters. It should be non-zero, and should
184
     * define a cluster heap (data area) that is within the boundaries of the
185
     * volume. */
186
0
    a_fatfs->clustcnt = tsk_getu32(fs->endian, exfatbs->cluster_cnt);
187
0
    last_sector_of_cluster_heap = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
188
0
    if ((a_fatfs->clustcnt == 0) ||
189
0
        (last_sector_of_cluster_heap >= vol_len_in_sectors)) {
190
0
        tsk_error_reset();
191
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
192
0
        tsk_error_set_errstr("Not an exFAT file system (invalid cluster count)");
193
0
        if (tsk_verbose) {
194
0
            fprintf(stderr, "%s: Invalid cluster count (%" PRIuDADDR ")\n", func_name, a_fatfs->clustcnt);
195
0
        }
196
0
        return FATFS_FAIL;
197
0
    }
198
199
    /* The first cluster is #2, so the final cluster is: */
200
0
     a_fatfs->lastclust = 1 + a_fatfs->clustcnt;
201
202
     /* This bit mask is required to make the FATFS_CLUST_2_SECT macro work
203
      * for exFAT. It is the same as the FAT32 mask. */
204
0
     a_fatfs->mask = EXFATFS_MASK;
205
206
    /* Get the sector address of the root directory. It should be within the
207
     * cluster heap (data area). */
208
0
    a_fatfs->rootsect = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, exfatbs->root_dir_cluster));
209
0
    if ((a_fatfs->rootsect < a_fatfs->firstdatasect) ||
210
0
        ((uint64_t)a_fatfs->rootsect > last_sector_of_cluster_heap)) {
211
0
        tsk_error_reset();
212
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
213
0
        tsk_error_set_errstr("Not an exFAT file system (invalid root directory sector address)");
214
0
        if (tsk_verbose) {
215
0
            fprintf(stderr, "%s: Invalid root directory sector address (%" PRIuDADDR ")\n", func_name, a_fatfs->rootsect);
216
0
        }
217
0
        return FATFS_FAIL;
218
0
    }
219
220
    /* The number of directory entries in the root directory is not specified
221
     * in the exFAT boot sector. */
222
0
    a_fatfs->numroot = 0;
223
224
0
    return FATFS_OK;
225
0
}
226
227
/**
228
 * \internal
229
 * Searches the root directory of an exFAT file system for an allocation bitmap
230
 * directory entry. If the entry is found, data from the entry is saved to a
231
 * FATFS_INFO object.
232
 *
233
 * @param [in, out] a_fatfs Generic FAT file system info structure.
234
 * @return 0 on success, 1 otherwise, per TSK convention.
235
 */
236
static uint8_t
237
exfatfs_get_alloc_bitmap(FATFS_INFO *a_fatfs)
238
0
{
239
0
    const char *func_name = "exfatfs_get_alloc_bitmap";
240
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
241
0
    TSK_DADDR_T current_sector = 0;
242
0
    TSK_DADDR_T last_sector_of_data_area = 0;
243
0
    char *sector_buf = NULL;
244
0
    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
245
0
    uint64_t i = 0;
246
0
    uint64_t first_sector_of_alloc_bitmap = 0;
247
0
    uint64_t alloc_bitmap_length_in_bytes = 0;
248
0
    uint64_t last_sector_of_alloc_bitmap = 0;
249
250
0
    assert(a_fatfs != NULL);
251
252
0
    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
253
0
        return FATFS_FAIL;
254
0
    }
255
256
0
    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
257
0
    for (current_sector = a_fatfs->rootsect; current_sector < last_sector_of_data_area; current_sector++) {
258
0
        ssize_t bytes_read = 0;
259
260
        /* Read in a sector from the root directory. The allocation bitmap
261
         * directory entries will probably be near the beginning of the
262
         * directory, probably in the first sector. */
263
0
        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
264
0
        if (bytes_read != a_fatfs->ssize) {
265
0
            if (bytes_read >= 0) {
266
0
                tsk_error_reset();
267
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
268
0
            }
269
0
            tsk_error_set_errstr2("%s: sector: %" PRIuDADDR, func_name, current_sector);
270
0
            free(sector_buf);
271
0
            return FATFS_FAIL;
272
0
        }
273
274
        /* Read the directory entries in the sector, looking for allocation
275
         * bitmap entries. There will be one entry unless the file system is
276
         * TexFAT (transactional exFAT), in which case there will be two. */
277
0
        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
278
0
            dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)&(sector_buf[i]);
279
280
            /* The type of the directory entry is encoded in the first byte
281
             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */
282
0
            if (exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
283
                /* Do an in-depth test. */
284
0
                if (!exfatfs_is_alloc_bitmap_dentry((FATFS_DENTRY*)dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN, a_fatfs)) {
285
0
                    continue;
286
0
                }
287
288
                /* The first bit of the flags byte is 0 for the first
289
                 * allocation bitmap directory entry and 1 for the second
290
                 * bitmap directory entry. If TexFAT is in use and there are
291
                 * two allocation bitmaps, the first bitmap should be the
292
                 * stable copy of the last known good allocation bitmap.
293
                 * Therefore, the SleuthKit will use the first bitmap to
294
                 * determine which clusters are allocated. */
295
0
                if (!(dentry->flags & 0x01)) {
296
0
                    first_sector_of_alloc_bitmap = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, dentry->first_cluster_of_bitmap));
297
0
                    alloc_bitmap_length_in_bytes = tsk_getu64(fs->endian, dentry->length_of_alloc_bitmap_in_bytes);
298
0
                    last_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap + (roundup(alloc_bitmap_length_in_bytes, a_fatfs->ssize) / a_fatfs->ssize) - 1;
299
300
                    /* The allocation bitmap must lie within the boundaries of the data area.
301
                     * It also must be big enough for the number of clusters reported in the VBR. */
302
0
                    if ((first_sector_of_alloc_bitmap >= a_fatfs->firstdatasect) &&
303
0
                        (last_sector_of_alloc_bitmap <= last_sector_of_data_area) &&
304
0
                        (alloc_bitmap_length_in_bytes >= (a_fatfs->clustcnt + 7) / 8))
305
0
                    {
306
0
                        a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap;
307
0
                        a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes = alloc_bitmap_length_in_bytes;
308
0
                        free(sector_buf);
309
0
                        return FATFS_OK;
310
0
                    }
311
0
                }
312
0
            }
313
0
        }
314
0
    }
315
0
    free(sector_buf);
316
317
0
    return FATFS_FAIL;
318
0
}
319
320
/**
321
 * \internal
322
 * Parses the MBR of an exFAT file system to obtain a volume serial number to
323
 * add to a FATFS_INFO object.
324
 *
325
 * @param [in, out] a_fatfs Generic FAT file system info structure.
326
 */
327
static void
328
exfatfs_get_volume_id(FATFS_INFO *a_fatfs)
329
0
{
330
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
331
0
    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
332
333
0
    assert(a_fatfs != NULL);
334
335
0
    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
336
0
    for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) {
337
0
        fs->fs_id[fs->fs_id_used] = exfatbs->vol_serial_no[fs->fs_id_used];
338
0
    }
339
0
}
340
341
/**
342
 * \internal
343
 * Sets the file system layout members of a FATFS_INFO object for an exFAT file
344
 * system. Note that there are no "block" or "inode" concepts in exFAT. So, to
345
 * conform to the SleuthKit generic file system model, sectors are treated as
346
 * blocks, directory entries are treated as inodes, and inode addresses (inode
347
 * numbers) are assigned to every directory-entry-sized chunk of the file
348
 * system. This is the same mapping previously established for TSK treatment of
349
 * the other FAT file systems. As with those sister file systems, any given
350
 * inode address may or may not point to a conceptual exFAT inode.
351
 *
352
 * @param [in, out] a_fatfs Generic FAT file system info structure.
353
 */
354
static void
355
exfatfs_setup_fs_layout_model(FATFS_INFO *a_fatfs)
356
0
{
357
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
358
0
    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
359
360
0
    assert(a_fatfs != NULL);
361
362
0
    fs->duname = "Sector";
363
364
0
    fs->block_size = a_fatfs->ssize;
365
366
0
    exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer);
367
0
    fs->block_count = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors);
368
369
0
    fs->first_block = 0;
370
0
    fs->last_block = fs->last_block_act = fs->block_count - 1;
371
372
    /* Determine the last block actually included in the image, since the
373
     * end of the file system could be "cut off." */
374
0
    if ((TSK_DADDR_T) ((fs->img_info->size - fs->offset) / fs->block_size) <
375
0
        fs->block_count) {
376
0
        fs->last_block_act = (fs->img_info->size - fs->offset) / fs->block_size - 1;
377
0
    }
378
379
    /* Calculate the maximum number of directory entries that will fit in a
380
     * sector and a cluster. */
381
0
    a_fatfs->dentry_cnt_se = a_fatfs->ssize / sizeof(FATFS_DENTRY);
382
0
    a_fatfs->dentry_cnt_cl = a_fatfs->dentry_cnt_se * a_fatfs->csize;
383
384
    /* The first entry in an exFAT FAT is a media type indicator.
385
     * The second entry is simply a meaningless 0xFFFFFFFF.
386
     * The first inode address is therefore 2. */
387
0
    fs->first_inum = FATFS_FIRSTINO;
388
389
0
    fs->root_inum = FATFS_ROOTINO;
390
391
    /* Calculate inode addresses for the virtual files (MBR, one or two FATS)
392
     * and the virtual orphan files directory. */
393
0
    fs->last_inum = (FATFS_SECT_2_INODE(a_fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(a_fatfs);
394
0
    a_fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(a_fatfs) + 1;
395
0
    a_fatfs->fat1_virt_inum = a_fatfs->mbr_virt_inum + 1;
396
0
    if (a_fatfs->numfat == 2) {
397
0
        a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum + 1;
398
0
    }
399
0
    else {
400
0
        a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum;
401
0
    }
402
403
    /* Calculate the total number of inodes. */
404
0
    fs->inum_count = fs->last_inum - fs->first_inum + 1;
405
0
}
406
407
/**
408
 * \internal
409
 * Initializes the data structures used to cache the cluster addresses that
410
 * make up FAT chains in an exFAT file system, and the lock used to make the
411
 * data structures thread-safe.
412
 *
413
 * @param [in, out] a_fatfs Generic FAT file system info structure.
414
 */
415
static void
416
exfatfs_init_fat_cache(FATFS_INFO *a_fatfs)
417
0
{
418
0
    uint32_t i = 0;
419
420
0
    assert(a_fatfs != NULL);
421
422
0
    for (i = 0; i < FATFS_FAT_CACHE_N; i++) {
423
0
        a_fatfs->fatc_addr[i] = 0;
424
0
        a_fatfs->fatc_ttl[i] = 0;
425
0
    }
426
427
0
    tsk_init_lock(&a_fatfs->cache_lock);
428
0
    tsk_init_lock(&a_fatfs->dir_lock);
429
0
    a_fatfs->inum2par = NULL;
430
0
}
431
432
/**
433
 * \internal
434
 * Initializes the data structure used to map inode addresses to parent inode
435
 * addresses in an exFAT file system, and the lock used to make the data
436
 * structure thread-safe.
437
 *
438
 * @param [in, out] a_fatfs Generic FAT file system info structure.
439
 */
440
static void
441
exfatfs_init_inums_map(FATFS_INFO *a_fatfs)
442
0
{
443
0
    assert(a_fatfs != NULL);
444
445
0
    tsk_init_lock(&a_fatfs->dir_lock);
446
0
    a_fatfs->inum2par = NULL;
447
0
}
448
449
/**
450
 * \internal
451
 *
452
 * Sets the function pointers in a FATFS_INFO object for an exFAT file system
453
 * to point to either generic FAT file system functions or to exFAT file system
454
 * functions.
455
 *
456
 * @param [in, out] a_fatfs Generic FAT file system info structure.
457
 */
458
static void
459
exfatfs_set_func_ptrs(FATFS_INFO *a_fatfs)
460
0
{
461
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
462
463
0
    assert(a_fatfs != NULL);
464
465
0
    fs->close = fatfs_close;
466
467
    /* File system category functions. */
468
0
    fs->fsstat = exfatfs_fsstat;
469
0
    fs->fscheck = fatfs_fscheck;
470
471
    /* Content category functions. */
472
0
    fs->block_walk = fatfs_block_walk;
473
0
    fs->block_getflags = fatfs_block_getflags;
474
475
    /* Meta data category functions. */
476
0
    fs->inode_walk = fatfs_inode_walk;
477
0
    fs->istat = fatfs_istat;
478
0
    fs->file_add_meta = fatfs_inode_lookup;
479
0
    fs->get_default_attr_type = fatfs_get_default_attr_type;
480
0
    fs->load_attrs = fatfs_make_data_runs;
481
482
    /* Name category functions. */
483
0
    fs->dir_open_meta = fatfs_dir_open_meta;
484
0
    fs->name_cmp = fatfs_name_cmp;
485
486
    /* NOP journal functions - exFAT has no file system journal. */
487
0
    fs->jblk_walk = fatfs_jblk_walk;
488
0
    fs->jentry_walk = fatfs_jentry_walk;
489
0
    fs->jopen = fatfs_jopen;
490
491
    /* Specialization for exFAT functions. */
492
0
    a_fatfs->is_cluster_alloc = exfatfs_is_cluster_alloc;
493
0
    a_fatfs->is_dentry = exfatfs_is_dentry;
494
0
    a_fatfs->dinode_copy =  exfatfs_dinode_copy;
495
0
    a_fatfs->inode_lookup = exfatfs_inode_lookup;
496
0
    a_fatfs->inode_walk_should_skip_dentry = exfatfs_inode_walk_should_skip_dentry;
497
0
    a_fatfs->istat_attr_flags = exfatfs_istat_attr_flags;
498
0
    a_fatfs->dent_parse_buf = exfatfs_dent_parse_buf;
499
0
}
500
501
/**
502
 * Open an exFAT file system in an image file.
503
 *
504
 * @param [in, out] a_fatfs Generic FAT file system info structure.
505
 * @return 0 on success, 1 otherwise, per TSK convention.
506
 */
507
uint8_t
508
exfatfs_open(FATFS_INFO *a_fatfs)
509
0
{
510
0
    const char *func_name = "exfatfs_open";
511
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
512
513
0
    assert(a_fatfs != NULL);
514
515
0
    tsk_error_reset();
516
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
517
0
        return FATFS_FAIL;
518
0
    }
519
520
0
    if (exfatfs_get_fs_size_params(a_fatfs) == FATFS_FAIL ||
521
0
        exfatfs_get_fs_layout(a_fatfs) == FATFS_FAIL) {
522
0
        return FATFS_FAIL;
523
0
    }
524
525
0
    if (exfatfs_get_fs_layout(a_fatfs) == FATFS_OK) {
526
0
        exfatfs_setup_fs_layout_model(a_fatfs);
527
0
    }
528
0
    else {
529
0
        return FATFS_FAIL;
530
0
    }
531
532
0
    if (exfatfs_get_alloc_bitmap(a_fatfs) == FATFS_FAIL) {
533
0
        return FATFS_FAIL;
534
0
    }
535
536
0
    exfatfs_get_volume_id(a_fatfs);
537
0
    exfatfs_init_inums_map(a_fatfs);
538
0
    exfatfs_init_fat_cache(a_fatfs);
539
0
    exfatfs_set_func_ptrs(a_fatfs);
540
541
0
    fs->ftype = TSK_FS_TYPE_EXFAT;
542
543
0
    return FATFS_OK;
544
0
}
545
546
/**
547
 * \internal
548
 * Searches an exFAT file system for its volume label directory entry, which
549
 * should be in the root directory of the file system. If the entry is found,
550
 * its metadata is copied into the TSK_FS_META object of a TSK_FS_FILE object.
551
 *
552
 * @param [in] a_fatfs Generic FAT file system info structure.
553
 * @param [out] a_fatfs Generic file system file structure.
554
 * @return 0 on success, 1 otherwise, per TSK convention.
555
 */
556
static uint8_t
557
exfatfs_find_volume_label_dentry(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file)
558
0
{
559
0
    const char *func_name = "exfatfs_find_volume_label_dentry";
560
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *)a_fatfs;
561
0
    TSK_DADDR_T current_sector = 0;
562
0
    TSK_DADDR_T last_sector_of_data_area = 0;
563
0
    char *sector_buf = NULL;
564
0
    ssize_t bytes_read = 0;
565
0
    TSK_INUM_T current_inum = 0;
566
0
    FATFS_DENTRY *dentry = NULL;
567
0
    uint64_t i = 0;
568
569
0
    assert(a_fatfs != NULL);
570
0
    assert(a_fs_file != NULL);
571
572
0
    tsk_error_reset();
573
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
574
0
        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) {
575
0
        return FATFS_FAIL;
576
0
    }
577
578
    /* Allocate or reset the TSK_FS_META object. */
579
0
    if (a_fs_file->meta == NULL) {
580
0
        if ((a_fs_file->meta =
581
0
                tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
582
0
            return FATFS_FAIL;
583
0
        }
584
0
    }
585
0
    else {
586
0
        tsk_fs_meta_reset(a_fs_file->meta);
587
0
    }
588
589
    /* Allocate a buffer for reading in sector-size chunks of the image. */
590
0
    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
591
0
        return FATFS_FAIL;
592
0
    }
593
594
0
    current_sector = a_fatfs->rootsect;
595
0
    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
596
0
    while (current_sector < last_sector_of_data_area) {
597
0
        int8_t sector_is_alloc = 0;
598
599
        /* Read in a sector from the root directory. The volume label
600
         * directory entry will probably be near the beginning of the
601
         * directory, probably in the first sector. */
602
0
        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
603
0
        if (bytes_read != a_fatfs->ssize) {
604
0
            if (bytes_read >= 0) {
605
0
                tsk_error_reset();
606
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
607
0
            }
608
0
            tsk_error_set_errstr2("%s: error reading sector: %" PRIuDADDR, func_name, current_sector);
609
0
            free(sector_buf);
610
0
            return FATFS_FAIL;
611
0
        }
612
613
        /* Get the allocation status of the sector (yes, it should be allocated). */
614
0
        sector_is_alloc = fatfs_is_sectalloc(a_fatfs, current_sector);
615
0
        if (sector_is_alloc == -1) {
616
0
            return FATFS_FAIL;
617
0
        }
618
619
        /* Get the inode address of the first directory entry of the sector. */
620
0
        current_inum = FATFS_SECT_2_INODE(a_fatfs, current_sector);
621
622
        /* Loop through the putative directory entries in the sector,
623
         * until the volume label entry is found.  */
624
0
        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
625
0
            dentry = (FATFS_DENTRY*)&(sector_buf[i]);
626
627
            /* The type of the directory entry is encoded in the first byte
628
             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */
629
0
            if (exfatfs_get_enum_from_type(dentry->data[0]) == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) {
630
0
                if (!exfatfs_is_vol_label_dentry(dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN)) {
631
0
                    continue;
632
0
                }
633
634
                /* Found it, save it to the TSK_FS_META object of the
635
                 * TSK_FS_FILE object and exit. */
636
0
                if (exfatfs_dinode_copy(a_fatfs, current_inum, dentry,
637
0
                    sector_is_alloc, a_fs_file) == TSK_OK) {
638
0
                        return FATFS_OK;
639
0
                }
640
0
                else {
641
0
                    return FATFS_FAIL;
642
0
                }
643
0
            }
644
0
        }
645
0
    }
646
647
0
    free(sector_buf);
648
0
    return FATFS_OK;
649
0
}
650
651
/**
652
 * \internal
653
 * Prints file system category data for an exFAT file system to a file
654
 * handle.
655
 *
656
 * @param [in] a_fs Generic file system info structure for the file system.
657
 * @param [in] a_hFile The file handle.
658
 * @return 0 on success, 1 otherwise, per TSK convention.
659
 */
660
static uint8_t
661
exfatfs_fsstat_fs_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
662
0
{
663
0
    FATFS_INFO *fatfs = NULL;
664
0
    EXFATFS_MASTER_BOOT_REC *exfatbs = NULL;
665
666
0
    assert(a_fs != NULL);
667
0
    assert(a_hFile != NULL);
668
669
0
    fatfs = (FATFS_INFO*)a_fs;
670
0
    exfatbs = (EXFATFS_MASTER_BOOT_REC*)&(fatfs->boot_sector_buffer);
671
672
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
673
0
        tsk_fs_file_alloc(a_fs),
674
0
        tsk_fs_file_close
675
0
    };
676
677
0
    if (!fs_file) {
678
0
        return FATFS_FAIL;
679
0
    }
680
681
0
    if ((fs_file->meta = tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
682
0
        return FATFS_FAIL;
683
0
    }
684
685
0
    tsk_fprintf(a_hFile, "FILE SYSTEM INFORMATION\n");
686
0
    tsk_fprintf(a_hFile, "--------------------------------------------\n");
687
688
0
    tsk_fprintf(a_hFile, "File System Type: exFAT\n");
689
690
0
    tsk_fprintf(a_hFile, "\nVolume Serial Number: %x%x-%x%x\n",
691
0
        exfatbs->vol_serial_no[3], exfatbs->vol_serial_no[2],
692
0
        exfatbs->vol_serial_no[1], exfatbs->vol_serial_no[0]);
693
694
0
    if (exfatfs_find_volume_label_dentry(fatfs, fs_file.get()) == 0) {
695
0
        tsk_fprintf(a_hFile, "Volume Label (from root directory): %s\n", fs_file->meta->name2->name);
696
0
    }
697
0
    else {
698
0
        tsk_fprintf(a_hFile, "Volume Label:\n");
699
0
    }
700
701
0
    tsk_fprintf(a_hFile, "File System Name (from MBR): %s\n", exfatbs->fs_name);
702
703
0
    tsk_fprintf(a_hFile, "File System Revision: %x.%x\n",
704
0
        exfatbs->fs_revision[1], exfatbs->fs_revision[0]);
705
706
0
    tsk_fprintf(a_hFile, "Partition Offset: %" PRIuDADDR "\n",
707
0
        tsk_getu64(a_fs->endian, exfatbs->partition_offset));
708
709
0
    tsk_fprintf(a_hFile, "Number of FATs: %d\n", fatfs->numfat);
710
711
0
    return FATFS_OK;
712
0
}
713
714
/**
715
 * \internal
716
 * Prints file system layout data for an exFAT file system to a file
717
 * handle.
718
 *
719
 * @param [in] a_fs Generic file system info structure for the file system.
720
 * @param [in] a_hFile The file handle.
721
 * @return 0 on success, 1 otherwise, per TSK convention.
722
 */
723
static uint8_t
724
exfatfs_fsstat_fs_layout_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
725
0
{
726
0
    const char *func_name = "exfatfs_fsstat_fs_layout_info";
727
0
    FATFS_INFO *fatfs = NULL;
728
0
    uint64_t i = 0;
729
0
    TSK_DADDR_T fat_base_sect = 0;
730
0
    TSK_DADDR_T clust_heap_len = 0;
731
0
    TSK_LIST *root_dir_clusters_seen = NULL;
732
0
    TSK_DADDR_T current_cluster;
733
0
    TSK_DADDR_T next_cluster = 0;
734
735
0
    assert(a_fs != NULL);
736
0
    assert(a_hFile != NULL);
737
738
0
    fatfs = (FATFS_INFO*)a_fs;
739
740
0
    tsk_fprintf(a_hFile, "\nFile System Layout (in sectors):\n");
741
742
0
    tsk_fprintf(a_hFile, "Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
743
0
        a_fs->first_block, a_fs->last_block);
744
745
0
    if (a_fs->last_block != a_fs->last_block_act)
746
0
        tsk_fprintf(a_hFile,
747
0
            "Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
748
0
            a_fs->first_block, a_fs->last_block_act);
749
750
0
    tsk_fprintf(a_hFile, "* Reserved: 0 - %" PRIuDADDR "\n",
751
0
        fatfs->firstfatsect - 1);
752
753
0
    tsk_fprintf(a_hFile, "** Volume Boot Record (VBR): 0 - 11\n");
754
755
0
    tsk_fprintf(a_hFile, "*** Boot Sector (MBR): 0\n");
756
757
0
    tsk_fprintf(a_hFile, "** Backup Volume Boot Record (VBR): 12 - 23\n");
758
759
0
    tsk_fprintf(a_hFile, "*** Backup Boot Sector (MBR): 12\n");
760
761
0
    tsk_fprintf(a_hFile, "** FAT alignment space: 24 - %" PRIuDADDR "\n",
762
0
        fatfs->firstfatsect - 1);
763
764
0
    for (i = 0; i < fatfs->numfat; i++) {
765
0
        fat_base_sect = fatfs->firstfatsect + i * (fatfs->sectperfat);
766
0
        tsk_fprintf(a_hFile, "* FAT %" PRIuDADDR ": %" PRIuDADDR " - %" PRIuDADDR "\n",
767
0
            i + 1, fat_base_sect, (fat_base_sect + fatfs->sectperfat - 1));
768
0
    }
769
770
0
    if (fat_base_sect + fatfs->sectperfat < fatfs->firstdatasect) {
771
0
        tsk_fprintf(a_hFile, "* Data Area alignment space: %" PRIuDADDR " - %" PRIuDADDR "\n",
772
0
            fat_base_sect + fatfs->sectperfat, fatfs->firstdatasect - 1);
773
0
    }
774
775
0
    tsk_fprintf(a_hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
776
0
        fatfs->firstdatasect, a_fs->last_block);
777
778
0
    clust_heap_len = fatfs->csize * (fatfs->lastclust - 1);
779
0
    tsk_fprintf(a_hFile,
780
0
        "** Cluster Heap: %" PRIuDADDR " - %" PRIuDADDR "\n",
781
0
        fatfs->firstclustsect, (fatfs->firstclustsect + clust_heap_len - 1));
782
783
    /* Walk the FAT chain for the root directory. */
784
0
    current_cluster = fatfs->rootsect;
785
0
    next_cluster = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect);
786
0
    while ((next_cluster) && (0 == FATFS_ISEOF(next_cluster, FATFS_32_MASK))) {
787
0
        TSK_DADDR_T nxt;
788
0
        current_cluster = next_cluster;
789
790
        /* Make sure we do not get into an infinite loop */
791
0
        if (tsk_list_find(root_dir_clusters_seen, next_cluster)) {
792
0
            if (tsk_verbose) {
793
0
                tsk_fprintf(stderr,
794
0
                    "%s : Loop found while determining root directory size\n",
795
0
                    func_name);
796
0
            }
797
0
            break;
798
0
        }
799
800
0
        if (tsk_list_add(&root_dir_clusters_seen, next_cluster)) {
801
0
            tsk_list_free(root_dir_clusters_seen);
802
0
            root_dir_clusters_seen = NULL;
803
0
            return FATFS_FAIL;
804
0
        }
805
806
0
        if (fatfs_getFAT(fatfs, next_cluster, &nxt)) {
807
0
            break;
808
0
        }
809
810
0
        next_cluster = nxt;
811
0
    }
812
0
    tsk_list_free(root_dir_clusters_seen);
813
0
    root_dir_clusters_seen = NULL;
814
815
0
    tsk_fprintf(a_hFile,
816
0
        "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
817
0
        fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, current_cluster + 1) - 1));
818
819
0
    if ((fatfs->firstclustsect + clust_heap_len - 1) != a_fs->last_block) {
820
0
        tsk_fprintf(a_hFile,
821
0
            "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
822
0
            (fatfs->firstclustsect + clust_heap_len), a_fs->last_block);
823
0
    }
824
825
0
    return FATFS_OK;
826
0
}
827
828
/**
829
 * \internal
830
 * Prints metadata category data for an exFAT file system to a file
831
 * handle.
832
 *
833
 * @param [in] a_fs Generic file system info structure for the file system.
834
 * @param [in] a_hFile The file handle.
835
 */
836
static void
837
exfatfs_fsstat_fs_metadata_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
838
0
{
839
0
    assert(a_fs != NULL);
840
0
    assert(a_hFile != NULL);
841
842
0
    tsk_fprintf(a_hFile, "\nMETADATA INFORMATION\n");
843
0
    tsk_fprintf(a_hFile, "--------------------------------------------\n");
844
845
0
    tsk_fprintf(a_hFile, "Metadata Layout (in virtual inodes):\n");
846
847
0
    tsk_fprintf(a_hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
848
0
        a_fs->first_inum, a_fs->last_inum);
849
850
0
    tsk_fprintf(a_hFile, "* Root Directory: %" PRIuINUM "\n", a_fs->root_inum);
851
0
}
852
853
/**
854
 * \internal
855
 * Prints metadata category data for an exFAT file system to a file
856
 * handle.
857
 *
858
 * @param [in] a_fs Generic file system info structure for the file system.
859
 * @param [in] a_hFile The file handle.
860
 */
861
static void
862
exfatfs_fsstat_fs_content_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
863
0
{
864
0
    FATFS_INFO *fatfs = NULL;
865
0
    uint64_t i = 0;
866
0
    ssize_t bad_sector_cnt = 0;
867
868
0
    assert(a_fs != NULL);
869
0
    assert(a_hFile != NULL);
870
871
0
    fatfs = (FATFS_INFO*)a_fs;
872
873
0
    tsk_fprintf(a_hFile, "\nCONTENT INFORMATION\n");
874
0
    tsk_fprintf(a_hFile, "--------------------------------------------\n");
875
0
    tsk_fprintf(a_hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize);
876
0
    tsk_fprintf(a_hFile, "Cluster Size: %" PRIu32 "\n",
877
0
        (uint32_t) fatfs->csize << fatfs->ssize_sh);
878
879
0
    tsk_fprintf(a_hFile, "Cluster Range: 2 - %" PRIuDADDR "\n",
880
0
        fatfs->lastclust);
881
882
    /* Check each cluster of the data area to see if it is marked as bad in the
883
     * FAT. If the cluster is bad, list the bad sectors. */
884
0
    bad_sector_cnt = 0;
885
0
    for (i = 2; i <= fatfs->lastclust; ++i) {
886
0
        TSK_DADDR_T entry;
887
0
        TSK_DADDR_T sect;
888
889
        /* Get the FAT table entry */
890
0
        if (fatfs_getFAT(fatfs, i, &entry)) {
891
0
            break;
892
0
        }
893
894
0
        if (FATFS_ISBAD(entry, fatfs->mask) == 0) {
895
0
            continue;
896
0
        }
897
898
0
        if (bad_sector_cnt == 0) {
899
0
            tsk_fprintf(a_hFile, "Bad Sectors: ");
900
0
        }
901
902
0
        sect = FATFS_CLUST_2_SECT(fatfs, i);
903
0
        for (i = 0; i < fatfs->csize; ++i) {
904
0
            tsk_fprintf(a_hFile, "%" PRIuDADDR " ", sect + i);
905
0
            if ((++bad_sector_cnt % 8) == 0) {
906
0
                tsk_fprintf(a_hFile, "\n");
907
0
            }
908
0
        }
909
0
    }
910
0
    if ((bad_sector_cnt > 0) && ((bad_sector_cnt % 8) != 0)) {
911
0
        tsk_fprintf(a_hFile, "\n");
912
0
    }
913
0
}
914
915
/**
916
 * \internal
917
 * Prints FAT chains data for an exFAT file system to a file
918
 * handle.
919
 *
920
 * @param [in] a_fs Generic file system info structure for the file system.
921
 * @param [in] a_hFile The file handle.
922
 */
923
#if 0
924
static void
925
exfatfs_fsstat_fs_fat_chains_info(TSK_FS_INFO *a_fs, FILE *a_hFile)
926
{
927
    FATFS_INFO *fatfs = NULL;
928
    uint64_t i = 0;
929
    TSK_DADDR_T sect_run_start = 0;
930
    TSK_DADDR_T sect_run_end = 0;
931
    TSK_DADDR_T next_cluster = 0;
932
    TSK_DADDR_T next_sector = 0;
933
934
    assert(a_fs != NULL);
935
    assert(a_hFile != NULL);
936
937
    fatfs = (FATFS_INFO*)a_fs;
938
939
    tsk_fprintf(a_hFile, "\nFAT CHAINS (in sectors)\n");
940
    tsk_fprintf(a_hFile, "--------------------------------------------\n");
941
942
    /* Check each cluster of the data area to see if it has a FAT chain.
943
     * If so, print out the sectors tha make up the chain. Note that exFAT file
944
     * systems only use FAT chains for the root directory, the allocation
945
     * bitmap, the upcase table, and fragmented files.
946
     */
947
    sect_run_start = fatfs->firstclustsect;
948
    for (i = 2; i <= fatfs->lastclust; i++) {
949
        sect_run_end = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1;
950
951
        if (fatfs_getFAT(fatfs, i, &next_cluster)) {
952
            break;
953
        }
954
955
        next_sector = FATFS_CLUST_2_SECT(fatfs, next_cluster);
956
957
        if ((next_cluster & fatfs->mask) == (i + 1)) {
958
            continue;
959
        }
960
        else if ((next_cluster & fatfs->mask)) {
961
            if (FATFS_ISEOF(next_cluster, fatfs->mask)) {
962
                tsk_fprintf(a_hFile,
963
                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
964
                    ") -> EOF\n", sect_run_start, sect_run_end, sect_run_end - sect_run_start + 1);
965
            }
966
            else if (FATFS_ISBAD(next_cluster, fatfs->mask)) {
967
                tsk_fprintf(a_hFile,
968
                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
969
                    ") -> BAD\n", sect_run_start, sect_run_end, sect_run_end - sect_run_start + 1);
970
            }
971
            else {
972
                tsk_fprintf(a_hFile,
973
                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
974
                    ") -> %" PRIuDADDR "\n", sect_run_start, sect_run_end,
975
                    sect_run_end - sect_run_start + 1, next_sector);
976
            }
977
        }
978
979
        sect_run_start = sect_run_end + 1;
980
    }
981
}
982
#endif
983
984
/**
985
 * \internal
986
 * Print details about an exFAT file system to a file handle.
987
 *
988
 * @param [in] a_fs Generic file system info structure for the file system.
989
 * @param [in] a_hFile The file handle.
990
 * @return 0 on success, 1 otherwise, per TSK convention.
991
 */
992
uint8_t
993
exfatfs_fsstat(TSK_FS_INFO *a_fs, FILE *a_hFile)
994
0
{
995
0
    const char *func_name = "exfatfs_fsstat";
996
997
0
    assert(a_fs != NULL);
998
0
    assert(a_hFile != NULL);
999
1000
0
    tsk_error_reset();
1001
0
    if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) ||
1002
0
        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name)) {
1003
0
        return FATFS_FAIL;
1004
0
    }
1005
1006
0
    if (exfatfs_fsstat_fs_info(a_fs, a_hFile)) {
1007
0
        return FATFS_FAIL;
1008
0
    }
1009
1010
0
    if (exfatfs_fsstat_fs_layout_info(a_fs, a_hFile)) {
1011
0
        return FATFS_FAIL;
1012
0
    }
1013
1014
0
    exfatfs_fsstat_fs_metadata_info(a_fs, a_hFile);
1015
0
    exfatfs_fsstat_fs_content_info(a_fs, a_hFile);
1016
1017
    /* Since exFAT does not store all file data in FAT chains (only fragmented files),
1018
     * printing the chains could give the mistaken impression that those are the only
1019
     * sectors containing file data.
1020
     *
1021
     * exfatfs_fsstat_fs_fat_chains_info(a_fs, a_hFile);
1022
     */
1023
1024
0
    return FATFS_OK;
1025
0
}