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