/src/sleuthkit/tsk/fs/fatfs_dent.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** fatfs_dent |
3 | | ** The Sleuth Kit |
4 | | ** |
5 | | ** file name 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_dent.cpp |
23 | | * Contains the internal TSK FAT file name processing code. |
24 | | */ |
25 | | |
26 | | #include "tsk_fs_i.h" |
27 | | #include "tsk_fatxxfs.h" |
28 | | #include "tsk_exfatfs.h" |
29 | | #include "tsk_fatfs.h" |
30 | | |
31 | | #include <map> |
32 | | |
33 | | /* |
34 | | * DESIGN NOTES |
35 | | * |
36 | | * the basic goal of this code is to parse directory entry structures for |
37 | | * file names. The main function is fatfs_parse_buf, which parses |
38 | | * a buffer and stores the entries in FS_DIR. That structure is then |
39 | | * used by dir_get() or dir_walk() to provide the data back to the user. |
40 | | * |
41 | | * One of the odd aspects of this code is that the 'inode' values are |
42 | | * the 'slot-address'. Refer to the document on how FAT was implemented |
43 | | * for more details. This means that we need to search for the actual |
44 | | * 'inode' address for the '.' and '..' entries though! The search |
45 | | * for '..' is quite painful if this code is called from a random |
46 | | * location. It does save what the parent is though, so the search |
47 | | * only has to be done once per session. |
48 | | */ |
49 | | |
50 | | /* |
51 | | * name_walk callback used when finding the parent directory. It |
52 | | * forces the walking process to stop when we hit a target directory. |
53 | | * A list of directory to parent directory mappings is built up during |
54 | | * the walk and this function is used to stop that building process. |
55 | | */ |
56 | | TSK_WALK_RET_ENUM |
57 | | fatfs_find_parent_act(TSK_FS_FILE * fs_file, const char * /*a_path*/, void *ptr) |
58 | 0 | { |
59 | 0 | TSK_INUM_T par_inum = *(TSK_INUM_T *) ptr; |
60 | |
|
61 | 0 | if ((fs_file->meta == NULL) |
62 | 0 | || ( ! TSK_FS_IS_DIR_META(fs_file->meta->type))) |
63 | 0 | return TSK_WALK_CONT; |
64 | | |
65 | 0 | if (fs_file->meta->addr == par_inum) |
66 | 0 | return TSK_WALK_STOP; |
67 | | |
68 | 0 | return TSK_WALK_CONT; |
69 | 0 | } |
70 | | |
71 | | /** \internal |
72 | | * Casts the void * to a map. This obfuscation is done so that the rest of the library |
73 | | * can remain as C and only this code needs to be C++. |
74 | | * |
75 | | * Assumes that you already have the lock |
76 | | */ |
77 | 117k | static std::map<TSK_INUM_T, TSK_INUM_T> * getParentMap(FATFS_INFO *fatfs) { |
78 | | // allocate it if it hasn't already been |
79 | 117k | if (fatfs->inum2par == NULL) { |
80 | 24 | fatfs->inum2par = new std::map<TSK_INUM_T, TSK_INUM_T>; |
81 | 24 | } |
82 | 117k | return (std::map<TSK_INUM_T, TSK_INUM_T> *)fatfs->inum2par; |
83 | 117k | } |
84 | | |
85 | | /** |
86 | | * Adds an entry to the parent directory map. Used to make further processing |
87 | | * faster. |
88 | | * @param fatfs File system |
89 | | * @param par_inum Parent folder meta data address. |
90 | | * @param dir_inum Sub-folder meta data address. |
91 | | * @returns 0 |
92 | | */ |
93 | | uint8_t |
94 | | fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum, |
95 | | TSK_INUM_T dir_inum) |
96 | 116k | { |
97 | 116k | tsk_take_lock(&fatfs->dir_lock); |
98 | 116k | std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs); |
99 | 116k | (*tmpMap)[dir_inum] = par_inum; |
100 | 116k | tsk_release_lock(&fatfs->dir_lock); |
101 | | |
102 | 116k | return 0; |
103 | 116k | } |
104 | | |
105 | | /** |
106 | | * Looks up the parent meta address for a child from the cached list. |
107 | | * @param fatfs File system |
108 | | * @param dir_inum Inode of sub-directory to look up |
109 | | * @param par_inum [out] Result of lookup |
110 | | * @returns 0 if found and 1 if not. |
111 | | */ |
112 | | uint8_t |
113 | | fatfs_dir_buf_get(FATFS_INFO * fatfs, TSK_INUM_T dir_inum, |
114 | | TSK_INUM_T *par_inum) |
115 | 109 | { |
116 | 109 | uint8_t retval = 1; |
117 | 109 | tsk_take_lock(&fatfs->dir_lock); |
118 | 109 | std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs); |
119 | 109 | if (tmpMap->count( dir_inum) > 0) { |
120 | 93 | *par_inum = (*tmpMap)[dir_inum]; |
121 | 93 | retval = 0; |
122 | 93 | } |
123 | 109 | tsk_release_lock(&fatfs->dir_lock); |
124 | | |
125 | 109 | return retval; |
126 | 109 | } |
127 | | |
128 | | /** |
129 | | * Frees the memory associated with the parent map |
130 | | */ |
131 | 28 | void fatfs_dir_buf_free(FATFS_INFO *fatfs) { |
132 | 28 | tsk_take_lock(&fatfs->dir_lock); |
133 | 28 | if (fatfs->inum2par != NULL) { |
134 | 24 | std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs); |
135 | 24 | delete tmpMap; |
136 | 24 | fatfs->inum2par = NULL; |
137 | 24 | } |
138 | 28 | tsk_release_lock(&fatfs->dir_lock); |
139 | 28 | } |
140 | | |
141 | | /************************************************************************** |
142 | | * |
143 | | * dent_walk |
144 | | * |
145 | | *************************************************************************/ |
146 | | |
147 | | /* values used to copy the directory contents into a buffer */ |
148 | | |
149 | | |
150 | | typedef struct { |
151 | | /* ptr to the current location in a local buffer */ |
152 | | char *curdirptr; |
153 | | |
154 | | /* number of bytes left in curdirptr */ |
155 | | size_t dirleft; |
156 | | |
157 | | /* ptr to a local buffer for the stack of sector addresses */ |
158 | | TSK_DADDR_T *addrbuf; |
159 | | |
160 | | /* num of entries allocated to addrbuf */ |
161 | | size_t addrsize; |
162 | | |
163 | | /* The current index in the addrbuf stack */ |
164 | | size_t addridx; |
165 | | |
166 | | } FATFS_LOAD_DIR; |
167 | | |
168 | | |
169 | | |
170 | | /** |
171 | | * file walk callback that is used to load directory contents |
172 | | * into a buffer |
173 | | */ |
174 | | static TSK_WALK_RET_ENUM |
175 | | fatfs_dent_action(TSK_FS_FILE * /*fs_file*/, TSK_OFF_T /*a_off*/, |
176 | | TSK_DADDR_T addr, char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM /*flags*/, |
177 | | void *ptr) |
178 | 51.5k | { |
179 | 51.5k | FATFS_LOAD_DIR *load = (FATFS_LOAD_DIR *) ptr; |
180 | | |
181 | | /* how much of the buffer are we copying */ |
182 | 51.5k | size_t len = (load->dirleft < size) ? load->dirleft : size; |
183 | | |
184 | | /* Copy the sector into a buffer and increment the pointers */ |
185 | 51.5k | memcpy(load->curdirptr, buf, len); |
186 | 51.5k | load->curdirptr = (char *) ((uintptr_t) load->curdirptr + len); |
187 | 51.5k | load->dirleft -= len; |
188 | | |
189 | | /* fill in the stack of addresses of sectors |
190 | | * |
191 | | * if we are at the last entry, then realloc more */ |
192 | 51.5k | if (load->addridx == load->addrsize) { |
193 | 0 | tsk_error_reset(); |
194 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
195 | 0 | tsk_error_set_errstr |
196 | 0 | ("fatfs_dent_walk: Trying to put more sector address in stack than were allocated (%lu)", |
197 | 0 | (long) load->addridx); |
198 | 0 | return TSK_WALK_ERROR; |
199 | 0 | } |
200 | | |
201 | | /* Add this sector to the stack */ |
202 | 51.5k | load->addrbuf[load->addridx++] = addr; |
203 | | |
204 | 51.5k | if (load->dirleft) |
205 | 51.0k | return TSK_WALK_CONT; |
206 | 462 | else |
207 | 462 | return TSK_WALK_STOP; |
208 | 51.5k | } |
209 | | |
210 | | |
211 | | /** \internal |
212 | | * Process a directory and load up FS_DIR with the entries. If a pointer to |
213 | | * an already allocated FS_DIR structure is given, it will be cleared. If no existing |
214 | | * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return |
215 | | * value is error or corruption, then the FS_DIR structure could |
216 | | * have entries (depending on when the error occurred). |
217 | | * |
218 | | * @param a_fs File system to analyze |
219 | | * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated |
220 | | * structure or a new structure. |
221 | | * @param a_addr Address of directory to process. |
222 | | * @param recursion_depth Recursion depth to limit the number of self-calls |
223 | | * @returns error, corruption, ok etc. |
224 | | */ |
225 | | |
226 | | TSK_RETVAL_ENUM |
227 | | fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, |
228 | | TSK_INUM_T a_addr, int recursion_depth) |
229 | 21.3k | { |
230 | 21.3k | const char *func_name = "fatfs_dir_open_meta"; |
231 | 21.3k | TSK_OFF_T size, len; |
232 | 21.3k | FATFS_INFO *fatfs = (FATFS_INFO *) a_fs; |
233 | 21.3k | char *dirbuf; |
234 | 21.3k | TSK_DADDR_T *addrbuf; |
235 | 21.3k | FATFS_LOAD_DIR load; |
236 | 21.3k | TSK_RETVAL_ENUM retval; |
237 | | |
238 | 21.3k | TSK_FS_DIR *fs_dir; |
239 | | |
240 | 21.3k | if ((a_addr < a_fs->first_inum) || (a_addr > a_fs->last_inum)) { |
241 | 0 | tsk_error_reset(); |
242 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
243 | 0 | tsk_error_set_errstr("%s: invalid a_addr value: %" |
244 | 0 | PRIuINUM "\n", func_name, a_addr); |
245 | 0 | return TSK_ERR; |
246 | 0 | } |
247 | 21.3k | else if (a_fs_dir == NULL) { |
248 | 0 | tsk_error_reset(); |
249 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
250 | 0 | tsk_error_set_errstr |
251 | 0 | ("%s: NULL fs_attr argument given", func_name); |
252 | 0 | return TSK_ERR; |
253 | 0 | } |
254 | | |
255 | 21.3k | fs_dir = *a_fs_dir; |
256 | 21.3k | if (fs_dir) { |
257 | 0 | tsk_fs_dir_reset(fs_dir); |
258 | 0 | fs_dir->addr = a_addr; |
259 | 0 | } |
260 | 21.3k | else { |
261 | 21.3k | if ((*a_fs_dir = fs_dir = |
262 | 21.3k | tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { |
263 | 0 | return TSK_ERR; |
264 | 0 | } |
265 | 21.3k | } |
266 | | |
267 | | // handle the orphan directory if its contents were requested |
268 | 21.3k | if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { |
269 | 27 | return tsk_fs_dir_find_orphans(a_fs, fs_dir); |
270 | 27 | } |
271 | | |
272 | 21.3k | fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr); |
273 | 21.3k | if (fs_dir->fs_file == NULL) { |
274 | 0 | tsk_error_reset(); |
275 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
276 | 0 | tsk_error_set_errstr("%s: %" PRIuINUM |
277 | 0 | " is not a valid inode", func_name, a_addr); |
278 | 0 | return TSK_COR; |
279 | 0 | } |
280 | | |
281 | 21.3k | size = fs_dir->fs_file->meta->size; |
282 | 21.3k | len = roundup(size, fatfs->ssize); |
283 | | |
284 | 21.3k | if (tsk_verbose) { |
285 | 0 | tsk_fprintf(stderr, |
286 | 0 | "%s: Processing directory %" PRIuINUM "\n", |
287 | 0 | func_name, a_addr); |
288 | 0 | } |
289 | | |
290 | 21.3k | if (size == 0) { |
291 | 18.7k | if (tsk_verbose) |
292 | 0 | tsk_fprintf(stderr, |
293 | 0 | "%s: directory has 0 size\n", func_name); |
294 | 18.7k | return TSK_OK; |
295 | 18.7k | } |
296 | | |
297 | | /* Make a copy of the directory contents using file_walk */ |
298 | 2.59k | if ((dirbuf = (char *)tsk_malloc((size_t) len)) == NULL) { |
299 | 0 | return TSK_ERR; |
300 | 0 | } |
301 | 2.59k | load.curdirptr = dirbuf; |
302 | 2.59k | load.dirleft = (size_t) size; |
303 | | |
304 | | /* We are going to save the address of each sector in the directory |
305 | | * in a stack - they are needed to determine the inode address. |
306 | | */ |
307 | 2.59k | load.addrsize = (size_t) (len / fatfs->ssize); |
308 | 2.59k | addrbuf = |
309 | 2.59k | (TSK_DADDR_T *) tsk_malloc(load.addrsize * sizeof(TSK_DADDR_T)); |
310 | 2.59k | if (addrbuf == NULL) { |
311 | 0 | free(dirbuf); |
312 | 0 | return TSK_ERR; |
313 | 0 | } |
314 | | |
315 | | /* Set the variables that are used during the copy */ |
316 | 2.59k | load.addridx = 0; |
317 | 2.59k | load.addrbuf = addrbuf; |
318 | | |
319 | | /* save the directory contents into dirbuf */ |
320 | 2.59k | if (tsk_fs_file_walk(fs_dir->fs_file, |
321 | 2.59k | TSK_FS_FILE_WALK_FLAG_SLACK, |
322 | 2.59k | fatfs_dent_action, (void *) &load)) { |
323 | 2.12k | tsk_error_errstr2_concat("- %s", func_name); |
324 | 2.12k | free(dirbuf); |
325 | 2.12k | free(addrbuf); |
326 | 2.12k | return TSK_COR; |
327 | 2.12k | } |
328 | | |
329 | | /* We did not copy the entire directory, which occurs if an error occurred */ |
330 | 462 | if (load.dirleft > 0) { |
331 | 0 | tsk_error_reset(); |
332 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
333 | 0 | tsk_error_set_errstr |
334 | 0 | ("%s: Error reading directory %" PRIuINUM, |
335 | 0 | func_name, a_addr); |
336 | | |
337 | | /* Free the local buffers */ |
338 | 0 | free(dirbuf); |
339 | 0 | free(addrbuf); |
340 | 0 | return TSK_COR; |
341 | 0 | } |
342 | | |
343 | 462 | if (tsk_verbose) |
344 | 0 | fprintf(stderr, |
345 | 0 | "%s: Parsing directory %" PRIuINUM "\n", |
346 | 0 | func_name, a_addr); |
347 | | |
348 | 462 | retval = fatfs->dent_parse_buf(fatfs, fs_dir, dirbuf, len, addrbuf, recursion_depth); |
349 | | |
350 | 462 | free(dirbuf); |
351 | 462 | free(addrbuf); |
352 | | |
353 | | // if we are listing the root directory, add the Orphan directory and special FAT file entries |
354 | 462 | if (a_addr == a_fs->root_inum) { |
355 | 102 | TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); |
356 | 102 | if (fs_name == NULL) |
357 | 0 | return TSK_ERR; |
358 | | |
359 | | // MBR Entry |
360 | 102 | strncpy(fs_name->name, FATFS_MBRNAME, fs_name->name_size); |
361 | 102 | fs_name->meta_addr = fatfs->mbr_virt_inum; |
362 | 102 | fs_name->type = TSK_FS_NAME_TYPE_VIRT; |
363 | 102 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
364 | 102 | if (tsk_fs_dir_add(fs_dir, fs_name)) { |
365 | 0 | tsk_fs_name_free(fs_name); |
366 | 0 | return TSK_ERR; |
367 | 0 | } |
368 | | |
369 | | // FAT1 Entry |
370 | 102 | strncpy(fs_name->name, FATFS_FAT1NAME, fs_name->name_size); |
371 | 102 | fs_name->meta_addr = fatfs->fat1_virt_inum; |
372 | 102 | fs_name->type = TSK_FS_NAME_TYPE_VIRT; |
373 | 102 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
374 | 102 | if (tsk_fs_dir_add(fs_dir, fs_name)) { |
375 | 0 | tsk_fs_name_free(fs_name); |
376 | 0 | return TSK_ERR; |
377 | 0 | } |
378 | | |
379 | | // FAT2 Entry |
380 | 102 | if (fatfs->numfat == 2) { |
381 | 13 | strncpy(fs_name->name, FATFS_FAT2NAME, fs_name->name_size); |
382 | 13 | fs_name->meta_addr = fatfs->fat2_virt_inum; |
383 | 13 | fs_name->type = TSK_FS_NAME_TYPE_VIRT; |
384 | 13 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
385 | 13 | if (tsk_fs_dir_add(fs_dir, fs_name)) { |
386 | 0 | tsk_fs_name_free(fs_name); |
387 | 0 | return TSK_ERR; |
388 | 0 | } |
389 | 13 | } |
390 | | |
391 | | // orphan directory |
392 | 102 | if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { |
393 | 0 | tsk_fs_name_free(fs_name); |
394 | 0 | return TSK_ERR; |
395 | 0 | } |
396 | 102 | if (tsk_fs_dir_add(fs_dir, fs_name)) { |
397 | 0 | tsk_fs_name_free(fs_name); |
398 | 0 | return TSK_ERR; |
399 | 0 | } |
400 | 102 | tsk_fs_name_free(fs_name); |
401 | 102 | } |
402 | | |
403 | 462 | return retval; |
404 | 462 | } |
405 | | |
406 | | int |
407 | | fatfs_name_cmp(TSK_FS_INFO * /*a_fs_info*/, const char *s1, const char *s2) |
408 | 0 | { |
409 | 0 | return strcasecmp(s1, s2); |
410 | 0 | } |