/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 | } |