Coverage Report

Created: 2025-07-12 06:14

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