Coverage Report

Created: 2025-07-01 06:59

/src/sleuthkit/tsk/fs/exfatfs_meta.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_meta.c
24
 * Contains the internal TSK exFAT file system code to access the data in the
25
 * metadata data category as defined in the book "File System Forensic
26
 * Analysis" by Brian Carrier (pp. 174-175).
27
 */
28
29
#include "tsk_exfatfs.h" /* Included first to make sure it stands alone. */
30
#include "tsk_fs_i.h"
31
#include "tsk_fatfs.h"
32
#include <assert.h>
33
34
/**
35
 * \internal
36
 * Checks whether a specified cluster is allocated according to the allocation
37
 * bitmap of an exFAT file system.
38
 *
39
 * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system.
40
 * @param [in] a_cluster_addr The cluster address of the cluster to check.
41
 * @return 1 if the cluster is allocated, 0 if the cluster is not allocated,
42
 * or -1 if an error occurs.
43
 */
44
int8_t
45
exfatfs_is_cluster_alloc(FATFS_INFO *a_fatfs, TSK_DADDR_T a_cluster_addr)
46
0
{
47
0
    const char *func_name = "exfatfs_is_clust_alloc";
48
0
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
49
0
    TSK_DADDR_T bitmap_byte_offset = 0;
50
0
    uint8_t bitmap_byte;
51
0
    ssize_t bytes_read = 0;
52
53
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) {
54
0
        return -1;
55
0
    }
56
57
0
    if ((a_cluster_addr < FATFS_FIRST_CLUSTER_ADDR) || (a_cluster_addr > a_fatfs->lastclust)) {
58
0
        tsk_error_reset();
59
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
60
0
        tsk_error_set_errstr("%s: cluster address %" PRIuINUM " out of range", func_name, a_cluster_addr);
61
0
        return -1;
62
0
    }
63
64
     /* Normalize the cluster address. */
65
0
    a_cluster_addr = a_cluster_addr - FATFS_FIRST_CLUSTER_ADDR;
66
67
    /* Determine the offset of the byte in the allocation bitmap that contains
68
     * the bit for the specified cluster. */
69
0
    bitmap_byte_offset = (a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap * a_fatfs->ssize) + (a_cluster_addr / 8);
70
71
    /* Read the byte. */
72
0
    bytes_read = tsk_fs_read(fs, bitmap_byte_offset, (char*)&bitmap_byte, 1);
73
0
    if (bytes_read != 1) {
74
0
        if (bytes_read >= 0) {
75
0
            tsk_error_reset();
76
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
77
0
        }
78
0
        tsk_error_set_errstr2("%s: failed to read bitmap byte at offset %" PRIuINUM "", func_name, bitmap_byte_offset);
79
0
        return -1;
80
0
    }
81
82
    /* Check the bit that corresponds to the specified cluster. Note that this
83
     * computation does not yield 0 or 1. */
84
0
    if (bitmap_byte & (1 << (a_cluster_addr % 8))) {
85
0
        return 1;
86
0
    }
87
0
    else {
88
0
        return 0;
89
0
    }
90
0
}
91
92
/**
93
 * \internal
94
 * Determine whether the contents of a buffer may be an exFAT volume label
95
 * directory entry.
96
 *
97
 * @param [in] a_dentry A directory entry buffer.
98
 * @param [in] a_alloc_status The allocation status, possibly unknown, of the
99
 * cluster from which the buffer was filled.
100
 * @returns 1 if the directory entry buffer likely contains a volume label
101
 * directory entry, 0 otherwise.
102
 */
103
uint8_t
104
exfatfs_is_vol_label_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc)
105
0
{
106
0
    const char *func_name = "exfatfs_is_vol_label_dentry";
107
0
    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
108
0
    uint8_t i = 0;
109
110
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
111
0
        return 0;
112
0
    }
113
114
    /* Check the entry type byte. */
115
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) {
116
0
        return 0;
117
0
    }
118
119
    /* There should be a single volume label directory entry at the
120
     * beginning of the root directory, so check the allocation status, if
121
     * known, of the cluster from which the buffer was filled. */
122
0
    if (a_cluster_is_alloc == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
123
0
        return 0;
124
0
    }
125
126
0
    if(exfatfs_get_alloc_status_from_type(dentry->entry_type) == 1){
127
        /* There is supposed to be a label, check its length. */
128
0
        if ((dentry->volume_label_length_chars < 1) || (dentry->volume_label_length_chars > EXFATFS_MAX_VOLUME_LABEL_LEN_CHAR)) {
129
0
            if (tsk_verbose) {
130
0
                fprintf(stderr, "%s: incorrect volume label length\n", func_name);
131
0
            }
132
0
            return 0;
133
0
        }
134
0
    }
135
0
    else {
136
        /* There is supposed to be no label, check for a zero in the length
137
         * field. */
138
0
        if (dentry->volume_label_length_chars != 0x00) {
139
0
            if (tsk_verbose) {
140
0
                fprintf(stderr, "%s: volume label length non-zero for no label entry\n", func_name);
141
0
            }
142
0
            return 0;
143
0
        }
144
145
        /* Every byte of the UTF-16 volume label string should be 0. */
146
0
        for (i = 0; i < EXFATFS_MAX_VOLUME_LABEL_LEN_BYTE; ++i) {
147
0
            if (dentry->volume_label[i] != 0x00) {
148
0
                if (tsk_verbose) {
149
0
                    fprintf(stderr, "%s: non-zero byte in label for no label entry\n", func_name);
150
0
                }
151
0
                return 0;
152
0
            }
153
0
        }
154
0
    }
155
156
0
    return 1;
157
0
}
158
159
/**
160
 * \internal
161
 * Determine whether the contents of a buffer may be an exFAT volume GUID
162
 * directory entry.
163
 *
164
 * @param [in] a_dentry A directory entry buffer.
165
 * @param [in] a_alloc_status The allocation status, possibly unknown, of the
166
 * cluster from which the buffer was filled.
167
 * @returns 1 if the directory entry buffer likely contains a volume GUID
168
 * directory entry, 0 otherwise.
169
 */
170
uint8_t
171
exfatfs_is_vol_guid_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
172
0
{
173
0
    const char *func_name = "exfatfs_is_vol_guid_dentry";
174
0
    EXFATFS_VOL_GUID_DIR_ENTRY *dentry = (EXFATFS_VOL_GUID_DIR_ENTRY*)a_dentry;
175
176
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
177
0
        return 0;
178
0
    }
179
180
    /* Check the entry type byte. */
181
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID) {
182
0
        return 0;
183
0
    }
184
185
    /* There is not enough data in a volume GUID directory entry to test
186
     * anything but the entry type byte. However, a volume GUID directory
187
     * entry should be in allocated space, so check the allocation status, if
188
     * known, of the cluster from which the buffer was filled to reduce false
189
     * positives. */
190
0
    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
191
0
            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
192
0
}
193
194
/**
195
 * \internal
196
 * Determine whether the contents of a buffer may be an exFAT allocation bitmap
197
 * directory entry. The test will be more reliable if an optional FATFS_INFO
198
 * struct representing the file system is provided.
199
 *
200
 * @param [in] a_dentry A directory entry buffer.
201
 * @param [in] a_alloc_status The allocation status, possibly unknown, of the
202
 * cluster from which the buffer was filled.
203
 * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
204
 * may be NULL.
205
 * @returns 1 if the directory entry buffer likely contains an allocation
206
 * bitmap directory entry, 0 otherwise.
207
 */
208
uint8_t
209
exfatfs_is_alloc_bitmap_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status, FATFS_INFO *a_fatfs)
210
0
{
211
0
    const char *func_name = "exfatfs_is_alloc_bitmap_dentry";
212
0
    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)a_dentry;
213
0
    uint32_t first_cluster_of_bitmap = 0;
214
0
    uint64_t length_of_alloc_bitmap_in_bytes = 0;
215
216
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
217
0
        return 0;
218
0
    }
219
220
    /* Check the entry type byte. */
221
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
222
0
        return 0;
223
0
    }
224
225
    /* There should be a single allocation bitmap directory entry near the the
226
     * beginning of the root directory, so check the allocation status, if
227
     * known, of the cluster from which the buffer was filled. */
228
0
    if (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
229
0
        return 0;
230
0
    }
231
232
0
    if (a_fatfs != NULL) {
233
        /* The length of the allocation bitmap should be consistent with the
234
         * number of clusters in the data area as specified in the volume boot
235
         * record. */
236
0
        length_of_alloc_bitmap_in_bytes = tsk_getu64(a_fatfs->fs_info.endian, dentry->length_of_alloc_bitmap_in_bytes);
237
0
        if (length_of_alloc_bitmap_in_bytes != (a_fatfs->clustcnt + 7) / 8) {
238
0
            if (tsk_verbose) {
239
0
                fprintf(stderr, "%s: bitmap length incorrect\n", func_name);
240
0
            }
241
0
            return 0;
242
0
        }
243
244
        /* The first cluster of the bit map should be within the data area.
245
         * It is usually in the first cluster. */
246
0
        first_cluster_of_bitmap = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_bitmap);
247
0
        if ((first_cluster_of_bitmap < EXFATFS_FIRST_CLUSTER) ||
248
0
            (first_cluster_of_bitmap > a_fatfs->lastclust)) {
249
0
            if (tsk_verbose) {
250
0
                fprintf(stderr, "%s: first cluster not in cluster heap\n", func_name);
251
0
            }
252
0
            return 0;
253
0
        }
254
255
        /* The first cluster of the allocation bitmap should be allocated (the
256
         * other conditions allow this function to be safely used to look for
257
         * the allocation bitmap during FATFS_INFO initialization, before a
258
         * cluster allocation is possible). */
259
0
        if ((a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap > 0) &&
260
0
            (a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes > 0) &&
261
0
            (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster_of_bitmap) != 1)) {
262
0
            if (tsk_verbose) {
263
0
                fprintf(stderr,
264
0
                    "%s: first cluster of allocation bitmap not allocated\n", func_name);
265
0
            }
266
0
            return 0;
267
0
        }
268
0
    }
269
270
0
    return 1;
271
0
}
272
273
/**
274
 * \internal
275
 * Determine whether the contents of a buffer may be an exFAT upcase table
276
 * directory entry. The test will be more reliable if an optional FATFS_INFO
277
 * struct representing the file system is provided.
278
 *
279
 * @param [in] a_dentry A directory entry buffer.
280
 * @param [in] a_alloc_status The allocation status, possibly unknown, of the
281
 * cluster from which the buffer was filled.
282
 * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
283
 * may be NULL.
284
 * @returns 1 if the directory entry buffer likely contains an upcase table
285
 * directory entry, 0 otherwise.
286
 */
