/src/sleuthkit/tsk/fs/fatxxfs_meta.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** fatxxfs |
3 | | ** The Sleuth Kit |
4 | | ** |
5 | | ** Content and meta data layer support for the FATXX 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 fatxxfs.c |
23 | | * Contains the internal TSK FATXX (FAT12, FAT16, FAT32) file system code to |
24 | | * handle basic file system processing for opening file system, processing |
25 | | * sectors, and directory entries. |
26 | | */ |
27 | | |
28 | | #include "tsk_fatxxfs.h" |
29 | | #include <assert.h> |
30 | | |
31 | | /* |
32 | | * Identify if the dentry is a valid 8.3 name |
33 | | * |
34 | | * returns 1 if it is, 0 if it does not |
35 | | */ |
36 | | static uint8_t |
37 | | is_83_name(FATXXFS_DENTRY * de) |
38 | 35.4k | { |
39 | 35.4k | if (!de) |
40 | 0 | return 0; |
41 | | |
42 | | /* The IS_NAME macro will fail if the value is 0x05, which is only |
43 | | * valid in name[0], similarly with '.' */ |
44 | 35.4k | if ((de->name[0] != FATXXFS_SLOT_E5) && (de->name[0] != '.') && |
45 | 35.4k | (FATXXFS_IS_83_NAME(de->name[0]) == 0)) { |
46 | 27.1k | if (tsk_verbose) |
47 | 0 | fprintf(stderr, "fatfs_is_83_name: name[0] is invalid\n"); |
48 | 27.1k | return 0; |
49 | 27.1k | } |
50 | | |
51 | | // the name cannot start with 0x20 |
52 | 8.31k | else if (de->name[0] == 0x20) { |
53 | 1 | if (tsk_verbose) |
54 | 0 | fprintf(stderr, "fatfs_is_83_name: name[0] has 0x20\n"); |
55 | 1 | return 0; |
56 | 1 | } |
57 | | |
58 | | /* the second name field can only be . if the first one is a . */ |
59 | 8.31k | if (de->name[1] == '.') { |
60 | 2 | if (de->name[0] != '.') { |
61 | 2 | if (tsk_verbose) |
62 | 0 | fprintf(stderr, "fatfs_is_83_name: name[1] is .\n"); |
63 | 2 | return 0; |
64 | 2 | } |
65 | 2 | } |
66 | 8.31k | else if (FATXXFS_IS_83_NAME(de->name[1]) == 0) { |
67 | 957 | if (tsk_verbose) |
68 | 0 | fprintf(stderr, "fatfs_is_83_name: name[1] is invalid\n"); |
69 | 957 | return 0; |
70 | 957 | } |
71 | | |
72 | 7.35k | if (FATXXFS_IS_83_NAME(de->name[2]) == 0) { |
73 | 2.29k | if (tsk_verbose) |
74 | 0 | fprintf(stderr, "fatfs_is_83_name: name[2] is invalid\n"); |
75 | 2.29k | return 0; |
76 | 2.29k | } |
77 | 5.06k | else if (FATXXFS_IS_83_NAME(de->name[3]) == 0) { |
78 | 2.46k | if (tsk_verbose) |
79 | 0 | fprintf(stderr, "fatfs_is_83_name: name[3] is invalid\n"); |
80 | 2.46k | return 0; |
81 | 2.46k | } |
82 | 2.59k | else if (FATXXFS_IS_83_NAME(de->name[4]) == 0) { |
83 | 2.46k | if (tsk_verbose) |
84 | 0 | fprintf(stderr, "fatfs_is_83_name: name[4] is invalid\n"); |
85 | 2.46k | return 0; |
86 | 2.46k | } |
87 | 129 | else if (FATXXFS_IS_83_NAME(de->name[5]) == 0) { |
88 | 31 | if (tsk_verbose) |
89 | 0 | fprintf(stderr, "fatfs_is_83_name: name[5] is invalid\n"); |
90 | 31 | return 0; |
91 | 31 | } |
92 | 98 | else if (FATXXFS_IS_83_NAME(de->name[6]) == 0) { |
93 | 25 | if (tsk_verbose) |
94 | 0 | fprintf(stderr, "fatfs_is_83_name: name[6] is invalid\n"); |
95 | 25 | return 0; |
96 | 25 | } |
97 | 73 | else if (FATXXFS_IS_83_NAME(de->name[7]) == 0) { |
98 | 17 | if (tsk_verbose) |
99 | 0 | fprintf(stderr, "fatfs_is_83_name: name[7] is invalid\n"); |
100 | 17 | return 0; |
101 | 17 | } |
102 | 56 | else if (FATXXFS_IS_83_NAME(de->ext[0]) == 0) { |
103 | 15 | if (tsk_verbose) |
104 | 0 | fprintf(stderr, "fatfs_is_83_name: ext[0] is invalid\n"); |
105 | 15 | return 0; |
106 | 15 | } |
107 | 41 | else if (FATXXFS_IS_83_NAME(de->ext[1]) == 0) { |
108 | 18 | if (tsk_verbose) |
109 | 0 | fprintf(stderr, "fatfs_is_83_name: ext[1] is invalid\n"); |
110 | 18 | return 0; |
111 | 18 | } |
112 | 23 | else if (FATXXFS_IS_83_NAME(de->ext[2]) == 0) { |
113 | 19 | if (tsk_verbose) |
114 | 0 | fprintf(stderr, "fatfs_is_83_name: ext[2] is invalid\n"); |
115 | 19 | return 0; |
116 | 19 | } |
117 | | |
118 | | /* Ensure that if we get a "space", that the rest of the |
119 | | * name is spaces. This is not in the spec, but is how |
120 | | * windows operates and serves as a good check to remove |
121 | | * false positives. We do not do this check for the |
122 | | * volume label though. */ |
123 | 4 | if ((de->attrib & FATFS_ATTR_VOLUME) != FATFS_ATTR_VOLUME) { |
124 | 3 | if (((de->name[1] == 0x20) && (de->name[2] != 0x20)) || |
125 | 3 | ((de->name[2] == 0x20) && (de->name[3] != 0x20)) || |
126 | 3 | ((de->name[3] == 0x20) && (de->name[4] != 0x20)) || |
127 | 3 | ((de->name[4] == 0x20) && (de->name[5] != 0x20)) || |
128 | 3 | ((de->name[5] == 0x20) && (de->name[6] != 0x20)) || |
129 | 3 | ((de->name[6] == 0x20) && (de->name[7] != 0x20)) || |
130 | 3 | ((de->ext[1] == 0x20) && (de->ext[2] != 0x20))) { |
131 | 0 | if (tsk_verbose) |
132 | 0 | fprintf(stderr, |
133 | 0 | "fatfs_is_83_name: space before non-space\n"); |
134 | 0 | return 0; |
135 | 0 | } |
136 | 3 | } |
137 | | |
138 | 4 | return 1; |
139 | 4 | } |
140 | | |
141 | | /** |
142 | | * \internal |
143 | | * Determine whether a buffer likely contains a directory entry. |
144 | | * For the most reliable results, request the in-depth test. |
145 | | * |
146 | | * @param [in] a_fatfs Source file system for the directory entry. |
147 | | * @param [in] a_dentry Buffer that may contain a directory entry. |
148 | | * @param [in] a_cluster_is_alloc The allocation status, possibly unknown, of the |
149 | | * cluster from which the buffer was filled. |
150 | | * @param [in] a_do_basic_tests_only Whether to do basic or in-depth testing. |
151 | | * @return 1 if the buffer likely contains a directory entry, 0 otherwise |
152 | | */ |
153 | | uint8_t |
154 | | fatxxfs_is_dentry( |
155 | | FATFS_INFO *a_fatfs, |
156 | | FATFS_DENTRY *a_dentry, |
157 | | [[maybe_unused]] FATFS_DATA_UNIT_ALLOC_STATUS_ENUM a_cluster_is_alloc, |
158 | | uint8_t a_do_basic_tests_only) |
159 | 1.88M | { |
160 | 1.88M | const char *func_name = "fatxxfs_is_dentry"; |
161 | 1.88M | TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_fatfs->fs_info; |
162 | 1.88M | FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry; |
163 | | |
164 | 1.88M | if (!a_dentry) |
165 | 0 | return 0; |
166 | | |
167 | | /* LFN have their own checks, which are pretty weak since most |
168 | | * fields are UTF16 */ |
169 | 1.88M | if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { |
170 | 17.8k | FATXXFS_DENTRY_LFN *de_lfn = (FATXXFS_DENTRY_LFN*) dentry; |
171 | | |
172 | 17.8k | if ((de_lfn->seq > (FATXXFS_LFN_SEQ_FIRST | 0x0f)) |
173 | 17.8k | && (de_lfn->seq != FATXXFS_SLOT_DELETED)) { |
174 | 5.98k | if (tsk_verbose) |
175 | 0 | fprintf(stderr, "%s: LFN seq\n", func_name); |
176 | 5.98k | return 0; |
177 | 5.98k | } |
178 | | |
179 | 11.9k | return 1; |
180 | 17.8k | } |
181 | 1.86M | else { |
182 | | // the basic test is only for the 'essential data'. |
183 | 1.86M | if (a_do_basic_tests_only == 0) { |
184 | 302k | if (dentry->ntbyte & ~(FATXXFS_CASE_LOWER_ALL)) { |
185 | 79.4k | if (tsk_verbose) |
186 | 0 | fprintf(stderr, "%s: lower case all\n", func_name); |
187 | 79.4k | return 0; |
188 | 79.4k | } |
189 | 223k | else if (dentry->attrib & ~(FATFS_ATTR_ALL)) { |
190 | 1.95k | if (tsk_verbose) |
191 | 0 | fprintf(stderr, "%s: attribute all\n", func_name); |
192 | 1.95k | return 0; |
193 | 1.95k | } |
194 | | |
195 | | // verify we do not have too many flags set |
196 | 221k | if (dentry->attrib & FATFS_ATTR_VOLUME) { |
197 | 9.10k | if ((dentry->attrib & FATFS_ATTR_DIRECTORY) || |
198 | 9.10k | (dentry->attrib & FATFS_ATTR_READONLY) || |
199 | 9.10k | (dentry->attrib & FATFS_ATTR_ARCHIVE)) { |
200 | 8.72k | if (tsk_verbose) |
201 | 0 | fprintf(stderr, |
202 | 0 | "%s: Vol and Dir/RO/Arch\n", func_name); |
203 | 8.72k | return 0; |
204 | 8.72k | } |
205 | 9.10k | } |
206 | | |
207 | | /* The ctime, cdate, and adate fields are optional and |
208 | | * therefore 0 is a valid value |
209 | | * We have had scenarios where ISDATE and ISTIME return true, |
210 | | * but the unix2dos fail during the conversion. This has been |
211 | | * useful to detect corrupt entries, so we do both. |
212 | | */ |
213 | 212k | if ((tsk_getu16(fs->endian, dentry->ctime) != 0) && |
214 | 212k | (FATFS_ISTIME(tsk_getu16(fs->endian, dentry->ctime)) == 0)) { |
215 | 1.90k | if (tsk_verbose) |
216 | 0 | fprintf(stderr, "%s: ctime\n", func_name); |
217 | 1.90k | return 0; |
218 | 1.90k | } |
219 | 210k | else if ((tsk_getu16(fs->endian, dentry->wtime) != 0) && |
220 | 210k | (FATFS_ISTIME(tsk_getu16(fs->endian, dentry->wtime)) == 0)) { |
221 | 2.47k | if (tsk_verbose) |
222 | 0 | fprintf(stderr, "%s: wtime\n", func_name); |
223 | 2.47k | return 0; |
224 | 2.47k | } |
225 | 208k | else if ((tsk_getu16(fs->endian, dentry->cdate) != 0) && |
226 | 208k | ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->cdate)) == 0) || |
227 | 61.2k | (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->cdate), |
228 | 34.4k | tsk_getu16(fs->endian, dentry->ctime), |
229 | 34.4k | dentry->ctimeten) == 0))) { |
230 | 27.3k | if (tsk_verbose) |
231 | 0 | fprintf(stderr, "%s: cdate\n", func_name); |
232 | 27.3k | return 0; |
233 | 27.3k | } |
234 | 180k | else if (dentry->ctimeten > 200) { |
235 | 157 | if (tsk_verbose) |
236 | 0 | fprintf(stderr, "%s: ctimeten\n", func_name); |
237 | 157 | return 0; |
238 | 157 | } |
239 | 180k | else if ((tsk_getu16(fs->endian, dentry->adate) != 0) && |
240 | 180k | ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->adate)) == 0) || |
241 | 42.3k | (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->adate), |
242 | 22.4k | 0, 0) == 0))) { |
243 | 20.0k | if (tsk_verbose) |
244 | 0 | fprintf(stderr, "%s: adate\n", func_name); |
245 | 20.0k | return 0; |
246 | 20.0k | } |
247 | 160k | else if ((tsk_getu16(fs->endian, dentry->wdate) != 0) && |
248 | 160k | ((FATFS_ISDATE(tsk_getu16(fs->endian, dentry->wdate)) == 0) || |
249 | 46.7k | (fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->wdate), |
250 | 25.6k | tsk_getu16(fs->endian, dentry->wtime), 0) == 0))) { |
251 | 25.6k | if (tsk_verbose) |
252 | 0 | fprintf(stderr, "%s: wdate\n", func_name); |
253 | 25.6k | return 0; |
254 | 25.6k | } |
255 | 212k | } |
256 | | |
257 | | /* verify the starting cluster is small enough */ |
258 | 1.69M | if ((FATXXFS_DENTRY_CLUST(fs, dentry) > (a_fatfs->lastclust)) && |
259 | 1.69M | (FATFS_ISEOF(FATXXFS_DENTRY_CLUST(fs, dentry), a_fatfs->mask) == 0)) { |
260 | 248k | if (tsk_verbose) |
261 | 0 | fprintf(stderr, "%s: start cluster\n", func_name); |
262 | 248k | return 0; |
263 | 248k | } |
264 | | |
265 | | /* Verify the file size is smaller than the data area */ |
266 | 1.44M | else if (tsk_getu32(fs->endian, dentry->size) > |
267 | 1.44M | ((a_fatfs->clustcnt * a_fatfs->csize) << a_fatfs->ssize_sh)) { |
268 | 260k | if (tsk_verbose) |
269 | 0 | fprintf(stderr, "%s: size\n", func_name); |
270 | 260k | return 0; |
271 | 260k | } |
272 | | |
273 | 1.18M | else if ((tsk_getu32(fs->endian, dentry->size) > 0) |
274 | 1.18M | && (FATXXFS_DENTRY_CLUST(fs, dentry) == 0)) { |
275 | 131k | if (tsk_verbose) |
276 | 0 | fprintf(stderr, |
277 | 0 | "%s: non-zero size and NULL starting cluster\n", func_name); |
278 | 131k | return 0; |
279 | 131k | } |
280 | | |
281 | 1.05M | else if((a_fatfs->subtype == TSK_FATFS_SUBTYPE_SPEC) && (is_83_name(dentry) == 0)) |
282 | 35.4k | return 0; |
283 | | |
284 | | // basic sanity check on values |
285 | 1.01M | else if ((tsk_getu16(fs->endian, dentry->ctime) == 0) |
286 | 1.01M | && (tsk_getu16(fs->endian, dentry->wtime) == 0) |
287 | 1.01M | && (tsk_getu16(fs->endian, dentry->cdate) == 0) |
288 | 1.01M | && (tsk_getu16(fs->endian, dentry->adate) == 0) |
289 | 1.01M | && (tsk_getu16(fs->endian, dentry->wdate) == 0) |
290 | 1.01M | && (FATXXFS_DENTRY_CLUST(fs, dentry) == 0) |
291 | 1.01M | && (tsk_getu32(fs->endian, dentry->size) == 0)) { |
292 | 20.0k | if (tsk_verbose) |
293 | 0 | fprintf(stderr, |
294 | 0 | "%s: nearly all values zero\n", func_name); |
295 | 20.0k | return 0; |
296 | 20.0k | } |
297 | | |
298 | 999k | return 1; |
299 | 1.69M | } |
300 | 1.88M | } |
301 | | |
302 | | /* |
303 | | * convert the attribute list in FAT to a UNIX mode |
304 | | */ |
305 | | static TSK_FS_META_TYPE_ENUM |
306 | | attr2type(uint16_t attr) |
307 | 489k | { |
308 | 489k | if (attr & FATFS_ATTR_DIRECTORY) |
309 | 37.8k | return TSK_FS_META_TYPE_DIR; |
310 | 451k | else |
311 | 451k | return TSK_FS_META_TYPE_REG; |
312 | 489k | } |
313 | | |
314 | | static int |
315 | | attr2mode(uint16_t attr) |
316 | 489k | { |
317 | 489k | int mode; |
318 | | |
319 | | /* every file is executable */ |
320 | 489k | mode = |
321 | 489k | (TSK_FS_META_MODE_IXUSR | TSK_FS_META_MODE_IXGRP | |
322 | 489k | TSK_FS_META_MODE_IXOTH); |
323 | | |
324 | 489k | if ((attr & FATFS_ATTR_READONLY) == 0) |
325 | 426k | mode |= |
326 | 426k | (TSK_FS_META_MODE_IRUSR | TSK_FS_META_MODE_IRGRP | |
327 | 426k | TSK_FS_META_MODE_IROTH); |
328 | | |
329 | 489k | if ((attr & FATFS_ATTR_HIDDEN) == 0) |
330 | 438k | mode |= |
331 | 438k | (TSK_FS_META_MODE_IWUSR | TSK_FS_META_MODE_IWGRP | |
332 | 438k | TSK_FS_META_MODE_IWOTH); |
333 | | |
334 | 489k | return mode; |
335 | 489k | } |
336 | | |
337 | | TSK_RETVAL_ENUM |
338 | | fatxxfs_dinode_copy(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, |
339 | | FATFS_DENTRY *a_dentry, uint8_t a_cluster_is_alloc, TSK_FS_FILE *a_fs_file) |
340 | 489k | { |
341 | 489k | const char *func_name = "fatxxfs_dinode_copy"; |
342 | 489k | int i; |
343 | 489k | TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_fatfs->fs_info; |
344 | 489k | TSK_FS_META *fs_meta = a_fs_file->meta; |
345 | 489k | FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry; |
346 | 489k | TSK_DADDR_T *addr_ptr; |
347 | 489k | uint32_t flags = 0; |
348 | | |
349 | 489k | if (fs_meta->content_len < FATFS_FILE_CONTENT_LEN) { |
350 | 0 | if ((fs_meta = |
351 | 0 | tsk_fs_meta_realloc(fs_meta, |
352 | 0 | FATFS_FILE_CONTENT_LEN)) == NULL) { |
353 | 0 | return TSK_ERR; |
354 | 0 | } |
355 | 0 | } |
356 | | |
357 | 489k | fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
358 | 489k | if (fs_meta->attr) { |
359 | 40.6k | tsk_fs_attrlist_markunused(fs_meta->attr); |
360 | 40.6k | } |
361 | | |
362 | 489k | fs_meta->mode = (TSK_FS_META_MODE_ENUM)attr2mode(dentry->attrib); |
363 | 489k | fs_meta->type = attr2type(dentry->attrib); |
364 | | |
365 | 489k | fs_meta->addr = a_inum; |
366 | | |
367 | 489k | if (a_cluster_is_alloc) { |
368 | 462k | if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){ |
369 | 292k | flags = TSK_FS_META_FLAG_UNALLOC; |
370 | 292k | } |
371 | 169k | else{ |
372 | 169k | flags = TSK_FS_META_FLAG_ALLOC; |
373 | 169k | } |
374 | 462k | } |
375 | 27.3k | else { |
376 | 27.3k | flags = TSK_FS_META_FLAG_UNALLOC; |
377 | 27.3k | } |
378 | | |
379 | 489k | flags |= (dentry->name[0] == FATXXFS_SLOT_EMPTY ? |
380 | 355k | TSK_FS_META_FLAG_UNUSED : TSK_FS_META_FLAG_USED); |
381 | | |
382 | 489k | fs_meta->flags = (TSK_FS_META_FLAG_ENUM)flags; |
383 | | |
384 | 489k | if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { |
385 | | /* LFN entries don't have these values */ |
386 | 0 | fs_meta->nlink = 0; |
387 | 0 | fs_meta->size = 0; |
388 | 0 | fs_meta->mtime = 0; |
389 | 0 | fs_meta->atime = 0; |
390 | 0 | fs_meta->ctime = 0; |
391 | 0 | fs_meta->crtime = 0; |
392 | 0 | fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = |
393 | 0 | fs_meta->crtime_nano = 0; |
394 | 0 | } |
395 | 489k | else { |
396 | | /* There is no notion of link in FAT, just deleted or not */ |
397 | 489k | if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){ |
398 | 311k | fs_meta->nlink = 0; |
399 | 311k | } |
400 | 177k | else{ |
401 | 177k | fs_meta->nlink = 1; |
402 | 177k | } |
403 | 489k | fs_meta->size = (TSK_OFF_T) tsk_getu32(fs->endian, dentry->size); |
404 | | |
405 | | /* If these are valid dates, then convert to a unix date format */ |
406 | 489k | if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->wdate))) |
407 | 58.3k | fs_meta->mtime = |
408 | 58.3k | fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->wdate), |
409 | 58.3k | tsk_getu16(fs->endian, dentry->wtime), 0); |
410 | 431k | else |
411 | 431k | fs_meta->mtime = 0; |
412 | 489k | fs_meta->mtime_nano = 0; |
413 | | |
414 | 489k | if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->adate))) |
415 | 105k | fs_meta->atime = |
416 | 105k | fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->adate), 0, 0); |
417 | 384k | else |
418 | 384k | fs_meta->atime = 0; |
419 | 489k | fs_meta->atime_nano = 0; |
420 | | |
421 | | |
422 | | /* cdate is the creation date in FAT and there is no change, |
423 | | * so we just put in into change and set create to 0. The other |
424 | | * front-end code knows how to handle it and display it |
425 | | */ |
426 | 489k | if (FATFS_ISDATE(tsk_getu16(fs->endian, dentry->cdate))) { |
427 | 94.9k | fs_meta->crtime = |
428 | 94.9k | fatfs_dos_2_unix_time(tsk_getu16(fs->endian, dentry->cdate), |
429 | 94.9k | tsk_getu16(fs->endian, dentry->ctime), dentry->ctimeten); |
430 | 94.9k | fs_meta->crtime_nano = fatfs_dos_2_nanosec(dentry->ctimeten); |
431 | 94.9k | } |
432 | 394k | else { |
433 | 394k | fs_meta->crtime = 0; |
434 | 394k | fs_meta->crtime_nano = 0; |
435 | 394k | } |
436 | | |
437 | | // FAT does not have a changed time |
438 | 489k | fs_meta->ctime = 0; |
439 | 489k | fs_meta->ctime_nano = 0; |
440 | 489k | } |
441 | | |
442 | | /* Values that do not exist in FAT */ |
443 | 489k | fs_meta->uid = 0; |
444 | 489k | fs_meta->gid = 0; |
445 | 489k | fs_meta->seq = 0; |
446 | | |
447 | | |
448 | | /* We will be copying a name, so allocate a structure */ |
449 | 489k | if (fs_meta->name2 == NULL) { |
450 | 448k | if ((fs_meta->name2 = (TSK_FS_META_NAME_LIST *) |
451 | 448k | tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) |
452 | 0 | return TSK_ERR; |
453 | 448k | fs_meta->name2->next = NULL; |
454 | 448k | } |
455 | | |
456 | | /* If we have a LFN entry, then we need to convert the three |
457 | | * parts of the name to UTF-8 and copy it into the name structure . |
458 | | */ |
459 | 489k | if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { |
460 | 0 | FATXXFS_DENTRY_LFN *lfn = (FATXXFS_DENTRY_LFN *) dentry; |
461 | | |
462 | | /* Convert the first part of the name */ |
463 | 0 | UTF8 *name8 = (UTF8 *) fs_meta->name2->name; |
464 | 0 | UTF16 *name16 = (UTF16 *) lfn->part1; |
465 | |
|
466 | 0 | int retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
467 | 0 | (UTF16 *) & lfn->part1[10], |
468 | 0 | &name8, |
469 | 0 | (UTF8 *) ((uintptr_t) fs_meta->name2->name + |
470 | 0 | sizeof(fs_meta->name2->name)), |
471 | 0 | TSKlenientConversion); |
472 | |
|
473 | 0 | if (retVal != TSKconversionOK) { |
474 | 0 | tsk_error_reset(); |
475 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNICODE); |
476 | 0 | tsk_error_set_errstr |
477 | 0 | ("%s: Error converting FAT LFN (1) to UTF8: %d", |
478 | 0 | func_name, retVal); |
479 | 0 | *name8 = '\0'; |
480 | |
|
481 | 0 | return TSK_COR; |
482 | 0 | } |
483 | | |
484 | | /* Convert the second part of the name */ |
485 | 0 | name16 = (UTF16 *) lfn->part2; |
486 | 0 | retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
487 | 0 | (UTF16 *) & lfn->part2[12], |
488 | 0 | &name8, |
489 | 0 | (UTF8 *) ((uintptr_t) fs_meta->name2->name + |
490 | 0 | sizeof(fs_meta->name2->name)), TSKlenientConversion); |
491 | |
|
492 | 0 | if (retVal != TSKconversionOK) { |
493 | 0 | tsk_error_reset(); |
494 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNICODE); |
495 | 0 | tsk_error_set_errstr |
496 | 0 | ("%s: Error converting FAT LFN (2) to UTF8: %d", |
497 | 0 | func_name, retVal); |
498 | 0 | *name8 = '\0'; |
499 | |
|
500 | 0 | return TSK_COR; |
501 | 0 | } |
502 | | |
503 | | /* Convert the third part of the name */ |
504 | 0 | name16 = (UTF16 *) lfn->part3; |
505 | 0 | retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
506 | 0 | (UTF16 *) & lfn->part3[4], |
507 | 0 | &name8, |
508 | 0 | (UTF8 *) ((uintptr_t) fs_meta->name2->name + |
509 | 0 | sizeof(fs_meta->name2->name)), TSKlenientConversion); |
510 | |
|
511 | 0 | if (retVal != TSKconversionOK) { |
512 | 0 | tsk_error_reset(); |
513 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNICODE); |
514 | 0 | tsk_error_set_errstr |
515 | 0 | ("%s: Error converting FAT LFN (3) to UTF8: %d", |
516 | 0 | func_name, retVal); |
517 | 0 | *name8 = '\0'; |
518 | |
|
519 | 0 | return TSK_COR; |
520 | 0 | } |
521 | | |
522 | | /* Make sure it is NULL Terminated */ |
523 | 0 | if ((uintptr_t) name8 > |
524 | 0 | (uintptr_t) fs_meta->name2->name + |
525 | 0 | sizeof(fs_meta->name2->name)) |
526 | 0 | fs_meta->name2->name[sizeof(fs_meta->name2->name) - 1] = '\0'; |
527 | 0 | else |
528 | 0 | *name8 = '\0'; |
529 | 0 | } |
530 | | /* If the entry is for a volume label, then copy the label. |
531 | | */ |
532 | 489k | else if ((dentry->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) { |
533 | 50.4k | int a; |
534 | | |
535 | 50.4k | i = 0; |
536 | 454k | for (a = 0; a < 8; a++) { |
537 | 403k | if ((dentry->name[a] != 0x00) && (dentry->name[a] != 0xff)) |
538 | 96.7k | fs_meta->name2->name[i++] = dentry->name[a]; |
539 | 403k | } |
540 | 201k | for (a = 0; a < 3; a++) { |
541 | 151k | if ((dentry->ext[a] != 0x00) && (dentry->ext[a] != 0xff)) |
542 | 40.1k | fs_meta->name2->name[i++] = dentry->ext[a]; |
543 | 151k | } |
544 | 50.4k | fs_meta->name2->name[i] = '\0'; |
545 | | |
546 | | /* clean up non-ASCII because we are |
547 | | * copying it into a buffer that is supposed to be UTF-8 and |
548 | | * we don't know what encoding it is actually in or if it is |
549 | | * simply junk. */ |
550 | 50.4k | fatfs_cleanup_ascii(fs_meta->name2->name); |
551 | 50.4k | } |
552 | | /* If the entry is a normal short entry, then copy the name |
553 | | * and add the '.' for the extension |
554 | | */ |
555 | 439k | else { |
556 | 755k | for (i = 0; (i < 8) && (dentry->name[i] != 0) && (dentry->name[i] != ' '); |
557 | 439k | i++) { |
558 | 315k | if ((i == 0) && (dentry->name[0] == FATXXFS_SLOT_DELETED)) |
559 | 81 | fs_meta->name2->name[0] = '_'; |
560 | 315k | else if ((dentry->ntbyte & FATXXFS_CASE_LOWER_BASE) && |
561 | 315k | (dentry->name[i] >= 'A') && (dentry->name[i] <= 'Z')) |
562 | 365 | fs_meta->name2->name[i] = dentry->name[i] + 32; |
563 | 315k | else |
564 | 315k | fs_meta->name2->name[i] = dentry->name[i]; |
565 | 315k | } |
566 | | |
567 | 439k | if ((dentry->ext[0]) && (dentry->ext[0] != ' ')) { |
568 | 84.3k | int a; |
569 | 84.3k | fs_meta->name2->name[i++] = '.'; |
570 | 84.3k | for (a = 0; |
571 | 222k | (a < 3) && (dentry->ext[a] != 0) && (dentry->ext[a] != ' '); |
572 | 138k | a++, i++) { |
573 | 138k | if ((dentry->ntbyte & FATXXFS_CASE_LOWER_EXT) |
574 | 138k | && (dentry->ext[a] >= 'A') && (dentry->ext[a] <= 'Z')) |
575 | 277 | fs_meta->name2->name[i] = dentry->ext[a] + 32; |
576 | 137k | else |
577 | 137k | fs_meta->name2->name[i] = dentry->ext[a]; |
578 | 138k | } |
579 | 84.3k | } |
580 | 439k | fs_meta->name2->name[i] = '\0'; |
581 | | |
582 | | /* clean up non-ASCII because we are |
583 | | * copying it into a buffer that is supposed to be UTF-8 and |
584 | | * we don't know what encoding it is actually in or if it is |
585 | | * simply junk. */ |
586 | 439k | fatfs_cleanup_ascii(fs_meta->name2->name); |
587 | 439k | } |
588 | | |
589 | | /* Clean up name to remove control characters */ |
590 | 489k | i = 0; |
591 | 1.16M | while (fs_meta->name2->name[i] != '\0') { |
592 | 675k | if (TSK_IS_CNTRL(fs_meta->name2->name[i])) |
593 | 286k | fs_meta->name2->name[i] = '^'; |
594 | 675k | i++; |
595 | 675k | } |
596 | | |
597 | | /* get the starting cluster */ |
598 | 489k | addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr; |
599 | 489k | if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { |
600 | 0 | addr_ptr[0] = 0; |
601 | 0 | } |
602 | 489k | else { |
603 | 489k | addr_ptr[0] = FATXXFS_DENTRY_CLUST(fs, dentry) & a_fatfs->mask; |
604 | 489k | } |
605 | | |
606 | | /* FAT does not store a size for its directories so make one based |
607 | | * on the number of allocated sectors |
608 | | */ |
609 | 489k | if ((dentry->attrib & FATFS_ATTR_DIRECTORY) && |
610 | 489k | ((dentry->attrib & FATFS_ATTR_LFN) != FATFS_ATTR_LFN)) { |
611 | 37.8k | if (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) { |
612 | 29.7k | TSK_LIST *list_seen = NULL; |
613 | | |
614 | | /* count the total number of clusters in this file */ |
615 | 29.7k | TSK_DADDR_T clust = FATXXFS_DENTRY_CLUST(fs, dentry); |
616 | 29.7k | int cnum = 0; |
617 | | |
618 | 32.7k | while ((clust) && (0 == FATFS_ISEOF(clust, a_fatfs->mask))) { |
619 | 3.01k | TSK_DADDR_T nxt; |
620 | | |
621 | | /* Make sure we do not get into an infinite loop */ |
622 | 3.01k | if (tsk_list_find(list_seen, clust)) { |
623 | 10 | if (tsk_verbose) |
624 | 0 | tsk_fprintf(stderr, |
625 | 0 | "Loop found while determining directory size\n"); |
626 | 10 | break; |
627 | 10 | } |
628 | 3.00k | if (tsk_list_add(&list_seen, clust)) { |
629 | 0 | tsk_list_free(list_seen); |
630 | 0 | list_seen = NULL; |
631 | 0 | return TSK_ERR; |
632 | 0 | } |
633 | | |
634 | 3.00k | cnum++; |
635 | | |
636 | 3.00k | if (fatfs_getFAT(a_fatfs, clust, &nxt)) |
637 | 60 | break; |
638 | 2.94k | else |
639 | 2.94k | clust = nxt; |
640 | 3.00k | } |
641 | | |
642 | 29.7k | tsk_list_free(list_seen); |
643 | 29.7k | list_seen = NULL; |
644 | | |
645 | 29.7k | fs_meta->size = |
646 | 29.7k | (TSK_OFF_T) ((cnum * a_fatfs->csize) << a_fatfs->ssize_sh); |
647 | 29.7k | } |
648 | | /* if the dir is unallocated, then assume 0 or cluster size |
649 | | * Ideally, we would have a smart algo here to do recovery |
650 | | * and look for dentries. However, we do not have that right |
651 | | * now and if we do not add this special check then it can |
652 | | * assume that an allocated file cluster chain belongs to the |
653 | | * directory */ |
654 | 8.13k | else { |
655 | | // if the first cluster is allocated, then set size to be 0 |
656 | 8.13k | if (fatxxfs_is_cluster_alloc(a_fatfs, FATXXFS_DENTRY_CLUST(fs, |
657 | 8.13k | dentry)) == 1) |
658 | 4.06k | fs_meta->size = 0; |
659 | 4.06k | else |
660 | 4.06k | fs_meta->size = a_fatfs->csize << a_fatfs->ssize_sh; |
661 | 8.13k | } |
662 | 37.8k | } |
663 | | |
664 | 489k | return TSK_OK; |
665 | 489k | } |
666 | | |
667 | | /** |
668 | | * \internal |
669 | | * Populate the TSK_FS_META object of a TSK_FS_FILE object for a |
670 | | * given inode address. |
671 | | * |
672 | | * @param [in] a_fs File system that contains the inode. |
673 | | * @param [out] a_fs_file The file corresponding to the inode. |
674 | | * @param [in] a_inum The inode address. |
675 | | * @returns 1 if an error occurs or if the inode address is not |
676 | | * for a valid inode, 0 otherwise. |
677 | | */ |
678 | | uint8_t |
679 | | fatxxfs_inode_lookup(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file, |
680 | | TSK_INUM_T a_inum) |
681 | 448k | { |
682 | 448k | const char *func_name = "fatxxfs_inode_lookup"; |
683 | 448k | TSK_DADDR_T sector = 0; |
684 | 448k | int8_t ret_val = 0; |
685 | 448k | FATFS_DATA_UNIT_ALLOC_STATUS_ENUM sector_alloc_status = FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN; |
686 | 448k | FATFS_DENTRY dentry; |
687 | 448k | TSK_RETVAL_ENUM copy_result = TSK_OK; |
688 | | |
689 | 448k | tsk_error_reset(); |
690 | 448k | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) || |
691 | 448k | fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name) || |
692 | 448k | !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) { |
693 | 0 | return 1; |
694 | 0 | } |
695 | | |
696 | 448k | sector = FATFS_INODE_2_SECT(a_fatfs, a_inum); |
697 | 448k | if (sector > a_fatfs->fs_info.last_block) { |
698 | 0 | tsk_error_reset(); |
699 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
700 | 0 | tsk_error_set_errstr("%s: Inode %" PRIuINUM |
701 | 0 | " in sector too big for image: %" PRIuDADDR, func_name, a_inum, sector); |
702 | 0 | return 1; |
703 | 0 | } |
704 | | |
705 | 448k | if (fatfs_dentry_load(a_fatfs, &dentry, a_inum) != 0) { |
706 | 0 | return 1; |
707 | 0 | } |
708 | | |
709 | 448k | ret_val = fatfs_is_sectalloc(a_fatfs, sector); |
710 | 448k | if (ret_val == -1) { |
711 | 0 | return 1; |
712 | 0 | } |
713 | 448k | else { |
714 | 448k | sector_alloc_status = (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)ret_val; |
715 | 448k | } |
716 | | |
717 | | /* Note that only the sector allocation status is used to choose |
718 | | * between the basic or in-depth version of the inode validity |
719 | | * test. In other places in the code information about whether or not |
720 | | * the sector that contains the inode is part of a folder is used to |
721 | | * make this decision. Here, that information is not available. Thus, |
722 | | * the test here is less reliable and may result in some false |
723 | | * positives. */ |
724 | 448k | if (!fatxxfs_is_dentry(a_fatfs, &dentry, sector_alloc_status, sector_alloc_status)) { |
725 | 0 | tsk_error_reset(); |
726 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
727 | 0 | tsk_error_set_errstr("%s: %" PRIuINUM |
728 | 0 | " is not an inode", func_name, a_inum); |
729 | 0 | return 1; |
730 | 0 | } |
731 | | |
732 | 448k | copy_result = fatxxfs_dinode_copy(a_fatfs, a_inum, &dentry, (uint8_t)sector_alloc_status, a_fs_file); |
733 | 448k | if (copy_result == TSK_OK) { |
734 | 448k | return 0; |
735 | 448k | } |
736 | 0 | else if (copy_result == TSK_COR) { |
737 | | /* If there was a Unicode conversion error, |
738 | | * then still return the inode. */ |
739 | 0 | if (tsk_verbose) { |
740 | 0 | tsk_error_print(stderr); |
741 | 0 | } |
742 | 0 | tsk_error_reset(); |
743 | 0 | return 0; |
744 | 0 | } |
745 | 0 | else { |
746 | 0 | return 1; |
747 | 0 | } |
748 | 448k | } |
749 | | |
750 | | /** |
751 | | * Output the file attributes of an exFAT file directory entry in |
752 | | * human-readable form. |
753 | | * |
754 | | * @param a_fatfs Source file system for the directory entry. |
755 | | * @param a_inum Inode address associated with the directory entry. |
756 | | * @param a_hFile Handle of the file to which to write. |
757 | | * @return 0 on success, 1 on failure, per TSK convention |
758 | | */ |
759 | | uint8_t |
760 | | fatxxfs_istat_attr_flags(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, FILE *a_hFile) |
761 | 0 | { |
762 | 0 | const char *func_name = "fatxxfs_istat_attr_flags"; |
763 | 0 | FATXXFS_DENTRY dentry; |
764 | |
|
765 | 0 | tsk_error_reset(); |
766 | 0 | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) || |
767 | 0 | fatfs_ptr_arg_is_null(a_hFile, "a_hFile", func_name) || |
768 | 0 | !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name)) { |
769 | 0 | return 1; |
770 | 0 | } |
771 | | |
772 | 0 | if (fatfs_dentry_load(a_fatfs, (FATFS_DENTRY*)(&dentry), a_inum)) { |
773 | 0 | return 1; |
774 | 0 | } |
775 | | |
776 | 0 | if ((dentry.attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { |
777 | 0 | tsk_fprintf(a_hFile, "Long File Name\n"); |
778 | 0 | } |
779 | 0 | else { |
780 | 0 | if (dentry.attrib & FATFS_ATTR_DIRECTORY) |
781 | 0 | tsk_fprintf(a_hFile, "Directory"); |
782 | 0 | else if (dentry.attrib & FATFS_ATTR_VOLUME) |
783 | 0 | tsk_fprintf(a_hFile, "Volume Label"); |
784 | 0 | else |
785 | 0 | tsk_fprintf(a_hFile, "File"); |
786 | |
|
787 | 0 | if (dentry.attrib & FATFS_ATTR_READONLY) |
788 | 0 | tsk_fprintf(a_hFile, ", Read Only"); |
789 | 0 | if (dentry.attrib & FATFS_ATTR_HIDDEN) |
790 | 0 | tsk_fprintf(a_hFile, ", Hidden"); |
791 | 0 | if (dentry.attrib & FATFS_ATTR_SYSTEM) |
792 | 0 | tsk_fprintf(a_hFile, ", System"); |
793 | 0 | if (dentry.attrib & FATFS_ATTR_ARCHIVE) |
794 | 0 | tsk_fprintf(a_hFile, ", Archive"); |
795 | 0 | if (dentry.ntbyte & FATXXFS_ENCRYPTED_DATA) { |
796 | 0 | tsk_fprintf(a_hFile, ", Encrypted"); |
797 | |
|
798 | 0 | if (dentry.ntbyte & FATXXFS_LARGE_EFS_HEADER) |
799 | 0 | tsk_fprintf(a_hFile, ", Large EFS Header"); |
800 | 0 | } |
801 | |
|
802 | 0 | tsk_fprintf(a_hFile, "\n"); |
803 | 0 | } |
804 | |
|
805 | 0 | return 0; |
806 | 0 | } |
807 | | |
808 | | uint8_t |
809 | | fatxxfs_inode_walk_should_skip_dentry(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, |
810 | | FATFS_DENTRY *a_dentry, unsigned int a_selection_flags, |
811 | | int a_cluster_is_alloc) |
812 | 61.1k | { |
813 | 61.1k | const char *func_name = "fatxxfs_inode_walk_should_skip_dentry"; |
814 | 61.1k | FATXXFS_DENTRY *dentry = (FATXXFS_DENTRY*)a_dentry; |
815 | 61.1k | unsigned int dentry_flags = 0; |
816 | | |
817 | 61.1k | assert(a_fatfs != NULL); |
818 | 61.1k | assert(fatfs_inum_is_in_range(a_fatfs, a_inum)); |
819 | 61.1k | assert(a_dentry != NULL); |
820 | | |
821 | 61.1k | tsk_error_reset(); |
822 | 61.1k | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) || |
823 | 61.1k | !fatfs_inum_arg_is_in_range(a_fatfs, a_inum, func_name) || |
824 | 61.1k | fatfs_ptr_arg_is_null(a_dentry, "a_dentry", func_name)) { |
825 | 0 | return 1; |
826 | 0 | } |
827 | | |
828 | | /* If this is a long file name entry, then skip it and |
829 | | * wait for the short name. */ |
830 | 61.1k | if ((dentry->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { |
831 | 2.48k | return 1; |
832 | 2.48k | } |
833 | | |
834 | | /* Skip the "." and ".." entries because they are redundant. */ |
835 | 58.6k | if (((dentry->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) && |
836 | 58.6k | (dentry->name[0] == '.')) { |
837 | 2 | return 1; |
838 | 2 | } |
839 | | |
840 | | /* Compare directory entry allocation status with the inode selection |
841 | | * flags. Allocation status is determined first by the allocation status |
842 | | * of the sector that contains the entry, then by the deleted status of |
843 | | * the file. This is necessary because when a directory is deleted, its |
844 | | * contents are not always marked as unallocated. */ |
845 | 58.6k | if (a_cluster_is_alloc == 1) { |
846 | 45.1k | if(FATXXFS_IS_DELETED(dentry->name, a_fatfs)){ |
847 | 27.0k | dentry_flags = TSK_FS_META_FLAG_UNALLOC; |
848 | 27.0k | } |
849 | 18.0k | else{ |
850 | 18.0k | dentry_flags = TSK_FS_META_FLAG_ALLOC; |
851 | 18.0k | } |
852 | 45.1k | } |
853 | 13.5k | else { |
854 | 13.5k | dentry_flags = TSK_FS_META_FLAG_UNALLOC; |
855 | 13.5k | } |
856 | | |
857 | 58.6k | if ((a_selection_flags & dentry_flags) != dentry_flags) { |
858 | 18.0k | return 1; |
859 | 18.0k | } |
860 | | |
861 | | /* If the processing flags call for only processing orphan files, check |
862 | | * whether or not this inode is in list of non-orphan files found via name |
863 | | * walk. */ |
864 | 40.6k | if ((dentry_flags & TSK_FS_META_FLAG_UNALLOC) && |
865 | 40.6k | (a_selection_flags & TSK_FS_META_FLAG_ORPHAN) && |
866 | 40.6k | (tsk_fs_dir_find_inum_named(&(a_fatfs->fs_info), a_inum))) { |
867 | 0 | return 1; |
868 | 0 | } |
869 | | |
870 | 40.6k | return 0; |
871 | 40.6k | } |