1 /*
2 ** ntfs_dent
3 ** The Sleuth Kit
4 **
5 ** name layer support for the NTFS file system
6 **
7 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
8 ** Copyright (c) 2006-2011 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 #include "tsk_fs_i.h"
21 #include "tsk_ntfs.h"
22
23 /**
24 * \file ntfs_dent.cpp
25 * NTFS file name processing internal functions.
26 */
27
28 #include <map>
29 #include <vector>
30
31 /**
32 * Class to hold the pair of MFT entry and sequence.
33 */
34 class NTFS_META_ADDR {
35 private:
36 uint64_t addr; ///< MFT entry
37 uint32_t seq; ///< Sequence
38 uint32_t hash; ///< Hash of the path
39
40 public:
NTFS_META_ADDR(uint64_t a_addr,uint32_t a_seq,uint32_t a_hash)41 NTFS_META_ADDR(uint64_t a_addr, uint32_t a_seq, uint32_t a_hash) {
42 addr = a_addr;
43 seq = a_seq;
44 hash = a_hash;
45 }
46
getAddr()47 uint64_t getAddr() {
48 return addr;
49 }
50
getSeq()51 uint32_t getSeq() {
52 return seq;
53 }
54
getHash()55 uint32_t getHash(){
56 return hash;
57 }
58 };
59
60
61 /* When we list a directory, we need to also look at MFT entries and what
62 * they list as their parents. We used to do this only for orphan files, but
63 * we were pointed to a case whereby allocated files were not in IDX_ALLOC, but were
64 * shown in Windows (when mounted). They must have been found via the MFT entry, so
65 * we now load all parent to child relationships into the map.
66 *
67 * One of these classes is created per parent folder */
68 class NTFS_PAR_MAP {
69 private:
70 // maps sequence number to list of inums for the folder at that seq.
71 std::map <uint32_t, std::vector <NTFS_META_ADDR> > seq2addrs;
72 public:
73 /**
74 * Add a child to this parent.
75 * @param seq Sequence of the parent that this child belonged to
76 * @param inum Address of child in the folder.
77 * @param seq Sequence of child in the folder
78 */
add(uint32_t parSeq,TSK_INUM_T inum,uint32_t seq,uint32_t hash)79 void add (uint32_t parSeq, TSK_INUM_T inum, uint32_t seq, uint32_t hash) {
80 NTFS_META_ADDR addr(inum, seq, hash);
81 seq2addrs[parSeq].push_back(addr);
82 }
83
84 /**
85 * Test if there are any children for this directory at a given sequence.
86 * @param seq Sequence to test.
87 * @returns true if children exist
88 */
exists(uint32_t seq)89 bool exists (uint32_t seq) {
90 if (seq2addrs.count(seq) > 0)
91 return true;
92 else
93 return false;
94 }
95
96 /**
97 * Get the children for this folder at a given sequence. Use exists first.
98 * @param seq Sequence number to retrieve children for.
99 * @returns list of INUMS for children.
100 */
get(uint32_t seq)101 std::vector <NTFS_META_ADDR> &get (uint32_t seq) {
102 return seq2addrs[seq];
103 }
104 };
105
106
107
108 /** \internal
109 * Casts the void * to a map. This obfuscation is done so that the rest of the library
110 * can remain as C and only this code needs to be C++.
111 *
112 * Assumes that you already have the lock
113 */
getParentMap(NTFS_INFO * ntfs)114 static std::map<TSK_INUM_T, NTFS_PAR_MAP> * getParentMap(NTFS_INFO *ntfs) {
115 // allocate it if it hasn't already been
116 if (ntfs->orphan_map == NULL) {
117 ntfs->orphan_map = new std::map<TSK_INUM_T, NTFS_PAR_MAP>;
118 }
119 return (std::map<TSK_INUM_T, NTFS_PAR_MAP> *)ntfs->orphan_map;
120 }
121
122
123
124 /** \internal
125 * Add a parent and child pair to the map stored in NTFS_INFO
126 *
127 * Note: This routine assumes &ntfs->orphan_map_lock is locked by the caller.
128 *
129 * @param ntfs structure to add the pair to
130 * @param par Parent address
131 * @param child_meta Child to add
132 * @returns 1 on error
133 */
134 static uint8_t
ntfs_parent_map_add(NTFS_INFO * ntfs,TSK_FS_META_NAME_LIST * name_list,TSK_FS_META * child_meta)135 ntfs_parent_map_add(NTFS_INFO * ntfs, TSK_FS_META_NAME_LIST *name_list, TSK_FS_META *child_meta)
136 {
137 std::map<TSK_INUM_T, NTFS_PAR_MAP> *tmpParentMap = getParentMap(ntfs);
138 NTFS_PAR_MAP &tmpParMap = (*tmpParentMap)[name_list->par_inode];
139 tmpParMap.add(name_list->par_seq, child_meta->addr, child_meta->seq, tsk_fs_dir_hash(name_list->name));
140 return 0;
141 }
142
143 /** \internal
144 * Returns if a parent has children or not.
145 *
146 * Note: This routine assumes &ntfs->orphan_map_lock is locked by the caller.
147 *
148 * @param ntfs File system that has already been analyzed
149 * @param par Parent inode to find child files for
150 * @seq seq Sequence of parent folder
151 * @returns true if parent has children.
152 */
153 static bool
ntfs_parent_map_exists(NTFS_INFO * ntfs,TSK_INUM_T par,uint32_t seq)154 ntfs_parent_map_exists(NTFS_INFO *ntfs, TSK_INUM_T par, uint32_t seq)
155 {
156 std::map<TSK_INUM_T, NTFS_PAR_MAP> *tmpParentMap = getParentMap(ntfs);
157 if (tmpParentMap->count(par) > 0) {
158 NTFS_PAR_MAP &tmpParMap = (*tmpParentMap)[par];
159 if (tmpParMap.exists(seq))
160 return true;
161 }
162 return false;
163 }
164
165 /** \internal
166 * Look up a map entry by the parent address. You should call ntfs_parent_map_exists() before this, otherwise
167 * an empty entry could be created.
168 *
169 * Note: This routine assumes &ntfs->orphan_map_lock is locked by the caller.
170 *
171 * @param ntfs File system that has already been analyzed
172 * @param par Parent inode to find child files for
173 * @param seq Sequence of parent inode
174 * @returns address of children files in the parent directory
175 */
176 static std::vector <NTFS_META_ADDR> &
ntfs_parent_map_get(NTFS_INFO * ntfs,TSK_INUM_T par,uint32_t seq)177 ntfs_parent_map_get(NTFS_INFO * ntfs, TSK_INUM_T par, uint32_t seq)
178 {
179 std::map<TSK_INUM_T, NTFS_PAR_MAP> *tmpParentMap = getParentMap(ntfs);
180 NTFS_PAR_MAP &tmpParMap = (*tmpParentMap)[par];
181 return tmpParMap.get(seq);
182 }
183
184
185
186 // note that for consistency, this should be called parent_map_free, but
187 // that would have required an API change in a point release and this better
188 // matches the name in NTFS_INFO
189 void
ntfs_orphan_map_free(NTFS_INFO * a_ntfs)190 ntfs_orphan_map_free(NTFS_INFO * a_ntfs)
191 {
192 // This routine is only called from ntfs_close, so it wouldn't
193 // normally need a lock. However, it's an extern function, so be
194 // safe in case someone else calls it. (Perhaps it's extern by
195 // mistake?)
196
197 tsk_take_lock(&a_ntfs->orphan_map_lock);
198
199 if (a_ntfs->orphan_map == NULL) {
200 tsk_release_lock(&a_ntfs->orphan_map_lock);
201 return;
202 }
203 std::map<TSK_INUM_T, NTFS_PAR_MAP> *tmpParentMap = getParentMap(a_ntfs);
204
205 delete tmpParentMap;
206 a_ntfs->orphan_map = NULL;
207 tsk_release_lock(&a_ntfs->orphan_map_lock);
208 }
209
210
211 /* inode_walk callback that is used to populate the orphan_map
212 * structure in NTFS_INFO */
213 static TSK_WALK_RET_ENUM
ntfs_parent_act(TSK_FS_FILE * fs_file,void *)214 ntfs_parent_act(TSK_FS_FILE * fs_file, void * /*ptr*/)
215 {
216 NTFS_INFO *ntfs = (NTFS_INFO *) fs_file->fs_info;
217 TSK_FS_META_NAME_LIST *fs_name_list;
218
219 if ((fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC) &&
220 fs_file->meta->type == TSK_FS_META_TYPE_REG) {
221 ++ntfs->alloc_file_count;
222 }
223
224 /* go through each file name structure */
225 fs_name_list = fs_file->meta->name2;
226 while (fs_name_list) {
227 if (ntfs_parent_map_add(ntfs, fs_name_list,
228 fs_file->meta)) {
229 return TSK_WALK_ERROR;
230 }
231 fs_name_list = fs_name_list->next;
232 }
233 return TSK_WALK_CONT;
234 }
235
236
237
238 /****************/
239
240 static uint8_t
ntfs_dent_copy(NTFS_INFO * ntfs,ntfs_idxentry * idxe,TSK_FS_NAME * fs_name)241 ntfs_dent_copy(NTFS_INFO * ntfs, ntfs_idxentry * idxe,
242 TSK_FS_NAME * fs_name)
243 {
244 ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream;
245 TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
246 UTF16 *name16;
247 UTF8 *name8;
248 int retVal;
249
250 tsk_fs_name_reset(fs_name);
251
252 fs_name->meta_addr = tsk_getu48(fs->endian, idxe->file_ref);
253 fs_name->meta_seq = tsk_getu16(fs->endian, idxe->seq_num);
254
255 name16 = (UTF16 *) & fname->name;
256 name8 = (UTF8 *) fs_name->name;
257
258 retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
259 (UTF16 *) ((uintptr_t) name16 +
260 fname->nlen * 2), &name8,
261 (UTF8 *) ((uintptr_t) name8 +
262 fs_name->name_size), TSKlenientConversion);
263
264 if (retVal != TSKconversionOK) {
265 *name8 = '\0';
266 if (tsk_verbose)
267 tsk_fprintf(stderr,
268 "Error converting NTFS name to UTF8: %d %" PRIuINUM,
269 retVal, fs_name->meta_addr);
270 }
271
272 /* Make sure it is NULL Terminated */
273 if ((uintptr_t) name8 > (uintptr_t) fs_name->name + fs_name->name_size)
274 fs_name->name[fs_name->name_size] = '\0';
275 else
276 *name8 = '\0';
277
278 if (tsk_getu64(fs->endian, fname->flags) & NTFS_FNAME_FLAGS_DIR)
279 fs_name->type = TSK_FS_NAME_TYPE_DIR;
280 else
281 fs_name->type = TSK_FS_NAME_TYPE_REG;
282
283 fs_name->flags = (TSK_FS_NAME_FLAG_ENUM)0;
284
285 return 0;
286 }
287
288
289 /* Copy the short file name pointed to by idxe into fs_name.
290 * No other fields are copied. Just the name into shrt_name. */
291 static uint8_t
ntfs_dent_copy_short_only(NTFS_INFO * ntfs,ntfs_idxentry * idxe,TSK_FS_NAME * fs_name)292 ntfs_dent_copy_short_only(NTFS_INFO * ntfs, ntfs_idxentry * idxe,
293 TSK_FS_NAME * fs_name)
294 {
295 ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream;
296 TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
297 UTF16 *name16;
298 UTF8 *name8;
299 int retVal;
300
301 name16 = (UTF16 *) & fname->name;
302 name8 = (UTF8 *) fs_name->shrt_name;
303
304 retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
305 (UTF16 *) ((uintptr_t) name16 +
306 fname->nlen * 2), &name8,
307 (UTF8 *) ((uintptr_t) name8 +
308 fs_name->shrt_name_size), TSKlenientConversion);
309
310 if (retVal != TSKconversionOK) {
311 *name8 = '\0';
312 if (tsk_verbose)
313 tsk_fprintf(stderr,
314 "Error converting NTFS 8.3 name to UTF8: %d %" PRIuINUM,
315 retVal, fs_name->meta_addr);
316 }
317
318 /* Make sure it is NULL Terminated */
319 if ((uintptr_t) name8 > (uintptr_t) fs_name->shrt_name + fs_name->shrt_name_size)
320 fs_name->shrt_name[fs_name->shrt_name_size] = '\0';
321 else
322 *name8 = '\0';
323
324 return 0;
325 }
326
327
328
329
330 /* This is a sanity check to see if the time is valid
331 * it is divided by 100 to keep it in a 32-bit integer
332 */
333
334 static uint8_t
is_time(uint64_t t)335 is_time(uint64_t t)
336 {
337 #define SEC_BTWN_1601_1970_DIV100 ((369*365 + 89) * 24 * 36)
338 #define SEC_BTWN_1601_2020_DIV100 (SEC_BTWN_1601_1970_DIV100 + (50*365 + 6) * 24 * 36)
339
340 t /= 1000000000; /* put the time in seconds div by additional 100 */
341
342 if (!t)
343 return 0;
344
345 if (t < SEC_BTWN_1601_1970_DIV100)
346 return 0;
347
348 if (t > SEC_BTWN_1601_2020_DIV100)
349 return 0;
350
351 return 1;
352 }
353
354
355
356 /**
357 * Process a lsit of index entries and add to FS_DIR
358 *
359 * @param a_is_del Set to 1 if these entries are for a deleted directory
360 * @param idxe Buffer with index entries to process
361 * @param idxe_len Length of idxe buffer (in bytes)
362 * @param used_len Length of data as reported by idexlist header (everything
363 * after which and less then idxe_len is considered deleted)
364 *
365 * @returns 1 to stop, 0 on success, and -1 on error
366 */
367
368 // @@@ Should make a_idxe const and use internal pointer in function loop
369 static TSK_RETVAL_ENUM
ntfs_proc_idxentry(NTFS_INFO * a_ntfs,TSK_FS_DIR * a_fs_dir,uint8_t a_is_del,ntfs_idxentry * a_idxe,uint32_t a_idxe_len,uint32_t a_used_len)370 ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir,
371 uint8_t a_is_del, ntfs_idxentry * a_idxe, uint32_t a_idxe_len,
372 uint32_t a_used_len)
373 {
374 uintptr_t endaddr, endaddr_alloc;
375 TSK_FS_NAME *fs_name;
376 TSK_FS_NAME *fs_name_preventry = NULL;
377 TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_ntfs->fs_info;
378
379 if ((fs_name = tsk_fs_name_alloc(NTFS_MAXNAMLEN_UTF8, 16)) == NULL) {
380 return TSK_ERR;
381 }
382
383 if (tsk_verbose)
384 tsk_fprintf(stderr,
385 "ntfs_proc_idxentry: Processing index entry: %" PRIu64
386 " Size: %" PRIu32 " Len: %" PRIu32 "\n",
387 (uint64_t) ((uintptr_t) a_idxe), a_idxe_len, a_used_len);
388
389 /* Sanity check */
390 if (a_idxe_len < a_used_len) {
391 tsk_error_reset();
392 tsk_error_set_errno(TSK_ERR_FS_ARG);
393 tsk_error_set_errstr
394 ("ntfs_proc_idxentry: Allocated length of index entries is larger than buffer length");
395 return TSK_ERR;
396 }
397
398 /* where is the end of the buffer */
399 endaddr = ((uintptr_t) a_idxe + a_idxe_len);
400
401 /* where is the end of the allocated data */
402 endaddr_alloc = ((uintptr_t) a_idxe + a_used_len);
403
404 /* cycle through the index entries, based on provided size */
405 while (((uintptr_t) & (a_idxe->stream) + sizeof(ntfs_attr_fname)) <
406 endaddr) {
407
408 ntfs_attr_fname *fname = (ntfs_attr_fname *) & a_idxe->stream;
409
410
411 if (tsk_verbose)
412 tsk_fprintf(stderr,
413 "ntfs_proc_idxentry: New IdxEnt: %" PRIu64
414 " $FILE_NAME Entry: %" PRIu64 " File Ref: %" PRIu64
415 " IdxEnt Len: %" PRIu16 " StrLen: %" PRIu16 "\n",
416 (uint64_t) ((uintptr_t) a_idxe),
417 (uint64_t) ((uintptr_t) fname),
418 (uint64_t) tsk_getu48(fs->endian, a_idxe->file_ref),
419 tsk_getu16(fs->endian, a_idxe->idxlen),
420 tsk_getu16(fs->endian, a_idxe->strlen));
421
422 /* perform some sanity checks on index buffer head
423 * and advance by 4-bytes if invalid
424 */
425 if ((tsk_getu48(fs->endian, a_idxe->file_ref) > fs->last_inum) ||
426 (tsk_getu48(fs->endian, a_idxe->file_ref) < fs->first_inum) ||
427 (tsk_getu16(fs->endian,
428 a_idxe->idxlen) <= tsk_getu16(fs->endian,
429 a_idxe->strlen))
430 || (tsk_getu16(fs->endian, a_idxe->idxlen) % 4)
431 || (tsk_getu16(fs->endian, a_idxe->idxlen) > a_idxe_len)) {
432 a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
433 continue;
434 }
435
436 #if 0
437 // @@@ BC: This hid a lot of entries in test images. They were
438 // only partial images, but they were not junk and the idea was
439 // that this check would strip out chunk. Commented it out and
440 // keeping it here as a reminder in case I think about doing it
441 // again.
442
443 // verify name length would fit in stream
444 if (fname->nlen > tsk_getu16(fs->endian, a_idxe->strlen)) {
445 a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
446 if (tsk_verbose)
447 tsk_fprintf(stderr,
448 "ntfs_proc_idxentry: Skipping because name is longer than stream\n");
449 continue;
450 }
451 #endif
452
453 // verify it has the correct parent address
454 if (tsk_getu48(fs->endian, fname->par_ref) != a_fs_dir->addr) {
455 a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
456 if (tsk_verbose)
457 tsk_fprintf(stderr,
458 "ntfs_proc_idxentry: Skipping because of wrong parent address\n");
459 continue;
460 }
461
462
463 /* do some sanity checks on the deleted entries
464 */
465 if ((tsk_getu16(fs->endian, a_idxe->strlen) == 0) ||
466 (((uintptr_t) a_idxe + tsk_getu16(fs->endian,
467 a_idxe->idxlen)) > endaddr_alloc)) {
468
469 /* name space checks */
470 if ((fname->nspace != NTFS_FNAME_POSIX) &&
471 (fname->nspace != NTFS_FNAME_WIN32) &&
472 (fname->nspace != NTFS_FNAME_DOS) &&
473 (fname->nspace != NTFS_FNAME_WINDOS)) {
474 a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
475 if (tsk_verbose)
476 tsk_fprintf(stderr,
477 "ntfs_proc_idxentry: Skipping because of invalid name space\n");
478 continue;
479 }
480
481 if ((tsk_getu64(fs->endian, fname->alloc_fsize) <
482 tsk_getu64(fs->endian, fname->real_fsize))
483 || (fname->nlen == 0)
484 || (*(uint8_t *) & fname->name == 0)) {
485
486 a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
487 if (tsk_verbose)
488 tsk_fprintf(stderr,
489 "ntfs_proc_idxentry: Skipping because of reported file sizes, name length, or NULL name\n");
490 continue;
491 }
492
493 if ((is_time(tsk_getu64(fs->endian, fname->crtime)) == 0) ||
494 (is_time(tsk_getu64(fs->endian, fname->atime)) == 0) ||
495 (is_time(tsk_getu64(fs->endian, fname->mtime)) == 0)) {
496
497 a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4);
498 if (tsk_verbose)
499 tsk_fprintf(stderr,
500 "ntfs_proc_idxentry: Skipping because of invalid times\n");
501 continue;
502 }
503 }
504
505
506 /* For all fname entries, there will exist a DOS style 8.3
507 * entry.
508 * If the original name is 8.3 compliant, it will be in
509 * a WINDOS type. If it is not compliant, then it will
510 * exist in a POSIX or WIN32 type and the 8.3 compliant
511 * one will be in DOS. The DOS entry typically follows
512 * the WIN32 or POSIX.
513 *
514 * Our approach is to stash away the non-compliant names
515 * for one more entry to see if the next try is its
516 * corresponding 8.3 entry.
517 *
518 * If the 8.3 entry is not for the previous entry, we
519 * skip it on the theory that it corresponds to a previous
520 * WIN32 or POSIX entry. Note that we could be missing some info from deleted files
521 * if the windows version was deleted and the DOS wasn't...
522 */
523
524 if (fname->nspace == NTFS_FNAME_DOS) {
525 // Was the previous entry not 8.3 compliant?
526 if (fs_name_preventry) {
527 // check its the same entry and if so, add short name
528 if (fs_name_preventry->meta_addr == tsk_getu48(fs->endian, a_idxe->file_ref)) {
529 ntfs_dent_copy_short_only(a_ntfs, a_idxe, fs_name_preventry);
530 }
531
532 // regardless, add preventry to dir and move on to next entry.
533 if (tsk_fs_dir_add(a_fs_dir, fs_name_preventry)) {
534 tsk_fs_name_free(fs_name);
535 return TSK_ERR;
536 }
537 fs_name_preventry = NULL;
538 }
539
540 goto incr_entry;
541 }
542 // if we stashed the previous entry and the next wasn't a DOS entry, add it to the list
543 else if (fs_name_preventry) {
544 if (tsk_fs_dir_add(a_fs_dir, fs_name_preventry)) {
545 tsk_fs_name_free(fs_name);
546 return TSK_ERR;
547 }
548 fs_name_preventry = NULL;
549 }
550
551 /* Copy it into the generic form */
552 if (ntfs_dent_copy(a_ntfs, a_idxe, fs_name)) {
553 if (tsk_verbose)
554 tsk_fprintf(stderr,
555 "ntfs_proc_idxentry: Skipping because error copying dent_entry\n");
556 goto incr_entry;
557 }
558
559 /*
560 * Check if this entry is deleted
561 *
562 * The final check is to see if the end of this entry is
563 * within the space that the idxallocbuf claimed was valid OR
564 * if the parent directory is deleted
565 */
566 if ((a_is_del == 1) ||
567 (tsk_getu16(fs->endian, a_idxe->strlen) == 0) ||
568 (((uintptr_t) a_idxe + tsk_getu16(fs->endian,
569 a_idxe->idxlen)) > endaddr_alloc)) {
570 fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
571 }
572 else {
573 fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
574 }
575
576 if (tsk_verbose)
577 tsk_fprintf(stderr,
578 "ntfs_proc_idxentry: Entry Details of %s: Str Len: %"
579 PRIu16 " Len to end after current: %" PRIu64
580 " flags: %x\n", fs_name->name, tsk_getu16(fs->endian,
581 a_idxe->strlen),
582 (uint64_t) (endaddr_alloc - (uintptr_t) a_idxe -
583 tsk_getu16(fs->endian, a_idxe->idxlen)),
584 fs_name->flags);
585
586 // WINDOS entries will not have a short 8.3 version, so add them now.
587 // otherwise, we stash the name to see if we get the 8.3 next.
588 if (fname->nspace == NTFS_FNAME_WINDOS) {
589 if (tsk_fs_dir_add(a_fs_dir, fs_name)) {
590 tsk_fs_name_free(fs_name);
591 return TSK_ERR;
592 }
593 fs_name_preventry = NULL;
594 }
595 else {
596 fs_name_preventry = fs_name;
597 }
598
599 incr_entry:
600
601 /* the theory here is that deleted entries have strlen == 0 and
602 * have been found to have idxlen == 16
603 *
604 * if the strlen is 0, then guess how much the indexlen was
605 * before it was deleted
606 */
607
608 /* 16: size of idxentry before stream
609 * 66: size of fname before name
610 * 2*nlen: size of name (in unicode)
611 */
612 if (tsk_getu16(fs->endian, a_idxe->strlen) == 0) {
613 a_idxe =
614 (ntfs_idxentry
615 *) ((((uintptr_t) a_idxe + 16 + 66 + 2 * fname->nlen +
616 3) / 4) * 4);
617 }
618 else {
619 a_idxe =
620 (ntfs_idxentry *) ((uintptr_t) a_idxe +
621 tsk_getu16(fs->endian, a_idxe->idxlen));
622 }
623
624 } /* end of loop of index entries */
625
626 // final check in case we were looking for the short name, we never saw
627 if (fs_name_preventry) {
628 if (tsk_fs_dir_add(a_fs_dir, fs_name_preventry)) {
629 tsk_fs_name_free(fs_name);
630 return TSK_ERR;
631 }
632 fs_name_preventry = NULL;
633 }
634
635 tsk_fs_name_free(fs_name);
636 return TSK_OK;
637 }
638
639
640
641
642 /*
643 * remove the update sequence values that are changed in the last two
644 * bytes of each sector
645 *
646 * return 1 on error and 0 on success
647 */
648 static uint8_t
ntfs_fix_idxrec(NTFS_INFO * ntfs,ntfs_idxrec * idxrec,uint32_t len)649 ntfs_fix_idxrec(NTFS_INFO * ntfs, ntfs_idxrec * idxrec, uint32_t len)
650 {
651 int i;
652 uint16_t orig_seq;
653 TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
654 ntfs_upd *upd;
655
656 if (tsk_verbose)
657 tsk_fprintf(stderr,
658 "ntfs_fix_idxrec: Fixing idxrec: %" PRIu64 " Len: %"
659 PRIu32 "\n", (uint64_t) ((uintptr_t) idxrec), len);
660
661 /* sanity check so we don't run over in the next loop */
662 if ((unsigned int) ((tsk_getu16(fs->endian, idxrec->upd_cnt) - 1) *
663 NTFS_UPDATE_SEQ_STRIDE) > len) {
664 tsk_error_reset();
665 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
666 tsk_error_set_errstr
667 ("fix_idxrec: More Update Sequence Entries than idx record size");
668 return 1;
669 }
670
671 /* Apply the update sequence structure template */
672 upd =
673 (ntfs_upd *) ((uintptr_t) idxrec + tsk_getu16(fs->endian,
674 idxrec->upd_off));
675
676 /* Get the sequence value that each 16-bit value should be */
677 orig_seq = tsk_getu16(fs->endian, upd->upd_val);
678
679 /* cycle through each sector */
680 for (i = 1; i < tsk_getu16(fs->endian, idxrec->upd_cnt); i++) {
681
682 /* The offset into the buffer of the value to analyze */
683 int offset = i * NTFS_UPDATE_SEQ_STRIDE - 2;
684 uint8_t *new_val, *old_val;
685
686 /* get the current sequence value */
687 uint16_t cur_seq =
688 tsk_getu16(fs->endian, (uintptr_t) idxrec + offset);
689
690 if (cur_seq != orig_seq) {
691 /* get the replacement value */
692 uint16_t cur_repl =
693 tsk_getu16(fs->endian, &upd->upd_seq + (i - 1) * 2);
694
695 tsk_error_reset();
696 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
697 tsk_error_set_errstr
698 ("fix_idxrec: Incorrect update sequence value in index buffer\nUpdate Value: 0x%"
699 PRIx16 " Actual Value: 0x%" PRIx16
700 " Replacement Value: 0x%" PRIx16
701 "\nThis is typically because of a corrupted entry",
702 orig_seq, cur_seq, cur_repl);
703 return 1;
704 }
705
706 new_val = &upd->upd_seq + (i - 1) * 2;
707 old_val = (uint8_t *) ((uintptr_t) idxrec + offset);
708
709 if (tsk_verbose)
710 tsk_fprintf(stderr,
711 "ntfs_fix_idxrec: upd_seq %i Replacing: %.4" PRIx16
712 " With: %.4" PRIx16 "\n", i, tsk_getu16(fs->endian,
713 old_val), tsk_getu16(fs->endian, new_val));
714
715 *old_val++ = *new_val++;
716 *old_val = *new_val;
717 }
718
719 return 0;
720 }
721
722
723
724
725
726 /** \internal
727 * Process a directory and load up FS_DIR with the entries. If a pointer to
728 * an already allocated FS_DIR structure is given, it will be cleared. If no existing
729 * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
730 * value is error or corruption, then the FS_DIR structure could
731 * have entries (depending on when the error occurred).
732 *
733 * @param a_fs File system to analyze
734 * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
735 * structure or a new structure.
736 * @param a_addr Address of directory to process.
737 * @returns error, corruption, ok etc.
738 */
739 TSK_RETVAL_ENUM
ntfs_dir_open_meta(TSK_FS_INFO * a_fs,TSK_FS_DIR ** a_fs_dir,TSK_INUM_T a_addr)740 ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
741 TSK_INUM_T a_addr)
742 {
743 NTFS_INFO *ntfs = (NTFS_INFO *) a_fs;
744 TSK_FS_DIR *fs_dir;
745 const TSK_FS_ATTR *fs_attr_root = NULL;
746 const TSK_FS_ATTR *fs_attr_idx;
747 char *idxalloc;
748 ntfs_idxentry *idxe;
749 ntfs_idxroot *idxroot;
750 ntfs_idxelist *idxelist;
751 ntfs_idxrec *idxrec_p, *idxrec;
752 TSK_OFF_T idxalloc_len;
753 TSK_FS_LOAD_FILE load_file;
754
755 /* In this function, we will return immediately if we get an error.
756 * If we get corruption though, we will record that in 'retval_final'
757 * and continue processing.
758 */
759 TSK_RETVAL_ENUM retval_final = TSK_OK;
760 TSK_RETVAL_ENUM retval_tmp;
761
762 /* sanity check */
763 if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
764 tsk_error_reset();
765 tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
766 tsk_error_set_errstr("ntfs_dir_open_meta: inode value: %" PRIuINUM
767 "\n", a_addr);
768 return TSK_ERR;
769 }
770 else if (a_fs_dir == NULL) {
771 tsk_error_reset();
772 tsk_error_set_errno(TSK_ERR_FS_ARG);
773 tsk_error_set_errstr
774 ("ntfs_dir_open_meta: NULL fs_attr argument given");
775 return TSK_ERR;
776 }
777
778 if (tsk_verbose)
779 tsk_fprintf(stderr,
780 "ntfs_open_dir: Processing directory %" PRIuINUM "\n", a_addr);
781
782
783 fs_dir = *a_fs_dir;
784 if (fs_dir) {
785 tsk_fs_dir_reset(fs_dir);
786 fs_dir->addr = a_addr;
787 }
788 else {
789 if ((*a_fs_dir = fs_dir =
790 tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) {
791 return TSK_ERR;
792 }
793 }
794
795 // handle the orphan directory if its contents were requested
796 if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) {
797 return tsk_fs_dir_find_orphans(a_fs, fs_dir);
798 }
799
800 /* Get the inode and verify it has attributes */
801 if ((fs_dir->fs_file =
802 tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) {
803 tsk_error_errstr2_concat("- ntfs_dir_open_meta");
804 return TSK_COR;
805 }
806
807 if (!(fs_dir->fs_file->meta->attr)) {
808 tsk_error_reset();
809 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
810 tsk_error_set_errstr("dent_walk: Error: Directory address %"
811 PRIuINUM " has no attributes", a_addr);
812 return TSK_COR;
813 }
814
815 // Update with the sequence number
816 fs_dir->seq = fs_dir->fs_file->meta->seq;
817
818 /*
819 * Read the Index Root Attribute -- we do some sanity checking here
820 * to report errors before we start to make up data for the "." and ".."
821 * entries
822 */
823 fs_attr_root =
824 tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr,
825 TSK_FS_ATTR_TYPE_NTFS_IDXROOT);
826 if (!fs_attr_root) {
827 tsk_error_errstr2_concat(" - dent_walk: $IDX_ROOT not found");
828 return TSK_COR;
829 }
830
831 if (fs_attr_root->flags & TSK_FS_ATTR_NONRES) {
832 tsk_error_reset();
833 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
834 tsk_error_set_errstr
835 ("dent_walk: $IDX_ROOT is not resident - it should be");
836 return TSK_COR;
837 }
838 idxroot = (ntfs_idxroot *) fs_attr_root->rd.buf;
839
840 /* Verify that the attribute type is $FILE_NAME */
841 if (tsk_getu32(a_fs->endian, idxroot->type) == 0) {
842 tsk_error_reset();
843 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
844 tsk_error_set_errstr
845 ("dent_walk: Attribute type in index root is 0");
846 return TSK_COR;
847 }
848 else if (tsk_getu32(a_fs->endian, idxroot->type) != NTFS_ATYPE_FNAME) {
849 tsk_error_reset();
850 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
851 tsk_error_set_errstr("ERROR: Directory index is sorted by type: %"
852 PRIu32 ".\nOnly $FNAME is currently supported",
853 tsk_getu32(a_fs->endian, idxroot->type));
854 return TSK_COR;
855 }
856
857
858
859 /*
860 * NTFS does not have "." and ".." entries in the index trees
861 * (except for a "." entry in the root directory)
862 *
863 * So, we'll make 'em up by making a TSK_FS_NAME structure for
864 * a '.' and '..' entry and call the action
865 */
866 if (a_addr != a_fs->root_inum) { // && (flags & TSK_FS_NAME_FLAG_ALLOC)) {
867 TSK_FS_NAME *fs_name;
868 TSK_FS_META_NAME_LIST *fs_name_list;
869
870 if (tsk_verbose)
871 tsk_fprintf(stderr,
872 "ntfs_dir_open_meta: Creating . and .. entries\n");
873
874 if ((fs_name = tsk_fs_name_alloc(16, 0)) == NULL) {
875 return TSK_ERR;
876 }
877 /*
878 * "."
879 */
880
881 fs_name->type = TSK_FS_NAME_TYPE_DIR;
882 strcpy(fs_name->name, ".");
883
884 fs_name->meta_addr = a_addr;
885 if (fs_dir->fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) {
886 fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
887 /* If the folder was deleted, the MFT entry sequence will have been incremented.
888 * File name entries are not incremented on delete, so make it one less to
889 * be consistent. */
890 fs_name->meta_seq = fs_dir->fs_file->meta->seq - 1;
891 }
892 else {
893 fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
894 fs_name->meta_seq = fs_dir->fs_file->meta->seq;
895 }
896 if (tsk_fs_dir_add(fs_dir, fs_name)) {
897 tsk_fs_name_free(fs_name);
898 return TSK_ERR;
899 }
900
901
902 /*
903 * ".."
904 */
905 strcpy(fs_name->name, "..");
906 fs_name->type = TSK_FS_NAME_TYPE_DIR;
907
908 /* The fs_name structure holds the parent inode value, so we
909 * just cycle using those
910 */
911 for (fs_name_list = fs_dir->fs_file->meta->name2;
912 fs_name_list != NULL; fs_name_list = fs_name_list->next) {
913 fs_name->meta_addr = fs_name_list->par_inode;
914 fs_name->meta_seq = fs_name_list->par_seq;
915 if (tsk_fs_dir_add(fs_dir, fs_name)) {
916 tsk_fs_name_free(fs_name);
917 return TSK_ERR;
918 }
919 }
920
921 tsk_fs_name_free(fs_name);
922 fs_name = NULL;
923 }
924
925
926 /* Now we return to processing the Index Root Attribute */
927 if (tsk_verbose)
928 tsk_fprintf(stderr,
929 "ntfs_dir_open_meta: Processing $IDX_ROOT of inum %" PRIuINUM
930 "\n", a_addr);
931
932 /* Get the header of the index entry list */
933 idxelist = &idxroot->list;
934
935 /* Verify the offset pointers */
936 if ((tsk_getu32(a_fs->endian, idxelist->seqend_off) <
937 tsk_getu32(a_fs->endian, idxelist->begin_off)) ||
938 (tsk_getu32(a_fs->endian, idxelist->bufend_off) <
939 tsk_getu32(a_fs->endian, idxelist->seqend_off)) ||
940 (((uintptr_t) idxelist + tsk_getu32(a_fs->endian,
941 idxelist->bufend_off)) >
942 ((uintptr_t) fs_attr_root->rd.buf +
943 fs_attr_root->rd.buf_size))) {
944 tsk_error_reset();
945 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
946 tsk_error_set_errstr
947 ("Error: Index list offsets are invalid on entry: %" PRIuINUM,
948 fs_dir->fs_file->meta->addr);
949 return TSK_COR;
950 }
951
952 /* Get the offset to the start of the index entry list */
953 idxe = (ntfs_idxentry *) ((uintptr_t) idxelist +
954 tsk_getu32(a_fs->endian, idxelist->begin_off));
955
956 retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir,
957 (fs_dir->fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0,
958 idxe,
959 tsk_getu32(a_fs->endian, idxelist->bufend_off) -
960 tsk_getu32(a_fs->endian, idxelist->begin_off),
961 tsk_getu32(a_fs->endian, idxelist->seqend_off) -
962 tsk_getu32(a_fs->endian, idxelist->begin_off));
963
964 // stop if we get an error, continue if we got corruption
965 if (retval_tmp == TSK_ERR) {
966 return TSK_ERR;
967 }
968 else if (retval_tmp == TSK_COR) {
969 retval_final = TSK_COR;
970 }
971
972 /*
973 * get the index allocation attribute if it exists (it doesn't for
974 * small directories
975 */
976 fs_attr_idx =
977 tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr,
978 TSK_FS_ATTR_TYPE_NTFS_IDXALLOC);
979
980
981 /* if we don't have an index alloc then return, we have processed
982 * all of the entries
983 */
984 if (!fs_attr_idx) {
985 if (tsk_getu32(a_fs->endian,
986 idxelist->flags) & NTFS_IDXELIST_CHILD) {
987 tsk_error_reset();
988 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
989 tsk_error_set_errstr
990 ("Error: $IDX_ROOT says there should be children, but there isn't");
991 return TSK_COR;
992 }
993 }
994 else {
995 int off;
996
997 if (fs_attr_idx->flags & TSK_FS_ATTR_RES) {
998 tsk_error_reset();
999 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1000 tsk_error_set_errstr
1001 ("$IDX_ALLOC is Resident - it shouldn't be");
1002 return TSK_COR;
1003 }
1004
1005 /*
1006 * Copy the index allocation run into a big buffer
1007 */
1008 idxalloc_len = fs_attr_idx->nrd.allocsize;
1009 if ((idxalloc = (char *)tsk_malloc((size_t) idxalloc_len)) == NULL) {
1010 return TSK_ERR;
1011 }
1012
1013 /* Fill in the loading data structure */
1014 load_file.total = load_file.left = (size_t) idxalloc_len;
1015 load_file.cur = load_file.base = idxalloc;
1016
1017 if (tsk_verbose)
1018 tsk_fprintf(stderr,
1019 "ntfs_dir_open_meta: Copying $IDX_ALLOC into buffer\n");
1020
1021 if (tsk_fs_attr_walk(fs_attr_idx,
1022 TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action,
1023 (void *) &load_file)) {
1024 free(idxalloc);
1025 tsk_error_errstr2_concat(" - ntfs_dir_open_meta");
1026 return TSK_COR; // this could be an error though
1027 }
1028
1029 /* Not all of the directory was copied, so we exit */
1030 if (load_file.left > 0) {
1031 free(idxalloc);
1032
1033 tsk_error_reset();
1034 tsk_error_set_errno(TSK_ERR_FS_FWALK);
1035 tsk_error_set_errstr("Error reading directory contents: %"
1036 PRIuINUM "\n", a_addr);
1037 return TSK_COR;
1038 }
1039
1040 /*
1041 * The idxalloc is a big buffer that contains one or more
1042 * idx buffer structures. Each idxrec is a node in the B-Tree.
1043 * We do not process the tree as a tree because then we could
1044 * not find the deleted file names.
1045 *
1046 * Therefore, we scan the big buffer looking for the index record
1047 * structures. We save a pointer to the known beginning (idxrec_p).
1048 * Then we scan for the beginning of the next one (idxrec) and process
1049 * everything in the middle as an ntfs_idxrec. We can't use the
1050 * size given because then we wouldn't see the deleted names
1051 */
1052
1053 /* Set the previous pointer to NULL */
1054 idxrec_p = idxrec = NULL;
1055
1056 /* Loop by cluster size */
1057 for (off = 0; off < idxalloc_len; off += ntfs->csize_b) {
1058 uint32_t list_len, rec_len;
1059
1060 idxrec = (ntfs_idxrec *) & idxalloc[off];
1061
1062 if (tsk_verbose)
1063 tsk_fprintf(stderr,
1064 "ntfs_dir_open_meta: Index Buffer Offset: %d Magic: %"
1065 PRIx32 "\n", off, tsk_getu32(a_fs->endian,
1066 idxrec->magic));
1067
1068 /* Is this the beginning of an index record? */
1069 if (tsk_getu32(a_fs->endian,
1070 idxrec->magic) != NTFS_IDXREC_MAGIC)
1071 continue;
1072
1073
1074 /* idxrec_p is only NULL for the first time
1075 * Set it and start again to find the next one */
1076 if (idxrec_p == NULL) {
1077 idxrec_p = idxrec;
1078 continue;
1079 }
1080
1081 /* Process the previous structure */
1082
1083 /* idxrec points to the next idxrec structure, idxrec_p
1084 * points to the one we are going to process
1085 */
1086 rec_len =
1087 (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxrec_p);
1088
1089 if (tsk_verbose)
1090 tsk_fprintf(stderr,
1091 "ntfs_dir_open_meta: Processing previous index record (len: %"
1092 PRIu32 ")\n", rec_len);
1093
1094 /* remove the update sequence in the index record */
1095 if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) {
1096 free(idxalloc);
1097 return TSK_COR;
1098 }
1099
1100 /* Locate the start of the index entry list */
1101 idxelist = &idxrec_p->list;
1102 idxe = (ntfs_idxentry *) ((uintptr_t) idxelist +
1103 tsk_getu32(a_fs->endian, idxelist->begin_off));
1104
1105 /* the length from the start of the next record to where our
1106 * list starts.
1107 * This should be the same as bufend_off in idxelist, but we don't
1108 * trust it.
1109 */
1110 list_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxe);
1111
1112 /* Verify the offset pointers */
1113 if (((uintptr_t) idxe > (uintptr_t) idxrec) ||
1114 ((uintptr_t) idxelist +
1115 tsk_getu32(a_fs->endian,
1116 idxelist->seqend_off) > (uintptr_t) idxrec)) {
1117 tsk_error_reset();
1118 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1119 tsk_error_set_errstr
1120 ("Error: Index list offsets are invalid on entry: %"
1121 PRIuINUM, fs_dir->fs_file->meta->addr);
1122 free(idxalloc);
1123 return TSK_COR;
1124 }
1125
1126
1127 /* process the list of index entries */
1128 retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir,
1129 (fs_dir->fs_file->meta->
1130 flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe,
1131 list_len, tsk_getu32(a_fs->endian,
1132 idxelist->seqend_off) - tsk_getu32(a_fs->endian,
1133 idxelist->begin_off));
1134 // stop if we get an error, record if we get corruption
1135 if (retval_tmp == TSK_ERR) {
1136 free(idxalloc);
1137 return TSK_ERR;
1138 }
1139 else if (retval_tmp == TSK_COR) {
1140 retval_final = TSK_COR;
1141 }
1142
1143 /* reset the pointer to the next record */
1144 idxrec_p = idxrec;
1145
1146 } /* end of cluster loop */
1147
1148
1149 /* Process the final record */
1150 if (idxrec_p) {
1151 uint32_t list_len, rec_len;
1152
1153 /* Length from end of attribute to start of this */
1154 rec_len =
1155 (uint32_t) (idxalloc_len - ((uintptr_t) idxrec_p -
1156 (uintptr_t) idxalloc));
1157
1158 if (tsk_verbose)
1159 tsk_fprintf(stderr,
1160 "ntfs_dir_open_meta: Processing final index record (len: %"
1161 PRIu32 ")\n", rec_len);
1162
1163 /* remove the update sequence */
1164 if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) {
1165 free(idxalloc);
1166 return TSK_COR;
1167 }
1168
1169 idxelist = &idxrec_p->list;
1170 if (tsk_getu32(a_fs->endian, idxelist->begin_off) > rec_len) {
1171 tsk_error_reset();
1172 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1173 tsk_error_set_errstr
1174 ("Error: Index list offsets are invalid on entry: %"
1175 PRIuINUM, fs_dir->fs_file->meta->addr);
1176 free(idxalloc);
1177 return TSK_COR;
1178 }
1179
1180 idxe = (ntfs_idxentry *) ((uintptr_t) idxelist +
1181 tsk_getu32(a_fs->endian, idxelist->begin_off));
1182
1183 /* This is the length of the idx entries */
1184 list_len =
1185 (uint32_t) (((uintptr_t) idxalloc + idxalloc_len) -
1186 (uintptr_t) idxe);
1187
1188 /* Verify the offset pointers */
1189 if ((list_len > rec_len) ||
1190 ((uintptr_t) idxelist +
1191 tsk_getu32(a_fs->endian, idxelist->seqend_off) >
1192 (uintptr_t) idxalloc + idxalloc_len)) {
1193 tsk_error_reset();
1194 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1195 tsk_error_set_errstr
1196 ("Error: Index list offsets are invalid on entry: %"
1197 PRIuINUM, fs_dir->fs_file->meta->addr);
1198 free(idxalloc);
1199 return TSK_COR;
1200 }
1201
1202 /* process the list of index entries */
1203 retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir,
1204 (fs_dir->fs_file->meta->
1205 flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe,
1206 list_len, tsk_getu32(a_fs->endian,
1207 idxelist->seqend_off) - tsk_getu32(a_fs->endian,
1208 idxelist->begin_off));
1209 // stop if we get an error, record if we get corruption
1210 if (retval_tmp == TSK_ERR) {
1211 free(idxalloc);
1212 return TSK_ERR;
1213 }
1214 else if (retval_tmp == TSK_COR) {
1215 retval_final = TSK_COR;
1216 }
1217 }
1218
1219 free(idxalloc);
1220 }
1221
1222
1223 // get the orphan files
1224 // load and cache the map if it has not already been done
1225 tsk_take_lock(&ntfs->orphan_map_lock);
1226 if (ntfs->orphan_map == NULL) {
1227 // we do this to make it non-NULL. WE had some images that
1228 // had no orphan files and it repeatedly did inode_walks
1229 // because orphan_map was always NULL
1230 getParentMap(ntfs);
1231
1232 if (a_fs->inode_walk(a_fs, a_fs->first_inum, a_fs->last_inum,
1233 (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_ALLOC), ntfs_parent_act, NULL)) {
1234 tsk_release_lock(&ntfs->orphan_map_lock);
1235 return TSK_ERR;
1236 }
1237 }
1238
1239
1240 /* see if there are any entries in MFT for this dir that we didn't see.
1241 * Need to make sure it is for this version (sequence) though.
1242 * NTFS Updates the sequence when a directory is deleted and not when
1243 * it is allocated. So, if we have a deleted directory, then use
1244 * its previous sequence number to find the files that were in it when
1245 * it was allocated.
1246 */
1247 uint16_t seqToSrch = fs_dir->fs_file->meta->seq;
1248 if (fs_dir->fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) {
1249 if (seqToSrch > 0)
1250 seqToSrch--;
1251 else
1252 // I can't imagine how we get here or what we should do except maybe not do the search.
1253 seqToSrch = 0;
1254 }
1255
1256 if (ntfs_parent_map_exists(ntfs, a_addr, seqToSrch)) {
1257 TSK_FS_NAME *fs_name;
1258
1259 std::vector <NTFS_META_ADDR> &childFiles = ntfs_parent_map_get(ntfs, a_addr, seqToSrch);
1260
1261 if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL)
1262 return TSK_ERR;
1263
1264 fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
1265 fs_name->par_addr = a_addr;
1266 fs_name->par_seq = fs_dir->fs_file->meta->seq;
1267
1268 for (size_t a = 0; a < childFiles.size(); a++) {
1269 TSK_FS_FILE *fs_file_orp = NULL;
1270
1271 /* Check if fs_dir already has an allocated entry for this
1272 * file. If so, ignore it. We used to rely on fs_dir_add
1273 * to get rid of this, but it wasted a lot of lookups. If
1274 * We have only unalloc for this same entry (from idx entries),
1275 * then try to add it. If we got an allocated entry from
1276 * the idx entries, then assume we have everything. */
1277 if (tsk_fs_dir_contains(fs_dir, childFiles[a].getAddr(), childFiles[a].getHash()) == TSK_FS_NAME_FLAG_ALLOC) {
1278 continue;
1279 }
1280
1281 /* Fill in the basics of the fs_name entry
1282 * so we can print in the fls formats */
1283 fs_name->meta_addr = childFiles[a].getAddr();
1284 fs_name->meta_seq = childFiles[a].getSeq();
1285
1286 // lookup the file to get more info (we did not cache that)
1287 fs_file_orp =
1288 tsk_fs_file_open_meta(a_fs, fs_file_orp, fs_name->meta_addr);
1289 if (fs_file_orp) {
1290 if (fs_file_orp->meta) {
1291 if (fs_file_orp->meta->flags & TSK_FS_META_FLAG_ALLOC) {
1292 fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
1293 }
1294 else {
1295 fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
1296 /* This sequence is the MFT entry, which gets
1297 * incremented when it is unallocated. So,
1298 * decrement it back down so that it is more
1299 * similar to the usual situation, where the
1300 * name sequence is 1 smaller than the meta
1301 * sequence. */
1302 fs_name->meta_seq--;
1303 }
1304
1305 if (fs_file_orp->meta->name2) {
1306 TSK_FS_META_NAME_LIST *n2 = fs_file_orp->meta->name2;
1307
1308 while (n2) {
1309 if (n2->par_inode == a_addr) {
1310 strncpy(fs_name->name, n2->name, fs_name->name_size);
1311 tsk_fs_dir_add(fs_dir, fs_name);
1312 }
1313 n2 = n2->next;
1314 }
1315 }
1316 }
1317 tsk_fs_file_close(fs_file_orp);
1318 }
1319 }
1320 tsk_fs_name_free(fs_name);
1321 }
1322 tsk_release_lock(&ntfs->orphan_map_lock);
1323
1324 // if we are listing the root directory, add the Orphan directory entry
1325 if (a_addr == a_fs->root_inum) {
1326 TSK_FS_NAME *fs_name;
1327
1328 if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL)
1329 return TSK_ERR;
1330
1331 if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) {
1332 tsk_fs_name_free(fs_name);
1333 return TSK_ERR;
1334 }
1335
1336 if (tsk_fs_dir_add(fs_dir, fs_name)) {
1337 tsk_fs_name_free(fs_name);
1338 return TSK_ERR;
1339 }
1340 tsk_fs_name_free(fs_name);
1341 }
1342
1343
1344 return retval_final;
1345 }
1346
1347
1348
1349 /****************************************************************************
1350 * FIND_FILE ROUTINES
1351 *
1352 */
1353
1354 #define MAX_DEPTH 128
1355 #define DIR_STRSZ 4096
1356
1357 typedef struct {
1358 /* Recursive path stuff */
1359
1360 /* how deep in the directory tree are we */
1361 unsigned int depth;
1362
1363 /* pointer in dirs string to where '/' is for given depth */
1364 char *didx[MAX_DEPTH];
1365
1366 /* The current directory name string */
1367 char dirs[DIR_STRSZ];
1368
1369 } NTFS_DINFO;
1370
1371
1372 /*
1373 * Looks up the parent inode described in fs_name.
1374 *
1375 * fs_name was filled in by ntfs_find_file and will get the final path
1376 * added to it before action is called
1377 *
1378 * return 1 on error and 0 on success
1379 */
1380 static uint8_t
ntfs_find_file_rec(TSK_FS_INFO * fs,NTFS_DINFO * dinfo,TSK_FS_FILE * fs_file,TSK_FS_META_NAME_LIST * fs_name_list,TSK_FS_DIR_WALK_CB action,void * ptr)1381 ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
1382 TSK_FS_FILE * fs_file, TSK_FS_META_NAME_LIST * fs_name_list,
1383 TSK_FS_DIR_WALK_CB action, void *ptr)
1384 {
1385 TSK_FS_FILE *fs_file_par;
1386 TSK_FS_META_NAME_LIST *fs_name_list_par;
1387 uint8_t decrem = 0;
1388 size_t len = 0, i;
1389 char *begin = NULL;
1390
1391
1392 if (fs_name_list->par_inode < fs->first_inum ||
1393 fs_name_list->par_inode > fs->last_inum) {
1394 tsk_error_reset();
1395 tsk_error_set_errno(TSK_ERR_FS_ARG);
1396 tsk_error_set_errstr("invalid inode value: %" PRIuINUM "\n",
1397 fs_name_list->par_inode);
1398 return 1;
1399 }
1400
1401 fs_file_par = tsk_fs_file_open_meta(fs, NULL, fs_name_list->par_inode);
1402 if (fs_file_par == NULL) {
1403 tsk_error_errstr2_concat(" - ntfs_find_file_rec");
1404 return 1;
1405 }
1406
1407 /*
1408 * Orphan File
1409 * This occurs when the file is deleted and either:
1410 * - The parent is no longer a directory
1411 * - The sequence number of the parent is no longer correct
1412 */
1413 if (( ! TSK_FS_IS_DIR_META(fs_file_par->meta->type))
1414 || (fs_file_par->meta->seq != fs_name_list->par_seq)) {
1415 const char *str = TSK_FS_ORPHAN_STR;
1416 int retval;
1417 len = strlen(str);
1418
1419 /* @@@ There should be a sanity check here to verify that the
1420 * previous name was unallocated ... but how do I get it again?
1421 */
1422 if ((((uintptr_t) dinfo->didx[dinfo->depth - 1] - len) >=
1423 (uintptr_t) & dinfo->dirs[0])
1424 && (dinfo->depth < MAX_DEPTH)) {
1425 begin = dinfo->didx[dinfo->depth] =
1426 (char *) ((uintptr_t) dinfo->didx[dinfo->depth - 1] - len);
1427
1428 dinfo->depth++;
1429 decrem = 1;
1430
1431 for (i = 0; i < len; i++)
1432 begin[i] = str[i];
1433 }
1434
1435 retval = action(fs_file, begin, ptr);
1436
1437 if (decrem)
1438 dinfo->depth--;
1439
1440 tsk_fs_file_close(fs_file_par);
1441 return (retval == TSK_WALK_ERROR) ? 1 : 0;
1442 }
1443
1444 for (fs_name_list_par = fs_file_par->meta->name2;
1445 fs_name_list_par != NULL;
1446 fs_name_list_par = fs_name_list_par->next) {
1447
1448 len = strlen(fs_name_list_par->name);
1449
1450 /* do some length checks on the dir structure
1451 * if we can't fit it then forget about it */
1452 if ((((uintptr_t) dinfo->didx[dinfo->depth - 1] - len - 1) >=
1453 (uintptr_t) & dinfo->dirs[0])
1454 && (dinfo->depth < MAX_DEPTH)) {
1455 begin = dinfo->didx[dinfo->depth] =
1456 (char *) ((uintptr_t) dinfo->didx[dinfo->depth - 1] - len -
1457 1);
1458
1459 dinfo->depth++;
1460 decrem = 1;
1461
1462 *begin = '/';
1463 for (i = 0; i < len; i++)
1464 begin[i + 1] = fs_name_list_par->name[i];
1465 }
1466 else {
1467 begin = dinfo->didx[dinfo->depth];
1468 decrem = 0;
1469 }
1470
1471
1472 /* if we are at the root, then fill out the rest of fs_name with
1473 * the full path and call the action
1474 */
1475 if (fs_name_list_par->par_inode == NTFS_ROOTINO) {
1476 /* increase the path by one so that we do not pass the '/'
1477 * if we do then the printed result will have '//' at
1478 * the beginning
1479 */
1480 if (TSK_WALK_ERROR == action(fs_file,
1481 (const char *) ((uintptr_t) begin + 1), ptr)) {
1482 tsk_fs_file_close(fs_file_par);
1483 return 1;
1484 }
1485 }
1486
1487 /* otherwise, recurse some more */
1488 else {
1489 if (ntfs_find_file_rec(fs, dinfo, fs_file, fs_name_list_par,
1490 action, ptr)) {
1491 tsk_fs_file_close(fs_file_par);
1492 return 1;
1493 }
1494 }
1495
1496 /* if we incremented before, then decrement the depth now */
1497 if (decrem)
1498 dinfo->depth--;
1499 }
1500
1501 tsk_fs_file_close(fs_file_par);
1502
1503 return 0;
1504 }
1505
1506 /* \ingroup fslib
1507 * NTFS can map a meta address to its name much faster than in other file systems
1508 * because each entry stores the address of its parent.
1509 *
1510 * This can not be called with dent_walk because the path
1511 * structure will get messed up!
1512 *
1513 * @param fs File system being analyzed
1514 * @param inode_toid Address of file to find the name for.
1515 * @param type_toid Attribute type to find the more specific name for (if you want more than just the base file name)
1516 * @param type_used 1 if the type_toid value was passed a valid value. 0 otherwise.
1517 * @param id_toid Attribute id to find the more specific name for (if you want more than just the base file name)
1518 * @param id_used 1 if the id_toid value was passed a valid value. 0 otherwise.
1519 * @param dir_walk_flags Flags to use during search
1520 * @param action Callback that will be called for each name that uses the specified addresses.
1521 * @param ptr Pointer that will be passed into action when it is called (so that you can pass in other data)
1522 * @returns 1 on error, 0 on success
1523 */
1524
1525 uint8_t
ntfs_find_file(TSK_FS_INFO * fs,TSK_INUM_T inode_toid,uint32_t type_toid,uint8_t type_used,uint16_t id_toid,uint8_t id_used,TSK_FS_DIR_WALK_FLAG_ENUM dir_walk_flags,TSK_FS_DIR_WALK_CB action,void * ptr)1526 ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
1527 uint8_t type_used, uint16_t id_toid, uint8_t id_used,
1528 TSK_FS_DIR_WALK_FLAG_ENUM dir_walk_flags, TSK_FS_DIR_WALK_CB action,
1529 void *ptr)
1530 {
1531 TSK_FS_META_NAME_LIST *fs_name_list;
1532 char *attr = NULL;
1533 NTFS_DINFO dinfo;
1534 TSK_FS_FILE *fs_file;
1535 ntfs_mft *mft;
1536 TSK_RETVAL_ENUM r_enum;
1537 NTFS_INFO *ntfs = (NTFS_INFO *) fs;
1538
1539 /* sanity check */
1540 if (inode_toid < fs->first_inum || inode_toid > fs->last_inum) {
1541 tsk_error_reset();
1542 tsk_error_set_errno(TSK_ERR_FS_ARG);
1543 tsk_error_set_errstr("ntfs_find_file: invalid inode value: %"
1544 PRIuINUM "\n", inode_toid);
1545 return 1;
1546 }
1547 if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
1548 return 1;
1549 }
1550 r_enum = ntfs_dinode_lookup(ntfs, (char *) mft, inode_toid);
1551 if (r_enum == TSK_ERR) {
1552 free(mft);
1553 return 1;
1554 }
1555 // open the file to ID
1556 fs_file = tsk_fs_file_open_meta(fs, NULL, inode_toid);
1557 if (fs_file == NULL) {
1558 tsk_error_errstr2_concat("- ntfs_find_file");
1559 tsk_fs_file_close(fs_file);
1560 free(mft);
1561 return 1;
1562 }
1563
1564 // see if its allocation status meets the callback needs
1565 if ((fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC)
1566 && ((dir_walk_flags & TSK_FS_DIR_WALK_FLAG_ALLOC) == 0)) {
1567 tsk_fs_file_close(fs_file);
1568 free(mft);
1569 return 1;
1570 }
1571 else if ((fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)
1572 && ((dir_walk_flags & TSK_FS_DIR_WALK_FLAG_UNALLOC) == 0)) {
1573 tsk_fs_file_close(fs_file);
1574 free(mft);
1575 return 1;
1576 }
1577
1578
1579 /* Allocate a name and fill in some details */
1580 if ((fs_file->name =
1581 tsk_fs_name_alloc(NTFS_MAXNAMLEN_UTF8, 0)) == NULL) {
1582 free(mft);
1583 return 1;
1584 }
1585 fs_file->name->meta_addr = inode_toid;
1586 fs_file->name->meta_seq = 0;
1587 fs_file->name->flags =
1588 ((tsk_getu16(fs->endian,
1589 mft->flags) & NTFS_MFT_INUSE) ? TSK_FS_NAME_FLAG_ALLOC :
1590 TSK_FS_NAME_FLAG_UNALLOC);
1591
1592 memset(&dinfo, 0, sizeof(NTFS_DINFO));
1593
1594 /* in this function, we use the dinfo->dirs array in the opposite order.
1595 * we set the end of it to NULL and then prepend the
1596 * directories to it
1597 *
1598 * dinfo->didx[dinfo->depth] will point to where the current level started their
1599 * dir name
1600 */
1601 dinfo.dirs[DIR_STRSZ - 2] = '/';
1602 dinfo.dirs[DIR_STRSZ - 1] = '\0';
1603 dinfo.didx[0] = &dinfo.dirs[DIR_STRSZ - 2];
1604 dinfo.depth = 1;
1605
1606
1607 /* Get the name for the attribute - if specified */
1608 if (type_used) {
1609 const TSK_FS_ATTR *fs_attr;
1610
1611 if (id_used)
1612 fs_attr =
1613 tsk_fs_attrlist_get_id(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM)type_toid,
1614 id_toid);
1615 else
1616 fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM)type_toid);
1617
1618 if (!fs_attr) {
1619 tsk_error_reset();
1620 tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
1621 tsk_error_set_errstr("find_file: Type %" PRIu32 " Id %" PRIu16
1622 " not found in MFT %" PRIuINUM "", type_toid, id_toid,
1623 inode_toid);
1624 tsk_fs_file_close(fs_file);
1625 free(mft);
1626 return 1;
1627 }
1628
1629 /* only add the attribute name if it is the non-default data stream */
1630 if (fs_attr->name != NULL)
1631 attr = fs_attr->name;
1632 }
1633
1634 /* loop through all the names it may have */
1635 for (fs_name_list = fs_file->meta->name2; fs_name_list != NULL;
1636 fs_name_list = fs_name_list->next) {
1637 int retval;
1638
1639 /* Append on the attribute name, if it exists */
1640 if (attr != NULL) {
1641 snprintf(fs_file->name->name, fs_file->name->name_size,
1642 "%s:%s", fs_name_list->name, attr);
1643 }
1644 else {
1645 strncpy(fs_file->name->name, fs_name_list->name,
1646 fs_file->name->name_size);
1647 }
1648
1649 /* if this is in the root directory, then call back */
1650 if (fs_name_list->par_inode == NTFS_ROOTINO) {
1651
1652 retval = action(fs_file, dinfo.didx[0], ptr);
1653 if (retval == TSK_WALK_STOP) {
1654 tsk_fs_file_close(fs_file);
1655 free(mft);
1656 return 0;
1657 }
1658 else if (retval == TSK_WALK_ERROR) {
1659 tsk_fs_file_close(fs_file);
1660 free(mft);
1661 return 1;
1662 }
1663 }
1664 /* call the recursive function on the parent to get the full path */
1665 else {
1666 if (ntfs_find_file_rec(fs, &dinfo, fs_file, fs_name_list,
1667 action, ptr)) {
1668 tsk_fs_file_close(fs_file);
1669 free(mft);
1670 return 1;
1671 }
1672 }
1673 } /* end of name loop */
1674
1675 tsk_fs_file_close(fs_file);
1676 free(mft);
1677 return 0;
1678 }
1679
1680
1681 int
ntfs_name_cmp(TSK_FS_INFO *,const char * s1,const char * s2)1682 ntfs_name_cmp(TSK_FS_INFO * /*a_fs_info*/, const char *s1, const char *s2)
1683 {
1684 return strcasecmp(s1, s2);
1685 }
1686