287
uint8_t
288
exfatfs_is_upcase_table_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status, FATFS_INFO *a_fatfs)
289
0
{
290
0
    const char *func_name = "exfatfs_is_upcase_table_dentry";
291
0
    EXFATFS_UPCASE_TABLE_DIR_ENTRY *dentry = (EXFATFS_UPCASE_TABLE_DIR_ENTRY*)a_dentry;
292
0
    uint64_t table_size = 0;
293
0
    uint32_t first_cluster_of_table = 0;
294
295
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
296
0
        return 0;
297
0
    }
298
299
    /* Check the entry type byte. */
300
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE) {
301
0
        return 0;
302
0
    }
303
304
    /* There should be a single upcase table directory entry near the the
305
     * beginning of the root directory, so check the allocation status, if
306
     * known, of the cluster from which the buffer was filled. */
307
0
    if (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNALLOC) {
308
0
        return 0;
309
0
    }
310
311
0
    if (a_fatfs != NULL) {
312
        /* Check the size of the table. */
313
0
        table_size = tsk_getu64(a_fatfs->fs_info.endian, dentry->table_length_in_bytes);
314
0
        if (table_size == 0) {
315
0
            if (tsk_verbose) {
316
0
                fprintf(stderr, "%s: table size is zero\n", func_name);
317
0
            }
318
0
            return 0;
319
0
        }
320
321
        /* Is the table size less than the size of the cluster heap
322
         * (data area)? The cluster heap size is computed by multiplying the
323
         * cluster size by the number of sectors in a cluster and then
324
         * multiplying by the number of bytes in a sector (the last operation
325
         * is optimized as a left shift by the base 2 log of sector size). */
326
0
        if (table_size > (a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh) {
327
0
            if (tsk_verbose) {
328
0
                fprintf(stderr, "%s: table size too big\n", func_name);
329
0
            }
330
0
            return 0;
331
0
        }
332
333
        /* Is the address of the first cluster in range? */
334
0
        first_cluster_of_table = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_table);
335
0
        if ((first_cluster_of_table < EXFATFS_FIRST_CLUSTER) ||
336
0
            (first_cluster_of_table > a_fatfs->lastclust)) {
337
0
            if (tsk_verbose) {
338
0
                fprintf(stderr,
339
0
                    "%s: first cluster not in cluster heap\n", func_name);
340
0
            }
341
0
            return 0;
342
0
        }
343
344
        /* The first cluster of the table should be allocated. */
345
0
        if (exfatfs_is_cluster_alloc(a_fatfs, (TSK_DADDR_T)first_cluster_of_table) != 1) {
346
0
            if (tsk_verbose) {
347
0
                fprintf(stderr,
348
0
                    "%s: first cluster of table not allocated\n", func_name);
349
0
            }
350
0
            return 0;
351
0
        }
352
0
    }
353
354
0
    return 1;
355
0
}
356
357
/**
358
 * \internal
359
 * Determine whether the contents of a buffer may be an exFAT TexFAT directory
360
 * entry.
361
 *
362
 * @param [in] a_dentry A directory entry buffer.
363
 * @param [in] a_alloc_status The allocation status, possibly unknown, of the
364
 * cluster from which the buffer was filled.
365
 * @returns 1 if the directory entry buffer likely contains a TexFAT directory
366
 * entry, 0 otherwise.
367
 */
368
uint8_t
369
exfatfs_is_texfat_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
370
0
{
371
0
    const char *func_name = "exfatfs_is_texfat_dentry";
372
0
    EXFATFS_TEXFAT_DIR_ENTRY *dentry = (EXFATFS_TEXFAT_DIR_ENTRY*)a_dentry;
373
374
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
375
0
        return 0;
376
0
    }
377
378
    /* Check the entry type byte. */
379
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_TEXFAT) {
380
0
        return 0;
381
0
    }
382
383
    /* There is not enough data in a TexFAT directory entry to test anything
384
     * but the entry type byte. However, a TexFAT directory entry should be in
385
     * allocated space, so check the allocation status, if known, of the
386
     * cluster from which the buffer was filled to reduce false positives. */
387
0
    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
388
0
            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
389
0
}
390
391
/**
392
 * \internal
393
 * Determine whether the contents of a buffer may be an exFAT access control
394
 * table directory entry.
395
 *
396
 * @param [in] a_dentry A directory entry buffer.
397
 * @param [in] a_alloc_status The allocation status, possibly unknown, of the
398
 * cluster from which the buffer was filled.
399
 * @returns 1 if the directory entry buffer likely contains an access control
400
 * table entry, 0 otherwise.
401
 */
402
uint8_t
403
exfatfs_is_access_ctrl_table_dentry(FATFS_DENTRY *a_dentry, FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_alloc_status)
404
0
{
405
0
    const char *func_name = "exfatfs_is_texfat_dentry";
406
0
    EXFATFS_TEXFAT_DIR_ENTRY *dentry = (EXFATFS_TEXFAT_DIR_ENTRY*)a_dentry;
407
408
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
409
0
        return 0;
410
0
    }
411
412
    /* Check the entry type byte. */
413
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_TEXFAT) {
414
0
        return 0;
415
0
    }
416
417
    /* There is not enough data in an access control table directory entry to
418
     * test anything but the entry type byte. However, an access control table
419
     * directory entry should be in allocated space, so check the allocation
420
     * status, if known, of the cluster from which the buffer was filled to
421
     * reduce false positives. */
422
0
    return ((a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_ALLOC) ||
423
0
            (a_alloc_status == FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN));
424
0
}
425
426
427
/**
428
 * \internal
429
 * Determine whether the contents of a buffer may be an exFAT file directory
430
 * entry. The test will be more reliable if an optional TSK_ENDIAN_ENUM value
431
 * is known. This function was split into two parts so that the main
432
 * test can be run without a FATFS_INFO object.
433
 *
434
 * @param [in] a_dentry A directory entry buffer.
435
 * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
436
 * may be NULL.
437
 * @returns 1 if the directory entry buffer likely contains a file directory
438
 * entry, 0 otherwise.
439
 */
440
uint8_t
441
exfatfs_is_file_dentry(FATFS_DENTRY *a_dentry, FATFS_INFO *a_fatfs)
442
0
{
443
0
    if (a_fatfs != NULL) {
444
0
        TSK_FS_INFO *fs = &(a_fatfs->fs_info);
445
0
        return exfatfs_is_file_dentry_standalone(a_dentry, fs->endian);
446
0
    }
447
0
    else {
448
0
        return exfatfs_is_file_dentry_standalone(a_dentry, TSK_UNKNOWN_ENDIAN);
449
0
    }
450
451
0
}
452
453
/**
454
 * \internal
455
 * Determine whether the contents of a buffer may be an exFAT file directory
456
 * entry. The test will be more reliable if an optional TSK_ENDIAN_ENUM value
457
 * is known. This version of the function can be called without a TSK_FS_INFO
458
 * object.
459
 *
460
 * @param [in] a_dentry A directory entry buffer.
461
 * @param [in] a_endian Endianness of the file system
462
 * @returns 1 if the directory entry buffer likely contains a file directory
463
 * entry, 0 otherwise.
464
 */
465
uint8_t
466
exfatfs_is_file_dentry_standalone(FATFS_DENTRY *a_dentry, TSK_ENDIAN_ENUM a_endian)
467
0
{
468
0
    const char *func_name = "exfatfs_is_file_dentry";
469
0
    EXFATFS_FILE_DIR_ENTRY *dentry = (EXFATFS_FILE_DIR_ENTRY*)a_dentry;
470
471
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
472
0
        return 0;
473
0
    }
474
475
    /* Check the entry type byte. */
476
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_FILE){
477
0
        return 0;
478
0
    }
479
480
    /* A file directory entry is the first entry of a file directory entry set
481
     * consisting of a file directory entry followed by a file stream directory
482
     * entry and from 1 to 17 file name directory entries. The file stream and
483
     * file name entries are called secondary entries. */
484
0
    if (dentry->secondary_entries_count < EXFATFS_MIN_FILE_SECONDARY_DENTRIES_COUNT ||
485
0
        dentry->secondary_entries_count > EXFATFS_MAX_FILE_SECONDARY_DENTRIES_COUNT) {
486
0
        if (tsk_verbose) {
487
0
            fprintf(stderr, "%s: secondary entries count out of range\n",
488
0
                func_name);
489
0
        }
490
0
        return 0;
491
0
    }
492
493
0
    if (a_endian != TSK_UNKNOWN_ENDIAN) {
494
495
        /* Make sure the time stamps aren't all zeros. */
496
0
        if ((tsk_getu16(a_endian, dentry->modified_date) == 0) &&
497
0
            (tsk_getu16(a_endian, dentry->modified_time) == 0) &&
498
0
            (dentry->modified_time_tenths_of_sec == 0) &&
499
0
            (tsk_getu16(a_endian, dentry->created_date) == 0) &&
500
0
            (tsk_getu16(a_endian, dentry->created_time) == 0) &&
501
0
            (dentry->created_time_tenths_of_sec == 0) &&
502
0
            (tsk_getu16(a_endian, dentry->accessed_date) == 0) &&
503
0
            (tsk_getu16(a_endian, dentry->accessed_time) == 0)) {
504
0
            if (tsk_verbose) {
505
0
                fprintf(stderr, "%s: time stamps all zero\n",
506
0
                    func_name);
507
0
            }
508
0
            return 0;
509
0
        }
510
0
    }
511
512
0
    return 1;
513
0
}
514
515
/**
516
 * \internal
517
 * Determine whether the contents of a buffer may be an exFAT file stream
518
 * directory entry. The test will be more reliable if an optional FATFS_INFO
519
 * struct representing the file system is provided. This function was
520
 * split into two parts so that the main test can be run
521
 * without a FATFS_INFO object.
522
 *
523
 * @param [in] a_dentry A directory entry buffer.
524
 * @param [in] a_fatfs A FATFS_INFO struct representing an exFAT file system,
525
 * may be NULL.
526
 * @returns 1 if the directory entry buffer likely contains a file stream
527
 * directory entry, 0 otherwise.
528
 */
