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