/src/sleuthkit/tsk/fs/fatfs_meta.cpp
Line | Count | Source |
1 | | /* |
2 | | ** fatfs |
3 | | ** The Sleuth Kit |
4 | | ** |
5 | | ** Meta data layer support for the FAT file system. |
6 | | ** |
7 | | ** Brian Carrier [carrier <at> sleuthkit [dot] org] |
8 | | ** Copyright (c) 2006-2013 Brian Carrier, Basis Technology. All Rights reserved |
9 | | ** Copyright (c) 2003-2005 Brian Carrier. All rights reserved |
10 | | ** |
11 | | ** TASK |
12 | | ** Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved |
13 | | ** |
14 | | ** |
15 | | ** This software is distributed under the Common Public License 1.0 |
16 | | ** |
17 | | ** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05) |
18 | | ** |
19 | | */ |
20 | | |
21 | | /** |
22 | | * \file fatfs_meta.c |
23 | | * Meta data layer support for FAT file systems. |
24 | | */ |
25 | | |
26 | | #include "tsk_fatfs.h" |
27 | | #include "tsk_fatxxfs.h" |
28 | | #include "tsk_exfatfs.h" |
29 | | |
30 | | #include <memory> |
31 | | |
32 | | TSK_FS_ATTR_TYPE_ENUM |
33 | | fatfs_get_default_attr_type([[maybe_unused]] const TSK_FS_FILE * a_file) |
34 | 7.49k | { |
35 | 7.49k | return TSK_FS_ATTR_TYPE_DEFAULT; |
36 | 7.49k | } |
37 | | |
38 | | /** |
39 | | * \internal |
40 | | * Create an TSK_FS_META structure for the root directory. FAT does |
41 | | * not have a directory entry for the root directory, but this |
42 | | * function collects the data needed to make one. |
43 | | * |
44 | | * @param fatfs File system to analyze. |
45 | | * @param fs_meta Inode structure to copy root directory information into. |
46 | | * @return 1 on error and 0 on success |
47 | | */ |
48 | | static uint8_t |
49 | | fatfs_make_root(FATFS_INFO *a_fatfs, TSK_FS_META *a_fs_meta) |
50 | 173 | { |
51 | 173 | const char *func_name = "fatfs_make_root"; |
52 | 173 | TSK_DADDR_T *first_clust_addr_ptr = NULL; |
53 | | |
54 | 173 | tsk_error_reset(); |
55 | 173 | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) || |
56 | 173 | fatfs_ptr_arg_is_null(a_fs_meta, "a_fs_meta", func_name)) { |
57 | 0 | return 1; |
58 | 0 | } |
59 | | |
60 | | /* Manufacture some metadata. */ |
61 | 173 | a_fs_meta->type = TSK_FS_META_TYPE_DIR; |
62 | 173 | a_fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED; |
63 | 173 | a_fs_meta->nlink = 1; |
64 | 173 | a_fs_meta->addr = FATFS_ROOTINO; |
65 | 173 | a_fs_meta->flags = (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC); |
66 | 173 | a_fs_meta->uid = a_fs_meta->gid = 0; |
67 | 173 | a_fs_meta->mtime = a_fs_meta->atime = a_fs_meta->ctime = a_fs_meta->crtime = 0; |
68 | 173 | a_fs_meta->mtime_nano = a_fs_meta->atime_nano = a_fs_meta->ctime_nano = |
69 | 173 | a_fs_meta->crtime_nano = 0; |
70 | | |
71 | | /* Give the root directory an empty name. */ |
72 | 173 | if (a_fs_meta->name2 == NULL) { |
73 | 173 | if ((a_fs_meta->name2 = (TSK_FS_META_NAME_LIST *) |
74 | 173 | tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) { |
75 | 0 | return 1; |
76 | 0 | } |
77 | 173 | a_fs_meta->name2->next = NULL; |
78 | 173 | } |
79 | 173 | a_fs_meta->name2->name[0] = '\0'; |
80 | | |
81 | | /* Mark the generic attribute list as not in use (in the generic file model |
82 | | * attributes are containers for data or metadata). Population of this |
83 | | * list is done by lazy look up. */ |
84 | 173 | a_fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
85 | 173 | if (a_fs_meta->attr) { |
86 | 0 | tsk_fs_attrlist_markunused(a_fs_meta->attr); |
87 | 0 | } |
88 | | |
89 | | /* Determine the size of the root directory and the address of its |
90 | | * first cluster. */ |
91 | 173 | first_clust_addr_ptr = (TSK_DADDR_T*)a_fs_meta->content_ptr; |
92 | 173 | if (a_fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32 || |
93 | 173 | a_fatfs->fs_info.ftype == TSK_FS_TYPE_EXFAT) { |
94 | 30 | TSK_DADDR_T cnum = 0; |
95 | 30 | TSK_DADDR_T clust = 0; |
96 | 30 | TSK_LIST *list_seen = NULL; |
97 | | |
98 | | /* Convert the address of the first sector of the root directory into |
99 | | * the address of its first cluster. */ |
100 | 30 | clust = FATFS_SECT_2_CLUST(a_fatfs, a_fatfs->rootsect); |
101 | 30 | first_clust_addr_ptr[0] = clust; |
102 | | |
103 | | /* Walk the FAT and count the clusters allocated to the root directory. */ |
104 | 30 | cnum = 0; |
105 | 60 | while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) { |
106 | 30 | TSK_DADDR_T nxt = 0; |
107 | | |
108 | | /* Make sure we do not get into an infinite loop */ |
109 | 30 | if (tsk_list_find(list_seen, clust)) { |
110 | 0 | if (tsk_verbose) { |
111 | 0 | tsk_fprintf(stderr, |
112 | 0 | "Loop found while determining root directory size\n"); |
113 | 0 | } |
114 | 0 | break; |
115 | 0 | } |
116 | 30 | if (tsk_list_add(&list_seen, clust)) { |
117 | 0 | tsk_list_free(list_seen); |
118 | 0 | list_seen = NULL; |
119 | 0 | return 1; |
120 | 0 | } |
121 | | |
122 | 30 | cnum++; |
123 | 30 | if (fatfs_getFAT(a_fatfs, clust, &nxt)) { |
124 | 0 | break; |
125 | 0 | } |
126 | 30 | else { |
127 | 30 | clust = nxt; |
128 | 30 | } |
129 | 30 | } |
130 | 30 | tsk_list_free(list_seen); |
131 | 30 | list_seen = NULL; |
132 | | |
133 | | /* Calculate the size of the root directory. */ |
134 | 30 | a_fs_meta->size = (cnum * a_fatfs->csize) << a_fatfs->ssize_sh; |
135 | 30 | } |
136 | 143 | else { |
137 | | /* FAT12 and FAT16 don't use the FAT for the root directory, so set |
138 | | * the first cluster address to a distinguished value that other code |
139 | | * will have to check as a special condition. */ |
140 | 143 | first_clust_addr_ptr[0] = 1; |
141 | | |
142 | | /* Set the size equal to the number of bytes between the end of the |
143 | | * FATs and the start of the clusters. */ |
144 | 143 | a_fs_meta->size = (a_fatfs->firstclustsect - a_fatfs->firstdatasect) << a_fatfs->ssize_sh; |
145 | 143 | } |
146 | | |
147 | 173 | return 0; |
148 | 173 | } |
149 | | |
150 | | /** |
151 | | * \internal |
152 | | * Create an TSK_FS_META structure for the master boot record. |
153 | | * |
154 | | * @param fatfs File system to analyze |
155 | | * @param fs_meta Inode structure to copy file information into. |
156 | | * @return 1 on error and 0 on success |
157 | | */ |
158 | | static uint8_t |
159 | | fatfs_make_mbr(FATFS_INFO *fatfs, TSK_FS_META *fs_meta) |
160 | 79 | { |
161 | 79 | TSK_DADDR_T *addr_ptr; |
162 | | |
163 | 79 | fs_meta->type = TSK_FS_META_TYPE_VIRT; |
164 | 79 | fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED; |
165 | 79 | fs_meta->nlink = 1; |
166 | 79 | fs_meta->addr = fatfs->mbr_virt_inum; |
167 | 79 | fs_meta->flags = (TSK_FS_META_FLAG_ENUM) |
168 | 79 | (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC); |
169 | 79 | fs_meta->uid = fs_meta->gid = 0; |
170 | 79 | fs_meta->mtime = fs_meta->atime = fs_meta->ctime = fs_meta->crtime = 0; |
171 | 79 | fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = |
172 | 79 | fs_meta->crtime_nano = 0; |
173 | | |
174 | 79 | if (fs_meta->name2 == NULL) { |
175 | 79 | if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *) |
176 | 79 | tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) { |
177 | 0 | return 1; |
178 | 0 | } |
179 | 79 | fs_meta->name2->next = NULL; |
180 | 79 | } |
181 | 79 | strncpy(fs_meta->name2->name, FATFS_MBRNAME, |
182 | 79 | TSK_FS_META_NAME_LIST_NSIZE); |
183 | | |
184 | 79 | fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
185 | 79 | if (fs_meta->attr) { |
186 | 0 | tsk_fs_attrlist_markunused(fs_meta->attr); |
187 | 0 | } |
188 | | |
189 | 79 | addr_ptr = (TSK_DADDR_T*)fs_meta->content_ptr; |
190 | 79 | addr_ptr[0] = 0; |
191 | 79 | fs_meta->size = 512; |
192 | | |
193 | 79 | return 0; |
194 | 79 | } |
195 | | |
196 | | /** |
197 | | * \internal |
198 | | * Create an TSK_FS_META structure for the FAT tables. |
199 | | * |
200 | | * @param fatfs File system to analyze |
201 | | * @param a_which 1 or 2 to choose between defining FAT1 or FAT2 |
202 | | * @param fs_meta Inode structure to copy file information into. |
203 | | * @return 1 on error and 0 on success |
204 | | */ |
205 | | static uint8_t |
206 | | fatfs_make_fat(FATFS_INFO *fatfs, uint8_t a_which, TSK_FS_META *fs_meta) |
207 | 99 | { |
208 | 99 | TSK_FS_INFO *fs = (TSK_FS_INFO*)fatfs; |
209 | 99 | TSK_DADDR_T *addr_ptr = (TSK_DADDR_T *)fs_meta->content_ptr; |
210 | | |
211 | 99 | if ((a_which != 1) && (a_which != 2)) { |
212 | 0 | return 1; |
213 | 0 | } |
214 | | |
215 | 99 | if (a_which > fatfs->numfat) { |
216 | 0 | return 1; |
217 | 0 | } |
218 | | |
219 | 99 | fs_meta->type = TSK_FS_META_TYPE_VIRT; |
220 | 99 | fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED; |
221 | 99 | fs_meta->nlink = 1; |
222 | | |
223 | 99 | fs_meta->flags = (TSK_FS_META_FLAG_ENUM) |
224 | 99 | (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC); |
225 | 99 | fs_meta->uid = fs_meta->gid = 0; |
226 | 99 | fs_meta->mtime = fs_meta->atime = fs_meta->ctime = fs_meta->crtime = 0; |
227 | 99 | fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = |
228 | 99 | fs_meta->crtime_nano = 0; |
229 | | |
230 | 99 | if (fs_meta->name2 == NULL) { |
231 | 99 | if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *) |
232 | 99 | tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) |
233 | 0 | return 1; |
234 | 99 | fs_meta->name2->next = NULL; |
235 | 99 | } |
236 | | |
237 | 99 | if (a_which == 1) { |
238 | 79 | fs_meta->addr = fatfs->fat1_virt_inum; |
239 | 79 | strncpy(fs_meta->name2->name, FATFS_FAT1NAME, |
240 | 79 | TSK_FS_META_NAME_LIST_NSIZE); |
241 | 79 | addr_ptr[0] = fatfs->firstfatsect; |
242 | 79 | } |
243 | 20 | else { |
244 | 20 | fs_meta->addr = fatfs->fat2_virt_inum; |
245 | 20 | strncpy(fs_meta->name2->name, FATFS_FAT2NAME, |
246 | 20 | TSK_FS_META_NAME_LIST_NSIZE); |
247 | 20 | addr_ptr[0] = fatfs->firstfatsect + fatfs->sectperfat; |
248 | 20 | } |
249 | | |
250 | 99 | fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
251 | 99 | if (fs_meta->attr) { |
252 | 0 | tsk_fs_attrlist_markunused(fs_meta->attr); |
253 | 0 | } |
254 | | |
255 | 99 | fs_meta->size = fatfs->sectperfat * fs->block_size; |
256 | | |
257 | 99 | return 0; |
258 | 99 | } |
259 | | |
260 | | /** |
261 | | * \internal |
262 | | * Load a FATFS_DENTRY structure with the bytes at a given inode address. |
263 | | * |
264 | | * @param [in] a_fs The file system from which to read the bytes. |
265 | | * @param [out] a_de The FATFS_DENTRY. |
266 | | * @param [in] a_inum An inode address. |
267 | | * @return 0 on success, 1 on failure. |
268 | | */ |
269 | | uint8_t |
270 | | fatfs_dentry_load(FATFS_INFO *a_fatfs, FATFS_DENTRY *a_dentry, TSK_INUM_T a_inum) |
271 | 461k | { |
272 | 461k | const char *func_name = "fatfs_dentry_load"; |
273 | 461k | TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs; |
274 | 461k | TSK_DADDR_T sect = 0; |
275 | 461k | size_t off = 0; |
276 | 461k | ssize_t cnt = 0; |
277 | | |
278 | 461k | tsk_error_reset(); |
279 | 461k | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) || |
280 | 461k | fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name) || |
281 | 461k | !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) { |
282 | 0 | return 1; |
283 | 0 | } |
284 | | |
285 | | /* Map the inode address to a sector. */ |
286 | 461k | sect = FATFS_INODE_2_SECT(a_fatfs, a_inum); |
287 | 461k | if (sect > fs->last_block) { |
288 | 0 | tsk_error_reset(); |
289 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
290 | 0 | tsk_error_set_errstr("%s: Inode %" PRIuINUM |
291 | 0 | " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sect); |
292 | 0 | return 1; |
293 | 0 | } |
294 | | |
295 | | /* Get the byte offset of the inode address within the sector. */ |
296 | 461k | off = FATFS_INODE_2_OFF(a_fatfs, a_inum); |
297 | | |
298 | | /* Read in the bytes. */ |
299 | 461k | cnt = tsk_fs_read(fs, sect * fs->block_size + off, (char*)a_dentry, sizeof(FATFS_DENTRY)); |
300 | 461k | if (cnt != sizeof(FATFS_DENTRY)) { |
301 | 0 | if (cnt >= 0) { |
302 | 0 | tsk_error_reset(); |
303 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
304 | 0 | } |
305 | 0 | tsk_error_set_errstr2("%s: block: %" PRIuDADDR, |
306 | 0 | func_name, sect); |
307 | 0 | return 1; |
308 | 0 | } |
309 | | |
310 | 461k | return 0; |
311 | 461k | } |
312 | | |
313 | | /** |
314 | | * \internal |
315 | | * Populate the TSK_FS_META structure of a TSK_FS_FILE structure for a |
316 | | * given inode address. |
317 | | * |
318 | | * @param [in] a_fs File system that contains the inode. |
319 | | * @param [out] a_fs_file The file corresponding to the inode. |
320 | | * @param [in] a_inum The inode address. |
321 | | * @returns 1 if an error occurs or if the inode address is not |
322 | | * for a valid inode, 0 otherwise. |
323 | | */ |
324 | | uint8_t |
325 | | fatfs_inode_lookup(TSK_FS_INFO *a_fs, TSK_FS_FILE *a_fs_file, |
326 | | TSK_INUM_T a_inum) |
327 | 433k | { |
328 | 433k | const char *func_name = "fatfs_inode_lookup"; |
329 | 433k | FATFS_INFO *fatfs = (FATFS_INFO*)a_fs; |
330 | | |
331 | 433k | tsk_error_reset(); |
332 | 433k | if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) || |
333 | 433k | fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) || |
334 | 433k | !fatfs_inum_arg_is_in_range(fatfs, a_inum, func_name)) { |
335 | 0 | return 1; |
336 | 0 | } |
337 | | |
338 | | /* Allocate or reset the TSK_FS_META struct. */ |
339 | 433k | if (a_fs_file->meta == NULL) { |
340 | 433k | if ((a_fs_file->meta = |
341 | 433k | tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) { |
342 | 0 | return 1; |
343 | 0 | } |
344 | 433k | } |
345 | 0 | else { |
346 | 0 | tsk_fs_meta_reset(a_fs_file->meta); |
347 | 0 | } |
348 | | |
349 | | /* Manufacture an inode for the root directory or a FAT virtual file, |
350 | | * or do a look up. */ |
351 | 433k | if (a_inum == a_fs->root_inum) { |
352 | 143 | if (fatfs_make_root(fatfs, a_fs_file->meta)) |
353 | 0 | return 1; |
354 | 143 | else |
355 | 143 | return 0; |
356 | 143 | } |
357 | 433k | else if (a_inum == fatfs->mbr_virt_inum) { |
358 | 79 | if (fatfs_make_mbr(fatfs, a_fs_file->meta)) |
359 | 0 | return 1; |
360 | 79 | else |
361 | 79 | return 0; |
362 | 79 | } |
363 | 433k | else if (a_inum == fatfs->fat1_virt_inum) { |
364 | 79 | if (fatfs_make_fat(fatfs, 1, a_fs_file->meta)) |
365 | 0 | return 1; |
366 | 79 | else |
367 | 79 | return 0; |
368 | 79 | } |
369 | 433k | else if (a_inum == fatfs->fat2_virt_inum && fatfs->numfat == 2) { |
370 | 20 | if (fatfs_make_fat(fatfs, 2, a_fs_file->meta)) |
371 | 0 | return 1; |
372 | 20 | else |
373 | 20 | return 0; |
374 | 20 | } |
375 | 433k | else if (a_inum == TSK_FS_ORPHANDIR_INUM(a_fs)) { |
376 | 153 | if (tsk_fs_dir_make_orphan_dir_meta(a_fs, a_fs_file->meta)) |
377 | 0 | return 1; |
378 | 153 | else |
379 | 153 | return 0; |
380 | 153 | } |
381 | 433k | else { |
382 | 433k | return fatfs->inode_lookup(fatfs, a_fs_file, a_inum); |
383 | 433k | } |
384 | 433k | } |
385 | | |
386 | | /** \internal |
387 | | * Make data runs out of the clusters allocated to a file represented by a |
388 | | * TSK_FS_FILE structure. Each data run will have a starting sector and a |
389 | | * length in sectors. The runs will be stored as a non-resident attribute in |
390 | | * the TSK_FS_ATTRLIST of the TSK_FS_META structure of the TSK_FS_FILE. |
391 | | * |
392 | | * @param a_fs_file A representation of a file. |
393 | | * @return 1 on error and 0 on success |
394 | | */ |
395 | | uint8_t |
396 | | fatfs_make_data_runs(TSK_FS_FILE * a_fs_file) |
397 | 8.36k | { |
398 | 8.36k | const char *func_name = "fatfs_make_data_runs"; |
399 | 8.36k | TSK_FS_INFO *fs = NULL; |
400 | 8.36k | TSK_FS_META *fs_meta = NULL; |
401 | 8.36k | FATFS_INFO *fatfs = NULL; |
402 | 8.36k | TSK_DADDR_T clust = 0; |
403 | 8.36k | TSK_OFF_T size_remain = 0; |
404 | 8.36k | TSK_FS_ATTR *fs_attr = NULL; |
405 | | |
406 | 8.36k | if ((fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) || |
407 | 8.36k | (fatfs_ptr_arg_is_null(a_fs_file->meta, "a_fs_file->meta", func_name)) || |
408 | 8.36k | (fatfs_ptr_arg_is_null(a_fs_file->fs_info, "a_fs_file->fs_info", func_name))) { |
409 | 0 | return TSK_ERR; |
410 | 0 | } |
411 | | |
412 | 8.36k | fs_meta = a_fs_file->meta; |
413 | 8.36k | fs = a_fs_file->fs_info; |
414 | 8.36k | fatfs = (FATFS_INFO*)fs; |
415 | | |
416 | | /* Check for an already populated attribute list, since a lazy strategy |
417 | | * is used to fill in attributes. If the attribute list is not yet |
418 | | * allocated, do so now. */ |
419 | 8.36k | if ((fs_meta->attr != NULL) |
420 | 0 | && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) { |
421 | 0 | return 0; |
422 | 0 | } |
423 | 8.36k | else if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) { |
424 | 0 | return 1; |
425 | 0 | } |
426 | | |
427 | 8.36k | if (fs_meta->attr != NULL) { |
428 | 0 | tsk_fs_attrlist_markunused(fs_meta->attr); |
429 | 0 | } |
430 | 8.36k | else { |
431 | 8.36k | fs_meta->attr = tsk_fs_attrlist_alloc(); |
432 | 8.36k | } |
433 | | |
434 | | /* Get the stashed first cluster address of the file. */ |
435 | 8.36k | clust = ((TSK_DADDR_T*)fs_meta->content_ptr)[0]; |
436 | 8.36k | if ((clust > (fatfs->lastclust)) && |
437 | 876 | (FATFS_ISEOF(clust, fatfs->mask) == 0)) { |
438 | 849 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
439 | 849 | tsk_error_reset(); |
440 | 849 | if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) { |
441 | 122 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
442 | 122 | } |
443 | 727 | else { |
444 | 727 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
445 | 727 | } |
446 | 849 | tsk_error_set_errstr |
447 | 849 | ("%s: Starting cluster address too large: %" |
448 | 849 | PRIuDADDR, func_name, clust); |
449 | 849 | return 1; |
450 | 849 | } |
451 | | |
452 | | /* Figure out the allocated length of the file in bytes. Because the |
453 | | * allocation unit for FAT file systems is the cluster, round the |
454 | | * size up to a multiple of cluster size. */ |
455 | 7.51k | size_remain = roundup(fs_meta->size, fatfs->csize * fs->block_size); |
456 | | |
457 | 7.51k | if ((a_fs_file->meta->addr == fs->root_inum) && |
458 | 157 | (fs->ftype != TSK_FS_TYPE_FAT32) && |
459 | 157 | (fs->ftype != TSK_FS_TYPE_EXFAT) && |
460 | 127 | (clust == 1)) { |
461 | | /* Make a single contiguous data run for a FAT12 or FAT16 root |
462 | | * directory. The root directory for these file systems is not |
463 | | * tracked in the FAT. */ |
464 | 127 | TSK_FS_ATTR_RUN *data_run; |
465 | | |
466 | 127 | if (tsk_verbose) { |
467 | 0 | tsk_fprintf(stderr, |
468 | 0 | "%s: Loading root directory\n", func_name); |
469 | 0 | } |
470 | | |
471 | | /* Allocate the run. */ |
472 | 127 | data_run = tsk_fs_attr_run_alloc(); |
473 | 127 | if (data_run == NULL) { |
474 | 0 | return 1; |
475 | 0 | } |
476 | | |
477 | | /* Set the starting sector address and run length. The run begins with |
478 | | * the first sector of the data area. */ |
479 | 127 | data_run->addr = fatfs->rootsect; |
480 | 127 | data_run->len = fatfs->firstclustsect - fatfs->firstdatasect; |
481 | | |
482 | | /* Allocate a non-resident attribute to hold the run and add it |
483 | | to the attribute list. */ |
484 | 127 | if ((fs_attr = |
485 | 127 | tsk_fs_attrlist_getnew(fs_meta->attr, |
486 | 127 | TSK_FS_ATTR_NONRES)) == NULL) { |
487 | 0 | return 1; |
488 | 0 | } |
489 | | |
490 | | /* Tie everything together. */ |
491 | 127 | if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL, |
492 | 127 | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
493 | 127 | data_run->len * fs->block_size, |
494 | 127 | data_run->len * fs->block_size, |
495 | 127 | data_run->len * fs->block_size, TSK_FS_ATTR_FLAG_NONE, 0)) { |
496 | 0 | return 1; |
497 | 0 | } |
498 | | |
499 | 127 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
500 | | |
501 | 127 | return 0; |
502 | 127 | } |
503 | 7.38k | else if ((a_fs_file->meta->addr >= fatfs->mbr_virt_inum) && |
504 | 30 | (a_fs_file->meta->addr <= fatfs->mbr_virt_inum + fatfs->numfat)) { |
505 | | /* Make a single contiguous data run for a virtual file (MBR, FAT). */ |
506 | 0 | TSK_FS_ATTR_RUN *data_run; |
507 | |
|
508 | 0 | if (tsk_verbose) { |
509 | 0 | tsk_fprintf(stderr, |
510 | 0 | "%s: Loading virtual file: %" PRIuINUM |
511 | 0 | "\n", func_name, a_fs_file->meta->addr); |
512 | 0 | } |
513 | | |
514 | | /* Allocate the run. */ |
515 | 0 | data_run = tsk_fs_attr_run_alloc(); |
516 | 0 | if (data_run == NULL) { |
517 | 0 | return 1; |
518 | 0 | } |
519 | | |
520 | | /* Set the starting sector address and run length. */ |
521 | 0 | data_run->addr = clust; |
522 | 0 | data_run->len = a_fs_file->meta->size / fs->block_size; |
523 | | |
524 | | /* Allocate a non-resident attribute to hold the run and add it |
525 | | to the attribute list. */ |
526 | 0 | if ((fs_attr = |
527 | 0 | tsk_fs_attrlist_getnew(fs_meta->attr, |
528 | 0 | TSK_FS_ATTR_NONRES)) == NULL) { |
529 | 0 | return 1; |
530 | 0 | } |
531 | | |
532 | | /* Tie everything together. */ |
533 | 0 | if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL, |
534 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
535 | 0 | data_run->len * fs->block_size, |
536 | 0 | data_run->len * fs->block_size, |
537 | 0 | data_run->len * fs->block_size, TSK_FS_ATTR_FLAG_NONE, 0)) { |
538 | 0 | return 1; |
539 | 0 | } |
540 | | |
541 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
542 | 0 | return 0; |
543 | 0 | } |
544 | 7.38k | else if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) { |
545 | | /* Make data runs for a deleted file that we want to recover. |
546 | | * In this case, we could get a lot of errors because of inconsistent |
547 | | * data. To make it clear that these are from a recovery, we set most |
548 | | * error codes to _RECOVER so that they can be more easily suppressed. |
549 | | */ |
550 | 1.18k | TSK_DADDR_T sbase; |
551 | 1.18k | TSK_DADDR_T startclust = clust; |
552 | 1.18k | TSK_OFF_T recoversize = fs_meta->size; |
553 | 1.18k | TSK_FS_ATTR_RUN *data_run = NULL; |
554 | 1.18k | TSK_FS_ATTR_RUN *data_run_tmp = NULL; |
555 | 1.18k | TSK_FS_ATTR_RUN *data_run_head = NULL; |
556 | 1.18k | uint8_t canRecover = 1; // set to 0 if recovery is not possible |
557 | | |
558 | 1.18k | if (tsk_verbose) |
559 | 0 | tsk_fprintf(stderr, |
560 | 0 | "%s: Processing deleted file %" PRIuINUM |
561 | 0 | " in recovery mode\n", func_name, fs_meta->addr); |
562 | | |
563 | | /* We know the size and the starting cluster |
564 | | * |
565 | | * We are going to take the clusters from the starting cluster |
566 | | * onwards and skip the clusters that are current allocated |
567 | | */ |
568 | | |
569 | | /* Quick check for exFAT only |
570 | | * Empty deleted files have a starting cluster of zero, which |
571 | | * causes problems in the exFAT functions since the first data |
572 | | * cluster should be 2. Since a starting cluster of zero indicates |
573 | | * no data, make an empty data run and skip any further processing |
574 | | */ |
575 | 1.18k | if((fs->ftype == TSK_FS_TYPE_EXFAT) && (startclust == 0)){ |
576 | | // initialize the data run |
577 | 0 | fs_attr = tsk_fs_attrlist_getnew(a_fs_file->meta->attr, TSK_FS_ATTR_NONRES); |
578 | 0 | if (fs_attr == NULL) { |
579 | 0 | a_fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR; |
580 | 0 | return 1; |
581 | 0 | } |
582 | | |
583 | | // Add the empty data run |
584 | 0 | if (tsk_fs_attr_set_run(a_fs_file, fs_attr, NULL, NULL, |
585 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
586 | 0 | 0, 0, 0, (TSK_FS_ATTR_FLAG_ENUM)0, 0)) { |
587 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
588 | 0 | return 1; |
589 | 0 | } |
590 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
591 | 0 | return 0; |
592 | 0 | } |
593 | | |
594 | | /* Sanity checks on the starting cluster */ |
595 | | /* Convert the cluster addr to a sector addr */ |
596 | 1.18k | sbase = FATFS_CLUST_2_SECT(fatfs, startclust); |
597 | | |
598 | 1.18k | if (sbase > fs->last_block) { |
599 | 23 | tsk_error_reset(); |
600 | 23 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
601 | 23 | tsk_error_set_errstr |
602 | 23 | ("%s: Starting cluster address too large (recovery): %" |
603 | 23 | PRIuDADDR, func_name, sbase); |
604 | 23 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
605 | 23 | return 1; |
606 | 23 | } |
607 | 1.16k | else { |
608 | 1.16k | int retval; |
609 | | |
610 | | /* If the starting cluster is already allocated then we can't |
611 | | * recover it */ |
612 | 1.16k | retval = fatfs->is_cluster_alloc(fatfs, startclust); |
613 | 1.16k | if (retval != 0) { |
614 | 217 | canRecover = 0; |
615 | 217 | } |
616 | 1.16k | } |
617 | | |
618 | | /* Part 1 is to make sure there are enough unallocated clusters |
619 | | * for the size of the file |
620 | | */ |
621 | 1.16k | clust = startclust; |
622 | 1.16k | size_remain = recoversize; |
623 | | |
624 | | // we could make this negative so sign it for the comparison |
625 | 1.53M | while (((int64_t) size_remain > 0) && (canRecover)) { |
626 | 1.53M | int retval; |
627 | 1.53M | sbase = FATFS_CLUST_2_SECT(fatfs, clust); |
628 | | |
629 | | /* Are we past the end of the FS? |
630 | | * that means we could not find enough unallocated clusters |
631 | | * for the file size */ |
632 | 1.53M | if (sbase + fatfs->csize - 1 > fs->last_block) { |
633 | 0 | canRecover = 0; |
634 | |
|
635 | 0 | if (tsk_verbose) |
636 | 0 | tsk_fprintf(stderr, |
637 | 0 | "%s: Could not find enough unallocated sectors to recover with - aborting\n", func_name); |
638 | 0 | break; |
639 | 0 | } |
640 | | |
641 | | /* Skip allocated clusters */ |
642 | 1.53M | retval = fatfs->is_cluster_alloc(fatfs, clust); |
643 | 1.53M | if (retval == -1) { |
644 | 0 | canRecover = 0; |
645 | 0 | break; |
646 | 0 | } |
647 | 1.53M | else if (retval == 1) { |
648 | 406k | clust++; |
649 | 406k | continue; |
650 | 406k | } |
651 | | |
652 | | /* We can use this sector */ |
653 | | // see if we need a new run |
654 | 1.13M | if ((data_run == NULL) |
655 | 1.13M | || (data_run->addr + data_run->len != sbase)) { |
656 | | |
657 | 211k | TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc(); |
658 | 211k | if (data_run_tmp == NULL) { |
659 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
660 | 0 | tsk_fs_attr_run_free(data_run_head); |
661 | 0 | return 1; |
662 | 0 | } |
663 | | |
664 | 211k | if (data_run_head == NULL) { |
665 | 948 | data_run_head = data_run_tmp; |
666 | 948 | data_run_tmp->offset = 0; |
667 | 948 | } |
668 | 211k | else if (data_run != NULL) { |
669 | 211k | data_run->next = data_run_tmp; |
670 | 211k | data_run_tmp->offset = |
671 | 211k | data_run->offset + data_run->len; |
672 | 211k | } |
673 | 211k | data_run = data_run_tmp; |
674 | 211k | data_run->len = 0; |
675 | 211k | data_run->addr = sbase; |
676 | 211k | } |
677 | 1.13M | data_run->len += fatfs->csize; |
678 | | |
679 | 1.13M | size_remain -= (fatfs->csize << fatfs->ssize_sh); |
680 | 1.13M | clust++; |
681 | 1.13M | } |
682 | | |
683 | | // Get a FS_DATA structure and add the runlist to it |
684 | 1.16k | if ((fs_attr = |
685 | 1.16k | tsk_fs_attrlist_getnew(fs_meta->attr, |
686 | 1.16k | TSK_FS_ATTR_NONRES)) == NULL) { |
687 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
688 | 0 | tsk_fs_attr_run_free(data_run_head); |
689 | 0 | return 1; |
690 | 0 | } |
691 | | |
692 | 1.16k | if (canRecover) { |
693 | | /* We can recover the file */ |
694 | | |
695 | | // initialize the data run |
696 | 948 | if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_head, |
697 | 948 | NULL, TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
698 | 948 | fs_meta->size, fs_meta->size, roundup(fs_meta->size, |
699 | 948 | fatfs->csize * fs->block_size), TSK_FS_ATTR_FLAG_NONE, 0)) { |
700 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
701 | 0 | return 1; |
702 | 0 | } |
703 | | |
704 | 948 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
705 | 948 | } |
706 | | // create a one cluster run |
707 | 217 | else { |
708 | 217 | tsk_fs_attr_run_free(data_run_head); |
709 | | |
710 | 217 | data_run_tmp = tsk_fs_attr_run_alloc(); |
711 | 217 | if (data_run_tmp == NULL) { |
712 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
713 | 0 | return 1; |
714 | 0 | } |
715 | 217 | data_run_tmp->addr = sbase; |
716 | 217 | data_run_tmp->len = fatfs->csize; |
717 | | |
718 | | // initialize the data run |
719 | 217 | if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_tmp, NULL, |
720 | 217 | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
721 | 217 | fs_meta->size, fs_meta->size, roundup(fs_meta->size, |
722 | 217 | fatfs->csize * fs->block_size), TSK_FS_ATTR_FLAG_NONE, 0)) { |
723 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
724 | 0 | return 1; |
725 | 0 | } |
726 | | |
727 | 217 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
728 | 217 | } |
729 | | |
730 | 1.16k | return 0; |
731 | 1.16k | } |
732 | 6.19k | else { |
733 | 6.19k | TSK_LIST *list_seen = NULL; |
734 | 6.19k | TSK_FS_ATTR_RUN *data_run = NULL; |
735 | 6.19k | TSK_FS_ATTR_RUN *data_run_head = NULL; |
736 | 6.19k | TSK_DADDR_T sbase; |
737 | | /* Do normal cluster chain walking for a file or directory, including |
738 | | * FAT32 and exFAT root directories. */ |
739 | | |
740 | 6.19k | if (tsk_verbose) { |
741 | 0 | tsk_fprintf(stderr, |
742 | 0 | "%s: Processing file %" PRIuINUM |
743 | 0 | " in normal mode\n", func_name, fs_meta->addr); |
744 | 0 | } |
745 | | |
746 | | /* Cycle through the cluster chain */ |
747 | 7.44k | while ((clust & fatfs->mask) > 0 && (int64_t) size_remain > 0 && |
748 | 1.24k | (0 == FATFS_ISEOF(clust, fatfs->mask))) { |
749 | | |
750 | | /* Convert the cluster addr to a sector addr */ |
751 | 1.24k | sbase = FATFS_CLUST_2_SECT(fatfs, clust); |
752 | | |
753 | 1.24k | if (sbase + fatfs->csize - 1 > fs->last_block) { |
754 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
755 | 0 | tsk_error_reset(); |
756 | |
|
757 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
758 | 0 | tsk_error_set_errstr |
759 | 0 | ("%s: Invalid sector address in FAT (too large): %" |
760 | 0 | PRIuDADDR " (plus %d sectors)", func_name, sbase, fatfs->csize); |
761 | 0 | tsk_fs_attr_run_free(data_run_head); |
762 | 0 | if (list_seen != NULL) { |
763 | 0 | tsk_list_free(list_seen); |
764 | 0 | list_seen = NULL; |
765 | 0 | } |
766 | 0 | return 1; |
767 | 0 | } |
768 | | |
769 | | // see if we need a new run |
770 | 1.24k | if ((data_run == NULL) |
771 | 1.24k | || (data_run->addr + data_run->len != sbase)) { |
772 | | |
773 | 1.24k | TSK_FS_ATTR_RUN *data_run_tmp = tsk_fs_attr_run_alloc(); |
774 | 1.24k | if (data_run_tmp == NULL) { |
775 | 0 | tsk_fs_attr_run_free(data_run_head); |
776 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
777 | 0 | if (list_seen != NULL) { |
778 | 0 | tsk_list_free(list_seen); |
779 | 0 | list_seen = NULL; |
780 | 0 | } |
781 | 0 | return 1; |
782 | 0 | } |
783 | | |
784 | 1.24k | if (data_run_head == NULL) { |
785 | 685 | data_run_head = data_run_tmp; |
786 | 685 | data_run_tmp->offset = 0; |
787 | 685 | } |
788 | 564 | else if (data_run != NULL) { |
789 | 564 | data_run->next = data_run_tmp; |
790 | 564 | data_run_tmp->offset = |
791 | 564 | data_run->offset + data_run->len; |
792 | 564 | } |
793 | 1.24k | data_run = data_run_tmp; |
794 | 1.24k | data_run->len = 0; |
795 | 1.24k | data_run->addr = sbase; |
796 | 1.24k | } |
797 | | |
798 | 1.24k | data_run->len += fatfs->csize; |
799 | 1.24k | size_remain -= (fatfs->csize * fs->block_size); |
800 | | |
801 | 1.24k | if ((int64_t) size_remain > 0) { |
802 | 564 | TSK_DADDR_T nxt; |
803 | 564 | if (fatfs_getFAT(fatfs, clust, &nxt)) { |
804 | 0 | tsk_error_set_errstr2("%s: Inode: %" PRIuINUM |
805 | 0 | " cluster: %" PRIuDADDR, func_name, fs_meta->addr, clust); |
806 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
807 | 0 | tsk_fs_attr_run_free(data_run_head); |
808 | 0 | if (list_seen != NULL) { |
809 | 0 | tsk_list_free(list_seen); |
810 | 0 | list_seen = NULL; |
811 | 0 | } |
812 | 0 | return 1; |
813 | 0 | } |
814 | 564 | clust = nxt; |
815 | | |
816 | | /* Make sure we do not get into an infinite loop */ |
817 | 564 | if (tsk_list_find(list_seen, clust)) { |
818 | 0 | if (tsk_verbose) |
819 | 0 | tsk_fprintf(stderr, |
820 | 0 | "Loop found while processing file\n"); |
821 | 0 | if (data_run_head != NULL ) { |
822 | 0 | tsk_fs_attr_run_free(data_run_head); |
823 | | // Make sure to set data_run_head to NULL to prevent a use-after-free |
824 | 0 | data_run_head = NULL; |
825 | 0 | } |
826 | 0 | if (list_seen != NULL) { |
827 | 0 | tsk_list_free(list_seen); |
828 | 0 | list_seen = NULL; |
829 | 0 | } |
830 | 0 | break; |
831 | 0 | } |
832 | | |
833 | 564 | if (tsk_list_add(&list_seen, clust)) { |
834 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
835 | 0 | tsk_fs_attr_run_free(data_run_head); |
836 | 0 | if (list_seen != NULL) { |
837 | 0 | tsk_list_free(list_seen); |
838 | 0 | list_seen = NULL; |
839 | 0 | } |
840 | 0 | return 1; |
841 | 0 | } |
842 | 564 | } |
843 | 1.24k | } |
844 | | |
845 | | // add the run list to the inode structure |
846 | 6.19k | if ((fs_attr = |
847 | 6.19k | tsk_fs_attrlist_getnew(fs_meta->attr, |
848 | 6.19k | TSK_FS_ATTR_NONRES)) == NULL) { |
849 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
850 | 0 | if (list_seen != NULL) { |
851 | 0 | tsk_list_free(list_seen); |
852 | 0 | list_seen = NULL; |
853 | 0 | } |
854 | 0 | return 1; |
855 | 0 | } |
856 | | |
857 | | // initialize the data run |
858 | 6.19k | if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run_head, NULL, |
859 | 6.19k | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
860 | 6.19k | fs_meta->size, fs_meta->size, roundup(fs_meta->size, |
861 | 6.19k | fatfs->csize * fs->block_size), TSK_FS_ATTR_FLAG_NONE, 0)) { |
862 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
863 | 0 | tsk_fs_attr_run_free(data_run_head); |
864 | 0 | if (list_seen != NULL) { |
865 | 0 | tsk_list_free(list_seen); |
866 | 0 | list_seen = NULL; |
867 | 0 | } |
868 | 0 | return 1; |
869 | 0 | } |
870 | | |
871 | 6.19k | tsk_list_free(list_seen); |
872 | 6.19k | list_seen = NULL; |
873 | | |
874 | 6.19k | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
875 | | |
876 | 6.19k | return 0; |
877 | 6.19k | } |
878 | 7.51k | } |
879 | | |
880 | | /* Used for istat callback */ |
881 | | typedef struct { |
882 | | FILE *hFile; |
883 | | int idx; |
884 | | int istat_seen; |
885 | | } FATFS_PRINT_ADDR; |
886 | | |
887 | | /* Callback a_action for file_walk to print the sector addresses |
888 | | * of a file, used for istat |
889 | | */ |
890 | | static TSK_WALK_RET_ENUM |
891 | | print_addr_act( |
892 | | [[maybe_unused]] TSK_FS_FILE * fs_file, |
893 | | [[maybe_unused]] TSK_OFF_T a_off, |
894 | | TSK_DADDR_T addr, |
895 | | [[maybe_unused]] char *buf, |
896 | | [[maybe_unused]] size_t size, |
897 | | [[maybe_unused]] TSK_FS_BLOCK_FLAG_ENUM a_flags, |
898 | | void *a_ptr) |
899 | 0 | { |
900 | 0 | FATFS_PRINT_ADDR *print = (FATFS_PRINT_ADDR *) a_ptr; |
901 | |
|
902 | 0 | tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr); |
903 | |
|
904 | 0 | if (++(print->idx) == 8) { |
905 | 0 | tsk_fprintf(print->hFile, "\n"); |
906 | 0 | print->idx = 0; |
907 | 0 | } |
908 | 0 | print->istat_seen = 1; |
909 | |
|
910 | 0 | return TSK_WALK_CONT; |
911 | 0 | } |
912 | | |
913 | | /** |
914 | | * Print details on a specific file to a file handle. |
915 | | * |
916 | | * @param a_fs File system file is located in. |
917 | | * @param a_hFile File handle to print text to. |
918 | | * @param a_inum Address of file in file system. |
919 | | * @param a_numblock The number of blocks in file to force print (can go beyond file size). |
920 | | * @param a_sec_skew Clock skew in seconds to also print times in. |
921 | | * |
922 | | * @returns 1 on error and 0 on success. |
923 | | */ |
924 | | uint8_t |
925 | | fatfs_istat(TSK_FS_INFO *a_fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE *a_hFile, TSK_INUM_T a_inum, |
926 | | TSK_DADDR_T a_numblock, int32_t a_sec_skew) |
927 | 0 | { |
928 | 0 | const char* func_name = "fatfs_istat"; |
929 | 0 | FATFS_INFO *fatfs = (FATFS_INFO*)a_fs; |
930 | 0 | TSK_FS_META *fs_meta = NULL; |
931 | 0 | TSK_FS_META_NAME_LIST *fs_name_list = NULL; |
932 | 0 | FATFS_PRINT_ADDR print; |
933 | 0 | char timeBuf[128]; |
934 | |
|
935 | 0 | tsk_error_reset(); |
936 | 0 | if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) || |
937 | 0 | fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) || |
938 | 0 | !fatfs_inum_arg_is_in_range(fatfs, a_inum, func_name)) { |
939 | 0 | return 1; |
940 | 0 | } |
941 | | |
942 | | /* Create a TSK_FS_FILE corresponding to the specified inode. */ |
943 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
944 | 0 | tsk_fs_file_open_meta(a_fs, NULL, a_inum), |
945 | 0 | tsk_fs_file_close |
946 | 0 | }; |
947 | |
|
948 | 0 | if (!fs_file) { |
949 | 0 | return 1; |
950 | 0 | } |
951 | 0 | fs_meta = fs_file->meta; |
952 | | |
953 | | /* Print the inode address. */ |
954 | 0 | tsk_fprintf(a_hFile, "Directory Entry: %" PRIuINUM "\n", a_inum); |
955 | | |
956 | | /* Print the allocation status. */ |
957 | 0 | tsk_fprintf(a_hFile, "%sAllocated\n", |
958 | 0 | (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC) ? "Not " : ""); |
959 | | |
960 | | /* Print the attributes. */ |
961 | 0 | tsk_fprintf(a_hFile, "File Attributes: "); |
962 | |
|
963 | 0 | if (a_inum == a_fs->root_inum) { |
964 | 0 | tsk_fprintf(a_hFile, "Root Directory\n"); |
965 | 0 | } |
966 | 0 | else if (fs_meta->type == TSK_FS_META_TYPE_VIRT) { |
967 | 0 | tsk_fprintf(a_hFile, "Virtual File\n"); |
968 | 0 | } |
969 | 0 | else if (fs_meta->addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { |
970 | 0 | tsk_fprintf(a_hFile, "Virtual Directory\n"); |
971 | 0 | } |
972 | 0 | else { |
973 | 0 | if (fatfs->istat_attr_flags(fatfs, a_inum, a_hFile)) { |
974 | 0 | return 1; |
975 | 0 | } |
976 | 0 | } |
977 | | |
978 | | /* Print the file size. */ |
979 | 0 | tsk_fprintf(a_hFile, "Size: %" PRIdOFF "\n", fs_meta->size); |
980 | | |
981 | | /* Print the name. */ |
982 | 0 | if (fs_meta->name2) { |
983 | 0 | fs_name_list = fs_meta->name2; |
984 | 0 | tsk_fprintf(a_hFile, "Name: %s\n", fs_name_list->name); |
985 | 0 | } |
986 | | |
987 | | /* Print the times. */ |
988 | 0 | if (a_sec_skew != 0) { |
989 | 0 | tsk_fprintf(a_hFile, "\nAdjusted Directory Entry Times:\n"); |
990 | |
|
991 | 0 | if (fs_meta->mtime) |
992 | 0 | fs_meta->mtime -= a_sec_skew; |
993 | 0 | if (fs_meta->atime) |
994 | 0 | fs_meta->atime -= a_sec_skew; |
995 | 0 | if (fs_meta->crtime) |
996 | 0 | fs_meta->crtime -= a_sec_skew; |
997 | |
|
998 | 0 | tsk_fprintf(a_hFile, "Written:\t%s\n", |
999 | 0 | tsk_fs_time_to_str(fs_meta->mtime, timeBuf)); |
1000 | 0 | tsk_fprintf(a_hFile, "Accessed:\t%s\n", |
1001 | 0 | tsk_fs_time_to_str(fs_meta->atime, timeBuf)); |
1002 | 0 | tsk_fprintf(a_hFile, "Created:\t%s\n", |
1003 | 0 | tsk_fs_time_to_str(fs_meta->crtime, timeBuf)); |
1004 | |
|
1005 | 0 | if (fs_meta->mtime) |
1006 | 0 | fs_meta->mtime += a_sec_skew; |
1007 | 0 | if (fs_meta->atime) |
1008 | 0 | fs_meta->atime += a_sec_skew; |
1009 | 0 | if (fs_meta->crtime) |
1010 | 0 | fs_meta->crtime += a_sec_skew; |
1011 | |
|
1012 | 0 | tsk_fprintf(a_hFile, "\nOriginal Directory Entry Times:\n"); |
1013 | 0 | } |
1014 | 0 | else { |
1015 | 0 | tsk_fprintf(a_hFile, "\nDirectory Entry Times:\n"); |
1016 | 0 | } |
1017 | |
|
1018 | 0 | tsk_fprintf(a_hFile, "Written:\t%s\n", tsk_fs_time_to_str(fs_meta->mtime, |
1019 | 0 | timeBuf)); |
1020 | 0 | tsk_fprintf(a_hFile, "Accessed:\t%s\n", |
1021 | 0 | tsk_fs_time_to_str(fs_meta->atime, timeBuf)); |
1022 | 0 | tsk_fprintf(a_hFile, "Created:\t%s\n", |
1023 | 0 | tsk_fs_time_to_str(fs_meta->crtime, timeBuf)); |
1024 | | |
1025 | | /* Print the specified number of sector addresses. */ |
1026 | 0 | tsk_fprintf(a_hFile, "\nSectors:\n"); |
1027 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
1028 | 0 | const TSK_FS_ATTR *fs_attr_default = |
1029 | 0 | tsk_fs_file_attr_get_type(fs_file.get(), |
1030 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, 0, 0); |
1031 | 0 | if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) { |
1032 | 0 | if (tsk_fs_attr_print(fs_attr_default, a_hFile)) { |
1033 | 0 | tsk_fprintf(a_hFile, "\nError creating run lists\n"); |
1034 | 0 | tsk_error_print(a_hFile); |
1035 | 0 | tsk_error_reset(); |
1036 | 0 | } |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 | else { |
1040 | |
|
1041 | 0 | if (a_numblock > 0) { |
1042 | | /* A bad hack to force a specified number of blocks */ |
1043 | 0 | fs_meta->size = a_numblock * a_fs->block_size; |
1044 | 0 | } |
1045 | 0 | print.istat_seen = 0; |
1046 | 0 | print.idx = 0; |
1047 | 0 | print.hFile = a_hFile; |
1048 | 0 | if (tsk_fs_file_walk(fs_file.get(), |
1049 | 0 | (TSK_FS_FILE_WALK_FLAG_ENUM)(TSK_FS_FILE_WALK_FLAG_AONLY | TSK_FS_FILE_WALK_FLAG_SLACK), |
1050 | 0 | print_addr_act, (void *)&print)) { |
1051 | 0 | tsk_fprintf(a_hFile, "\nError reading file\n"); |
1052 | 0 | tsk_error_print(a_hFile); |
1053 | 0 | tsk_error_reset(); |
1054 | 0 | } |
1055 | 0 | else if (print.idx != 0) { |
1056 | 0 | tsk_fprintf(a_hFile, "\n"); |
1057 | 0 | } |
1058 | 0 | } |
1059 | |
|
1060 | 0 | return 0; |
1061 | 0 | } |
1062 | | |
1063 | | /* Mark the sector used in the bitmap */ |
1064 | | static TSK_WALK_RET_ENUM |
1065 | | inode_walk_file_act( |
1066 | | [[maybe_unused]] TSK_FS_FILE * fs_file, |
1067 | | [[maybe_unused]] TSK_OFF_T a_off, |
1068 | | TSK_DADDR_T addr, |
1069 | | [[maybe_unused]] char *buf, |
1070 | | [[maybe_unused]] size_t size, |
1071 | | [[maybe_unused]] TSK_FS_BLOCK_FLAG_ENUM a_flags, |
1072 | | void *a_ptr) |
1073 | 18.4k | { |
1074 | 18.4k | setbit((uint8_t *) a_ptr, addr); |
1075 | 18.4k | return TSK_WALK_CONT; |
1076 | 18.4k | } |
1077 | | |
1078 | | /* The inode_walk call back for each file. we want only the directories */ |
1079 | | static TSK_WALK_RET_ENUM |
1080 | | inode_walk_dent_act( |
1081 | | TSK_FS_FILE * fs_file, |
1082 | | [[maybe_unused]] const char *a_path, |
1083 | | void *a_ptr) |
1084 | 58.9k | { |
1085 | 58.9k | unsigned int flags = TSK_FS_FILE_WALK_FLAG_SLACK | TSK_FS_FILE_WALK_FLAG_AONLY; |
1086 | | |
1087 | 58.9k | if ((fs_file->meta == NULL) |
1088 | 58.9k | || ( ! TSK_FS_IS_DIR_META(fs_file->meta->type))) |
1089 | 52.5k | return TSK_WALK_CONT; |
1090 | | |
1091 | | /* Get the sector addresses & ignore any errors */ |
1092 | 6.41k | if (tsk_fs_file_walk(fs_file, |
1093 | 6.41k | (TSK_FS_FILE_WALK_FLAG_ENUM)flags, |
1094 | 6.41k | inode_walk_file_act, a_ptr)) { |
1095 | 728 | tsk_error_reset(); |
1096 | 728 | } |
1097 | | |
1098 | 6.41k | return TSK_WALK_CONT; |
1099 | 58.9k | } |
1100 | | |
1101 | | /** |
1102 | | * Walk the inodes in a specified range and do a TSK_FS_META_WALK_CB callback |
1103 | | * for each inode that satisfies criteria specified by a set of |
1104 | | * TSK_FS_META_FLAG_ENUM flags. The following flags are supported: |
1105 | | * TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN, |
1106 | | * TSK_FS_META_FLAG_USED (FATXX only), and TSK_FS_META_FLAG_UNUSED |
1107 | | * (FATXX only). |
1108 | | * |
1109 | | * @param [in] a_fs File system that contains the inodes. |
1110 | | * @param [in] a_start_inum Inclusive lower bound of inode range. |
1111 | | * @param [in] a_end_inum Inclusive upper bound of inode range. |
1112 | | * @param [in] a_selection_flags Inode selection criteria. |
1113 | | * @param [in] a_action Callback function for selected inodes. |
1114 | | * @param [in] a_ptr Private data pointer passed through to callback function. |
1115 | | * @return 0 on success, 1 on failure, per TSK convention |
1116 | | */ |
1117 | | uint8_t |
1118 | | fatfs_inode_walk(TSK_FS_INFO *a_fs, TSK_INUM_T a_start_inum, |
1119 | | TSK_INUM_T a_end_inum, TSK_FS_META_FLAG_ENUM a_selection_flags, |
1120 | | TSK_FS_META_WALK_CB a_action, void *a_ptr) |
1121 | 32 | { |
1122 | 32 | const char *func_name = "fatfs_inode_walk"; |
1123 | 32 | FATFS_INFO *fatfs = (FATFS_INFO*)a_fs; |
1124 | 32 | unsigned int flags = a_selection_flags; |
1125 | 32 | TSK_INUM_T end_inum_tmp = 0; |
1126 | 32 | TSK_DADDR_T ssect = 0; |
1127 | 32 | TSK_DADDR_T lsect = 0; |
1128 | 32 | TSK_DADDR_T sect = 0; |
1129 | 32 | char *dino_buf = NULL; |
1130 | 32 | FATFS_DENTRY *dep = NULL; |
1131 | 32 | unsigned int dentry_idx = 0; |
1132 | 32 | uint8_t *dir_sectors_bitmap = NULL; |
1133 | 32 | ssize_t cnt = 0; |
1134 | 32 | uint8_t done = 0; |
1135 | | |
1136 | 32 | tsk_error_reset(); |
1137 | 32 | if (fatfs_ptr_arg_is_null(a_fs, "a_fs", func_name) || |
1138 | 32 | fatfs_ptr_arg_is_null(*(void **) &a_action, "a_action", func_name)) { |
1139 | 0 | return 1; |
1140 | 0 | } |
1141 | | |
1142 | 32 | if (a_start_inum < a_fs->first_inum || a_start_inum > a_fs->last_inum) { |
1143 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1144 | 0 | tsk_error_set_errstr("%s: Begin inode out of range: %" PRIuINUM "", |
1145 | 0 | func_name, a_start_inum); |
1146 | 0 | return 1; |
1147 | 0 | } |
1148 | 32 | else if (a_end_inum < a_fs->first_inum || |
1149 | 32 | a_end_inum > a_fs->last_inum || |
1150 | 32 | a_end_inum < a_start_inum) { |
1151 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1152 | 0 | tsk_error_set_errstr("%s: End inode out of range: %" PRIuINUM "", |
1153 | 0 | func_name, a_end_inum); |
1154 | 0 | return 1; |
1155 | 0 | } |
1156 | | |
1157 | | /* FAT file systems do not really have the concept of unused inodes. */ |
1158 | 32 | if ((flags & TSK_FS_META_FLAG_UNUSED) && !(flags & TSK_FS_META_FLAG_USED)) { |
1159 | 0 | return 0; |
1160 | 0 | } |
1161 | 32 | flags |= TSK_FS_META_FLAG_USED; |
1162 | 32 | flags &= ~TSK_FS_META_FLAG_UNUSED; |
1163 | | |
1164 | | /* Make sure the inode selection flags are set correctly. */ |
1165 | 32 | if (flags & TSK_FS_META_FLAG_ORPHAN) { |
1166 | | /* If ORPHAN file inodes are wanted, make sure that the UNALLOC |
1167 | | * selection flag is set. */ |
1168 | 0 | flags |= TSK_FS_META_FLAG_UNALLOC; |
1169 | 0 | flags &= ~TSK_FS_META_FLAG_ALLOC; |
1170 | 0 | } |
1171 | 32 | else { |
1172 | | /* If neither of the ALLOC or UNALLOC inode selection flags are set, |
1173 | | * then set them both. */ |
1174 | 32 | if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) && |
1175 | 32 | ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) { |
1176 | 0 | flags |= (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC); |
1177 | 0 | } |
1178 | 32 | } |
1179 | | |
1180 | 32 | if (tsk_verbose) { |
1181 | 0 | tsk_fprintf(stderr, |
1182 | 0 | "%s: Inode walking %" PRIuINUM " to %" |
1183 | 0 | PRIuINUM "\n", func_name, a_start_inum, a_end_inum); |
1184 | 0 | } |
1185 | | |
1186 | | /* If we are looking for orphan files and have not yet populated |
1187 | | * the list of files reachable by name for this file system, do so now. |
1188 | | */ |
1189 | 32 | if ((flags & TSK_FS_META_FLAG_ORPHAN)) { |
1190 | 0 | if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) { |
1191 | 0 | tsk_error_errstr2_concat( |
1192 | 0 | "%s: Identifying orphan inodes", func_name); |
1193 | 0 | return 1; |
1194 | 0 | } |
1195 | 0 | } |
1196 | | |
1197 | | /* Allocate a TSK_FS_FILE object with a TSK_FS_META object to populate and |
1198 | | * pass to the callback function when an inode that fits the inode |
1199 | | * selection criteria is found. */ |
1200 | 32 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
1201 | 32 | tsk_fs_file_alloc(a_fs), |
1202 | 32 | tsk_fs_file_close |
1203 | 32 | }; |
1204 | 32 | if (!fs_file) { |
1205 | 0 | return 1; |
1206 | 0 | } |
1207 | | |
1208 | 32 | if ((fs_file->meta = |
1209 | 32 | tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) { |
1210 | 0 | return 1; |
1211 | 0 | } |
1212 | | |
1213 | | /* Process the root directory inode, if it's included in the walk. */ |
1214 | 32 | if (a_start_inum == a_fs->root_inum) { |
1215 | 32 | if (((TSK_FS_META_FLAG_ALLOC & flags) == TSK_FS_META_FLAG_ALLOC) |
1216 | 0 | && ((TSK_FS_META_FLAG_ORPHAN & flags) == 0)) { |
1217 | 0 | TSK_WALK_RET_ENUM retval = TSK_WALK_CONT; |
1218 | |
|
1219 | 0 | if (fatfs_make_root(fatfs, fs_file->meta)) { |
1220 | 0 | return 1; |
1221 | 0 | } |
1222 | | |
1223 | 0 | retval = a_action(fs_file.get(), a_ptr); |
1224 | 0 | if (retval == TSK_WALK_STOP) { |
1225 | 0 | return 0; |
1226 | 0 | } |
1227 | 0 | else if (retval == TSK_WALK_ERROR) { |
1228 | 0 | return 1; |
1229 | 0 | } |
1230 | 0 | } |
1231 | | |
1232 | 32 | a_start_inum++; |
1233 | 32 | if (a_start_inum == a_end_inum) { |
1234 | 0 | return 0; |
1235 | 0 | } |
1236 | 32 | } |
1237 | 32 | size_t bitmap_len = (a_fs->block_count + 7) / 8; |
1238 | | |
1239 | | // Taking 128 MiB as an arbitrary upper bound |
1240 | 32 | if ((bitmap_len == 0) || (bitmap_len > (128 * 1024 * 1024))) { |
1241 | 2 | return 1; |
1242 | 2 | } |
1243 | | |
1244 | | /* Allocate a bitmap to keep track of which sectors are allocated to |
1245 | | * directories. */ |
1246 | 30 | if ((dir_sectors_bitmap = (uint8_t*)tsk_malloc(bitmap_len)) == NULL) { |
1247 | 0 | return 1; |
1248 | 0 | } |
1249 | | |
1250 | | /* If not doing an orphan files search, populate the directory sectors |
1251 | | * bitmap. The bitmap will be used to make sure that no sector marked as |
1252 | | * allocated to a directory is skipped when searching for directory |
1253 | | * entries to map to inodes. */ |
1254 | 30 | if ((flags & TSK_FS_META_FLAG_ORPHAN) == 0) { |
1255 | 30 | if (tsk_verbose) { |
1256 | 0 | tsk_fprintf(stderr, |
1257 | 0 | "fatfs_inode_walk: Walking directories to collect sector info\n"); |
1258 | 0 | } |
1259 | | |
1260 | | /* Manufacture an inode for the root directory. */ |
1261 | 30 | if (fatfs_make_root(fatfs, fs_file->meta)) { |
1262 | 0 | free(dir_sectors_bitmap); |
1263 | 0 | return 1; |
1264 | 0 | } |
1265 | | |
1266 | | /* Do a file_walk on the root directory to set the bits in the |
1267 | | * directory sectors bitmap for each sector allocated to the root |
1268 | | * directory. */ |
1269 | 30 | if (tsk_fs_file_walk(fs_file.get(), |
1270 | 30 | (TSK_FS_FILE_WALK_FLAG_ENUM)(TSK_FS_FILE_WALK_FLAG_SLACK | TSK_FS_FILE_WALK_FLAG_AONLY), |
1271 | 30 | inode_walk_file_act, (void*)dir_sectors_bitmap)) { |
1272 | 0 | free(dir_sectors_bitmap); |
1273 | 0 | return 1; |
1274 | 0 | } |
1275 | | |
1276 | | /* Now walk recursively through the entire directory tree to set the |
1277 | | * bits in the directory sectors bitmap for each sector allocated to |
1278 | | * the children of the root directory. */ |
1279 | 30 | if (tsk_fs_dir_walk(a_fs, a_fs->root_inum, |
1280 | 30 | (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE | |
1281 | 30 | TSK_FS_DIR_WALK_FLAG_NOORPHAN), inode_walk_dent_act, |
1282 | 30 | (void *) dir_sectors_bitmap)) { |
1283 | 0 | tsk_error_errstr2_concat |
1284 | 0 | ("- fatfs_inode_walk: mapping directories"); |
1285 | 0 | free(dir_sectors_bitmap); |
1286 | 0 | return 1; |
1287 | 0 | } |
1288 | 30 | } |
1289 | | |
1290 | | /* If the end inode is the one of the virtual virtual FAT files or the |
1291 | | * virtual orphan files directory, adjust the end inum and handle the |
1292 | | * virtual inodes after the main inode walking loop below completes. */ |
1293 | 30 | if (a_end_inum > a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs)) { |
1294 | 30 | end_inum_tmp = a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs); |
1295 | 30 | } |
1296 | 0 | else { |
1297 | 0 | end_inum_tmp = a_end_inum; |
1298 | 0 | } |
1299 | | |
1300 | | /* Map the begin and end inodes to the sectors that contain them. |
1301 | | * This sets the image level boundaries for the inode walking loop. */ |
1302 | 30 | ssect = FATFS_INODE_2_SECT(fatfs, a_start_inum); |
1303 | 30 | if (ssect > a_fs->last_block) { |
1304 | 0 | tsk_error_reset(); |
1305 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1306 | 0 | tsk_error_set_errstr |
1307 | 0 | ("%s: Begin inode in sector too big for image: %" |
1308 | 0 | PRIuDADDR, func_name, ssect); |
1309 | 0 | free(dir_sectors_bitmap); |
1310 | 0 | return 1; |
1311 | 0 | } |
1312 | | |
1313 | 30 | lsect = FATFS_INODE_2_SECT(fatfs, end_inum_tmp); |
1314 | 30 | if (lsect > a_fs->last_block) { |
1315 | 0 | tsk_error_reset(); |
1316 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1317 | 0 | tsk_error_set_errstr |
1318 | 0 | ("%s: End inode in sector too big for image: %" |
1319 | 0 | PRIuDADDR, func_name, lsect); |
1320 | 0 | free(dir_sectors_bitmap); |
1321 | 0 | return 1; |
1322 | 0 | } |
1323 | | |
1324 | | /* Allocate a buffer big enough to read in a cluster at a time. */ |
1325 | 30 | if ((dino_buf = (char*)tsk_malloc(fatfs->csize << fatfs->ssize_sh)) == |
1326 | 30 | NULL) { |
1327 | 0 | free(dir_sectors_bitmap); |
1328 | 0 | return 1; |
1329 | 0 | } |
1330 | | |
1331 | | /* Walk the inodes. */ |
1332 | 30 | sect = ssect; |
1333 | 17.5k | while (sect <= lsect) { |
1334 | 17.5k | int cluster_is_alloc = 0; |
1335 | 17.5k | size_t num_sectors_to_process = 0; |
1336 | 17.5k | size_t sector_idx = 0; |
1337 | 17.5k | uint8_t do_basic_dentry_test = 0; |
1338 | | |
1339 | | /* Read in a chunk of the image to process on this iteration of the inode |
1340 | | * walk. The actual size of the read will depend on whether or not it is |
1341 | | * coming from the root directory of a FAT12 or FAT16 file system. As |
1342 | | * indicated by the size of the buffer, the data area (exFAT cluster |
1343 | | * heap) will for the most part be read in a cluster at a time. |
1344 | | * However, the root directory for a FAT12/FAT16 file system precedes |
1345 | | * the data area and the read size for it should be a sector, not a |
1346 | | * cluster. */ |
1347 | 17.5k | if (sect < fatfs->firstclustsect) { |
1348 | | |
1349 | 4.75k | if ((flags & TSK_FS_META_FLAG_ORPHAN) != 0) { |
1350 | | /* If orphan file hunting, there are no orphans in the root |
1351 | | * directory, so skip ahead to the data area. */ |
1352 | 0 | sect = fatfs->firstclustsect; |
1353 | 0 | continue; |
1354 | 0 | } |
1355 | | |
1356 | | /* Read in a FAT12/FAT16 root directory sector. */ |
1357 | 4.75k | cnt = tsk_fs_read_block(a_fs, sect, dino_buf, fatfs->ssize); |
1358 | 4.75k | if (cnt != fatfs->ssize) { |
1359 | 0 | if (cnt >= 0) { |
1360 | 0 | tsk_error_reset(); |
1361 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1362 | 0 | } |
1363 | 0 | tsk_error_set_errstr2 |
1364 | 0 | ("%s (root dir): sector: %" PRIuDADDR, |
1365 | 0 | func_name, sect); |
1366 | 0 | free(dir_sectors_bitmap); |
1367 | 0 | free(dino_buf); |
1368 | 0 | return 1; |
1369 | 0 | } |
1370 | | |
1371 | 4.75k | cluster_is_alloc = 1; |
1372 | 4.75k | num_sectors_to_process = 1; |
1373 | 4.75k | } |
1374 | 12.7k | else { |
1375 | | /* The walk has proceeded into the data area (exFAT cluster heap). |
1376 | | * It's time to read in a cluster at a time. Get the base sector |
1377 | | * for the cluster that contains the current sector. */ |
1378 | 12.7k | sect = |
1379 | 12.7k | FATFS_CLUST_2_SECT(fatfs, (FATFS_SECT_2_CLUST(fatfs, |
1380 | 12.7k | sect))); |
1381 | | |
1382 | | /* Determine whether the cluster is allocated. Skip it if it is |
1383 | | * not allocated and the UNALLOCATED inode selection flag is not |
1384 | | * set. */ |
1385 | 12.7k | cluster_is_alloc = fatfs_is_sectalloc(fatfs, sect); |
1386 | 12.7k | if ((cluster_is_alloc == 0) |
1387 | 9.27k | && ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) { |
1388 | 0 | sect += fatfs->csize; |
1389 | 0 | continue; |
1390 | 0 | } |
1391 | 12.7k | else if (cluster_is_alloc == -1) { |
1392 | 0 | free(dir_sectors_bitmap); |
1393 | 0 | free(dino_buf); |
1394 | 0 | return 1; |
1395 | 0 | } |
1396 | | |
1397 | | /* If the cluster is allocated but is not allocated to a |
1398 | | * directory, then skip it. NOTE: This will miss orphan file |
1399 | | * entries in the slack space of files. |
1400 | | */ |
1401 | 12.7k | if ((cluster_is_alloc == 1) && (isset(dir_sectors_bitmap, sect) == 0)) { |
1402 | 3.46k | sect += fatfs->csize; |
1403 | 3.46k | continue; |
1404 | 3.46k | } |
1405 | | |
1406 | | /* The final cluster may not be full. */ |
1407 | 9.29k | if (lsect - sect + 1 < fatfs->csize) { |
1408 | 16 | num_sectors_to_process = (size_t) (lsect - sect + 1); |
1409 | 16 | } |
1410 | 9.27k | else { |
1411 | 9.27k | num_sectors_to_process = fatfs->csize; |
1412 | 9.27k | } |
1413 | | |
1414 | | /* Read in a cluster. */ |
1415 | 9.29k | cnt = tsk_fs_read_block |
1416 | 9.29k | (a_fs, sect, dino_buf, num_sectors_to_process << fatfs->ssize_sh); |
1417 | 9.29k | if (cnt != (ssize_t)(num_sectors_to_process << fatfs->ssize_sh)) { |
1418 | 0 | if (cnt >= 0) { |
1419 | 0 | tsk_error_reset(); |
1420 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1421 | 0 | } |
1422 | 0 | tsk_error_set_errstr2("%s: sector: %" |
1423 | 0 | PRIuDADDR, func_name, sect); |
1424 | 0 | free(dir_sectors_bitmap); |
1425 | 0 | free(dino_buf); |
1426 | 0 | return 1; |
1427 | 0 | } |
1428 | 9.29k | } |
1429 | | |
1430 | | /* Now that the sectors are read in, prepare to step through them in |
1431 | | * directory entry size chunks. Only do a basic test to confirm the |
1432 | | * contents of each chunk is a directory entry unless the sector that |
1433 | | * contains it is not allocated to a directory or is unallocated.*/ |
1434 | 14.0k | do_basic_dentry_test = 1; |
1435 | 14.0k | if ((isset(dir_sectors_bitmap, sect) == 0) || (cluster_is_alloc == 0)) { |
1436 | 9.27k | do_basic_dentry_test = 0; |
1437 | 9.27k | } |
1438 | | |
1439 | | /* Walk through the sectors read in. */ |
1440 | 52.3k | for (sector_idx = 0; sector_idx < num_sectors_to_process; sector_idx++) { |
1441 | 38.2k | TSK_INUM_T inum = 0; |
1442 | | |
1443 | | /* If the last inode in this sector is before the start |
1444 | | * inode, skip the sector. */ |
1445 | 38.2k | if (FATFS_SECT_2_INODE(fatfs, sect + 1) < a_start_inum) { |
1446 | 0 | sect++; |
1447 | 0 | continue; |
1448 | 0 | } |
1449 | | |
1450 | | /* Advance the directory entry pointer to the start of the |
1451 | | * sector. */ |
1452 | 38.2k | dep = (FATFS_DENTRY*)(&dino_buf[sector_idx << fatfs->ssize_sh]); |
1453 | | |
1454 | | /* If the sector is not allocated to a directory and the first |
1455 | | * chunk is not a directory entry, skip the sector. */ |
1456 | 38.2k | if (!isset(dir_sectors_bitmap, sect) && |
1457 | 33.0k | !fatfs->is_dentry(fatfs, dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)cluster_is_alloc, do_basic_dentry_test)) { |
1458 | 24.3k | sect++; |
1459 | 24.3k | continue; |
1460 | 24.3k | } |
1461 | | |
1462 | | /* Get the base inode address of this sector. */ |
1463 | 13.9k | inum = FATFS_SECT_2_INODE(fatfs, sect); |
1464 | 13.9k | if (tsk_verbose) { |
1465 | 0 | tsk_fprintf(stderr, |
1466 | 0 | "%s: Processing sector %" PRIuDADDR |
1467 | 0 | " starting at inode %" PRIuINUM "\n", func_name, sect, inum); |
1468 | 0 | } |
1469 | | |
1470 | | /* Walk through the potential directory entries in the sector. */ |
1471 | 304k | for (dentry_idx = 0; dentry_idx < fatfs->dentry_cnt_se; |
1472 | 290k | dentry_idx++, inum++, dep++) { |
1473 | 290k | int retval; |
1474 | 290k | TSK_RETVAL_ENUM retval2 = TSK_OK; |
1475 | | |
1476 | | /* If the inode address of the potential entry is less than |
1477 | | * the beginning inode address for the inode walk, skip it. */ |
1478 | 290k | if (inum < a_start_inum) { |
1479 | 0 | continue; |
1480 | 0 | } |
1481 | | |
1482 | | /* If inode address of the potential entry is greater than the |
1483 | | * ending inode address for the walk, terminate the inode walk. */ |
1484 | 290k | if (inum > end_inum_tmp) { |
1485 | 0 | done = 1; |
1486 | 0 | break; |
1487 | 0 | } |
1488 | | |
1489 | | /* If the potential entry is likely not an entry, or it is an |
1490 | | * entry that is not reported in an inode walk, or it does not |
1491 | | * satisfy the inode selection flags, then skip it. */ |
1492 | 290k | if (!fatfs->is_dentry(fatfs, dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)cluster_is_alloc, do_basic_dentry_test) || |
1493 | 173k | fatfs->inode_walk_should_skip_dentry(fatfs, inum, dep, flags, cluster_is_alloc)) { |
1494 | 142k | continue; |
1495 | 142k | } |
1496 | | |
1497 | 148k | retval2 = fatfs->dinode_copy(fatfs, inum, dep, cluster_is_alloc, fs_file.get()); |
1498 | | |
1499 | 148k | if (retval2 != TSK_OK) { |
1500 | 0 | if (retval2 == TSK_COR) { |
1501 | | /* Corrupted, move on to the next chunk. */ |
1502 | 0 | if (tsk_verbose) { |
1503 | 0 | tsk_error_print(stderr); |
1504 | 0 | } |
1505 | 0 | tsk_error_reset(); |
1506 | 0 | continue; |
1507 | 0 | } |
1508 | 0 | else { |
1509 | 0 | free(dir_sectors_bitmap); |
1510 | 0 | free(dino_buf); |
1511 | 0 | return 1; |
1512 | 0 | } |
1513 | 0 | } |
1514 | | |
1515 | 148k | if (tsk_verbose) { |
1516 | 0 | tsk_fprintf(stderr, |
1517 | 0 | "%s: Directory Entry %" PRIuINUM |
1518 | 0 | " (%u) at sector %" PRIuDADDR "\n", func_name, inum, dentry_idx, |
1519 | 0 | sect); |
1520 | 0 | } |
1521 | | |
1522 | | /* Do the callback. */ |
1523 | 148k | retval = a_action(fs_file.get(), a_ptr); |
1524 | 148k | if (retval == TSK_WALK_STOP) { |
1525 | 0 | free(dir_sectors_bitmap); |
1526 | 0 | free(dino_buf); |
1527 | 0 | return 0; |
1528 | 0 | } |
1529 | 148k | else if (retval == TSK_WALK_ERROR) { |
1530 | 0 | free(dir_sectors_bitmap); |
1531 | 0 | free(dino_buf); |
1532 | 0 | return 1; |
1533 | 0 | } |
1534 | 148k | } |
1535 | 13.9k | sect++; |
1536 | 13.9k | if (done) { |
1537 | 0 | break; |
1538 | 0 | } |
1539 | 13.9k | } |
1540 | 14.0k | if (done) { |
1541 | 0 | break; |
1542 | 0 | } |
1543 | 14.0k | } |
1544 | | |
1545 | 30 | free(dir_sectors_bitmap); |
1546 | 30 | free(dino_buf); |
1547 | | |
1548 | | // handle the virtual orphans folder and FAT files if they asked for them |
1549 | 30 | if ((a_end_inum > a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs)) |
1550 | 30 | && (flags & TSK_FS_META_FLAG_ALLOC) |
1551 | 0 | && ((flags & TSK_FS_META_FLAG_ORPHAN) == 0)) { |
1552 | 0 | TSK_INUM_T inum; |
1553 | | |
1554 | | // cycle through the special files |
1555 | 0 | for (inum = a_fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1; |
1556 | 0 | inum <= a_end_inum; inum++) { |
1557 | 0 | int retval; |
1558 | |
|
1559 | 0 | tsk_fs_meta_reset(fs_file->meta); |
1560 | |
|
1561 | 0 | if (inum == fatfs->mbr_virt_inum) { |
1562 | 0 | if (fatfs_make_mbr(fatfs, fs_file->meta)) { |
1563 | 0 | return 1; |
1564 | 0 | } |
1565 | 0 | } |
1566 | 0 | else if (inum == fatfs->fat1_virt_inum) { |
1567 | 0 | if (fatfs_make_fat(fatfs, 1, fs_file->meta)) { |
1568 | 0 | return 1; |
1569 | 0 | } |
1570 | 0 | } |
1571 | 0 | else if (inum == fatfs->fat2_virt_inum && fatfs->numfat == 2) { |
1572 | 0 | if (fatfs_make_fat(fatfs, 2, fs_file->meta)) { |
1573 | 0 | return 1; |
1574 | 0 | } |
1575 | 0 | } |
1576 | 0 | else if (inum == TSK_FS_ORPHANDIR_INUM(a_fs)) { |
1577 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(a_fs, fs_file->meta)) { |
1578 | 0 | return 1; |
1579 | 0 | } |
1580 | 0 | } |
1581 | | |
1582 | 0 | retval = a_action(fs_file.get(), a_ptr); |
1583 | 0 | if (retval == TSK_WALK_STOP) { |
1584 | 0 | return 0; |
1585 | 0 | } |
1586 | 0 | else if (retval == TSK_WALK_ERROR) { |
1587 | 0 | return 1; |
1588 | 0 | } |
1589 | 0 | } |
1590 | 0 | } |
1591 | | |
1592 | 30 | return 0; |
1593 | 30 | } |