529
uint8_t
530
exfatfs_is_file_stream_dentry(FATFS_DENTRY *a_dentry, FATFS_INFO *a_fatfs)
531
0
{
532
0
    TSK_FS_INFO *fs = NULL;
533
534
0
    uint64_t cluster_heap_size = 0;
535
536
0
    if (a_fatfs != NULL) {
537
0
        fs = &(a_fatfs->fs_info);
538
539
        /* Calculate the size of the cluster heap. The cluster heap size
540
         * is computed by multiplying the cluster size
541
         * by the number of sectors in a cluster and then
542
         * multiplying by the number of bytes in a sector (the last operation
543
         * is optimized as a left shift by the base 2 log of sector size). */
544
0
        cluster_heap_size = (a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh;
545
546
0
        return exfatfs_is_file_stream_dentry_standalone(a_dentry, fs->endian, cluster_heap_size, a_fatfs->lastclust);
547
0
    }
548
0
    else{
549
0
        return exfatfs_is_file_stream_dentry_standalone(a_dentry, TSK_UNKNOWN_ENDIAN, 0, 0);
550
0
    }
551
552
0
}
553
554
/**
555
 * \internal
556
 * Determine whether the contents of a buffer may be an exFAT file stream
557
 * directory entry. The test will be more reliable if the optional endianness
558
 * and cluster information are used. This version of the function can be
559
 * called without a TSK_FS_INFO object.
560
 *
561
 * The endianness must be known to run all of the extended tests. The other
562
 * parameters can be set to zero if unknown and the function will run whichever
563
 * tests are possible with the given information.
564
 *
565
 * @param [in] a_dentry A directory entry buffer.
566
 * @param [in] a_endian Endianness of the file system
567
 * @param [in] a_cluster_heap_size Size of the cluster heap (in bytes)
568
 * @param [in] a_last_cluster Last cluster in the file system
569
 * @returns 1 if the directory entry buffer likely contains a file stream
570
 * directory entry, 0 otherwise.
571
 */
572
uint8_t
573
exfatfs_is_file_stream_dentry_standalone(FATFS_DENTRY *a_dentry, TSK_ENDIAN_ENUM a_endian,
574
    uint64_t a_cluster_heap_size, TSK_DADDR_T a_last_cluster)
575
0
{
576
0
    const char *func_name = "exfatfs_is_file_stream_dentry";
577
0
    EXFATFS_FILE_STREAM_DIR_ENTRY *dentry = (EXFATFS_FILE_STREAM_DIR_ENTRY*)a_dentry;
578
0
    uint64_t file_size = 0;
579
0
    uint32_t first_cluster = 0;
580
581
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
582
0
        return 0;
583
0
    }
584
585
    /* Check the entry type byte. */
586
0
    if (exfatfs_get_enum_from_type(dentry->entry_type) != EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) {
587
0
        return 0;
588
0
    }
589
590
0
   if (a_endian != TSK_UNKNOWN_ENDIAN) {
591
592
        /* Check the size. */
593
0
        file_size = tsk_getu64(a_endian, dentry->data_length);
594
0
        if (file_size > 0) {
595
            /* Is the file size less than the size of the cluster heap
596
             * (data area)? The cluster heap size is computed by multiplying the
597
             * cluster size by the number of sectors in a cluster and then
598
             * multiplying by the number of bytes in a sector (the last operation
599
             * is optimized as a left shift by the base 2 log of sector size). */
600
0
            if(a_cluster_heap_size > 0){
601
0
                if (file_size > a_cluster_heap_size) {
602
0
                    if (tsk_verbose) {
603
0
                        fprintf(stderr, "%s: file size too big\n", func_name);
604
0
                    }
605
0
                    return 0;
606
0
                }
607
0
            }
608
609
            /* Is the address of the first cluster in range? */
610
0
            first_cluster = tsk_getu32(a_endian, dentry->first_cluster_addr);
611
0
            if ((first_cluster < EXFATFS_FIRST_CLUSTER) ||
612
0
                ((a_last_cluster > 0) && (first_cluster > a_last_cluster))) {
613
0
                if (tsk_verbose) {
614
0
                    fprintf(stderr,
615
0
                        "%s: first cluster not in cluster heap\n", func_name);
616
0
                }
617
0
                return 0;
618
0
            }
619
0
        }
620
0
   }
621
0
   return 1;
622
623
0
}
624
625
/**
626
 * \internal
627
 * Determine whether the contents of a buffer may be an exFAT file name
628
 * directory entry.
629
 *
630
 * @param [in] a_dentry A directory entry buffer.
631
 * @returns 1 if the directory entry buffer likely contains an file name
632
 * directory entry, 0 otherwise.
633
 */
634
uint8_t
635
exfatfs_is_file_name_dentry(FATFS_DENTRY *a_dentry)
636
0
{
637
0
    const char *func_name = "exfatfs_is_file_name_dentry";
638
0
    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
639
640
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
641
0
        return 0;
642
0
    }
643
644
    /* There is not enough data in a file name directory entry
645
     * to test anything but the entry type byte. */
646
0
    return (exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME);
647
0
}
648
649
650
/**
651
 * \internal
652
 * Determine whether a buffer likely contains a directory entry.
653
 * For the most reliable results, request the in-depth test.
654
 *
655
 * @param [in] a_fatfs Source file system for the directory entry.
656
 * @param [in] a_dentry Buffer that may contain a directory entry.
657
 * @param [in] a_cluster_is_alloc The allocation status, possibly unknown, of the
658
 * cluster from which the buffer was filled.
659
 * @param [in] a_do_basic_tests_only Whether to do basic or in-depth testing.
660
 * @return 1 if the buffer likely contains a directory entry, 0 otherwise
661
 */
662
uint8_t
663
exfatfs_is_dentry(
664
  FATFS_INFO *a_fatfs,
665
  FATFS_DENTRY *a_dentry,
666
  FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc,
667
  [[maybe_unused]] uint8_t a_do_basic_tests_only)
668
0
{
669
0
    const char *func_name = "exfatfs_is_dentry";
670
671
0
    if (fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
672
0
        return 0;
673
0
    }
674
675
0
    switch (exfatfs_get_enum_from_type(a_dentry->data[0]))
676
0
    {
677
0
    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
678
0
        return exfatfs_is_vol_label_dentry(a_dentry, a_cluster_is_alloc);
679
0
    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
680
0
        return exfatfs_is_vol_guid_dentry(a_dentry, a_cluster_is_alloc);
681
0
    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
682
0
        return exfatfs_is_alloc_bitmap_dentry(a_dentry, a_cluster_is_alloc, a_fatfs);
683
0
    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
684
0
        return exfatfs_is_upcase_table_dentry(a_dentry, a_cluster_is_alloc, a_fatfs);
685
0
    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
686
0
        return exfatfs_is_texfat_dentry(a_dentry, a_cluster_is_alloc);
687
0
    case EXFATFS_DIR_ENTRY_TYPE_ACT:
688
0
        return exfatfs_is_access_ctrl_table_dentry(a_dentry, a_cluster_is_alloc);
689
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE:
690
0
        return exfatfs_is_file_dentry(a_dentry, a_fatfs);
691
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
692
0
        return exfatfs_is_file_stream_dentry(a_dentry, a_fatfs);
693
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
694
0
        return exfatfs_is_file_name_dentry(a_dentry);
695
0
    default:
696
0
        return 0;
697
0
    }
698
0
}
699
700
/**
701
 * \internal
702
 * Construct a single, non-resident data run for the TSK_FS_META object of a
703
 * TSK_FS_FILE object.
704
 *
705
 * @param [in, out] a_fs_file Generic file with generic inode structure (TSK_FS_META).
706
 * @return 0 on success, 1 on failure, per TSK convention
707
 */
708
static uint8_t
709
exfatfs_make_contiguous_data_run(TSK_FS_FILE *a_fs_file)
710
0
{
711
0
    const char *func_name = "exfatfs_make_contiguous_data_run";
712
0
    TSK_FS_META *fs_meta = NULL;
713
0
    TSK_FS_INFO *fs = NULL;
714
0
    FATFS_INFO *fatfs = NULL;
715
0
    TSK_DADDR_T first_cluster = 0;
716
0
    TSK_FS_ATTR_RUN *data_run;
717
0
    TSK_FS_ATTR *fs_attr = NULL;
718
//    TSK_OFF_T alloc_size = 0;
719
720
0
    assert(a_fs_file != NULL);
721
0
    assert(a_fs_file->meta != NULL);
722
0
    assert(a_fs_file->fs_info != NULL);
723
724
0
    fs_meta = a_fs_file->meta;
725
0
    fs = (TSK_FS_INFO*)a_fs_file->fs_info;
726
0
    fatfs = (FATFS_INFO*)fs;
727
728
0
    if (tsk_verbose) {
729
0
        tsk_fprintf(stderr,
730
0
            "%s: Loading attrs for inode: %" PRIuINUM
731
0
            "\n", func_name, a_fs_file->meta->addr);
732
0
    }
733
734
    /* Get the stashed first cluster address of the file. If the address does
735
     * not make sense, set the attribute state to TSK_FS_META_ATTR_ERROR so
736
     * that there is no subsequent attempt to load a data run for this
737
     * file object. */
738
0
    first_cluster = ((TSK_DADDR_T*)fs_meta->content_ptr)[0];
739
0
    if ((first_cluster > (fatfs->lastclust)) &&
740
0
        (FATFS_ISEOF(first_cluster, fatfs->mask) == 0)) {
741
0
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
742
0
        tsk_error_reset();
743
0
        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) {
744
0
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
745
0
        }
746
0
        else {
747
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
748
0
        }
749
0
        tsk_error_set_errstr
750
0
            ("%s: Starting cluster address too large: %"
751
0
            PRIuDADDR, func_name, first_cluster);
752
0
        return 1;
753
0
    }
754
755
    /* Figure out the allocated size of the file. The minimum allocation unit
756
     * for exFAT is a cluster, so the the roundup() function is used to round
757
     * up the file size in bytes to a multiple of cluser size in bytes. */
758
//    alloc_size = roundup(fs_meta->size, (fatfs->csize * fs->block_size));
759
760
    /* Allocate an attribute list for the file. */
761
0
    fs_meta->attr = tsk_fs_attrlist_alloc();
762
763
    /* Allocate a non-resident attribute for the file and add it to the
764
     * attribute list. */
765
0
    if ((fs_attr = tsk_fs_attrlist_getnew(fs_meta->attr,
766
0
        TSK_FS_ATTR_NONRES)) == NULL) {
767
0
        return 1;
768
0
    }
769
770
    /* Allocate a single data run for the attribute. For exFAT, a data run is
771
     * a contiguous run of sectors. */
772
0
    data_run = tsk_fs_attr_run_alloc();
773
0
    if (data_run == NULL) {
774
0
        return 1;
775
0
    }
776
777
    /* Set the starting sector address of the run and the length of the run
778
     * in sectors. */
779
0
    data_run->addr = FATFS_CLUST_2_SECT(fatfs, first_cluster);
780
0
    data_run->len = roundup(fs_meta->size,
781
0
        (fatfs->csize * fs->block_size)) / fs->block_size;
782
783
    /* Add the data run to the attribute and add the attribute to the
784
     * attribute list. Note that the initial size and the allocation
785
     * size are the same for exFAT. */
