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