786
0
    if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
787
0
            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
788
0
            fs_meta->size,
789
0
            fs_meta->size,
790
0
            data_run->len * fs->block_size,
791
0
            TSK_FS_ATTR_FLAG_NONE, 0)) {
792
0
        return 1;
793
0
    }
794
795
    /* Mark the attribute list as loaded. */
796
0
    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;
797
798
0
    return 0;
799
0
}
800
801
/**
802
 * \internal
803
 * Use a volume label directory entry corresponding to the exFAT
804
 * equivalent of an inode to populate the TSK_FS_META object of a
805
 * TSK_FS_FILE object.
806
 *
807
 * @param [in] a_fatfs Source file system for the directory entry.
808
 * @param [in] a_inum Address of the inode.
809
 * @param [in] a_dentry A volume label directory entry.
810
 * @param a_fs_file Generic file with generic inode structure (TSK_FS_META).
811
 * @return TSK_RETVAL_ENUM.
812
 */
813
static TSK_RETVAL_ENUM
814
exfatfs_copy_vol_label_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
815
0
{
816
0
    EXFATFS_VOL_LABEL_DIR_ENTRY *dentry = NULL;
817
818
0
    assert(a_fatfs != NULL);
819
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
820
0
    assert(a_dentry != NULL);
821
0
    assert(a_fs_file != NULL);
822
0
    assert(a_fs_file->meta != NULL);
823
824
0
    dentry = (EXFATFS_VOL_LABEL_DIR_ENTRY*)a_dentry;
825
0
    assert(exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL);
826
827
    /* If there is a volume label, copy it to the name field of the
828
     * TSK_FS_META structure. */
829
0
    if (exfatfs_get_alloc_status_from_type(dentry->entry_type) == 1) {
830
0
        if (fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16*)dentry->volume_label, (size_t)dentry->volume_label_length_chars,
831
0
            (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "volume label") != TSKconversionOK) {
832
0
            return TSK_COR;
833
0
        }
834
0
    }
835
0
    else {
836
0
        strcpy(a_fs_file->meta->name2->name, EXFATFS_EMPTY_VOLUME_LABEL_DENTRY_NAME);
837
0
    }
838
839
0
    return TSK_OK;
840
0
}
841
842
/**
843
 * \internal
844
 * Use an allocation bitmap directory entry corresponding to the exFAT
845
 * equivalent of an inode to populate the TSK_FS_META object of a
846
 * TSK_FS_FILE object.
847
 *
848
 * @param a_fatfs [in] Source file system for the directory entries.
849
 * @param [in] a_dentry An allocation bitmap directory entry.
850
 * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
851
 * @return TSK_RETVAL_ENUM.
852
 */
853
static TSK_RETVAL_ENUM
854
exfatfs_copy_alloc_bitmap_inode(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
855
0
{
856
0
    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
857
858
0
    assert(a_fatfs != NULL);
859
0
    assert(a_dentry != NULL);
860
0
    assert(a_fs_file != NULL);
861
0
    assert(a_fs_file->meta != NULL);
862
863
0
    dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)a_dentry;
864
0
    assert(exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP);
865
866
    /* Set the file name to a descriptive pseudo file name. */
867
0
    strcpy(a_fs_file->meta->name2->name, EXFATFS_ALLOC_BITMAP_DENTRY_NAME);
868
869
    /* Set the size of the allocation bitmap and the address of its
870
     * first cluster. */
871
0
    ((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = FATFS_SECT_2_CLUST(a_fatfs, a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap);
872
0
    a_fs_file->meta->size = a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes;
873
874
    /* There is no FAT chain walk for the allocation bitmap. Do an eager
875
     * load instead of a lazy load of its data run. */
876
0
    if (exfatfs_make_contiguous_data_run(a_fs_file)) {
877
0
        return TSK_ERR;
878
0
    }
879
880
0
    return TSK_OK;
881
0
}
882
883
/**
884
 * \internal
885
 * Use an UP-Case table directory entry corresponding to the exFAT equivalent
886
 * of an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
887
 *
888
 * @param a_fatfs [in] Source file system for the directory entries.
889
 * @param [in] a_dentry An upcase table directory entry.
890
 * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
891
 * @return TSK_RETVAL_ENUM.
892
 */
893
static TSK_RETVAL_ENUM
894
exfatfs_copy_upcase_table_inode(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_FS_FILE *a_fs_file)
895
0
{
896
0
    EXFATFS_UPCASE_TABLE_DIR_ENTRY *dentry = NULL;
897
898
0
    assert(a_fatfs != NULL);
899
0
    assert(a_dentry != NULL);
900
0
    assert(a_fs_file != NULL);
901
0
    assert(a_fs_file->meta != NULL);
902
903
0
    dentry = (EXFATFS_UPCASE_TABLE_DIR_ENTRY*)a_dentry;
904
0
    assert(exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE);
905
906
0
    strcpy(a_fs_file->meta->name2->name, EXFATFS_UPCASE_TABLE_DENTRY_NAME);
907
908
    /* Set the size of the Up-Case table and the address of its
909
0
     * first cluster. */((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] = tsk_getu32(a_fatfs->fs_info.endian, dentry->first_cluster_of_table);
910
0
    a_fs_file->meta->size = tsk_getu64(a_fatfs->fs_info.endian, dentry->table_length_in_bytes);
911
912
    /* There is no FAT chain walk for the upcase table. Do an eager
913
     * load instead of a lazy load of its data run. */
914
0
    if (exfatfs_make_contiguous_data_run(a_fs_file)) {
915
0
        return TSK_ERR;
916
0
    }
917
918
0
    return TSK_OK;
919
0
}
920
921
/**
922
 * \internal
923
 * Given an inode address, load the corresponding directory entry and test
924
 * to see if it's an exFAT file stream directory entry.
925
 *
926
 * @param a_fatfs [in] Source file system for the directory entries.
927
 * @param a_stream_entry_inum [in] The inode address associated with the
928
 * supposed file stream entry.
929
 * @param a_sector_is_alloc [in] The allocation status of the sector that
930
 * contains the supposed file stream entry.
931
 * @param a_file_dentry_type [in] The companion file entry type,
932
 * i.e., deleted or not.
933
 * @param a_dentry [in, out] A directory entry structure. The stream
934
 * entry, if found, will be loaded into it.
935
 * @return 0 on success, 1 on failure, per TSK convention
936
 */
937
static uint8_t
938
exfatfs_load_file_stream_dentry(FATFS_INFO *a_fatfs,
939
    TSK_INUM_T a_stream_entry_inum, uint8_t a_sector_is_alloc,
940
    EXFATFS_DIR_ENTRY_TYPE a_file_dentry_type,
941
    FATFS_DENTRY *a_dentry)
942
0
{
943
0
    assert(a_fatfs != NULL);
944
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_stream_entry_inum));
945
0
    assert(a_dentry != NULL);
946
947
0
    if (fatfs_dentry_load(a_fatfs, a_dentry, a_stream_entry_inum) == 0 &&
948
0
        exfatfs_is_dentry(a_fatfs, a_dentry, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)a_sector_is_alloc, a_sector_is_alloc)) {
949
        /* If the bytes at the specified inode address are a file stream entry
950
         * with the same allocation status as the file entry, report success. */
951
0
        if((exfatfs_get_alloc_status_from_type(a_file_dentry_type) == exfatfs_get_alloc_status_from_type(a_dentry->data[0])) &&
952
0
            (exfatfs_get_enum_from_type(a_file_dentry_type) == EXFATFS_DIR_ENTRY_TYPE_FILE) &&
953
0
            (exfatfs_get_enum_from_type(a_dentry->data[0]) == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM)) {
954
0
            return 0;
955
0
        }
956
0
    }
957
958
0
    memset((void*)a_dentry, 0, sizeof(FATFS_DENTRY));
959
0
    return 1;
960
0
}
961
962
/**
963
 * \internal
964
 * Given an exFAT directory entry, try to find the corresponding file
965
 * stream directory or file name directory entry that follows it and
966
 * return the inum.
967
 *
968
 * @param [in] a_fatfs Source file system for the directory entries.
969
 * @param [in] a_current_entry_inum The inode address associated with the current
970
 * entry.
971
 * @param [in] a_file_dentry The file entry type (only use deleted or not)
972
 * @param [in] a_next_dentry_type The type of the dentry we're searching for
973
 * @param [out] a_next_inum The inode of the next stream/file name directory entry will be stored here (if found)
974
 * @return 0 on success, 1 on failure, per TSK convention
975
 */
976
static uint8_t
977
exfatfs_next_dentry_inum(FATFS_INFO *a_fatfs, TSK_INUM_T a_current_entry_inum,
978
    EXFATFS_FILE_DIR_ENTRY *a_file_dentry, EXFATFS_DIR_ENTRY_TYPE_ENUM a_next_dentry_type,
979
  TSK_INUM_T * a_next_inum)
980
0
{
981
0
    int8_t alloc_check_ret_val = 0;
982
0
    uint8_t cluster_is_alloc = 0;
983
0
    TSK_DADDR_T sector = 0;
984
0
    TSK_DADDR_T cluster = 0;
985
0
    TSK_DADDR_T cluster_base_sector = 0;
986
0
    TSK_DADDR_T last_entry_offset = 0;
987
0
    TSK_DADDR_T file_entry_offset = 0;
988
0
    TSK_DADDR_T next_cluster = 0;
989
0
  FATFS_DENTRY temp_dentry;
990
991
0
    assert(a_fatfs != NULL);
992
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_current_entry_inum));
993
0
    assert(a_file_dentry != NULL);
994
995
  /* Only look for file stream and file name directory entries */
996
0
  if((a_next_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) &&
997
0
    (a_next_dentry_type != EXFATFS_DIR_ENTRY_TYPE_FILE_NAME)){
998
0
      return FATFS_FAIL;
999
0
  }
1000
1001
0
    sector = FATFS_INODE_2_SECT(a_fatfs, a_current_entry_inum);
1002
0
    cluster = FATFS_SECT_2_CLUST(a_fatfs, sector);
1003
0
    alloc_check_ret_val = exfatfs_is_cluster_alloc(a_fatfs, cluster);
1004
0
    if (alloc_check_ret_val != -1) {
1005
0
        cluster_is_alloc = (uint8_t)alloc_check_ret_val;
1006
0
    }
1007
0
    else {
1008
0
        return FATFS_FAIL;
1009
0
    }
1010
1011
    /* Check for the most common case first - the file stream/name entry is located
1012
     * immediately after the specified one. This should always be true for any
1013
     * in-use file entry in an allocated cluster that is not the last entry in
1014
     * the cluster. It will also be true if the previous entry is the last entry in
1015
     * the cluster and the directory that contains the file is not fragmented -
1016
     * the stream/name entry will simply be the first entry of the next cluster.
1017
     * Finally, if the previous entry is not in-use and was found in an unallocated
1018
     * sector, the only viable place to look for the next entry is in the
1019
     * bytes following the file entry, since there is no FAT chain to
1020
     * consult. */
1021
0
    *a_next_inum = a_current_entry_inum + 1;
1022
0
    if (fatfs_inum_is_in_range(a_fatfs, *a_next_inum)) {
1023
0
    if(fatfs_dentry_load(a_fatfs, &temp_dentry, *a_next_inum) == 0){
1024
0
      if(a_next_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM){
1025
0
        if(exfatfs_is_file_stream_dentry(&temp_dentry, a_fatfs)){
1026
0
          return FATFS_OK;
1027
0
        }
1028
0
      }
1029
0
      else if(a_next_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME){
1030
0
        if (exfatfs_is_file_name_dentry(&temp_dentry)){
1031
0
          return FATFS_OK;
1032
0
        }
1033
0
      }
1034
0
    }
1035
0
    }
1036
1037
    /* If the stream/name entry was not found immediately following the file entry
1038
     * and the cluster is allocated, it is possible that the previous entry was the
1039
     * last entry of a cluster in a fragmented directory. In this
1040
     * case, the FAT can be consulted to see if there is a next cluster. If
1041
     * so, the stream/name entry may be the first entry of that cluster. */
1042
0
    if (cluster_is_alloc) {
1043
        /* Calculate the byte offset of the last possible directory entry in
1044
         * the current cluster. */
1045
0
        cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, cluster);
1046
0
        last_entry_offset = (cluster_base_sector * a_fatfs->ssize) +
1047
0
            (a_fatfs->csize * a_fatfs->ssize) - sizeof(FATFS_DENTRY);
1048
1049
        /* Get the byte offset of the file entry. Note that FATFS_INODE_2_OFF
1050
         * gives the offset relative to start of a sector. */
1051
0
        file_entry_offset = (sector * a_fatfs->ssize) +
1052
0
            FATFS_INODE_2_OFF(a_fatfs, a_current_entry_inum);
1053
1054
0
        if (file_entry_offset == last_entry_offset) {
1055
            /* The file entry is the last in its cluster. Look up the next
1056
             * cluster. */
1057
0
            if ((fatfs_getFAT(a_fatfs, cluster, &next_cluster) == 0) &&
1058
0
                (next_cluster != 0)) {
1059
                /* Found the next cluster in the FAT, so get its first sector
1060
                 * and the inode address of the first entry of the sector. */
1061
0
                cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, next_cluster);
1062
0
                *a_next_inum = FATFS_SECT_2_INODE(a_fatfs,
1063
0
                    cluster_base_sector);
1064
1065
0
                if (fatfs_inum_is_in_range(a_fatfs, *a_next_inum)) {
1066
0
          if(fatfs_dentry_load(a_fatfs, &temp_dentry, *a_next_inum) == 0){
1067
0
            if(a_next_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM){
1068
0
              if(exfatfs_is_file_stream_dentry(&temp_dentry, a_fatfs)){
1069
0
                return FATFS_OK;
1070
0
              }
1071
0
            }
1072
0
            else if(a_next_dentry_type == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME){
1073
0
              if (exfatfs_is_file_name_dentry(&temp_dentry)){
1074
0
                return FATFS_OK;
1075
0
              }
1076
0
            }
1077
0
          }
1078
0
                }
1079
0
            }
1080
0
        }
1081
0
    }
1082
1083
    /* Did not find the file stream/name entry. */
1084
0
    return FATFS_FAIL;
1085
0
}
1086
1087
1088
/**
1089
 * \internal
1090
 * Use a a file and a file stream directory entry corresponding to the exFAT
1091
 * equivalent of an inode to populate the TSK_FS_META object of a TSK_FS_FILE
1092
 * object.
1093
 *
1094
 * @param a_fatfs [in] Source file system for the directory entries.
1095
 * @param [in] a_inum Address of the inode.
1096
 * @param [in] a_dentry A file directory entry.
1097
 * @param [in] a_is_alloc Allocation status of the sector that contains the
1098
 * file directory entry.
1099
 * @param a_fs_file [in, out] Generic file with generic inode structure (TSK_FS_META).
1100
 * @return TSK_RETVAL_ENUM.
1101
 */
1102
static TSK_RETVAL_ENUM
1103
exfatfs_copy_file_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,
1104
    FATFS_DENTRY *a_file_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
1105
0
{
1106
0
    TSK_FS_INFO *fs = NULL;
1107
0
    TSK_FS_META *fs_meta =  NULL;
1108
0
    EXFATFS_FILE_DIR_ENTRY *file_dentry = (EXFATFS_FILE_DIR_ENTRY*)a_file_dentry;
1109
0
    EXFATFS_FILE_STREAM_DIR_ENTRY stream_dentry;
1110
0
    uint32_t mode = 0;
1111
0
  TSK_INUM_T stream_inum;
1112
0
  TSK_INUM_T name_inum;
1113
0
  TSK_INUM_T prev_inum;
1114
0
    uint8_t name_bytes_written;
1115
0
  int name_index;
1116
0
  uint8_t bytes_to_copy;
1117
0
  FATFS_DENTRY temp_dentry;
1118
0
  char utf16_name[512];
1119
1120
0
    assert(a_fatfs != NULL);
1121
0
    assert(a_file_dentry != NULL);
1122
0
    assert(a_fs_file != NULL);
1123
0
    assert(a_fs_file->meta != NULL);
1124
0
    assert(exfatfs_get_enum_from_type(file_dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_FILE);
1125
1126
0
    fs = &(a_fatfs->fs_info);
1127
0
    fs_meta = a_fs_file->meta;
1128
1129
    /* Determine whether the file is a regular file or directory. */
1130
0
    if (file_dentry->attrs[0] & FATFS_ATTR_DIRECTORY) {
1131
0
        fs_meta->type = TSK_FS_META_TYPE_DIR;
1132
0
    }
1133
0
    else {
1134
0
        fs_meta->type = TSK_FS_META_TYPE_REG;
1135
0
    }
1136
1137
    /* Add mode flags corresponding to file attribute flags. */
1138
0
    mode = fs_meta->mode;
1139
0
    if ((file_dentry->attrs[0] & FATFS_ATTR_READONLY) == 0) {
1140
0
        mode |=
1141
0
            (TSK_FS_META_MODE_IRUSR | TSK_FS_META_MODE_IRGRP |
1142
0
            TSK_FS_META_MODE_IROTH);
1143
0
    }
1144
0
    if ((file_dentry->attrs[0] & FATFS_ATTR_HIDDEN) == 0) {
1145
0
        mode |=
1146
0
            (TSK_FS_META_MODE_IWUSR | TSK_FS_META_MODE_IWGRP |
1147
0
            TSK_FS_META_MODE_IWOTH);
1148
0
    }
1149
0
    fs_meta->mode = (TSK_FS_META_MODE_ENUM)mode;
1150
1151
    /* There is no notion of links in exFAT, just deleted or not deleted.
1152
     * If the file is not deleted, treat this as having one link. */
1153
0
    fs_meta->nlink = (exfatfs_get_alloc_status_from_type(file_dentry->entry_type) == 0) ? 0 : 1;
1154
1155
    /* Copy the last modified time, converted to UNIX date format. */
1156
0
    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->modified_date))) {
1157
0
        fs_meta->mtime =
1158
0
            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->modified_date),
1159
0
                tsk_getu16(fs->endian, file_dentry->modified_time),
1160
0
                file_dentry->modified_time_tenths_of_sec);
1161
0
        fs_meta->mtime_nano = fatfs_dos_2_nanosec(file_dentry->modified_time_tenths_of_sec);
1162
0
    }
1163
0
    else {
1164
0
        fs_meta->mtime = 0;
1165
0
        fs_meta->mtime_nano = 0;
1166
0
    }
1167
1168
    /* Copy the last accessed time, converted to UNIX date format. */
1169
0
    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->accessed_date))) {
1170
0
        fs_meta->atime =
1171
0
            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->accessed_date),
1172
0
                tsk_getu16(fs->endian, file_dentry->accessed_time), 0);
1173
0
    }
1174
0
    else {
1175
0
        fs_meta->atime = 0;
1176
0
    }
1177
0
    fs_meta->atime_nano = 0;
1178
1179
    /* exFAT does not have a last changed time. */
1180
0
    fs_meta->ctime = 0;
1181
0
    fs_meta->ctime_nano = 0;
1182
1183
    /* Copy the created time, converted to UNIX date format. */
1184
0
    if (FATFS_ISDATE(tsk_getu16(fs->endian, file_dentry->created_date))) {
1185
0
        fs_meta->crtime =
1186
0
            fatfs_dos_2_unix_time(tsk_getu16(fs->endian, file_dentry->created_date),
1187
0
                tsk_getu16(fs->endian, file_dentry->created_time),
1188
0
                file_dentry->created_time_tenths_of_sec);
1189
0
        fs_meta->crtime_nano = fatfs_dos_2_nanosec(file_dentry->created_time_tenths_of_sec);
1190
0
    }
1191
0
    else {
1192
0
        fs_meta->crtime = 0;
1193
0
        fs_meta->crtime_nano = 0;
1194
0
    }
1195
1196
    /* Attempt to load the file stream entry that goes with this file entry.
1197
     * If not successful, at least the file entry meta data will be returned. */
1198
0
  if(exfatfs_next_dentry_inum(a_fatfs, a_inum, file_dentry, EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM, &stream_inum)){
1199
0
    return TSK_OK;
1200
0
  }
1201
1202
0
  if (exfatfs_load_file_stream_dentry(a_fatfs, stream_inum, a_is_alloc,
1203
0
        file_dentry->entry_type, (FATFS_DENTRY *)(&stream_dentry)) ) {
1204
0
      return TSK_OK;
1205
0
  }
1206
1207
    /* Set the size of the file and the address of its first cluster. */
1208
0
    ((TSK_DADDR_T*)a_fs_file->meta->content_ptr)[0] =
1209
0
        tsk_getu32(a_fatfs->fs_info.endian, stream_dentry.first_cluster_addr);
1210
0
    fs_meta->size = tsk_getu64(fs->endian, stream_dentry.data_length);
1211
1212
    /* Set the allocation status using both the allocation status of the
1213
     * sector that contains the directory entries and the entry type
1214
     * settings - essentially a "belt and suspenders" check. */
1215
0
    if ((a_is_alloc) &&
1216
0
        (exfatfs_get_alloc_status_from_type(file_dentry->entry_type) == 1) &&
1217
0
        (exfatfs_get_alloc_status_from_type(stream_dentry.entry_type) == 1)) {
1218
0
        a_fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED);
1219
1220
        /* If the FAT chain bit of the secondary flags of the stream entry is set,
1221
         * the file is not fragmented and there is no FAT chain to walk. If the
1222
         * file is not deleted, do an eager load instead of a lazy load of its
1223
         * data run. */
1224
0
        if ((stream_dentry.flags & EXFATFS_INVALID_FAT_CHAIN_MASK) &&
1225
0
            (exfatfs_make_contiguous_data_run(a_fs_file))) {
1226
0
            return TSK_ERR;
1227
0
        }
1228
0
    }
1229
0
    else {
1230
0
        a_fs_file->meta->flags = TSK_FS_META_FLAG_UNALLOC;
1231
0
    }
1232
1233
  /* Attempt to load the file name entry(entries) that go with this file entry
1234
   * First copy all UTF16 data into a single buffer
1235
   * If not successful, return what we have to this point with no error */
1236
0
  memset(utf16_name, 0, sizeof(utf16_name));
1237
0
    name_bytes_written = 0;
1238
0
  prev_inum = stream_inum;
1239
0
  for(name_index = 1; name_index < file_dentry->secondary_entries_count;name_index++){
1240
0
    if(exfatfs_next_dentry_inum(a_fatfs, prev_inum, file_dentry, EXFATFS_DIR_ENTRY_TYPE_FILE_NAME, &name_inum)){
1241
      /* Try to save what we've got (if we found at least one file name dentry) */
1242
0
      if(name_index > 1){
1243
0
        if(fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16 *)utf16_name, name_bytes_written / 2,
1244
0
          (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "file name (partial)") != TSKconversionOK){
1245
0
            return TSK_OK; /* Don't want to disregard valid data read earlier */
1246
0
        }
1247
0
      }
1248
0
      return TSK_OK;
1249
0
    }
1250
0
    fatfs_dentry_load(a_fatfs, &temp_dentry, name_inum);
1251
0
    if(stream_dentry.file_name_length_UTF16_chars * 2 - name_bytes_written > EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH_UTF16_BYTES){
1252
0
            bytes_to_copy = EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH_UTF16_BYTES;
1253
0
    }
1254
0
    else{
1255
0
            bytes_to_copy = stream_dentry.file_name_length_UTF16_chars * 2 - name_bytes_written;
1256
0
    }
1257
0
        memcpy(utf16_name + name_bytes_written, &(temp_dentry.data[2]), bytes_to_copy);
1258
1259
0
    prev_inum = name_inum;
1260
0
        name_bytes_written += bytes_to_copy;
1261
0
  }
1262
1263
  /* Copy the file name segment. */
1264
0
  if(fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16 *)utf16_name, name_bytes_written / 2,
1265
0
    (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "file name") != TSKconversionOK){
1266
0
      return TSK_OK; /* Don't want to disregard valid data read earlier */
1267
0
  }
1268
1269
0
    return TSK_OK;
1270
0
}
1271
1272
/**
1273
 * \internal
1274
 * Use a file name directory entry corresponding to the exFAT equivalent of
1275
 * an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
1276
 *
1277
 * @param [in] a_fatfs Source file system for the directory entry.
1278
 * @param [in] a_inum Address of the inode.
1279
 * @param [in] a_is_alloc Allocation status of the sector that contains the
1280
 * inode.
1281
 * @param [in] a_dentry A file name directory entry.
1282
 * @param a_fs_file Generic file with generic inode structure (TSK_FS_META).
1283
 * @return TSK_RETVAL_ENUM.
1284
 */
1285
static TSK_RETVAL_ENUM
1286
exfatfs_copy_file_name_inode(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,
1287
    FATFS_DENTRY *a_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
1288
0
{
1289
0
    EXFATFS_FILE_NAME_DIR_ENTRY *dentry = NULL;
1290
1291
0
    assert(a_fatfs != NULL);
1292
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
1293
0
    assert(a_dentry != NULL);
1294
0
    assert(a_fs_file != NULL);
1295
0
    assert(a_fs_file->meta != NULL);
1296
1297
0
    dentry = (EXFATFS_FILE_NAME_DIR_ENTRY*)a_dentry;
1298
0
    assert(exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME);
1299
1300
    /* Set the allocation status using both the allocation status of the
1301
     * sector that contains the directory entries and the entry type
1302
     * settings - essentially a "belt and suspenders" check. */
1303
0
    if ((a_is_alloc) &&
1304
0
        (exfatfs_get_alloc_status_from_type(dentry->entry_type) == 1)) {
1305
0
        a_fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED);
1306
0
    }
1307
0
    else {
1308
0
        a_fs_file->meta->flags = TSK_FS_META_FLAG_UNALLOC;
1309
0
    }
1310
1311
    /* Copy the file name segment. */
1312
0
    if (fatfs_utf16_inode_str_2_utf8(a_fatfs, (UTF16*)dentry->utf16_name_chars, EXFATFS_MAX_FILE_NAME_SEGMENT_LENGTH_UTF16_CHARS,
1313
0
        (UTF8*)a_fs_file->meta->name2->name, sizeof(a_fs_file->meta->name2->name), a_inum, "file name segment") != TSKconversionOK) {
1314
0
        return TSK_COR;
1315
0
    }
1316
0
    return TSK_OK;
1317
0
}
1318
1319
/**
1320
 * \internal
1321
 * Initialize the members of a TSK_FS_META object before copying the contents
1322
 * of an an inode consisting of one or more raw exFAT directory entries into it.
1323
 *
1324
 * @param [in] a_fatfs Source file system for the directory entries.
1325
 * @param [in] a_inum Address of the inode.
1326
 * @param [in] a_is_alloc Allocation status of the sector that contains the
1327
 * inode.
1328
 * @param [in, out] a_fs_file Generic file with generic inode structure to
1329
 * initialize.
1330
 * @return 0 on success, 1 on failure, per TSK convention
1331
 */
1332
static uint8_t
1333
exfatfs_inode_copy_init(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,
1334
    uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
1335
0
{
1336
0
    TSK_FS_META *fs_meta = NULL;
1337
1338
0
    assert(a_fatfs != NULL);
1339
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
1340
0
    assert(a_fs_file != NULL);
1341
0
    assert(a_fs_file->meta != NULL);
1342
1343
0
    fs_meta = a_fs_file->meta;
1344
0
    fs_meta->addr = a_inum;
1345
1346
    /* Set the allocation status based on the cluster allocation status. File
1347
     * entry set entries may change this. */
1348
0
    a_fs_file->meta->flags = a_is_alloc ? (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED) : TSK_FS_META_FLAG_UNALLOC;
1349
1350
    /* As for FATXX, make regular file the default type. */
1351
0
    fs_meta->type = TSK_FS_META_TYPE_REG;
1352
1353
    /* As for FATXX, mark everything as executable. */
1354
0
    fs_meta->mode = (TSK_FS_META_MODE_ENUM)(TSK_FS_META_MODE_IXUSR | TSK_FS_META_MODE_IXGRP |
1355
0
        TSK_FS_META_MODE_IXOTH);
1356
1357
    /* There is no notion of links in exFAT, just deleted or not deleted.
1358
     * With not deleted being equivalent to having one link, set nlink to 1
1359
     * here so that it will be set for static things like the allocation
1360
     * bitmap. The code for file inodes can reset or unset it appropriately. */
1361
0
    fs_meta->nlink = 1;
1362
1363
    /* Initialize size to zero. The code for particular inode types will
1364
     * fill in another value, if appropriate. */
1365
0
    fs_meta->size = 0;
1366
1367
    /* Default values for time stamp metadata. The code for file inodes will
1368
     * fill in actual time stamp data. */
1369
0
    fs_meta->mtime = 0;
1370
0
    fs_meta->mtime_nano = 0;
1371
0
    fs_meta->atime = 0;
1372
0
    fs_meta->atime_nano = 0;
1373
0
    fs_meta->ctime = 0;
1374
0
    fs_meta->ctime_nano = 0;
1375
0
    fs_meta->crtime = 0;
1376
0
    fs_meta->crtime_nano = 0;
1377
1378
    /* Metadata that does not exist in exFAT. */
1379
0
    fs_meta->uid = 0;
1380
0
    fs_meta->gid = 0;
1381
0
    fs_meta->seq = 0;
1382
1383
    /* Allocate space for a name. */
1384
0
    if (fs_meta->name2 == NULL) {
1385
0
        if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST*)tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
1386
0
            return 1;
1387
0
        }
1388
0
        fs_meta->name2->next = NULL;
1389
0
    }
1390
0
    fs_meta->name2->name[0] = '\0';
1391
1392
    /* Allocate space for saving the cluster address of the first cluster
1393
     * of file inodes, including allocation bitmaps and upcase tables. */
1394
0
    if (fs_meta->content_len < FATFS_FILE_CONTENT_LEN) {
1395
0
        if ((fs_meta =
1396
0
                tsk_fs_meta_realloc(fs_meta,
1397
0
                    FATFS_FILE_CONTENT_LEN)) == NULL) {
1398
0
            return 1;
1399
0
        }
1400
0
    }
1401
1402
    /* Mark the generic attribute list as not in use (in the generic file model
1403
     * attributes are containers for data or metadata). Population of this
1404
     * stuff is done on demand (lazy look up). */
1405
0
    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
1406
0
    if (fs_meta->attr) {
1407
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
1408
0
    }
1409
1410
0
    return 0;
1411
0
}
1412
1413
/**
1414
 * \internal
1415
 * Use one or more directory entries corresponding to the exFAT equivalent of
1416
 * an inode to populate the TSK_FS_META object of a TSK_FS_FILE object.
1417
 *
1418
 * @param [in] a_fatfs Source file system for the directory entries.
1419
 * @param [in] a_inum Address of the inode.
1420
 * @param [in] a_dentry A directory entry.
1421
 * @param [in] a_is_alloc Allocation status of the inode.
1422
 * @param [in, out] a_fs_file Generic file object with a generic inode
1423
 * metadata structure.
1424
 * @return TSK_RETVAL_ENUM.
1425
 */
1426
TSK_RETVAL_ENUM
1427
exfatfs_dinode_copy(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,
1428
    FATFS_DENTRY *a_dentry, uint8_t a_is_alloc, TSK_FS_FILE *a_fs_file)
1429
0
{
1430
0
    const char *func_name = "exfatfs_dinode_copy";
1431
1432
0
    assert(a_fatfs != NULL);
1433
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
1434
0
    assert(a_dentry != NULL);
1435
0
    assert(a_fs_file != NULL);
1436
0
    assert(a_fs_file->meta != NULL);
1437
0
    assert(a_fs_file->fs_info != NULL);
1438
1439
0
    tsk_error_reset();
1440
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
1441
0
        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name) ||
1442
0
        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
1443
0
        fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name) ||
1444
0
        fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name) ||
1445
0
        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
1446
0
        return TSK_ERR;
1447
0
    }
1448
1449
0
    if (exfatfs_inode_copy_init(a_fatfs, a_inum, a_is_alloc, a_fs_file)) {
1450
0
        return TSK_ERR;
1451
0
    }
1452
1453
0
    switch (exfatfs_get_enum_from_type(a_dentry->data[0]))
1454
0
    {
1455
0
    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
1456
0
        return exfatfs_copy_vol_label_inode(a_fatfs, a_inum, a_dentry, a_fs_file);
1457
0
    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
1458
0
        strcpy(a_fs_file->meta->name2->name, EXFATFS_VOLUME_GUID_DENTRY_NAME);
1459
0
        return TSK_OK;
1460
0
    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
1461
0
        return exfatfs_copy_alloc_bitmap_inode(a_fatfs, a_dentry, a_fs_file);
1462
0
    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
1463
0
        return exfatfs_copy_upcase_table_inode(a_fatfs, a_dentry, a_fs_file);
1464
0
    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
1465
0
        strcpy(a_fs_file->meta->name2->name, EXFATFS_TEX_FAT_DENTRY_NAME);
1466
0
        return TSK_OK;
1467
0
    case EXFATFS_DIR_ENTRY_TYPE_ACT:
1468
0
        strcpy(a_fs_file->meta->name2->name, EXFATFS_ACT_DENTRY_NAME);
1469
0
        return TSK_OK;
1470
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE:
1471
0
        return exfatfs_copy_file_inode(a_fatfs, a_inum, a_dentry, a_is_alloc, a_fs_file);
1472
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
1473
0
        return exfatfs_copy_file_name_inode(a_fatfs, a_inum, a_dentry, a_is_alloc, a_fs_file);
1474
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
1475
0
    default:
1476
        /* Stream entries are copied in tandem with the corresponding file entry. */
1477
0
        return TSK_ERR;
1478
0
    }
1479
1480
0
    return TSK_OK;
1481
0
}
1482
1483
/**
1484
 * \internal
1485
 * Given an exFAT file directory entry, try to find the corresponding file
1486
 * stream directory entry.
1487
 *
1488
 * @param [in] a_fatfs Source file system for the directory entries.
1489
 * @param [in] a_file_entry_inum The inode address associated with the file
1490
 * entry.
1491
 * @param [in] a_sector The address of the sector where the file entry was
1492
 * found.
1493
 * @param [in] a_sector_is_alloc The allocation status of the sector.
1494
 * @param [in] a_file_dentry_type The file entry type, deleted or not.
1495
 * @param [in, out] The stream entry, if found, will be loaded into the
1496
 * this generic directory entry structure.
1497
 * @return 0 on success, 1 on failure, per TSK convention
1498
 */
1499
uint8_t
1500
exfatfs_find_file_stream_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_file_entry_inum,
1501
    TSK_DADDR_T a_sector, uint8_t a_sector_is_alloc,
1502
    EXFATFS_DIR_ENTRY_TYPE a_file_dentry_type,
1503
    FATFS_DENTRY *a_stream_dentry)
1504
0
{
1505
0
    const char *func_name = "exfatfs_find_file_stream_dentry";
1506
0
    TSK_INUM_T stream_entry_inum = 0;
1507
0
    TSK_DADDR_T cluster = 0;
1508
0
    TSK_DADDR_T cluster_base_sector = 0;
1509
0
    TSK_DADDR_T last_entry_offset = 0;
1510
0
    TSK_DADDR_T file_entry_offset = 0;
1511
0
    TSK_DADDR_T next_cluster = 0;
1512
1513
0
    assert(a_fatfs != NULL);
1514
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_file_entry_inum));
1515
0
    assert(a_stream_dentry != NULL);
1516
1517
0
    tsk_error_reset();
1518
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
1519
0
        fatfs_ptr_arg_is_null(a_stream_dentry, "a_stream_dentry", func_name) ||
1520
0
        !fatfs_inum_arg_is_in_range(a_fatfs, a_file_entry_inum, func_name)) {
1521
0
        return FATFS_FAIL;
1522
0
    }
1523
1524
    /* Check for the most common case first - the file stream entry is located
1525
     * immediately after the file entry. This should always be true for any
1526
     * in-use file entry in an allocated cluster that is not the last entry in
1527
     * the cluster. It will also be true if the file entry is the last entry in
1528
     * the cluster and the directory that contains the file is not fragmented -
1529
     * the stream entry will simply be the first entry of the next cluster.
1530
     * Finally, if the file entry is not in-use and was found in an unallocated
1531
     * sector, the only viable place to look for the stream entry is in the
1532
     * bytes following the file entry, since there is no FAT chain to
1533
     * consult. */
1534
0
    stream_entry_inum = a_file_entry_inum + 1;
1535
0
    if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
1536
0
        if (exfatfs_load_file_stream_dentry(a_fatfs,
1537
0
            stream_entry_inum, a_sector_is_alloc,
1538
0
            a_file_dentry_type,
1539
0
            a_stream_dentry) == 0) {
1540
            /* Found it. */
1541
0
            return FATFS_OK;
1542
0
        }
1543
0
    }
1544
1545
    /* If the stream entry was not found immediately following the file entry
1546
     * and the cluster is allocated, it is possible that the file entry was the
1547
     * last entry of a cluster in a fragmented directory. In this
1548
     * case, the FAT can be consulted to see if there is a next cluster. If
1549
     * so, the stream entry may be the first entry of that cluster. */
1550
0
    if (a_sector_is_alloc) {
1551
        /* Calculate the byte offset of the last possible directory entry in
1552
         * the current cluster. */
1553
0
        cluster = FATFS_SECT_2_CLUST(a_fatfs, a_sector);
1554
0
        cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, cluster);
1555
0
        last_entry_offset = (cluster_base_sector * a_fatfs->ssize) +
1556
0
            (a_fatfs->csize * a_fatfs->ssize) - sizeof(FATFS_DENTRY);
1557
1558
        /* Get the byte offset of the file entry. Note that FATFS_INODE_2_OFF
1559
         * gices the offset relative to start of a sector. */
1560
0
        file_entry_offset = (a_sector * a_fatfs->ssize) +
1561
0
            FATFS_INODE_2_OFF(a_fatfs, a_file_entry_inum);
1562
1563
0
        if (file_entry_offset == last_entry_offset) {
1564
            /* The file entry is the last in its cluster. Look up the next
1565
             * cluster. */
1566
0
            if ((fatfs_getFAT(a_fatfs, cluster, &next_cluster) == 0) &&
1567
0
                (next_cluster != 0)) {
1568
                /* Found the next cluster in the FAT, so get its first sector
1569
                 * and the inode address of the first entry of the sector. */
1570
0
                cluster_base_sector = FATFS_CLUST_2_SECT(a_fatfs, next_cluster);
1571
0
                stream_entry_inum = FATFS_SECT_2_INODE(a_fatfs,
1572
0
                    cluster_base_sector);
1573
1574
0
                if (fatfs_inum_is_in_range(a_fatfs, stream_entry_inum)) {
1575
0
                    if (exfatfs_load_file_stream_dentry(a_fatfs,
1576
0
                        stream_entry_inum, a_sector_is_alloc,
1577
0
                        a_file_dentry_type,
1578
0
                        a_stream_dentry) == 0) {
1579
                        /* Found it. */
1580
0
                        return FATFS_OK;
1581
0
                    }
1582
0
                }
1583
0
            }
1584
0
        }
1585
0
    }
1586
1587
    /* Did not find the file stream entry. */
1588
0
    return FATFS_FAIL;
1589
0
}
1590
1591
/**
1592
 * \internal
1593
 * Read in the bytes from an exFAT file system that correspond to the exFAT
1594
 * equivalent of an inode and use them to populate the TSK_FS_META object of
1595
 * a TSK_FS_FILE object.
1596
 *
1597
 * @param [in] a_fatfs Source file system for the directory entries.
1598
 * @param [in, out] a_fs_file The TSK_FS_FILE object.
1599
 * @param [in] a_inum Inode address.
1600
 * @return 0 on success, 1 on failure, per TSK convention
1601
 */
1602
uint8_t
1603
exfatfs_inode_lookup(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file,
1604
    TSK_INUM_T a_inum)
1605
0
{
1606
0
    const char *func_name = "exfatfs_inode_lookup";
1607
0
    TSK_DADDR_T sector = 0;
1608
0
    int8_t sect_is_alloc = 0;
1609
0
    FATFS_DENTRY dentry;
1610
    //FATFS_DENTRY stream_dentry;
1611
    //FATFS_DENTRY *secondary_dentry = NULL;
1612
0
    EXFATFS_DIR_ENTRY_TYPE dentry_type = EXFATFS_DIR_ENTRY_TYPE_NONE;
1613
0
    TSK_RETVAL_ENUM copy_result = TSK_OK;
1614
1615
0
    tsk_error_reset();
1616
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
1617
0
        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) ||
1618
0
        fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name) ||
1619
0
        fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name) ||
1620
0
        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
1621
0
        return 1;
1622
0
    }
1623
1624
    /* Map the inode address to a sector. */
1625
0
    sector = FATFS_INODE_2_SECT(a_fatfs, a_inum);
1626
0
    if (sector > a_fatfs->fs_info.last_block) {
1627
0
        tsk_error_reset();
1628
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
1629
0
        tsk_error_set_errstr("%s: Inode %" PRIuINUM
1630
0
            " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sector);
1631
0
        return 1;
1632
0
    }
1633
1634
    /* Check the allocation status of the sector. This status will be used
1635
     * not only as meta data to be reported, but also as a way to choose
1636
     * between the basic or in-depth version of the tests (below) that
1637
     * determine whether or not the bytes corresponding to the inode are
1638
     * likely to be a directory entry. Note that in other places in the code
1639
     * information about whether or not the sector that contains the inode is
1640
     * part of a folder is used to select the test. Here, that information is
1641
     * not available, so the test here is less reliable and may result in some
1642
     * false positives. */
1643
0
    sect_is_alloc = fatfs_is_sectalloc(a_fatfs, sector);
1644
0
    if (sect_is_alloc == -1) {
1645
0
        return 1;
1646
0
    }
1647
1648
    /* Load the bytes at the specified inode address. */
1649
0
    memset((void*)&dentry, 0, sizeof(FATFS_DENTRY));
1650
0
    if (fatfs_dentry_load(a_fatfs, &dentry, a_inum)) {
1651
0
        return 1;
1652
0
    }
1653
1654
    /* Try typing the bytes as a directory entry.*/
1655
0
    if (exfatfs_is_dentry(a_fatfs, &dentry, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sect_is_alloc, sect_is_alloc)) {
1656
0
        dentry_type = (EXFATFS_DIR_ENTRY_TYPE)dentry.data[0];
1657
0
    }
1658
0
    else {
1659
0
        return 1;
1660
0
    }
1661
1662
    /* For the purposes of inode lookup, the file and file stream entries
1663
     * that begin a file entry set are mapped to a single inode. Thus,
1664
     * file stream entries are not treated as independent inodes. */
1665
0
    if (exfatfs_get_enum_from_type(dentry_type) == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM) {
1666
0
        tsk_error_reset();
1667
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
1668
0
        tsk_error_set_errstr("%s: %" PRIuINUM " is not an inode", func_name,
1669
0
            a_inum);
1670
0
        return 1;
1671
0
    }
1672
1673
    /* Populate the TSK_FS_META object of the TSK_FS_FILE object. */
1674
0
    copy_result = exfatfs_dinode_copy(a_fatfs, a_inum, &dentry, sect_is_alloc, a_fs_file);
1675
0
    if (copy_result == TSK_OK) {
1676
0
        return 0;
1677
0
    }
1678
0
    else if (copy_result == TSK_COR) {
1679
        /* There was a Unicode conversion error on a string, but the rest
1680
         * of the inode meta data is probably o.k., so report the error (if in
1681
         * verbose mode), but also report a successful look up.*/
1682
0
        if (tsk_verbose) {
1683
0
            tsk_error_print(stderr);
1684
0
        }
1685
0
        tsk_error_reset();
1686
0
        return 0;
1687
0
    }
1688
0
    else {
1689
0
        return 1;
1690
0
    }
1691
0
}
1692
1693
/**
1694
 * \internal
1695
 * Outputs file attributes for an exFAT directory entry/inode in
1696
 * human-readable form.
1697
 *
1698
 * @param [in] a_fatfs Source file system for the directory entry.
1699
 * @param [in] a_inum Inode address associated with the directory entry.
1700
 * @param [in] a_hFile Handle of a file to which to write.
1701
 * @return 0 on success, 1 on failure, per TSK convention
1702
 */
1703
uint8_t
1704
exfatfs_istat_attr_flags(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,  FILE *a_hFile)
1705
0
{
1706
0
    const char *func_name = "exfatfs_istat_attr_flags";
1707
0
    FATFS_DENTRY dentry;
1708
0
    EXFATFS_FILE_DIR_ENTRY *file_dentry = NULL;
1709
0
    uint16_t attr_flags = 0;
1710
1711
0
    assert(a_fatfs != NULL);
1712
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
1713
0
    assert(a_hFile != NULL);
1714
1715
0
    tsk_error_reset();
1716
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
1717
0
        fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) ||
1718
0
        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) {
1719
0
        return FATFS_FAIL;
1720
0
    }
1721
1722
    /* Load the bytes at the given inode address. */
1723
0
    if (fatfs_dentry_load(a_fatfs, (FATFS_DENTRY*)(&dentry), a_inum)) {
1724
0
        return FATFS_FAIL;
1725
0
    }
1726
1727
    /* Print the attributes. */
1728
0
    switch (exfatfs_get_enum_from_type(dentry.data[0]))
1729
0
    {
1730
0
    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL:
1731
0
        tsk_fprintf(a_hFile, "Volume Label\n");
1732
0
        break;
1733
0
    case EXFATFS_DIR_ENTRY_TYPE_VOLUME_GUID:
1734
0
        tsk_fprintf(a_hFile, "Volume GUID\n");
1735
0
        break;
1736
0
    case EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP:
1737
0
        tsk_fprintf(a_hFile, "Allocation Bitmap\n");
1738
0
        break;
1739
0
    case EXFATFS_DIR_ENTRY_TYPE_UPCASE_TABLE:
1740
0
        tsk_fprintf(a_hFile, "Up-Case Table\n");
1741
0
        break;
1742
0
    case EXFATFS_DIR_ENTRY_TYPE_TEXFAT:
1743
0
        tsk_fprintf(a_hFile, "TexFAT\n");
1744
0
        break;
1745
0
    case EXFATFS_DIR_ENTRY_TYPE_ACT:
1746
0
        tsk_fprintf(a_hFile, "Access Control Table\n");
1747
0
        break;
1748
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE:
1749
0
        file_dentry = (EXFATFS_FILE_DIR_ENTRY*)&dentry;
1750
0
        attr_flags = tsk_getu16(a_fatfs->fs_info.endian, file_dentry->attrs);
1751
1752
0
        if (attr_flags & FATFS_ATTR_DIRECTORY) {
1753
0
            tsk_fprintf(a_hFile, "Directory");
1754
0
        }
1755
0
        else {
1756
0
            tsk_fprintf(a_hFile, "File");
1757
0
        }
1758
1759
0
        if (attr_flags & FATFS_ATTR_READONLY) {
1760
0
            tsk_fprintf(a_hFile, ", Read Only");
1761
0
        }
1762
1763
0
        if (attr_flags & FATFS_ATTR_HIDDEN) {
1764
0
            tsk_fprintf(a_hFile, ", Hidden");
1765
0
        }
1766
1767
0
        if (attr_flags & FATFS_ATTR_SYSTEM) {
1768
0
            tsk_fprintf(a_hFile, ", System");
1769
0
        }
1770
1771
0
        if (attr_flags & FATFS_ATTR_ARCHIVE) {
1772
0
            tsk_fprintf(a_hFile, ", Archive");
1773
0
        }
1774
1775
0
        tsk_fprintf(a_hFile, "\n");
1776
1777
0
        break;
1778
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM:
1779
0
        tsk_fprintf(a_hFile, "File Stream\n");
1780
0
        break;
1781
0
    case EXFATFS_DIR_ENTRY_TYPE_FILE_NAME:
1782
0
        tsk_fprintf(a_hFile, "File Name\n");
1783
0
        break;
1784
0
    default:
1785
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
1786
0
        tsk_error_set_errstr("%s: Inode %" PRIuINUM
1787
0
            " is not an exFAT directory entry", func_name, a_inum);
1788
0
        return FATFS_FAIL;
1789
0
    }
1790
1791
0
    return FATFS_OK;
1792
0
}
1793
1794
/**
1795
 * \internal
1796
 * Determine whether an exFAT directory entry should be included in an inode
1797
 *  walk.
1798
 *
1799
 * @param [in] a_fatfs Source file system for the directory entry.
1800
 * @param [in] a_inum Inode address associated with the directory entry.
1801
 * @param [in] a_dentry A directory entry buffer.
1802
 * @param [in] a_selection_flags The inode selection falgs for the inode walk.
1803
 * @param [in] a_cluster_is_alloc The allocation status of the cluster that
1804
 * contains the directory entry.
1805
 * @return 1 if the entry should be skipped, 0 otherwise
1806
 */
1807
uint8_t
1808
exfatfs_inode_walk_should_skip_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum,
1809
    FATFS_DENTRY *a_dentry, unsigned int a_selection_flags,
1810
    int a_cluster_is_alloc)
1811
0
{
1812
0
    const char *func_name = "exfatfs_inode_walk_should_skip_dentry";
1813
0
    unsigned int dentry_flags = 0;
1814
1815
0
    assert(a_fatfs != NULL);
1816
0
    assert(fatfs_inum_is_in_range(a_fatfs, a_inum));
1817
0
    assert(a_dentry != NULL);
1818
1819
0
    tsk_error_reset();
1820
0
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
1821
0
        !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name) ||
1822
0
        fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) {
1823
0
        return 1;
1824
0
    }
1825
1826
    /* Skip file stream and file name entries. For inode walks, these entries
1827
     * are handled with the file entry with which they are associated in a file
1828
     * entry set. */
1829
0
    if (exfatfs_get_enum_from_type(a_dentry->data[0]) == EXFATFS_DIR_ENTRY_TYPE_FILE_STREAM ||
1830
0
        exfatfs_get_enum_from_type(a_dentry->data[0]) == EXFATFS_DIR_ENTRY_TYPE_FILE_NAME) {
1831
0
        return 1;
1832
0
    }
1833
1834
    /* Assign an allocation status to the entry. Allocation status is
1835
     * determined first by the allocation status of the cluster that contains
1836
     * the entry, then by the allocated status of the entry. */
1837
0
    if ((a_cluster_is_alloc) && (exfatfs_get_alloc_status_from_type(a_dentry->data[0]) == 1)) {
1838
0
        dentry_flags = TSK_FS_META_FLAG_ALLOC;
1839
0
    }
1840
0
    else {
1841
0
        dentry_flags = TSK_FS_META_FLAG_UNALLOC;
1842
0
    }
1843
1844
    /* Does the allocation status of the entry match that of the inode
1845
     * selection flags? */
1846
0
    if ((a_selection_flags & dentry_flags) != dentry_flags) {
1847
0
        return 1;
1848
0
    }
1849
1850
    /* If the inode selection flags call for only processing orphan files,
1851
     * check whether or not this inode is in list of non-orphan files found via
1852
     * name walk. */
1853
0
    if ((dentry_flags & TSK_FS_META_FLAG_UNALLOC) &&
1854
0
        (a_selection_flags & TSK_FS_META_FLAG_ORPHAN) &&
1855
0
        (tsk_fs_dir_find_inum_named(&(a_fatfs->fs_info), a_inum))) {
1856
0
        return 1;
1857
0
    }
1858
1859
0
    return 0;
1860
0
}
1861
1862
1863
/**
1864
 * \internal
1865
 * Returns the allocation status of a dir entry given its
1866
 * dir entry type byte (stored in the high bit)
1867
 *
1868
 * @param [in] a_dir_entry_type Entry type byte
1869
 * @return 0 if unused, 1 if used
1870
 */
1871
uint8_t
1872
exfatfs_get_alloc_status_from_type(EXFATFS_DIR_ENTRY_TYPE a_dir_entry_type)
1873
0
{
1874
0
    return (a_dir_entry_type >> 7);
1875
0
}
1876
1877
1878
/**
1879
 * \internal
1880
 * Returns the directory type enum from the given entry type byte
1881
 * (Comes from the low 7 bits)
1882
 *
1883
 * @param [in] a_dir_entry_type
1884
 * @return Enum for this type
1885
 */
1886
EXFATFS_DIR_ENTRY_TYPE_ENUM
1887
0
exfatfs_get_enum_from_type(EXFATFS_DIR_ENTRY_TYPE a_dir_entry_type){
1888
0
    return ((EXFATFS_DIR_ENTRY_TYPE_ENUM)(a_dir_entry_type & 0x7f));
1889
0
}