Coverage Report

Created: 2026-04-01 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/fs/ntfs.cpp
Line
Count
Source
1
/*
2
** ntfs
3
** The Sleuth Kit
4
**
5
** Content and meta data 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
** This software is distributed under the Common Public License 1.0
15
**
16
** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)
17
**
18
*/
19
#include "tsk_fs_i.h"
20
#include "tsk_ntfs.h"
21
22
#include <ctype.h>
23
#include <stddef.h>
24
25
#include <memory>
26
27
#include "encryptionHelper.h"
28
29
/**
30
 * \file ntfs.c
31
 * Contains the TSK internal general NTFS processing code
32
 */
33
/*
34
 * NOTES TO SELF:
35
 *
36
 * - multiple ".." entries may exist
37
 */
38
39
/*
40
 * How are we to handle the META flag? Is the MFT $Data Attribute META?
41
 */
42
43
44
/* Macro to pass in both the epoch time value and the nano time value */
45
0
#define WITHNANO(x) x, (unsigned int)x##_nano
46
47
48
/* mini-design note:
49
 * The MFT has entries for every file and dir in the fs.
50
 * The first entry ($MFT) is for the MFT itself and it is used to find
51
 * the location of the entire table because it can become fragmented.
52
 * Therefore, the $Data attribute of $MFT is saved in the NTFS_INFO
53
 * structure for easy access.  We also use the size of the MFT as
54
 * a way to calculate the maximum MFT entry number (last_inum).
55
 *
56
 * Ok, that is simple, but getting the full $Data attribute can be tough
57
 * because $MFT may not fit into one MFT entry (i.e. an attribute list).
58
 * We need to process the attribute list attribute to find out which
59
 * other entries to process.  But, the attribute list attribute comes
60
 * before any $Data attribute (so it could refer to an MFT that has not
61
 * yet been 'defined').  Although, the $Data attribute seems to always
62
 * exist and define at least the run for the entry in the attribute list.
63
 *
64
 * So, the way this is solved is that generic mft_lookup is used to get
65
 * any MFT entry, even $MFT.  If $MFT is not cached then we calculate
66
 * the address of where to read based on multiplication and guessing.
67
 * When we are loading the $MFT, we set 'loading_the_MFT' to 1 so
68
 * that we can update things as we go along.  When we read $MFT we
69
 * read all the attributes and save info about the $Data one.  If
70
 * there is an attribute list, we will have the location of the
71
 * additional MFT in the cached $Data location, which will be
72
 * updated as we process the attribute list.  After each MFT
73
 * entry that we process while loading the MFT, the 'final_inum'
74
 * value is updated to reflect what we can currently load so
75
 * that the sanity checks still work.
76
 */
77
78
79
/**********************************************************************
80
 *
81
 *  MISC FUNCS
82
 *
83
 **********************************************************************/
84
85
/* convert the NT Time (UTC hundred nanoseconds from 1/1/1601)
86
 * to UNIX (UTC seconds from 1/1/1970)
87
 *
88
 * The basic calculation is to remove the nanoseconds and then
89
 * subtract the number of seconds between 1601 and 1970
90
 * i.e. TIME - DELTA
91
 *
92
 * Returns 0 if NT date is outside of Unix range
93
 *
94
 */
95
uint32_t
96
nt2unixtime(uint64_t ntdate)
97
0
{
98
// (369*365 + 89) * 24 * 3600 * 10000000
99
0
#define NSEC_BTWN_1601_1970 (uint64_t)(116444736000000000ULL)
100
101
    // return 0 if before 1970
102
0
    if (ntdate < NSEC_BTWN_1601_1970)
103
0
        return 0;
104
105
0
    ntdate -= (uint64_t) NSEC_BTWN_1601_1970;
106
0
    ntdate /= (uint64_t) 10000000;
107
108
    // return if beyond 32-bit epoch range
109
0
    if (ntdate > 0xffffffffULL)
110
0
        return 0;
111
112
0
    return (uint32_t) ntdate;
113
0
}
114
115
/* convert the NT Time (UTC hundred nanoseconds from 1/1/1601)
116
 * to only the nanoseconds
117
 *
118
 */
119
uint32_t
120
nt2nano(uint64_t ntdate)
121
0
{
122
0
    return (uint32_t) (ntdate % 10000000)*100;
123
0
}
124
125
126
/**********************************************************************
127
 *
128
 * Lookup Functions
129
 *
130
 **********************************************************************/
131
132
133
134
135
/**
136
 * Read an MFT entry and save it in raw form in the given buffer.
137
 * NOTE: This will remove the update sequence integrity checks in the
138
 * structure.
139
 *
140
 * @param a_ntfs File system to read from
141
 * @param a_buf Buffer to save raw data to.  Must be of size NTFS_INFO.mft_rsize_b
142
 * @param a_mftnum Address of MFT entry to read
143
 * @param mft_start_addr Within-file-system byte address of start of MFT entry
144
 *
145
 * @returns Error value
146
 */
147
TSK_RETVAL_ENUM
148
ntfs_dinode_lookup(NTFS_INFO * a_ntfs, char *a_buf, TSK_INUM_T a_mftnum, TSK_OFF_T * mft_start_addr)
149
0
{
150
0
    TSK_OFF_T mftaddr_b, mftaddr2_b, offset;
151
0
    size_t mftaddr_len = 0;
152
0
    int i;
153
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_ntfs->fs_info;
154
0
    TSK_FS_ATTR_RUN *data_run;
155
0
    ntfs_upd *upd;
156
0
    uint16_t sig_seq;
157
0
    ntfs_mft *mft;
158
159
160
    /* sanity checks */
161
0
    if (!a_buf) {
162
0
        tsk_error_reset();
163
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
164
0
        tsk_error_set_errstr("mft_lookup: null mft buffer");
165
0
        return TSK_ERR;
166
0
    }
167
168
0
    if (a_mftnum < fs->first_inum) {
169
0
        tsk_error_reset();
170
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
171
0
        tsk_error_set_errstr("mft_lookup: inode number is too small (%"
172
0
            PRIuINUM ")", a_mftnum);
173
0
        return TSK_ERR;
174
0
    }
175
176
    /* Because this code reads teh actual MFT, we need to make sure we
177
     * decrement the last_inum because the last value is a special value
178
     * for the ORPHANS directory */
179
0
    if (a_mftnum > fs->last_inum - 1) {
180
0
        tsk_error_reset();
181
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
182
0
        tsk_error_set_errstr("mft_lookup: inode number is too large (%"
183
0
            PRIuINUM ")", a_mftnum);
184
0
        return TSK_ERR;
185
0
    }
186
187
188
0
    if (tsk_verbose)
189
0
        tsk_fprintf(stderr,
190
0
            "ntfs_dinode_lookup: Processing MFT %" PRIuINUM "\n",
191
0
            a_mftnum);
192
193
    /* If mft_data (the cached $Data attribute of $MFT) is not there yet,
194
     * then we have not started to load $MFT yet.  In that case, we will
195
     * 'cheat' and calculate where it goes.  This should only be for
196
     * $MFT itself, in which case the calculation is easy
197
     */
198
0
    if (!a_ntfs->mft_data) {
199
200
        /* This is just a random check with the assumption being that
201
         * we don't want to just do a guess calculation for a very large
202
         * MFT entry
203
         */
204
0
        if (a_mftnum > NTFS_LAST_DEFAULT_INO) {
205
0
            tsk_error_reset();
206
0
            tsk_error_set_errno(TSK_ERR_FS_ARG);
207
0
            tsk_error_set_errstr
208
0
                ("Error trying to load a high MFT entry when the MFT itself has not been loaded (%"
209
0
                PRIuINUM ")", a_mftnum);
210
0
            return TSK_ERR;
211
0
        }
212
213
0
        mftaddr_b = a_ntfs->root_mft_addr + a_mftnum * a_ntfs->mft_rsize_b;
214
0
        mftaddr2_b = 0;
215
0
    }
216
0
    else {
217
        /* The MFT may not be in consecutive clusters, so we need to use its
218
         * data attribute run list to find out what address to read
219
         *
220
         * This is why we cached it
221
         */
222
223
        // will be set to the address of the MFT entry
224
0
        mftaddr_b = mftaddr2_b = 0;
225
226
        /* The byte offset within the $Data stream */
227
0
        offset = a_mftnum * a_ntfs->mft_rsize_b;
228
229
        /* NOTE: data_run values are in clusters
230
         *
231
         * cycle through the runs in $Data and identify which
232
         * has the MFT entry that we want
233
         */
234
0
        for (data_run = a_ntfs->mft_data->nrd.run;
235
0
            data_run != NULL; data_run = data_run->next) {
236
237
            /* Test for possible overflows / error conditions */
238
0
            if ((offset < 0) || (data_run->len >= (TSK_DADDR_T)(LLONG_MAX / a_ntfs->csize_b))){
239
0
                tsk_error_reset();
240
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
241
0
                tsk_error_set_errstr
242
0
                ("ntfs_dinode_lookup: Overflow when calculating run length");
243
0
                return TSK_COR;
244
0
            }
245
246
            /* The length of this specific run */
247
0
            TSK_OFF_T run_len = data_run->len * a_ntfs->csize_b;
248
249
            /* Is our MFT entry is in this run somewhere ? */
250
0
            if (offset < run_len) {
251
252
0
                if (tsk_verbose)
253
0
                    tsk_fprintf(stderr,
254
0
                        "ntfs_dinode_lookup: Found in offset: %"
255
0
                        PRIuDADDR "  size: %" PRIuDADDR " at offset: %"
256
0
            PRIdOFF "\n", data_run->addr, data_run->len,
257
0
                        offset);
258
259
                /* special case where the MFT entry crosses
260
                 * a run (only happens when cluster size is 512-bytes
261
                 * and there are an odd number of clusters in the run)
262
                 */
263
0
                if (run_len < offset + a_ntfs->mft_rsize_b) {
264
265
0
                    if (tsk_verbose)
266
0
                        tsk_fprintf(stderr,
267
0
                            "ntfs_dinode_lookup: Entry crosses run border\n");
268
269
0
                    if (data_run->next == NULL) {
270
0
                        tsk_error_reset();
271
0
                        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
272
0
                        tsk_error_set_errstr
273
0
                            ("mft_lookup: MFT entry crosses a cluster and there are no more clusters!");
274
0
                        return TSK_COR;
275
0
                    }
276
277
                    /* Assign address where the remainder of the entry is */
278
0
                    mftaddr2_b = data_run->next->addr * a_ntfs->csize_b;
279
                    /* this should always be 512, but just in case */
280
0
                    mftaddr_len = (size_t) (run_len - offset);
281
0
                }
282
283
                /* Assign address of where the MFT entry starts */
284
0
                mftaddr_b = data_run->addr * a_ntfs->csize_b + offset;
285
0
                if (tsk_verbose)
286
0
                    tsk_fprintf(stderr,
287
0
                        "ntfs_dinode_lookup: Entry address at: %"
288
0
            PRIdOFF "\n", mftaddr_b);
289
0
                break;
290
0
            }
291
292
            /* decrement the offset we are looking for */
293
0
            offset -= run_len;
294
0
        }
295
296
        /* Did we find it? */
297
0
        if (!mftaddr_b) {
298
0
            tsk_error_reset();
299
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
300
0
            tsk_error_set_errstr("mft_lookup: Error finding MFT entry %"
301
0
                PRIuINUM " in $MFT", a_mftnum);
302
0
            return TSK_ERR;
303
0
        }
304
0
    }
305
306
307
    /* can we do just one read or do we need multiple? */
308
0
    if (mftaddr2_b) {
309
0
        ssize_t cnt;
310
        /* read the first part into mft */
311
0
        cnt = tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, a_buf, mftaddr_len);
312
0
        if (cnt != (ssize_t)mftaddr_len) {
313
0
            if (cnt >= 0) {
314
0
                tsk_error_reset();
315
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
316
0
            }
317
0
            tsk_error_set_errstr2
318
0
                ("ntfs_dinode_lookup: Error reading MFT Entry (part 1) at %"
319
0
          PRIdOFF, mftaddr_b);
320
0
            return TSK_ERR;
321
0
        }
322
323
        /* read the second part into mft */
324
0
        cnt = tsk_fs_read
325
0
            (&a_ntfs->fs_info, mftaddr2_b,
326
0
            (char *) ((uintptr_t) a_buf + (uintptr_t) mftaddr_len),
327
0
            a_ntfs->mft_rsize_b - mftaddr_len);
328
0
        if (cnt != (ssize_t)(a_ntfs->mft_rsize_b - mftaddr_len)) {
329
0
            if (cnt >= 0) {
330
0
                tsk_error_reset();
331
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
332
0
            }
333
0
            tsk_error_set_errstr2
334
0
                ("ntfs_dinode_lookup: Error reading MFT Entry (part 2) at %"
335
0
          PRIdOFF, mftaddr2_b);
336
0
            return TSK_ERR;
337
0
        }
338
0
    }
339
0
    else {
340
0
        ssize_t cnt;
341
        /* read the raw entry into mft */
342
0
        cnt =
343
0
            tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, a_buf,
344
0
            a_ntfs->mft_rsize_b);
345
0
        if (cnt != a_ntfs->mft_rsize_b) {
346
0
            if (cnt >= 0) {
347
0
                tsk_error_reset();
348
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
349
0
            }
350
0
            tsk_error_set_errstr2
351
0
                ("ntfs_dinode_lookup: Error reading MFT Entry at %"
352
0
          PRIdOFF, mftaddr_b);
353
0
            return TSK_ERR;
354
0
        }
355
0
    }
356
357
    /* A nonzero address means that mftaddr_b has been requested for export */
358
0
    if (mft_start_addr) {
359
0
        *mft_start_addr = mftaddr_b;
360
0
    }
361
362
    /* Sanity Check */
363
#if 0
364
    /* This is no longer applied because it caused too many problems
365
     * with images that had 0 and 1 etc. as values.  Testing shows that
366
     * even Windows XP doesn't care if entries have an invalid entry, so
367
     * this is no longer checked.  The update sequence check should find
368
     * corrupt entries
369
     * */
370
    if ((tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC)
371
        && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_BAAD)
372
        && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_ZERO)) {
373
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
374
        tsk_error_set_errstr("entry %d has an invalid MFT magic: %x",
375
            mftnum, tsk_getu32(fs->endian, mft->magic));
376
        return 1;
377
    }
378
#endif
379
    /* The MFT entries have error and integrity checks in them
380
     * called update sequences.  They must be checked and removed
381
     * so that later functions can process the data as normal.
382
     * They are located in the last 2 bytes of each 512-bytes of data.
383
     *
384
     * We first verify that the the 2-byte value is a give value and
385
     * then replace it with what should be there
386
     */
387
    /* sanity check so we don't run over in the next loop */
388
0
    mft = (ntfs_mft *) a_buf;
389
0
    if ((tsk_getu16(fs->endian, mft->upd_cnt) > 0) &&
390
0
        (((uint32_t) (tsk_getu16(fs->endian,
391
0
                        mft->upd_cnt) - 1) * NTFS_UPDATE_SEQ_STRIDE) >
392
0
            a_ntfs->mft_rsize_b)) {
393
0
        tsk_error_reset();
394
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
395
0
        tsk_error_set_errstr
396
0
            ("dinode_lookup: More Update Sequence Entries than MFT size");
397
0
        return TSK_COR;
398
0
    }
399
0
    uint16_t upd_cnt = tsk_getu16(fs->endian, mft->upd_cnt);
400
0
    uint16_t upd_off = tsk_getu16(fs->endian, mft->upd_off);
401
402
    // Make sure upd_cnt > 0 to prevent an integer wrap around.
403
    // NOTE: There is a bug here because upd_cnt can be for unused entries.
404
    // They are now skipped (as of July 2021). We shoudl refactor this code
405
    // to allow upd_cnt = 0.
406
0
    if ((upd_cnt == 0) || (upd_cnt > (((a_ntfs->mft_rsize_b) / 2) + 1))) {
407
0
        tsk_error_reset();
408
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
409
0
        tsk_error_set_errstr
410
0
            ("dinode_lookup: Invalid update count value out of bounds");
411
0
        return TSK_COR;
412
0
    }
413
0
    size_t mft_rsize_b = ((size_t) upd_cnt - 1) * 2;
414
415
0
    if ((size_t) upd_off + sizeof(ntfs_upd) > (a_ntfs->mft_rsize_b - mft_rsize_b)) {
416
0
        tsk_error_reset();
417
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
418
0
        tsk_error_set_errstr
419
0
            ("dinode_lookup: Update sequence would read past MFT size");
420
0
        return TSK_COR;
421
0
    }
422
423
    /* Apply the update sequence structure template */
424
425
0
    upd = (ntfs_upd *) ((uintptr_t) a_buf + upd_off);
426
    /* Get the sequence value that each 16-bit value should be */
427
0
    sig_seq = tsk_getu16(fs->endian, upd->upd_val);
428
    /* cycle through each sector */
429
0
    for (i = 1; i < tsk_getu16(fs->endian, mft->upd_cnt); i++) {
430
0
        uint8_t *new_val, *old_val;
431
        /* The offset into the buffer of the value to analyze */
432
0
        size_t offset = i * NTFS_UPDATE_SEQ_STRIDE - 2;
433
434
        /* Check that there is room in the buffer to read the current sequence value */
435
0
        if (offset + 2 > a_ntfs->mft_rsize_b) {
436
0
            tsk_error_reset();
437
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
438
0
            tsk_error_set_errstr
439
0
            ("dinode_lookup: Ran out of data while parsing update sequence values");
440
0
            return TSK_COR;
441
0
        }
442
443
        /* get the current sequence value */
444
0
        uint16_t cur_seq =
445
0
            tsk_getu16(fs->endian, (uintptr_t) a_buf + offset);
446
0
        if (cur_seq != sig_seq) {
447
            /* get the replacement value */
448
0
            uint16_t cur_repl =
449
0
                tsk_getu16(fs->endian, &upd->upd_seq + (i - 1) * 2);
450
0
            tsk_error_reset();
451
0
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
452
453
0
            tsk_error_set_errstr
454
0
                ("Incorrect update sequence value in MFT entry\nSignature Value: 0x%"
455
0
                PRIx16 " Actual Value: 0x%" PRIx16
456
0
                " Replacement Value: 0x%" PRIx16
457
0
                "\nThis is typically because of a corrupted entry",
458
0
                sig_seq, cur_seq, cur_repl);
459
0
            return TSK_COR;
460
0
        }
461
462
0
        new_val = &upd->upd_seq + (i - 1) * 2;
463
0
        old_val = (uint8_t *) ((uintptr_t) a_buf + offset);
464
        /*
465
           if (tsk_verbose)
466
           tsk_fprintf(stderr,
467
           "ntfs_dinode_lookup: upd_seq %i   Replacing: %.4"
468
           PRIx16 "   With: %.4" PRIx16 "\n", i,
469
           tsk_getu16(fs->endian, old_val), tsk_getu16(fs->endian,
470
           new_val));
471
         */
472
0
        *old_val++ = *new_val++;
473
0
        *old_val = *new_val;
474
0
    }
475
476
0
    return TSK_OK;
477
0
}
478
479
480
481
/*
482
 * given a cluster, return the allocation status or
483
 * -1 if an error occurs
484
 */
485
static int
486
is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
487
0
{
488
0
    int bits_p_clust, b;
489
0
    TSK_DADDR_T base;
490
0
    int8_t ret;
491
0
    bits_p_clust = 8 * ntfs->fs_info.block_size;
492
493
    /* While we are loading the MFT, assume that everything
494
     * is allocated.  This should only be needed when we are
495
     * dealing with an attribute list ...
496
     */
497
0
    if (ntfs->loading_the_MFT == 1) {
498
0
        return 1;
499
0
    }
500
0
    else if (ntfs->bmap == NULL) {
501
0
        tsk_error_reset();
502
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
503
504
0
        tsk_error_set_errstr("is_clustalloc: Bitmap pointer is null: %"
505
0
            PRIuDADDR "\n", addr);
506
0
        return -1;
507
0
    }
508
509
    /* Is the cluster too big? */
510
0
    if (addr > ntfs->fs_info.last_block) {
511
0
        tsk_error_reset();
512
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
513
0
        tsk_error_set_errstr("is_clustalloc: cluster too large");
514
0
        return -1;
515
0
    }
516
517
    /* identify the base cluster in the bitmap file */
518
0
    base = addr / bits_p_clust;
519
0
    b = (int) (addr % bits_p_clust);
520
521
0
    tsk_take_lock(&ntfs->lock);
522
523
    /* is this the same as in the cached buffer? */
524
0
    if (base != ntfs->bmap_buf_off) {
525
0
        TSK_DADDR_T c = base;
526
0
        TSK_FS_ATTR_RUN *run;
527
0
        TSK_DADDR_T fsaddr = 0;
528
0
        ssize_t cnt;
529
530
        /* get the file system address of the bitmap cluster */
531
0
        for (run = ntfs->bmap; run; run = run->next) {
532
0
            if (run->len <= c) {
533
0
                c -= run->len;
534
0
            }
535
0
            else {
536
0
                fsaddr = run->addr + c;
537
0
                break;
538
0
            }
539
0
        }
540
541
0
        if (fsaddr == 0) {
542
0
            tsk_release_lock(&ntfs->lock);
543
0
            tsk_error_reset();
544
0
            tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
545
0
            tsk_error_set_errstr
546
0
                ("is_clustalloc: cluster not found in bitmap: %" PRIuDADDR
547
0
                "", c);
548
0
            return -1;
549
0
        }
550
0
        if (fsaddr > ntfs->fs_info.last_block) {
551
0
            tsk_release_lock(&ntfs->lock);
552
0
            tsk_error_reset();
553
0
            tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
554
0
            tsk_error_set_errstr
555
0
                ("is_clustalloc: Cluster in bitmap too large for image: %"
556
0
                PRIuDADDR, fsaddr);
557
0
            return -1;
558
0
        }
559
0
        ntfs->bmap_buf_off = base;
560
0
        cnt = tsk_fs_read_block
561
0
            (&ntfs->fs_info, fsaddr, ntfs->bmap_buf,
562
0
            ntfs->fs_info.block_size);
563
0
        if (cnt != ntfs->fs_info.block_size) {
564
0
            tsk_release_lock(&ntfs->lock);
565
0
            if (cnt >= 0) {
566
0
                tsk_error_reset();
567
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
568
0
            }
569
0
            tsk_error_set_errstr2
570
0
                ("is_clustalloc: Error reading bitmap at %" PRIuDADDR,
571
0
                fsaddr);
572
0
            return -1;
573
0
        }
574
0
    }
575
576
    /* identify if the cluster is allocated or not */
577
0
    ret = (isset(ntfs->bmap_buf, b)) ? 1 : 0;
578
579
0
    tsk_release_lock(&ntfs->lock);
580
0
    return ret;
581
0
}
582
583
584
585
/**********************************************************************
586
 *
587
 *  TSK_FS_ATTR functions
588
 *
589
 **********************************************************************/
590
591
592
/**
593
 * Process a non-resident runlist and convert its contents into the generic fs_attr_run
594
 * structure.
595
 * @param ntfs File system that attribute is located in.
596
 * @param start_vcn The starting VCN for this run.
597
 * @param runlist The raw runlist data from the MFT entry.
598
 * @param runlist_size The size of the raw runlist data from the MFT entry.
599
 * @param a_data_run_head [out] Pointer to pointer of run that is created. (NULL on error and for $BadClust - special case because it is a sparse file for the entire FS).
600
 * @param totlen [out] Pointer to location where total length of run (in bytes) can be returned (or NULL)
601
 * @param mnum MFT entry address
602
 *
603
 * @returns Return status of error, corrupt, or OK (note a_data_run can be NULL even when OK is returned if $BadClust is encountered)
604
 */
605
static TSK_RETVAL_ENUM
606
ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
607
    ntfs_runlist * runlist_head, uint32_t runlist_size, TSK_FS_ATTR_RUN ** a_data_run_head,
608
    TSK_OFF_T * totlen, TSK_INUM_T mnum)
609
0
{
610
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) ntfs;
611
0
    ntfs_runlist *run;
612
0
    TSK_FS_ATTR_RUN *data_run, *data_run_prev = NULL;
613
0
    unsigned int i, idx;
614
0
    TSK_DADDR_T prev_addr = 0;
615
0
    TSK_OFF_T file_offset = start_vcn;
616
0
    uint32_t runlist_offset = 0;
617
618
0
    run = runlist_head;
619
0
    *a_data_run_head = NULL;
620
621
    /* initialize if non-NULL */
622
0
    if (totlen)
623
0
        *totlen = 0;
624
625
0
    if (runlist_size < 1) {
626
0
        return TSK_ERR;
627
0
    }
628
629
    /* Cycle through each run in the runlist
630
     * We go until we find an entry with no length
631
     * An entry with offset of 0 is for a sparse run
632
     */
633
0
    while ((runlist_offset < runlist_size) && NTFS_RUNL_LENSZ(run) != 0) {
634
0
        int64_t addr_offset = 0;
635
636
        /* allocate a new tsk_fs_attr_run */
637
0
        data_run = tsk_fs_attr_run_alloc();
638
0
        if (data_run == NULL) {
639
0
            tsk_fs_attr_run_free(*a_data_run_head);
640
0
            *a_data_run_head = NULL;
641
0
            return TSK_ERR;
642
0
        }
643
644
        /* make the list, unless its the first pass & then we set the head */
645
0
        if (data_run_prev)
646
0
            data_run_prev->next = data_run;
647
0
        else
648
0
            *a_data_run_head = data_run;
649
0
        data_run_prev = data_run;
650
651
        /* These fields are a variable number of bytes long
652
         * these for loops are the equivalent of the getuX macros
653
         */
654
0
        idx = 0;
655
656
        /* Get the length of this run.
657
         * A length of more than eight bytes will not fit in the
658
         * 64-bit length field (and is likely corrupt)
659
         */
660
0
        if (NTFS_RUNL_LENSZ(run) > 8 || (uint32_t) NTFS_RUNL_LENSZ(run) > runlist_size - runlist_offset - 1) {
661
0
            tsk_error_reset();
662
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
663
0
            tsk_error_set_errstr
664
0
            ("ntfs_make_run: Run length is too large to process");
665
0
            tsk_fs_attr_run_free(*a_data_run_head);
666
0
            *a_data_run_head = NULL;
667
0
            return TSK_COR;
668
0
        }
669
0
        for (i = 0, data_run->len = 0; i < NTFS_RUNL_LENSZ(run); i++) {
670
0
            data_run->len |= ((uint64_t)(run->buf[idx++]) << (i * 8));
671
0
            if (tsk_verbose)
672
0
                tsk_fprintf(stderr,
673
0
                    "ntfs_make_data_run: Len idx: %i cur: %"
674
0
                    PRIu8 " (%" PRIx8 ") tot: %" PRIuDADDR
675
0
                    " (%" PRIxDADDR ")\n", i,
676
0
                    run->buf[idx - 1], run->buf[idx - 1],
677
0
                    data_run->len, data_run->len);
678
0
        }
679
680
        /* Sanity check on length */
681
0
        if (data_run->len > fs->block_count) {
682
0
            tsk_error_reset();
683
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
684
0
            tsk_error_set_errstr
685
0
                ("ntfs_make_run: Run length is larger than file system");
686
0
            tsk_fs_attr_run_free(*a_data_run_head);
687
0
            *a_data_run_head = NULL;
688
0
            return TSK_COR;
689
0
        }
690
691
0
        data_run->offset = file_offset;
692
0
        file_offset += data_run->len;
693
694
        /* Update the length if we were passed a value */
695
0
        if (totlen)
696
0
            *totlen += (data_run->len * ntfs->csize_b);
697
698
        /* Get the address offset of this run.
699
         * An address offset of more than eight bytes will not fit in the
700
         * 64-bit addr_offset field (and is likely corrupt)
701
         */
702
0
        if (NTFS_RUNL_OFFSZ(run) > 8 || (uint32_t) ( NTFS_RUNL_LENSZ(run) + NTFS_RUNL_OFFSZ(run) ) > runlist_size - runlist_offset - 1) {
703
0
            tsk_error_reset();
704
0
            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
705
0
            tsk_error_set_errstr
706
0
            ("ntfs_make_run: Run address offset is too large to process");
707
0
            tsk_fs_attr_run_free(*a_data_run_head);
708
0
            *a_data_run_head = NULL;
709
0
            return TSK_COR;
710
0
        }
711
0
        for (i = 0, data_run->addr = 0; i < NTFS_RUNL_OFFSZ(run); i++) {
712
            //data_run->addr |= (run->buf[idx++] << (i * 8));
713
0
            addr_offset |= ((int64_t)(run->buf[idx++]) << (i * 8));
714
0
            if (tsk_verbose)
715
0
                tsk_fprintf(stderr,
716
0
                    "ntfs_make_data_run: Off idx: %i cur: %"
717
0
                    PRIu8 " (%" PRIx8 ") tot: %" PRIuDADDR
718
0
                    " (%" PRIxDADDR ")\n", i,
719
0
                    run->buf[idx - 1], run->buf[idx - 1], addr_offset,
720
0
                    addr_offset);
721
0
        }
722
723
        /* addr_offset value is signed so extend it to 64-bits */
724
0
        if ((int8_t) run->buf[idx - 1] < 0) {
725
0
            for (; i < sizeof(addr_offset); i++)
726
0
                addr_offset |= (int64_t) ((int64_t) 0xff << (i * 8));
727
0
        }
728
729
0
        if (tsk_verbose)
730
0
            tsk_fprintf(stderr,
731
0
                "ntfs_make_data_run: Signed addr_offset: %"
732
0
        PRId64 " Previous address: %"
733
0
        PRIuDADDR "\n", addr_offset, prev_addr);
734
735
        /* The NT 4.0 version of NTFS uses an offset of -1 to represent
736
         * a hole, so add the sparse flag and make it look like the 2K
737
         * version with a offset of 0
738
         *
739
         * A user reported an issue where the $Bad file started with
740
         * its offset as -1 and it was not NT (maybe a conversion)
741
         * Change the check now to not limit to NT, but make sure
742
         * that it is the first run
743
         */
744
0
        if (((addr_offset == -1) && (prev_addr == 0))
745
0
            || ((addr_offset == -1)
746
0
                && (ntfs->ver == NTFS_VINFO_NT))) {
747
0
            data_run->flags = (TSK_FS_ATTR_RUN_FLAG_ENUM) (data_run->flags | TSK_FS_ATTR_RUN_FLAG_SPARSE);
748
0
            data_run->addr = 0;
749
0
            if (tsk_verbose)
750
0
                tsk_fprintf(stderr, "ntfs_make_data_run: Sparse Run\n");
751
0
        }
752
753
        /* A Sparse file has a run with an offset of 0
754
         * there is a special case though of the BOOT MFT entry which
755
         * is the super block and has a legit offset of 0.
756
         *
757
         * The value given is a delta of the previous offset, so add
758
         * them for non-sparse files
759
         *
760
         * For sparse files the next run will have its offset relative
761
         * to the current "prev_addr" so skip that code
762
         */
763
        // @@@ BC: we'll need to pass in an inode value for this check
764
0
        else if ((addr_offset) || (mnum == NTFS_MFT_BOOT)) {
765
766
0
            data_run->addr = prev_addr + addr_offset;
767
0
            prev_addr = data_run->addr;
768
769
            /* Sanity check on length and offset */
770
0
            if (data_run->addr + data_run->len > fs->block_count) {
771
0
                tsk_error_reset();
772
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
773
0
                tsk_error_set_errstr
774
0
                    ("ntfs_make_run: Run offset and length is larger than file system");
775
0
                tsk_fs_attr_run_free(*a_data_run_head);
776
0
                *a_data_run_head = NULL;
777
0
                return TSK_COR;
778
0
            }
779
780
0
        }
781
0
        else {
782
0
            data_run->flags = (TSK_FS_ATTR_RUN_FLAG_ENUM) (data_run->flags | TSK_FS_ATTR_RUN_FLAG_SPARSE);
783
0
            if (tsk_verbose)
784
0
                tsk_fprintf(stderr, "ntfs_make_data_run: Sparse Run\n");
785
0
        }
786
787
        /* Advance run */
788
0
        uint32_t run_size = 1 + NTFS_RUNL_LENSZ(run) + NTFS_RUNL_OFFSZ(run);
789
0
        run = (ntfs_runlist *) ((uintptr_t) run + run_size);
790
791
        // Abritrary limit runlist_offset at INT32_MAX ((1 << 31) - 1)
792
0
        if (run_size > (((uint32_t) 1UL << 31 ) -1) - runlist_offset) {
793
0
            return TSK_ERR;
794
0
        }
795
0
        runlist_offset += run_size;
796
0
    }
797
798
    /* special case for $BADCLUST, which is a sparse file whose size is
799
     * the entire file system.
800
     *
801
     * If there is only one run entry and it is sparse, then there are no
802
     * bad blocks, so get rid of it.
803
     */
804
0
    if ((*a_data_run_head != NULL)
805
0
        && ((*a_data_run_head)->next == NULL)
806
0
        && ((*a_data_run_head)->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE)
807
0
        && ((*a_data_run_head)->len == fs->last_block + 1)) {
808
0
        tsk_fs_attr_run_free(*a_data_run_head);
809
0
        *a_data_run_head = NULL;
810
0
    }
811
812
0
    return TSK_OK;
813
0
}
814
815
816
817
/*********** UNCOMPRESSION CODE *************/
818
819
820
/*
821
 * NTFS Breaks compressed data into compression units, which are
822
 * typically 16 clusters in size. If the data in the comp  unit
823
 * compresses to something smaller than 16 clusters then the
824
 * compressed data is stored and the rest of the compression unit
825
 * is filled with sparse clusters. The entire compression unit
826
 * can also be sparse.
827
 *
828
 * The uncompressed content in the compression unit is further broken
829
 * into 4k (pre-compression) blocks.  When stored, each 4k block has
830
 * a 2-byte header that identifies the compressed size (and if there
831
 * was compression).
832
 *
833
 * The compressed data is a series of token groups.  Each token group
834
 * contains a 1-byte header and 8 tokens.  The 8-bits in the token
835
 * group header identify the type of each token in the group.
836
 *
837
 * There are two types of tokens.
838
 * Symbol tokens are 1 byte in length and the 1-byte value is the value
839
 * for that position in the file and it should be direcly copied into the
840
 * uncompressed data.  Phrase tokens identify a previous run of data
841
 * in the same compression unit that should be
842
 * copied to the current location.  These contain offset and length info.
843
 *
844
 * The attribute will have enough cluster addresses to store all of
845
 * the content, but the addresses will be 0 in the compression unit
846
 * if it is all sparse and the ending clusters will be 0 in the
847
 * compression unit if they are not needed.
848
 *
849
 */
850
851
 /* Variables used for ntfs_uncompress() method */
852
typedef struct {
853
    char *uncomp_buf;           // Buffer for uncompressed data
854
    char *comp_buf;             // buffer for compressed data
855
    size_t comp_len;            // number of bytes used in compressed data buffer
856
    size_t uncomp_idx;          // Index into buffer for next byte
857
    size_t buf_size_b;          // size of both buffers in bytes (1 compression unit)
858
} NTFS_COMP_INFO;
859
860
861
/**
862
 * Reset the values in the NTFS_COMP_INFO structure.  We need to
863
 * do this in between every compression unit that we process in the file.
864
 *
865
 * @param comp Structure to reset
866
 */
867
static void
868
ntfs_uncompress_reset(NTFS_COMP_INFO * comp)
869
0
{
870
0
    memset(comp->uncomp_buf, 0, comp->buf_size_b);
871
0
    comp->uncomp_idx = 0;
872
0
    memset(comp->comp_buf, 0, comp->buf_size_b);
873
0
    comp->comp_len = 0;
874
0
}
875
876
/**
877
 * Setup the NTFS_COMP_INFO structure with a buffer and
878
 * initialize the basic settings.
879
 *
880
 * @param fs File system state information
881
 * @param comp Compression state information to initialize
882
 * @param compunit_size_c The size (in clusters) of a compression
883
 * unit
884
 * @return 1 on error and 0 on success
885
 */
886
static int
887
ntfs_uncompress_setup(TSK_FS_INFO * fs, NTFS_COMP_INFO * comp,
888
    uint32_t compunit_size_c)
889
0
{
890
0
    if (fs->block_size == 0 || compunit_size_c == 0) {
891
0
        return 1;
892
0
    }
893
0
    comp->buf_size_b = fs->block_size * compunit_size_c;
894
895
    // Detect an integer overflow e.g. 65536 * 65536
896
0
    if (comp->buf_size_b < fs->block_size) {
897
0
        return 1;
898
0
    }
899
900
0
    if ((comp->uncomp_buf = (char*) tsk_malloc(comp->buf_size_b)) == NULL) {
901
0
        comp->buf_size_b = 0;
902
0
        return 1;
903
0
    }
904
0
    if ((comp->comp_buf = (char*) tsk_malloc(comp->buf_size_b)) == NULL) {
905
0
        free(comp->uncomp_buf);
906
0
        comp->uncomp_buf = NULL;
907
0
        comp->buf_size_b = 0;
908
0
        return 1;
909
0
    }
910
911
0
    ntfs_uncompress_reset(comp);
912
913
0
    return 0;
914
0
}
915
916
static void
917
ntfs_uncompress_done(NTFS_COMP_INFO * comp)
918
0
{
919
0
    free(comp->uncomp_buf);
920
0
    comp->uncomp_buf = NULL;
921
0
    free(comp->comp_buf);
922
0
    comp->comp_buf = NULL;
923
0
    comp->buf_size_b = 0;
924
0
}
925
926
927
 /**
928
  * Uncompress the block of data in comp->comp_buf.
929
  * Store the result in the comp->uncomp_buf.
930
  *
931
  * @param comp Compression unit structure
932
  *
933
  * @returns 1 on error and 0 on success
934
  */
935
static uint8_t
936
ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
937
0
{
938
0
    size_t cl_index;
939
0
    uint8_t recover_data = 0;
940
941
0
    tsk_error_reset();
942
943
0
    comp->uncomp_idx = 0;
944
945
    /* Cycle through the compressed data
946
     * We maintain state using different levels of loops.
947
     * We use +1 here because the size value at start of block is 2 bytes.
948
     */
949
0
    for (cl_index = 0; cl_index + 1 < comp->comp_len;) {
950
0
        size_t blk_end;         // index into the buffer to where block ends
951
0
        size_t blk_size;        // size of the current block
952
0
        uint8_t iscomp;         // set to 1 if block is compressed
953
0
        size_t blk_st_uncomp;   // index into uncompressed buffer where block started
954
0
        uint16_t sb_header;     // subblock header
955
956
0
        sb_header = tsk_getu16(TSK_LIT_ENDIAN, comp->comp_buf + cl_index);
957
958
        // If the sb_header isn't set, we just fill the rest of the buffer with zeros.
959
        // This seems to be what several different NTFS implementations do.
960
0
        if (sb_header == 0) {
961
0
            memset(comp->uncomp_buf + comp->uncomp_idx, 0, comp->buf_size_b - comp->uncomp_idx);
962
0
            comp->uncomp_idx = comp->buf_size_b;
963
0
            break;
964
0
        }
965
966
0
        blk_size = (sb_header & 0x0FFF) + 3;
967
968
0
        if (tsk_verbose)
969
0
            tsk_fprintf(stderr,
970
0
                "ntfs_uncompress_compunit: Start compression block (length=%" PRIuSIZE " index=%" PRIuSIZE
971
0
                " compressed buffer size=%" PRIuSIZE ")\n",
972
0
                blk_size, cl_index, comp->comp_len);
973
974
        // this seems to indicate end of block
975
0
        if (blk_size == 3)
976
0
            break;
977
978
0
        blk_end = cl_index + blk_size;
979
0
        if (blk_end > comp->comp_len) {
980
0
            blk_end = comp->comp_len - 1;
981
0
            if (tsk_verbose)
982
0
                tsk_fprintf(stderr,
983
0
                    "WARNING: ntfs_uncompress_compunit: Compression block length longer than buffer length. Attempting to continue.\n");
984
0
            recover_data = 1;
985
           // return 0; // zero out the entire block
986
           // if we don't return 0, let the function continue to display as much decompressed data as possible
987
0
        }
988
989
        /* The MSB identifies if the block is compressed */
990
0
        iscomp = ((sb_header & 0x8000) != 0);
991
992
        // keep track of where this block started in the buffer
993
0
        blk_st_uncomp = comp->uncomp_idx;
994
0
        cl_index += 2;
995
996
        // the 4096 size seems to occur at the same times as no compression
997
0
        if ((iscomp) && (blk_size - 2 != 4096)) {
998
0
            if (tsk_verbose)
999
0
                tsk_fprintf(stderr, "ntfs_uncompress_compunit: Compression block is compressed\n");
1000
1001
            // cycle through the token groups in the block
1002
0
            while (cl_index < blk_end) {
1003
0
                int a;
1004
1005
                // get the header header
1006
0
                unsigned char header = comp->comp_buf[cl_index];
1007
0
                cl_index++;
1008
1009
0
                if (tsk_verbose)
1010
0
                    tsk_fprintf(stderr,
1011
0
                        "ntfs_uncompress_compunit: Token Group Header: %x\n", header);
1012
1013
0
                for (a = 0; a < 8 && cl_index < blk_end; a++) {
1014
1015
                    /* Determine token type and parse appropriately. *
1016
                     * Symbol tokens are the symbol themselves, so copy it
1017
                     * into the uncompressed buffer
1018
                     */
1019
0
                    if ((header & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
1020
0
                        if (tsk_verbose)
1021
0
                            tsk_fprintf(stderr,
1022
0
                                "ntfs_uncompress_compunit: Symbol Token: (offset %"
1023
0
                                PRIuSIZE ")\n", cl_index);
1024
1025
0
                        if (comp->uncomp_idx >= comp->buf_size_b) {
1026
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1027
0
                            tsk_error_set_errstr
1028
0
                                ("ntfs_uncompress_compunit: Trying to write past end of uncompression buffer: %"
1029
0
                                PRIuSIZE "", comp->uncomp_idx);
1030
0
                            return 1;
1031
0
                        }
1032
0
                        comp->uncomp_buf[comp->uncomp_idx++] =
1033
0
                            comp->comp_buf[cl_index];
1034
1035
0
                        cl_index++;
1036
0
                    }
1037
1038
                    /* Otherwise, it is a phrase token, which points back
1039
                     * to a previous sequence of bytes.
1040
                     */
1041
0
                    else {
1042
0
                        size_t i;
1043
0
                        int shift;
1044
0
                        size_t start_position_index = 0;
1045
0
                        size_t end_position_index = 0;
1046
0
                        unsigned int offset = 0;
1047
0
                        unsigned int length = 0;
1048
0
                        uint16_t pheader;
1049
1050
0
                        if (cl_index + 1 >= blk_end) {
1051
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1052
0
                            tsk_error_set_errstr
1053
0
                                ("ntfs_uncompress_compunit: Phrase token index is past end of block: %d",
1054
0
                                a);
1055
0
                            return 1;
1056
0
                        }
1057
1058
0
                        pheader =
1059
0
                            ((((comp->comp_buf[cl_index +
1060
0
                                            1]) << 8) & 0xFF00) |
1061
0
                            (comp->comp_buf[cl_index] & 0xFF));
1062
0
                        cl_index += 2;
1063
1064
1065
                        /* The number of bits for the start and length
1066
                         * in the 2-byte header change depending on the
1067
                         * location in the compression unit.  This identifies
1068
                         * how many bits each has */
1069
0
                        shift = 0;
1070
0
                        for (i =
1071
0
                            comp->uncomp_idx -
1072
0
                            blk_st_uncomp - 1; i >= 0x10; i >>= 1) {
1073
0
                            shift++;
1074
0
                        }
1075
0
                        if (shift > 12) {
1076
0
                            tsk_error_reset();
1077
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1078
0
                            tsk_error_set_errstr
1079
0
                            ("ntfs_uncompress_compunit: Shift is too large: %d", shift);
1080
0
                            return 1;
1081
0
                        }
1082
1083
                        //tsk_fprintf(stderr, "Start: %X  Shift: %d  UnComp_IDX %d  BlkStart: %lu  BlkIdx: %d  BlkSize: %d\n", (int)(comp->uncomp_idx - comp->blk_st - 1), shift, comp->uncomp_idx, comp->blk_st, comp->blk_idx, comp->blk_size);
1084
1085
0
                        offset = (pheader >> (12 - shift)) + 1;
1086
0
                        length = (pheader & (0xFFF >> shift)) + 2;
1087
1088
0
                        start_position_index = comp->uncomp_idx - offset;
1089
0
                        end_position_index = start_position_index + length;
1090
1091
0
                        if (tsk_verbose)
1092
0
                            tsk_fprintf(stderr,
1093
0
                                "ntfs_uncompress_compunit: Phrase Token: (offset %"
1094
0
                                PRIuSIZE ")\tLen: %d\tPrevOffset: %d\tHeader=%x\n", cl_index-2,
1095
0
                                length, offset, pheader);
1096
1097
                        /* Sanity checks on values */
1098
0
                        if (offset > comp->uncomp_idx) {
1099
0
                            tsk_error_reset();
1100
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1101
0
                            tsk_error_set_errstr
1102
0
                                ("ntfs_uncompress_compunit: Phrase token offset is too large:  %d (max: %"
1103
0
                                PRIuSIZE ")", offset, comp->uncomp_idx);
1104
0
                            return 1;
1105
0
                        }
1106
0
                        else if (length + start_position_index >
1107
0
                            comp->buf_size_b) {
1108
0
                            tsk_error_reset();
1109
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1110
0
                            tsk_error_set_errstr
1111
0
                                ("ntfs_uncompress_compunit: Phrase token length is too large:  %d (max: %" PRIuSIZE")",
1112
0
                                length,
1113
0
                                comp->buf_size_b - start_position_index);
1114
0
                            return 1;
1115
0
                        }
1116
0
                        else if (end_position_index -
1117
0
                            start_position_index + 1 >
1118
0
                            comp->buf_size_b - comp->uncomp_idx) {
1119
0
                            tsk_error_reset();
1120
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1121
0
                            tsk_error_set_errstr
1122
0
                                ("ntfs_uncompress_compunit: Phrase token length is too large for rest of uncomp buf:  %" PRIuSIZE" (max: %"
1123
0
                                PRIuSIZE ")",
1124
0
                                end_position_index - start_position_index +
1125
0
                                1, comp->buf_size_b - comp->uncomp_idx);
1126
0
                            return 1;
1127
0
                        }
1128
1129
0
                        for (;
1130
0
                            start_position_index <= end_position_index
1131
0
                            && comp->uncomp_idx < comp->buf_size_b;
1132
0
                            start_position_index++) {
1133
1134
                            // Copy the previous data to the current position
1135
0
                            comp->uncomp_buf[comp->uncomp_idx++]
1136
0
                                = comp->uncomp_buf[start_position_index];
1137
0
                        }
1138
0
                    }
1139
0
                    header >>= 1;
1140
0
                }               // end of loop inside of token group
1141
1142
0
            }                   // end of loop inside of block
1143
0
        }
1144
1145
        // this block contains uncompressed data
1146
0
        else {
1147
0
            if (tsk_verbose)
1148
0
                tsk_fprintf(stderr, "ntfs_uncompress_compunit: Block size is not compressed\n");
1149
1150
0
            while (cl_index < blk_end && cl_index < comp->comp_len) {
1151
                /* This seems to happen only with corrupt data -- such as
1152
                 * when an unallocated file is being processed... */
1153
0
                if (comp->uncomp_idx >= comp->buf_size_b) {
1154
0
                    tsk_error_reset();
1155
0
                    tsk_error_set_errno(TSK_ERR_FS_FWALK);
1156
0
                    tsk_error_set_errstr
1157
0
                        ("ntfs_uncompress_compunit: Trying to write past end of uncompression buffer (1) -- corrupt data?)");
1158
0
                    return 1;
1159
0
                }
1160
1161
                // Place data in uncompression_buffer
1162
0
                comp->uncomp_buf[comp->uncomp_idx++] =
1163
0
                    comp->comp_buf[cl_index++];
1164
0
            }
1165
0
        }
1166
0
    }                           // end of loop inside of compression unit
1167
    // if we are attempting to recover, we may not have decompressed an entire CU. Set uncomp_idx to the expected size.
1168
0
    if (recover_data) {
1169
0
        comp->uncomp_idx = comp->buf_size_b;
1170
0
    }
1171
0
    return 0;
1172
0
}
1173
1174
1175
1176
/**
1177
 * Process a compression unit and return the decompressed data in a buffer in comp.
1178
 *
1179
 * @param ntfs File system
1180
 * @param comp Compression state info (output will be stored in here)
1181
 * @param comp_unit List of addresses that store compressed data
1182
 * @param comp_unit_size Number of addresses in comp_unit
1183
 * @returns 1 on error and 0 on success
1184
 */
1185
static uint8_t
1186
ntfs_proc_compunit(NTFS_INFO * ntfs, NTFS_COMP_INFO * comp,
1187
    TSK_DADDR_T * comp_unit, uint32_t comp_unit_size)
1188
0
{
1189
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) ntfs;
1190
0
    int sparse;
1191
0
    uint64_t a;
1192
1193
    /* With compressed attributes, there are three scenarios.
1194
     * 1: The compression unit is not compressed,
1195
     * 2: The compression unit is sparse
1196
     * 3: The compression unit is compressed
1197
     */
1198
1199
    /* Check if the entire compression unit is sparse */
1200
0
    sparse = 1;
1201
0
    for (a = 0; a < comp_unit_size && sparse == 1; a++) {
1202
0
        if (comp_unit[a]) {
1203
0
            sparse = 0;
1204
0
            break;
1205
0
        }
1206
0
    }
1207
1208
    /* Entire comp unit is sparse... */
1209
0
    if (sparse) {
1210
0
        if (tsk_verbose)
1211
0
            tsk_fprintf(stderr,
1212
0
                "ntfs_proc_compunit: Unit is fully sparse\n");
1213
1214
0
        memset(comp->uncomp_buf, 0, comp->buf_size_b);
1215
0
        comp->uncomp_idx = comp->buf_size_b;
1216
0
    }
1217
1218
    /* Check if the end of the unit is sparse, which means the
1219
     * unit is compressed */
1220
0
    else if (comp_unit[comp_unit_size - 1] == 0) {
1221
1222
0
        if (tsk_verbose)
1223
0
            tsk_fprintf(stderr,
1224
0
                "ntfs_proc_compunit: Unit is compressed\n");
1225
1226
        // load up the compressed buffer so we can decompress it
1227
0
        ntfs_uncompress_reset(comp);
1228
0
        for (a = 0; a < comp_unit_size; a++) {
1229
0
            ssize_t cnt;
1230
1231
0
            if (comp_unit[a] == 0)
1232
0
                break;
1233
1234
            /* To get the uncompressed size, we must uncompress the
1235
             * data -- even if addresses are only needed */
1236
0
            cnt =
1237
0
                tsk_fs_read_block(fs, comp_unit[a],
1238
0
                &comp->comp_buf[comp->comp_len], fs->block_size);
1239
0
            if (cnt != fs->block_size) {
1240
0
                if (cnt >= 0) {
1241
0
                    tsk_error_reset();
1242
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
1243
0
                }
1244
0
                tsk_error_set_errstr2
1245
0
                    ("ntfs_proc_compunit: Error reading block at %"
1246
0
                    PRIuDADDR, comp_unit[a]);
1247
0
                return 1;
1248
0
            }
1249
0
            comp->comp_len += fs->block_size;
1250
0
        }
1251
1252
0
        if (ntfs_uncompress_compunit(comp)) {
1253
0
            return 1;
1254
0
        }
1255
0
    }
1256
1257
    /* Uncompressed data */
1258
0
    else {
1259
0
        if (tsk_verbose)
1260
0
            tsk_fprintf(stderr,
1261
0
                "ntfs_proc_compunit: Unit is not compressed\n");
1262
1263
0
        comp->uncomp_idx = 0;
1264
0
        for (a = 0; a < comp_unit_size; a++) {
1265
0
            ssize_t cnt;
1266
1267
            // Prevent an OOB write of comp->uncomp_buf
1268
0
            if ((comp->uncomp_idx >= comp->buf_size_b) || (fs->block_size > comp->buf_size_b - comp->uncomp_idx)) {
1269
0
                tsk_error_reset();
1270
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
1271
0
                tsk_error_set_errstr("ntfs_proc_compunit: Buffer not big enough for uncompressed data (Index: %" PRIuSIZE ")", comp->uncomp_idx);
1272
0
                return 1;
1273
0
            }
1274
1275
0
            cnt =
1276
0
                tsk_fs_read_block(fs, comp_unit[a],
1277
0
                &comp->uncomp_buf[comp->uncomp_idx], fs->block_size);
1278
0
            if (cnt != fs->block_size) {
1279
0
                if (cnt >= 0) {
1280
0
                    tsk_error_reset();
1281
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
1282
0
                }
1283
0
                tsk_error_set_errstr2
1284
0
                    ("ntfs_proc_compunit: Error reading block at %"
1285
0
                    PRIuDADDR, comp_unit[a]);
1286
0
                return 1;
1287
0
            }
1288
0
            comp->uncomp_idx += fs->block_size;
1289
0
        }
1290
0
    }
1291
0
    return 0;
1292
0
}
1293
1294
1295
1296
/**
1297
 * Currently ignores the SPARSE flag
1298
 */
1299
static uint8_t
1300
ntfs_attr_walk_special(
1301
  const TSK_FS_ATTR * fs_attr,
1302
  [[maybe_unused]] int flags,
1303
  TSK_FS_FILE_WALK_CB a_action,
1304
  void *ptr)
1305
0
{
1306
0
    TSK_FS_INFO *fs;
1307
0
    NTFS_INFO *ntfs;
1308
1309
    // clean up any error messages that are lying around
1310
0
    tsk_error_reset();
1311
0
    if ((fs_attr == NULL) || (fs_attr->fs_file == NULL)
1312
0
        || (fs_attr->fs_file->meta == NULL)
1313
0
        || (fs_attr->fs_file->fs_info == NULL)) {
1314
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1315
0
        tsk_error_set_errstr
1316
0
            ("ntfs_attr_walk_special: Null arguments given\n");
1317
0
        return 1;
1318
0
    }
1319
1320
0
    fs = fs_attr->fs_file->fs_info;
1321
0
    ntfs = (NTFS_INFO *) fs;
1322
1323
    /* Process the compressed buffer
1324
     *
1325
     * The compsize value equal to 0 can occur if we are processing an
1326
     * isolated entry that is part of an attribute list.  The first
1327
     * sequence of the attribute has the compsize and the latter ones
1328
     * do not. So, if one of the non-base MFT entries is processed by
1329
     * itself, we have that case.  I tried to assume it was 16, but it
1330
     * caused decompression problems -- likely because this sequence
1331
     * did not start on a compression unit boundary.  So, now we just
1332
     * dump the compressed data instead of giving an error.
1333
     */
1334
0
    if (fs_attr->flags & TSK_FS_ATTR_COMP) {
1335
0
        TSK_DADDR_T addr;
1336
0
        TSK_FS_ATTR_RUN *fs_attr_run;
1337
0
        TSK_DADDR_T *comp_unit;
1338
0
        uint32_t comp_unit_idx = 0;
1339
0
        NTFS_COMP_INFO comp;
1340
0
        TSK_OFF_T off = 0;
1341
0
        int retval;
1342
0
        uint8_t stop_loop = 0;
1343
0
        uint8_t init_size_reached = 0;
1344
0
        uint8_t has_init_size = 0;
1345
1346
0
        if (fs_attr->nrd.compsize <= 0) {
1347
0
            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1348
0
            tsk_error_set_errstr
1349
0
                ("ntfs_attrwalk_special: Compressed attribute has compsize of 0 (%"
1350
0
                PRIuINUM ")", fs_attr->fs_file->meta->addr);
1351
0
            return 1;
1352
0
        }
1353
1354
        /* Allocate the buffers and state structure */
1355
0
        if (ntfs_uncompress_setup(fs, &comp, fs_attr->nrd.compsize)) {
1356
0
            return 1;
1357
0
        }
1358
1359
0
        comp_unit =
1360
0
            (TSK_DADDR_T *) tsk_malloc(fs_attr->nrd.compsize *
1361
0
            sizeof(TSK_DADDR_T));
1362
0
        if (comp_unit == NULL) {
1363
0
            ntfs_uncompress_done(&comp);
1364
0
            return 1;
1365
0
        }
1366
0
        retval = TSK_WALK_CONT;
1367
1368
0
        if (fs_attr->nrd.initsize != fs_attr->fs_file->meta->size)
1369
0
            has_init_size = 1;
1370
1371
        /* cycle through the number of runs we have */
1372
0
        for (fs_attr_run = fs_attr->nrd.run; fs_attr_run;
1373
0
            fs_attr_run = fs_attr_run->next) {
1374
0
            size_t len_idx;
1375
1376
            /* We may get a FILLER entry at the beginning of the run
1377
             * if we are processing a non-base file record since
1378
             * this $DATA attribute could not be the first sequence in the
1379
             * attribute. Therefore, do not error if it starts at 0 */
1380
0
            if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
1381
0
                if (fs_attr_run->addr != 0) {
1382
0
                    tsk_error_reset();
1383
1384
0
                    if (fs_attr->fs_file->meta->
1385
0
                        flags & TSK_FS_META_FLAG_UNALLOC)
1386
0
                        tsk_error_set_errno(TSK_ERR_FS_RECOVER);
1387
0
                    else
1388
0
                        tsk_error_set_errno(TSK_ERR_FS_GENFS);
1389
0
                    tsk_error_set_errstr
1390
0
                        ("ntfs_attr_walk_special: Filler Entry exists in fs_attr_run %"
1391
0
                        PRIuDADDR "@%" PRIuDADDR " - type: %" PRIu32
1392
0
                        "  id: %d Meta: %" PRIuINUM " Status: %s",
1393
0
                        fs_attr_run->len, fs_attr_run->addr, fs_attr->type,
1394
0
                        fs_attr->id, fs_attr->fs_file->meta->addr,
1395
0
                        (fs_attr->fs_file->meta->
1396
0
                            flags & TSK_FS_META_FLAG_ALLOC) ? "Allocated" :
1397
0
                        "Deleted");
1398
0
                    free(comp_unit);
1399
0
                    ntfs_uncompress_done(&comp);
1400
0
                    return 1;
1401
0
                }
1402
0
                else {
1403
0
                    if ((fs_attr_run->len > LLONG_MAX)
1404
0
                        || (LLONG_MAX / fs_attr_run->len < fs->block_size)) {
1405
0
                        if (fs_attr->fs_file->meta->
1406
0
                            flags & TSK_FS_META_FLAG_UNALLOC)
1407
0
                            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
1408
0
                        else
1409
0
                            tsk_error_set_errno(TSK_ERR_FS_GENFS);
1410
0
                        tsk_error_set_errstr
1411
0
                            ("ntfs_attr_walk_special: Attribute run length is too large %"
1412
0
                            PRIuDADDR "@%" PRIuDADDR " - type: %" PRIu32
1413
0
                            "  id: %d Meta: %" PRIuINUM " Status: %s",
1414
0
                            fs_attr_run->len, fs_attr_run->addr, fs_attr->type,
1415
0
                            fs_attr->id, fs_attr->fs_file->meta->addr,
1416
0
                            (fs_attr->fs_file->meta->
1417
0
                                flags & TSK_FS_META_FLAG_ALLOC) ? "Allocated" :
1418
0
                            "Deleted");
1419
0
                        free(comp_unit);
1420
0
                        ntfs_uncompress_done(&comp);
1421
0
                        return 1;
1422
0
                    }
1423
0
                    off += (fs_attr_run->len * fs->block_size);
1424
0
                    continue;
1425
0
                }
1426
0
            }
1427
0
            addr = fs_attr_run->addr;
1428
1429
            /* cycle through each cluster in the run */
1430
0
            for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) {
1431
1432
0
                if (addr > fs->last_block) {
1433
0
                    tsk_error_reset();
1434
1435
0
                    if (fs_attr->fs_file->meta->
1436
0
                        flags & TSK_FS_META_FLAG_UNALLOC)
1437
0
                        tsk_error_set_errno(TSK_ERR_FS_RECOVER);
1438
0
                    else
1439
0
                        tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
1440
0
                    tsk_error_set_errstr
1441
0
                        ("ntfs_attr_walk_special: Invalid address in run (too large): %"
1442
0
                        PRIuDADDR " Meta: %" PRIuINUM " Status: %s", addr,
1443
0
                        fs_attr->fs_file->meta->addr,
1444
0
                        (fs_attr->fs_file->meta->
1445
0
                            flags & TSK_FS_META_FLAG_ALLOC) ? "Allocated" :
1446
0
                        "Deleted");
1447
1448
0
                    free(comp_unit);
1449
0
                    ntfs_uncompress_done(&comp);
1450
0
                    return 1;
1451
0
                }
1452
1453
                // queue up the addresses until we get a full unit
1454
0
                comp_unit[comp_unit_idx++] = addr;
1455
1456
                // time to decompress (if queue is full or this is the last block)
1457
0
                if ((comp_unit_idx == fs_attr->nrd.compsize)
1458
0
                    || ((len_idx == fs_attr_run->len - 1)
1459
0
                        && (fs_attr_run->next == NULL))) {
1460
0
                    size_t i;
1461
1462
1463
0
                    if (tsk_verbose)
1464
0
                        tsk_fprintf(stderr,
1465
0
                            "ntfs_proc_compunit: Decompressing at file offset %" PRIdOFF "\n", off);
1466
1467
                    // decompress the unit if we have not passed initsize yet.
1468
0
                    if (!init_size_reached) {
1469
0
                        if (ntfs_proc_compunit(ntfs, &comp, comp_unit,
1470
0
                            comp_unit_idx)) {
1471
0
                            tsk_error_set_errstr2("%" PRIuINUM " - type: %"
1472
0
                                PRIu32 "  id: %d Status: %s",
1473
0
                                fs_attr->fs_file->meta->addr, fs_attr->type,
1474
0
                                fs_attr->id,
1475
0
                                (fs_attr->fs_file->meta->
1476
0
                                    flags & TSK_FS_META_FLAG_ALLOC) ?
1477
0
                                "Allocated" : "Deleted");
1478
0
                            free(comp_unit);
1479
0
                            ntfs_uncompress_done(&comp);
1480
0
                            return 1;
1481
0
                        }
1482
1483
                        /* if we've passed the initialized size while reading this block,
1484
                         * zero out the buffer beyond the initialized size. */
1485
0
                        if (has_init_size && (off < fs_attr->nrd.initsize)) {
1486
0
                            const int64_t prev_remanining_init_size = fs_attr->nrd.initsize - off;
1487
0
                            if (prev_remanining_init_size < (int64_t)comp.buf_size_b) {
1488
0
                                memset(&comp.uncomp_buf[prev_remanining_init_size], 0, comp.buf_size_b - prev_remanining_init_size);
1489
0
                                init_size_reached = 1;
1490
0
                            }
1491
0
                        }
1492
0
                    }
1493
                    // set the buffers to 0s if we are past initsize
1494
0
                    else {
1495
0
                        ntfs_uncompress_reset(&comp);
1496
0
                        comp.uncomp_idx = comp.buf_size_b;
1497
0
                    }
1498
1499
                    // now call the callback with the uncompressed data
1500
0
                    for (i = 0; i < comp_unit_idx; i++) {
1501
0
                        int myflags;
1502
0
                        size_t read_len;
1503
1504
0
                        myflags =
1505
0
                            TSK_FS_BLOCK_FLAG_CONT |
1506
0
                            TSK_FS_BLOCK_FLAG_COMP;
1507
0
                        retval = is_clustalloc(ntfs, comp_unit[i]);
1508
0
                        if (retval == -1) {
1509
0
                            if (fs_attr->fs_file->meta->
1510
0
                                flags & TSK_FS_META_FLAG_UNALLOC)
1511
0
                                tsk_error_set_errno(TSK_ERR_FS_RECOVER);
1512
0
                            free(comp_unit);
1513
0
                            ntfs_uncompress_done(&comp);
1514
0
                            return 1;
1515
0
                        }
1516
0
                        else if (retval == 1) {
1517
0
                            myflags |= TSK_FS_BLOCK_FLAG_ALLOC;
1518
0
                        }
1519
0
                        else if (retval == 0) {
1520
0
                            myflags |= TSK_FS_BLOCK_FLAG_UNALLOC;
1521
0
                        }
1522
1523
                        // Unclear what the behavior should be here
1524
                        // assuming POSIX like behavior is likely the required approach
1525
0
                        if (off >= fs_attr->size)
1526
0
                            read_len = 0;
1527
0
                        else if (fs_attr->size - off > fs->block_size)
1528
0
                            read_len = fs->block_size;
1529
0
                        else
1530
0
                            read_len = (size_t) (fs_attr->size - off);
1531
1532
0
                        if (i * fs->block_size + read_len >
1533
0
                            comp.uncomp_idx) {
1534
0
                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1535
0
                            tsk_error_set_errstr
1536
0
                                ("ntfs_attrwalk_special: Trying to read past end of uncompressed buffer: %"
1537
0
                                PRIuSIZE " %" PRIuSIZE " Meta: %" PRIuINUM
1538
0
                                " Status: %s",
1539
0
                                i * fs->block_size + read_len,
1540
0
                                comp.uncomp_idx,
1541
0
                                fs_attr->fs_file->meta->addr,
1542
0
                                (fs_attr->fs_file->meta->
1543
0
                                    flags & TSK_FS_META_FLAG_ALLOC) ?
1544
0
                                "Allocated" : "Deleted");
1545
0
                            free(comp_unit);
1546
0
                            ntfs_uncompress_done(&comp);
1547
0
                            return 1;
1548
0
                        }
1549
1550
                        // call the callback
1551
0
                        retval =
1552
0
                            a_action(fs_attr->fs_file, off, comp_unit[i],
1553
0
                            &comp.uncomp_buf[i * fs->block_size], read_len,
1554
0
                            (TSK_FS_BLOCK_FLAG_ENUM) myflags, ptr);
1555
1556
0
                        off += read_len;
1557
1558
0
                        if (off >= fs_attr->size) {
1559
0
                            stop_loop = 1;
1560
0
                            break;
1561
0
                        }
1562
0
                        if (retval != TSK_WALK_CONT) {
1563
0
                            stop_loop = 1;
1564
0
                            break;
1565
0
                        }
1566
0
                    }
1567
0
                    comp_unit_idx = 0;
1568
0
                }
1569
1570
0
                if (stop_loop)
1571
0
                    break;
1572
1573
                /* If it is a sparse run, don't increment the addr so that
1574
                 * it remains 0 */
1575
0
                if (((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) ==
1576
0
                        0)
1577
0
                    && ((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER)
1578
0
                        == 0))
1579
0
                    addr++;
1580
0
            }
1581
1582
0
            if (stop_loop)
1583
0
                break;
1584
0
        }
1585
1586
0
        ntfs_uncompress_done(&comp);
1587
0
        free(comp_unit);
1588
1589
0
        if (retval == TSK_WALK_ERROR)
1590
0
            return 1;
1591
0
        else
1592
0
            return 0;
1593
0
    }
1594
0
    else {
1595
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
1596
0
        tsk_error_set_errstr
1597
0
            ("ntfs_attrwalk_special: called with non-special attribute: %x",
1598
0
            fs_attr->flags);
1599
0
        return 1;
1600
0
    }
1601
0
}
1602
1603
1604
/** \internal
1605
 *
1606
 * @returns number of bytes read or -1 on error (incl if offset is past EOF)
1607
 */
1608
static ssize_t
1609
ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
1610
    TSK_OFF_T a_offset, char *a_buf, size_t a_len)
1611
0
{
1612
0
    TSK_FS_INFO *fs = NULL;
1613
0
    NTFS_INFO *ntfs = NULL;
1614
1615
0
    if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
1616
0
        || (a_fs_attr->fs_file->meta == NULL)
1617
0
        || (a_fs_attr->fs_file->fs_info == NULL)) {
1618
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1619
0
        tsk_error_set_errstr
1620
0
            ("ntfs_file_read_special: NULL parameters passed");
1621
0
        return -1;
1622
0
    }
1623
1624
0
    fs = a_fs_attr->fs_file->fs_info;
1625
0
    ntfs = (NTFS_INFO *) fs;
1626
1627
0
    if (a_fs_attr->flags & TSK_FS_ATTR_COMP) {
1628
0
        TSK_FS_ATTR_RUN *data_run_cur;
1629
0
        TSK_OFF_T cu_blkoffset; // block offset of starting compression unit to start reading from
1630
0
        size_t byteoffset;      // byte offset in compression unit of where we want to start reading from
1631
0
        TSK_DADDR_T *comp_unit;
1632
0
        uint32_t comp_unit_idx = 0;
1633
0
        NTFS_COMP_INFO comp;
1634
0
        size_t buf_idx = 0;
1635
0
        uint8_t init_size_reached = 0;
1636
0
        uint8_t has_init_size = 0;
1637
1638
0
        if (a_fs_attr->nrd.compsize <= 0) {
1639
0
            tsk_error_set_errno(TSK_ERR_FS_FWALK);
1640
0
            tsk_error_set_errstr
1641
0
                ("ntfs_file_read_special: Compressed attribute has compsize of 0");
1642
0
            return -1;
1643
0
        }
1644
1645
0
        if (a_offset >= a_fs_attr->size) {
1646
0
            tsk_error_reset();
1647
0
            tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
1648
0
            tsk_error_set_errstr("ntfs_file_read_special - %" PRIdOFF
1649
0
                " Meta: %" PRIuINUM, a_offset,
1650
0
                a_fs_attr->fs_file->meta->addr);
1651
0
            return -1;
1652
0
        }
1653
1654
        // we return 0s for reads past the initsize
1655
0
        if (a_offset >= a_fs_attr->nrd.initsize) {
1656
0
            ssize_t len;
1657
1658
0
            if (tsk_verbose)
1659
0
                fprintf(stderr,
1660
0
                    "ntfs_file_read_special: Returning 0s for read past end of initsize (%"
1661
0
                    PRIuINUM ")\n", a_fs_attr->fs_file->meta->addr);
1662
1663
0
            if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->nrd.allocsize)
1664
0
                len = (ssize_t) (a_fs_attr->nrd.allocsize - a_offset);
1665
0
            else
1666
0
                len = (ssize_t) a_len;
1667
0
            memset(a_buf, 0, a_len);
1668
0
            return len;
1669
0
        }
1670
1671
0
        if (a_fs_attr->nrd.initsize != a_fs_attr->fs_file->meta->size)
1672
0
            has_init_size = 1;
1673
1674
        /* Allocate the buffers and state structure */
1675
0
        if (ntfs_uncompress_setup(fs, &comp, a_fs_attr->nrd.compsize)) {
1676
0
            return -1;
1677
0
        }
1678
1679
0
        comp_unit =
1680
0
            (TSK_DADDR_T *) tsk_malloc(a_fs_attr->nrd.compsize *
1681
0
            sizeof(TSK_DADDR_T));
1682
0
        if (comp_unit == NULL) {
1683
0
            ntfs_uncompress_done(&comp);
1684
0
            return -1;
1685
0
        }
1686
1687
        // figure out the needed offsets
1688
0
        cu_blkoffset = a_offset / fs->block_size;
1689
0
        if (cu_blkoffset) {
1690
0
            cu_blkoffset /= a_fs_attr->nrd.compsize;
1691
0
            cu_blkoffset *= a_fs_attr->nrd.compsize;
1692
0
        }
1693
1694
0
        byteoffset = (size_t) (a_offset - cu_blkoffset * fs->block_size);
1695
1696
        // cycle through the run until we find where we can start to process the clusters
1697
0
        for (data_run_cur = a_fs_attr->nrd.run;
1698
0
            (data_run_cur) && (buf_idx < a_len);
1699
0
            data_run_cur = data_run_cur->next) {
1700
1701
0
            TSK_DADDR_T addr;
1702
0
            size_t a;
1703
1704
            // See if this run contains the starting offset they requested
1705
0
            if (data_run_cur->offset + data_run_cur->len <
1706
0
                (TSK_DADDR_T) cu_blkoffset)
1707
0
                continue;
1708
1709
1710
            // seek to the start of where we want to read (we may need to read several runs)
1711
0
            if (data_run_cur->offset > (TSK_DADDR_T) cu_blkoffset)
1712
0
                a = 0;
1713
0
            else
1714
0
                a = (size_t) (cu_blkoffset - data_run_cur->offset);
1715
1716
0
            addr = data_run_cur->addr;
1717
            // don't increment addr if it is 0 -- sparse
1718
0
            if (addr)
1719
0
                addr += a;
1720
1721
            /* cycle through the relevant in the run */
1722
0
            for (; a < data_run_cur->len && buf_idx < a_len; a++) {
1723
1724
                // queue up the addresses until we get a full unit
1725
0
                comp_unit[comp_unit_idx++] = addr;
1726
1727
                // time to decompress (if queue is full or this is the last block)
1728
0
                if ((comp_unit_idx == a_fs_attr->nrd.compsize)
1729
0
                    || ((a == data_run_cur->len - 1)
1730
0
                        && (data_run_cur->next == NULL))) {
1731
0
                    size_t cpylen;
1732
1733
                    // decompress the unit if we are still in initsize
1734
0
                    if (!init_size_reached) {
1735
0
                        if (ntfs_proc_compunit(ntfs, &comp, comp_unit,
1736
0
                            comp_unit_idx)) {
1737
0
                            tsk_error_set_errstr2("%" PRIuINUM " - type: %"
1738
0
                                PRIu32 "  id: %d  Status: %s",
1739
0
                                a_fs_attr->fs_file->meta->addr,
1740
0
                                a_fs_attr->type, a_fs_attr->id,
1741
0
                                (a_fs_attr->fs_file->meta->
1742
0
                                    flags & TSK_FS_META_FLAG_ALLOC) ?
1743
0
                                "Allocated" : "Deleted");
1744
0
                            free(comp_unit);
1745
0
                            ntfs_uncompress_done(&comp);
1746
0
                            return -1;
1747
0
                        }
1748
1749
                        /* if we've passed the initialized size while reading this block,
1750
                         * zero out the buffer beyond the initialized size
1751
                         */
1752
0
                        if (has_init_size) {
1753
0
                            const int64_t remanining_init_size = a_fs_attr->nrd.initsize - buf_idx - a_offset;
1754
0
                            if (remanining_init_size < (int64_t)comp.buf_size_b) {
1755
0
                                memset(comp.uncomp_buf + remanining_init_size, 0, comp.buf_size_b - remanining_init_size);
1756
0
                                init_size_reached = 1;
1757
0
                            }
1758
0
                        }
1759
0
                    }
1760
0
                    else {
1761
0
                        ntfs_uncompress_reset(&comp);
1762
0
                        comp.uncomp_idx = comp.buf_size_b;
1763
0
                    }
1764
1765
                    // copy uncompressed data to the output buffer
1766
0
                    if (comp.uncomp_idx < byteoffset) {
1767
1768
                        // @@ ERROR
1769
0
                        free(comp_unit);
1770
0
                        ntfs_uncompress_done(&comp);
1771
0
                        return -1;
1772
0
                    }
1773
0
                    else if (comp.uncomp_idx - byteoffset <
1774
0
                        a_len - buf_idx) {
1775
0
                        cpylen = comp.uncomp_idx - byteoffset;
1776
0
                    }
1777
0
                    else {
1778
0
                        cpylen = a_len - buf_idx;
1779
0
                    }
1780
                    // Make sure not to return more bytes than are in the file
1781
0
                    if (cpylen > (a_fs_attr->size - (a_offset + buf_idx)))
1782
0
                        cpylen =
1783
0
                            (size_t) (a_fs_attr->size - (a_offset +
1784
0
                                buf_idx));
1785
1786
0
                    memcpy(&a_buf[buf_idx], &comp.uncomp_buf[byteoffset],
1787
0
                        cpylen);
1788
1789
                    // reset this in case we need to also read from the next run
1790
0
                    byteoffset = 0;
1791
0
                    buf_idx += cpylen;
1792
0
                    comp_unit_idx = 0;
1793
1794
0
                }
1795
                /* If it is a sparse run, don't increment the addr so that
1796
                 * it remains 0 */
1797
0
                if (((data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) ==
1798
0
                        0)
1799
0
                    && ((data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER)
1800
0
                        == 0))
1801
0
                    addr++;
1802
0
            }
1803
0
        }
1804
1805
0
        free(comp_unit);
1806
0
        ntfs_uncompress_done(&comp);
1807
0
        return (ssize_t) buf_idx;
1808
0
    }
1809
0
    else {
1810
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1811
0
        tsk_error_set_errstr
1812
0
            ("ntfs_file_read_special: called with non-special attribute: %x",
1813
0
            a_fs_attr->flags);
1814
0
        return -1;
1815
0
    }
1816
0
}
1817
1818
1819
/* needs to be predefined for proc_attrseq */
1820
static TSK_RETVAL_ENUM ntfs_proc_attrlist(NTFS_INFO *, TSK_FS_FILE *,
1821
    const TSK_FS_ATTR *, TSK_STACK *);
1822
1823
1824
/* This structure is used when processing attrlist attributes.
1825
 * The Id part of the MFTNUM-TYPE-ID triple is unique only to a given
1826
 * MFTNUM. With the case of attribute lists, a file may use multiple
1827
 * MFT entires and therefore have multiple attributes with the same
1828
 * type and id pair (if they are in different MFT entries). This map
1829
 * is created by proc_attrlist when it assigns unique IDs to the
1830
 * other entries.  proc_attrseq uses this when it adds the attributes.
1831
 */
1832
typedef struct {
1833
    int num_used;
1834
    TSK_INUM_T extMft[256];
1835
    uint32_t type[256];
1836
    uint32_t extId[256];
1837
    uint8_t name[256][512];
1838
    uint32_t newId[256];
1839
} NTFS_ATTRLIST_MAP;
1840
1841
/*
1842
 * Process an NTFS attribute sequence and load the data into data
1843
 * structures.
1844
 * An attribute sequence is a linked list of the attributes in an MFT entry.
1845
 * This is called by copy_inode and proc_attrlist.
1846
 *
1847
 * @param ntfs File system to analyze
1848
 * @param fs_file Generic metadata structure to add the attribute info to
1849
 * @param attrseq Start of the attribute sequence to analyze
1850
 * @param len Length of the attribute sequence buffer
1851
 * @param a_attrinum MFT entry address that the attribute sequence came from (diff from fs_file for attribute lists)
1852
 * @param a_attr_map List that maps to new IDs that were assigned by processing
1853
 * the attribute list attribute (if it exists) or NULL if there is no attrlist.
1854
 * @param a_seen_inum_list List of inums that have been previously processed based on attribute lists.
1855
 *    Can be NULL when this is called for the first time. Should be non-NULL when this is called recursively by proc_attrlist.
1856
 * @returns Error code
1857
 */
1858
static TSK_RETVAL_ENUM
1859
ntfs_proc_attrseq(NTFS_INFO * ntfs,
1860
    TSK_FS_FILE * fs_file, const ntfs_attr * a_attrseq, size_t len,
1861
    TSK_INUM_T a_attrinum, const NTFS_ATTRLIST_MAP * a_attr_map, TSK_STACK * a_seen_inum_list)
1862
0
{
1863
0
    const ntfs_attr *attr;
1864
0
    const TSK_FS_ATTR *fs_attr_attrl = NULL;
1865
0
    char name[NTFS_MAXNAMLEN_UTF8 + 1];
1866
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
1867
1868
0
    if (tsk_verbose)
1869
0
        tsk_fprintf(stderr,
1870
0
            "ntfs_proc_attrseq: Processing extended entry for primary entry %"
1871
0
            PRIuINUM "\n", fs_file->meta->addr);
1872
1873
0
    if (fs_file->meta->attr == NULL) {
1874
0
        tsk_error_reset();
1875
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1876
0
        tsk_error_set_errstr("Null attribute list in ntfs_proc_attrseq");
1877
0
        return TSK_ERR;
1878
0
    }
1879
1880
0
    if (len > ntfs->mft_rsize_b) {
1881
0
        tsk_error_reset();
1882
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1883
0
        tsk_error_set_errstr("invalid length in ntfs_proc_attrseq");
1884
0
        return TSK_ERR;
1885
0
    }
1886
1887
1888
    /* Cycle through the list of attributes
1889
     * There are 16 bytes in the non-union part of
1890
     * an ntfs_attr, so make sure there is at least room for that */
1891
0
    for (attr = a_attrseq; ((uintptr_t) attr >= (uintptr_t) a_attrseq)
1892
0
        && ((uintptr_t) attr + 16 <= ((uintptr_t) a_attrseq + len))
1893
0
        && (tsk_getu32(fs->endian, attr->len) > 0
1894
0
            && (tsk_getu32(fs->endian, attr->type) !=
1895
0
                0xffffffff));
1896
0
        attr =
1897
0
        (ntfs_attr *) ((uintptr_t) attr + tsk_getu32(fs->endian,
1898
0
                attr->len))) {
1899
1900
0
        int retVal, i;
1901
0
        uint32_t type;
1902
0
        uint16_t id, id_new;
1903
1904
        // sanity check on bounds of attribute. Prevents other
1905
        // issues later on that use attr->len for bounds checks.
1906
0
        if (((uintptr_t) attr + tsk_getu32(fs->endian,
1907
0
                               attr->len)) > (uintptr_t)a_attrseq + len) {
1908
0
            break;
1909
0
        }
1910
1911
        // Ensure that the name offset doesn't refer to a location beyond
1912
        // the attribute.
1913
0
        uint16_t attr_name_off = tsk_getu16(fs->endian, attr->name_off);
1914
0
        uint32_t attr_len = tsk_getu32(fs->endian, attr->len);
1915
        // Note that it is valid for the name offset to be equal to the attribute length.
1916
0
        if ((uint32_t) attr_name_off > attr_len) {
1917
0
            break;
1918
0
        }
1919
1920
        /* Get the type of this attribute */
1921
0
        type = tsk_getu32(fs->endian, attr->type);
1922
0
        id = tsk_getu16(fs->endian, attr->id);
1923
0
        id_new = id;
1924
1925
        /* If the map was supplied, search through it to see if this
1926
         * entry is in there.  Use that ID instead so that we always have
1927
         * unique IDs for each attribute -- even if it spans multiple MFT entries. */
1928
0
        if (a_attr_map) {
1929
0
            if ((uint32_t) attr->nlen > (attr_len - (uint32_t) attr_name_off) / 2) {
1930
0
                break;
1931
0
            }
1932
0
            for (i = 0; i < a_attr_map->num_used; i++) {
1933
0
                if ((a_attr_map->type[i] == type) &&
1934
0
                    (memcmp(a_attr_map->name[i],
1935
0
                            (void *) ((uintptr_t) attr + attr_name_off),
1936
0
                            attr->nlen * 2) == 0)) {
1937
0
                    id_new = a_attr_map->newId[i];
1938
0
                    break;
1939
0
                }
1940
0
            }
1941
0
        }
1942
1943
        /* Copy the name and convert it to UTF8 */
1944
0
        if (attr->nlen && ((uint32_t) attr->nlen * 2) < attr_len - attr_name_off) {
1945
0
            int i;
1946
0
            UTF8 *name8;
1947
0
            UTF16 *name16;
1948
1949
0
            name8 = (UTF8 *) name;
1950
0
            name16 = (UTF16 *) ((uintptr_t) attr + attr_name_off);
1951
1952
0
            retVal =
1953
0
                tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
1954
0
                (UTF16 *) ((uintptr_t) name16 +
1955
0
                    attr->nlen * 2),
1956
0
                &name8,
1957
0
                (UTF8 *) ((uintptr_t) name8 +
1958
0
                    sizeof(name)), TSKlenientConversion);
1959
1960
0
            if (retVal != TSKconversionOK) {
1961
0
                if (tsk_verbose)
1962
0
                    tsk_fprintf(stderr,
1963
0
                        "ntfs_proc_attrseq: Error converting NTFS attribute name to UTF8: %d %"
1964
0
                        PRIuINUM, retVal, fs_file->meta->addr);
1965
0
                *name = '\0';
1966
0
            }
1967
1968
            /* Make sure it is NULL Terminated */
1969
0
            else if ((uintptr_t) name8 >= (uintptr_t) name + sizeof(name))
1970
0
                name[sizeof(name) - 1] = '\0';
1971
0
            else
1972
0
                *name8 = '\0';
1973
1974
            /* Clean up name */
1975
0
            i = 0;
1976
0
            while (name[i] != '\0') {
1977
0
                if (TSK_IS_CNTRL(name[i]))
1978
0
                    name[i] = '^';
1979
0
                i++;
1980
0
            }
1981
0
        }
1982
0
        else {
1983
0
            name[0] = '\0';
1984
0
        }
1985
1986
        /* For resident attributes, we will copy the buffer into
1987
         * a TSK_FS_ATTR buffer, which is stored in the TSK_FS_META
1988
         * structure
1989
         */
1990
0
        if (attr->res == NTFS_MFT_RES) {
1991
0
            TSK_FS_ATTR *fs_attr;
1992
1993
0
            if (tsk_verbose)
1994
0
                tsk_fprintf(stderr,
1995
0
                    "ntfs_proc_attrseq: Resident Attribute in Type: %"
1996
0
                    PRIu32 " Id: %" PRIu16 " IdNew: %" PRIu16
1997
0
                    " Name: %s\n", type, id, id_new, name);
1998
1999
            /* Check that there is room for the data.
2000
             * Resident data needs 24 bytes total */
2001
0
            if (((uintptr_t)attr + 24) > ((uintptr_t)a_attrseq + len)) {
2002
0
                tsk_error_reset();
2003
0
                tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
2004
0
                tsk_error_set_errstr("ntfs_attr_walk: Resident attribute %"
2005
0
                    PRIuINUM "-%" PRIu32
2006
0
                    " starting offset and length too large",
2007
0
                    fs_file->meta->addr, type);
2008
0
                return TSK_COR;
2009
0
            }
2010
2011
            /* Validate the offset lengths */
2012
0
            if (((tsk_getu16(fs->endian,
2013
0
                            attr->c.r.soff) + (uintptr_t) attr) >
2014
0
                    ((uintptr_t) a_attrseq + len))
2015
0
                || (((size_t)tsk_getu16(fs->endian,
2016
0
                            attr->c.r.soff) + tsk_getu32(fs->endian,
2017
0
                            attr->c.r.ssize) + (uintptr_t) attr) >
2018
0
                    ((uintptr_t) a_attrseq + len))) {
2019
0
                tsk_error_reset();
2020
0
                tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
2021
0
                tsk_error_set_errstr("ntfs_attr_walk: Resident attribute %"
2022
0
                    PRIuINUM "-%" PRIu32
2023
0
                    " starting offset and length too large",
2024
0
                    fs_file->meta->addr, type);
2025
0
                return TSK_COR;
2026
0
            }
2027
2028
            // Get a free fs_attr structure
2029
0
            if ((fs_attr =
2030
0
                    tsk_fs_attrlist_getnew(fs_file->meta->attr,
2031
0
                        TSK_FS_ATTR_RES)) == NULL) {
2032
0
                tsk_error_errstr2_concat(" - proc_attrseq");
2033
0
                return TSK_ERR;
2034
0
            }
2035
2036
            // set the details in the fs_attr structure
2037
0
            if (tsk_fs_attr_set_str(fs_file, fs_attr, name,
2038
0
                    (TSK_FS_ATTR_TYPE_ENUM) type,
2039
0
                    id_new, (void *) ((uintptr_t) attr +
2040
0
                        tsk_getu16(fs->endian,
2041
0
                            attr->c.r.soff)), tsk_getu32(fs->endian,
2042
0
                        attr->c.r.ssize))) {
2043
0
                tsk_error_errstr2_concat("- proc_attrseq");
2044
0
                return TSK_ERR;
2045
0
            }
2046
2047
            // set the meta size if we find the relevant attribute
2048
0
            if (TSK_FS_IS_DIR_META(fs_file->meta->type)
2049
0
                && (type == NTFS_ATYPE_IDXROOT)) {
2050
0
                fs_file->meta->size =
2051
0
                    tsk_getu32(fs->endian, attr->c.r.ssize);
2052
0
            }
2053
0
            else if ((fs_file->meta->type == TSK_FS_META_TYPE_REG)
2054
0
                && (type == NTFS_ATYPE_DATA) && (name[0] == '\0')) {
2055
0
                fs_file->meta->size =
2056
0
                    tsk_getu32(fs->endian, attr->c.r.ssize);
2057
0
            }
2058
0
        }
2059
2060
        /* For non-resident attributes, we will copy the runlist
2061
         * to the generic form and then save it in the TSK_FS_META->attr
2062
         * list
2063
         */
2064
0
        else {
2065
0
            TSK_FS_ATTR *fs_attr = NULL;
2066
0
            TSK_FS_ATTR_RUN *fs_attr_run = NULL;
2067
0
            uint8_t data_flag = 0;
2068
0
            uint32_t compsize = 0;
2069
0
            TSK_RETVAL_ENUM retval;
2070
2071
0
            if (tsk_verbose)
2072
0
                tsk_fprintf(stderr,
2073
0
                    "ntfs_proc_attrseq: Non-Resident Attribute Type: %"
2074
0
                    PRIu32 " Id: %" PRIu16 " IdNew: %" PRIu16
2075
0
                    " Name: %s  Start VCN: %" PRIu64 "\n", type, id,
2076
0
                    id_new, name, tsk_getu64(fs->endian,
2077
0
                        attr->c.nr.start_vcn));
2078
2079
            /* Check that there is room for the data.
2080
             * Non-resident data needs 64 bytes total */
2081
0
            if (((uintptr_t)attr + 64) > ((uintptr_t)a_attrseq + len)) {
2082
0
                tsk_error_reset();
2083
0
                tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
2084
0
                tsk_error_set_errstr("ntfs_attr_walk: Non-Resident attribute %"
2085
0
                    PRIuINUM "-%" PRIu32
2086
0
                    " starting offset and length too large",
2087
0
                    fs_file->meta->addr, type);
2088
0
                return TSK_COR;
2089
0
            }
2090
2091
0
            uint32_t attr_len = tsk_getu32(fs->endian, attr->len);
2092
0
            uint64_t run_start_vcn = tsk_getu64(fs->endian, attr->c.nr.start_vcn);
2093
0
            uint16_t run_off = tsk_getu16(fs->endian, attr->c.nr.run_off);
2094
2095
            // sanity check
2096
0
            if ((run_off < 48) || (run_off >= attr_len)) {
2097
0
                if (tsk_verbose)
2098
0
                    tsk_fprintf(stderr, "ntfs_proc_attrseq: run offset out of bounds\n");
2099
0
                break;
2100
0
            }
2101
2102
            /* convert the run to generic form */
2103
0
            retval = ntfs_make_data_run(ntfs,
2104
0
                run_start_vcn,
2105
0
                (ntfs_runlist *) ((uintptr_t) attr + run_off),
2106
0
                attr_len - run_off,
2107
0
                &fs_attr_run, NULL,
2108
0
                a_attrinum);
2109
0
            if (retval != TSK_OK) {
2110
0
                tsk_error_errstr2_concat(" - proc_attrseq");
2111
0
                return retval;
2112
0
            }
2113
2114
            /* Determine the flags based on compression and stuff */
2115
0
            data_flag = 0;
2116
0
            if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_COMP) {
2117
0
                data_flag |= TSK_FS_ATTR_COMP;
2118
0
                fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (fs_file->meta->flags | TSK_FS_META_FLAG_COMP);
2119
0
            }
2120
2121
0
            if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_ENC)
2122
0
                data_flag |= TSK_FS_ATTR_ENC;
2123
2124
0
            if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_SPAR)
2125
0
                data_flag |= TSK_FS_ATTR_SPARSE;
2126
2127
            /* SPECIAL CASE
2128
             * We are in non-res section, so we know this
2129
             * isn't $STD_INFO and $FNAME
2130
             *
2131
             * When we are processing a non-base entry, we may
2132
             * find an attribute with an id of 0 and it is an
2133
             * extension of a previous run (i.e. non-zero start VCN)
2134
             *
2135
             * We will lookup if we already have such an attribute
2136
             * and get its ID
2137
             *
2138
             * We could also check for a start_vcn if this does
2139
             * not fix the problem.
2140
             *
2141
             * NOTE: This should not be needed now that TSK assigns
2142
             * unique ID values to the extended attributes.
2143
             */
2144
0
            if (id_new == 0) {
2145
0
                int cnt, i;
2146
2147
                // cycle through the attributes
2148
0
                cnt = tsk_fs_file_attr_getsize(fs_file);
2149
0
                for (i = 0; i < cnt; i++) {
2150
2151
0
                    const TSK_FS_ATTR *fs_attr2 =
2152
0
                        tsk_fs_file_attr_get_idx(fs_file, i);
2153
0
                    if (!fs_attr2)
2154
0
                        continue;
2155
2156
                    /* We found an attribute with the same name and type */
2157
0
                    if (fs_attr2->type == type) {
2158
0
                        if (((name[0] == '\0') && (fs_attr2->name == NULL))
2159
0
                            || ((fs_attr2->name)
2160
0
                                && (strcmp(fs_attr2->name, name) == 0))) {
2161
0
                            id_new = fs_attr2->id;
2162
0
                            if (tsk_verbose)
2163
0
                                tsk_fprintf(stderr,
2164
0
                                    "ntfs_proc_attrseq: Updating id from 0 to %"
2165
0
                                    PRIu16 "\n", id_new);
2166
0
                            break;
2167
0
                        }
2168
0
                    }
2169
0
                }
2170
0
            }
2171
2172
            /* the compression unit size is stored in the header
2173
             * it is stored as the power of 2 (if it is not 0)
2174
             */
2175
0
            if (tsk_getu16(fs->endian, attr->c.nr.compusize) > 16) {
2176
                /* 64k is the maximum compression unit size */
2177
0
                tsk_error_reset();
2178
0
                tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
2179
0
                tsk_error_set_errstr("ntfs_proc_attrseq: Compression unit size 2^%d too large",
2180
0
                    tsk_getu16(fs->endian, attr->c.nr.compusize));
2181
0
                if (fs_attr_run) {
2182
0
                    tsk_fs_attr_run_free(fs_attr_run);
2183
0
                    fs_attr_run = NULL;
2184
0
                }
2185
0
                return TSK_COR;
2186
0
            }
2187
2188
0
            if (tsk_getu16(fs->endian, attr->c.nr.compusize) > 0) {
2189
0
                compsize =
2190
0
                    1 << (tsk_getu16(fs->endian, attr->c.nr.compusize));
2191
0
            }
2192
0
            else {
2193
0
                compsize = 0;
2194
                /* if this is 0, be sure to cancel out the COMP flag.
2195
                 * This occurs when we process an extended attribute
2196
                 * that has compressed data -- the attributes in the
2197
                 * latter MFT entries do not have compsize set.
2198
                 */
2199
0
                if (data_flag & TSK_FS_ATTR_COMP) {
2200
0
                    if (tsk_verbose)
2201
0
                        fprintf(stderr,
2202
0
                            "ntfs_proc_attrseq: Clearing compression setting for attribute %"
2203
0
                            PRIuINUM "-%d because compsize is 0\n",
2204
0
                            fs_file->meta->addr, type);
2205
0
                    data_flag &= ~TSK_FS_ATTR_COMP;
2206
0
                }
2207
0
            }
2208
2209
            /* Add the run to the list */
2210
            // see if this attribute has already been partially defined
2211
            // @@@ This is bad design, we are casting away the const...
2212
0
            fs_attr =
2213
0
                (TSK_FS_ATTR *) tsk_fs_attrlist_get_id(fs_file->meta->attr,
2214
0
                (TSK_FS_ATTR_TYPE_ENUM) type, id_new);
2215
0
            if (fs_attr == NULL) {
2216
0
                uint64_t ssize; // size
2217
0
                uint64_t alen;  // allocated length
2218
2219
0
                if ((fs_attr =
2220
0
                        tsk_fs_attrlist_getnew(fs_file->meta->attr,
2221
0
                            TSK_FS_ATTR_RES)) == NULL) {
2222
0
                    tsk_error_errstr2_concat(" - proc_attrseq: getnew");
2223
                    // JRB: Coverity found leak.
2224
0
                    if (fs_attr_run) {
2225
0
                        tsk_fs_attr_run_free(fs_attr_run);
2226
0
                        fs_attr_run = NULL;
2227
0
                    }
2228
0
                    return TSK_ERR;
2229
0
                }
2230
2231
0
                ssize = tsk_getu64(fs->endian, attr->c.nr.ssize);
2232
                /* This can happen with extended attributes, so
2233
                 * we set it based on what we currently have.
2234
                 * fs_attr_run can be NULL for $BadClust file. */
2235
0
                if ((ssize == 0) && (fs_attr_run)) {
2236
0
                    TSK_FS_ATTR_RUN *fs_attr_run_tmp;
2237
2238
0
                    ssize = fs_attr_run->offset * fs->block_size;
2239
0
                    fs_attr_run_tmp = fs_attr_run;
2240
0
                    while (fs_attr_run_tmp) {
2241
0
                        ssize += (fs_attr_run_tmp->len * fs->block_size);
2242
0
                        fs_attr_run_tmp = fs_attr_run_tmp->next;
2243
0
                    }
2244
0
                }
2245
2246
                // update the meta->size value if this is the default $Data attribute
2247
0
                if ((fs_file->meta->type == TSK_FS_META_TYPE_REG)
2248
0
                    && (type == NTFS_ATYPE_DATA) && (name[0] == '\0')) {
2249
0
                    fs_file->meta->size = ssize;
2250
0
                }
2251
2252
0
                alen = tsk_getu64(fs->endian, attr->c.nr.alen);
2253
                /* This can also happen with extended attributes.
2254
                 * set it to what we know about */
2255
0
                if (alen == 0) {
2256
0
                    alen = ssize;
2257
0
                }
2258
2259
0
                if (tsk_fs_attr_set_run(fs_file, fs_attr,
2260
0
                        fs_attr_run, name,
2261
0
                        (TSK_FS_ATTR_TYPE_ENUM) type, id_new, ssize,
2262
0
                        tsk_getu64(fs->endian, attr->c.nr.initsize),
2263
0
                        alen, (TSK_FS_ATTR_FLAG_ENUM) data_flag, compsize)) {
2264
0
                    tsk_error_errstr2_concat("- proc_attrseq: set run");
2265
2266
                    // If the run wasn't saved to the attribute, free it now
2267
0
                    if (fs_attr_run && (fs_attr->nrd.run == NULL)) {
2268
0
                        tsk_fs_attr_run_free(fs_attr_run);
2269
0
                        fs_attr_run = NULL;
2270
0
                    }
2271
0
                    return TSK_COR;
2272
0
                }
2273
                // fs_file has taken over management of fs_attr_run
2274
0
                fs_attr_run = NULL;
2275
2276
                // set the special functions
2277
0
                if (fs_file->meta->flags & TSK_FS_META_FLAG_COMP) {
2278
0
                    fs_attr->w = ntfs_attr_walk_special;
2279
0
                    fs_attr->r = ntfs_file_read_special;
2280
0
                }
2281
2282
0
            }
2283
0
            else {
2284
0
                if (tsk_fs_attr_add_run(fs, fs_attr, fs_attr_run)) {
2285
0
                    tsk_error_errstr2_concat(" - proc_attrseq: put run");
2286
0
                    if (fs_attr_run) {
2287
0
                        tsk_fs_attr_run_free(fs_attr_run);
2288
0
                        fs_attr_run = NULL;
2289
0
                    }
2290
0
                    return TSK_COR;
2291
0
                }
2292
0
            }
2293
0
        }
2294
2295
        /*
2296
         * Special Cases, where we grab additional information
2297
         * regardless if they are resident or not
2298
         */
2299
2300
        /* Standard Information (is always resident) */
2301
0
        if (type == NTFS_ATYPE_SI) {
2302
0
            uint32_t attr_len = tsk_getu32(fs->endian, attr->len);
2303
0
            uint16_t attr_off = tsk_getu16(fs->endian, attr->c.r.soff);
2304
2305
0
            if (attr->res != NTFS_MFT_RES) {
2306
0
                tsk_error_reset();
2307
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2308
0
                tsk_error_set_errstr
2309
0
                    ("proc_attrseq: Standard Information Attribute is not resident!");
2310
0
                return TSK_COR;
2311
0
            }
2312
0
            if ((attr_off < 16) || (attr_off >= attr_len)) {
2313
0
                tsk_error_reset();
2314
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2315
0
                tsk_error_set_errstr
2316
0
                    ("proc_attrseq: resident data offset of Standard Information Attribute is out of bounds!");
2317
0
                return TSK_COR;
2318
0
            }
2319
            // A Standard Information Attribute can be 48 or 72 bytes in size (ntfs_attr_si is 72)
2320
0
            if ((attr_len < 48) || (attr_off > attr_len - 48)) {
2321
0
                tsk_error_reset();
2322
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2323
0
                tsk_error_set_errstr
2324
0
                    ("proc_attrseq: resident data of Standard Information Attribute is too small!");
2325
0
                return TSK_COR;
2326
0
            }
2327
0
            ntfs_attr_si *si = (ntfs_attr_si *) ((uintptr_t) attr + attr_off);
2328
2329
0
            fs_file->meta->mtime =
2330
0
                nt2unixtime(tsk_getu64(fs->endian, si->mtime));
2331
0
            fs_file->meta->mtime_nano =
2332
0
                nt2nano(tsk_getu64(fs->endian, si->mtime));
2333
2334
0
            fs_file->meta->atime =
2335
0
                nt2unixtime(tsk_getu64(fs->endian, si->atime));
2336
0
            fs_file->meta->atime_nano =
2337
0
                nt2nano(tsk_getu64(fs->endian, si->atime));
2338
2339
0
            fs_file->meta->ctime =
2340
0
                nt2unixtime(tsk_getu64(fs->endian, si->ctime));
2341
0
            fs_file->meta->ctime_nano =
2342
0
                nt2nano(tsk_getu64(fs->endian, si->ctime));
2343
2344
0
            fs_file->meta->crtime =
2345
0
                nt2unixtime(tsk_getu64(fs->endian, si->crtime));
2346
0
            fs_file->meta->crtime_nano =
2347
0
                nt2nano(tsk_getu64(fs->endian, si->crtime));
2348
2349
0
            fs_file->meta->uid = tsk_getu32(fs->endian, si->own_id);
2350
0
            fs_file->meta->mode = (TSK_FS_META_MODE_ENUM)
2351
0
                (fs_file->meta->mode | TSK_FS_META_MODE_IXUSR |
2352
0
                 TSK_FS_META_MODE_IXGRP | TSK_FS_META_MODE_IXOTH);
2353
0
            if ((tsk_getu32(fs->endian, si->dos) & NTFS_SI_RO) == 0)
2354
0
                fs_file->meta->mode = (TSK_FS_META_MODE_ENUM)
2355
0
                    (fs_file->meta->mode | TSK_FS_META_MODE_IRUSR |
2356
0
                    TSK_FS_META_MODE_IRGRP | TSK_FS_META_MODE_IROTH);
2357
0
            if ((tsk_getu32(fs->endian, si->dos) & NTFS_SI_HID) == 0)
2358
0
                fs_file->meta->mode = (TSK_FS_META_MODE_ENUM)
2359
0
                    (fs_file->meta->mode | TSK_FS_META_MODE_IWUSR |
2360
0
                    TSK_FS_META_MODE_IWGRP | TSK_FS_META_MODE_IWOTH);
2361
0
        }
2362
2363
        /* File Name (always resident) */
2364
0
        else if (type == NTFS_ATYPE_FNAME) {
2365
0
            uint32_t attr_len = tsk_getu32(fs->endian, attr->len);
2366
0
            uint16_t attr_off = tsk_getu16(fs->endian, attr->c.r.soff);
2367
2368
0
            if (attr->res != NTFS_MFT_RES) {
2369
0
                tsk_error_reset();
2370
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2371
0
                tsk_error_set_errstr
2372
0
                    ("proc_attr_seq: File Name Attribute is not resident!");
2373
0
                return TSK_COR;
2374
0
            }
2375
0
            if ((attr_off < 16) || (attr_off >= attr_len)) {
2376
0
                tsk_error_reset();
2377
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2378
0
                tsk_error_set_errstr
2379
0
                    ("proc_attrseq: resident data offset of File Name Attribute is out of bounds!");
2380
0
                return TSK_COR;
2381
0
            }
2382
            // A File Name Attribute must be large enough to hold the fixed fields.
2383
0
            if (attr_off + (uint32_t)offsetof(ntfs_attr_fname, name) > attr_len) {
2384
0
                tsk_error_reset();
2385
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2386
0
                tsk_error_set_errstr
2387
0
                    ("proc_attrseq: resident data of File Name Attribute is too small!");
2388
0
                return TSK_COR;
2389
0
            }
2390
0
            ntfs_attr_fname *fname = (ntfs_attr_fname *) ((uintptr_t) attr + attr_off);
2391
0
            if (fname->nspace == NTFS_FNAME_DOS) {
2392
0
                continue;
2393
0
            }
2394
2395
0
            fs_file->meta->time2.ntfs.fn_mtime =
2396
0
                nt2unixtime(tsk_getu64(fs->endian, fname->mtime));
2397
0
            fs_file->meta->time2.ntfs.fn_mtime_nano =
2398
0
                nt2nano(tsk_getu64(fs->endian, fname->mtime));
2399
2400
0
            fs_file->meta->time2.ntfs.fn_atime =
2401
0
                nt2unixtime(tsk_getu64(fs->endian, fname->atime));
2402
0
            fs_file->meta->time2.ntfs.fn_atime_nano =
2403
0
                nt2nano(tsk_getu64(fs->endian, fname->atime));
2404
2405
0
            fs_file->meta->time2.ntfs.fn_ctime =
2406
0
                nt2unixtime(tsk_getu64(fs->endian, fname->ctime));
2407
0
            fs_file->meta->time2.ntfs.fn_ctime_nano =
2408
0
                nt2nano(tsk_getu64(fs->endian, fname->ctime));
2409
2410
0
            fs_file->meta->time2.ntfs.fn_crtime =
2411
0
                nt2unixtime(tsk_getu64(fs->endian, fname->crtime));
2412
0
            fs_file->meta->time2.ntfs.fn_crtime_nano =
2413
0
                nt2nano(tsk_getu64(fs->endian, fname->crtime));
2414
2415
0
            fs_file->meta->time2.ntfs.fn_id = id;
2416
2417
0
            TSK_FS_META_NAME_LIST *fs_name;
2418
2419
            /* Seek to the end of the fs_name structures in TSK_FS_META */
2420
0
            if (fs_file->meta->name2) {
2421
0
                for (fs_name = fs_file->meta->name2;
2422
0
                    (fs_name) && (fs_name->next != NULL);
2423
0
                    fs_name = fs_name->next) {
2424
0
                }
2425
2426
                /* add to the end of the existing list */
2427
0
                fs_name->next = (TSK_FS_META_NAME_LIST *)
2428
0
                    tsk_malloc(sizeof(TSK_FS_META_NAME_LIST));
2429
0
                if (fs_name->next == NULL) {
2430
0
                    return TSK_ERR;
2431
0
                }
2432
0
                fs_name = fs_name->next;
2433
0
                fs_name->next = NULL;
2434
0
            }
2435
0
            else {
2436
                /* First name, so we start a list */
2437
0
                fs_file->meta->name2 = fs_name = (TSK_FS_META_NAME_LIST *)
2438
0
                    tsk_malloc(sizeof(TSK_FS_META_NAME_LIST));
2439
0
                if (fs_name == NULL) {
2440
0
                    return TSK_ERR;
2441
0
                }
2442
0
                fs_name->next = NULL;
2443
0
            }
2444
0
      if (attr_off + (uint32_t)offsetof(ntfs_attr_fname, name) + (uint32_t)fname->nlen * 2 > attr_len) {
2445
0
                tsk_error_reset();
2446
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2447
0
                tsk_error_set_errstr
2448
0
                    ("proc_attrseq: invalid name value size out of bounds!");
2449
0
                return TSK_COR;
2450
0
            }
2451
0
            UTF16 *name16 = (UTF16 *) & fname->name;
2452
0
            UTF8 *name8 = (UTF8 *) fs_name->name;
2453
2454
0
            retVal =
2455
0
                tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
2456
0
                (UTF16 *) ((uintptr_t) name16 +
2457
0
                    fname->nlen * 2),
2458
0
                &name8,
2459
0
                (UTF8 *) ((uintptr_t) name8 +
2460
0
                    sizeof(fs_name->name)), TSKlenientConversion);
2461
0
            if (retVal != TSKconversionOK) {
2462
0
                if (tsk_verbose)
2463
0
                    tsk_fprintf(stderr,
2464
0
                        "proc_attr_seq: Error converting NTFS name in $FNAME to UTF8: %d",
2465
0
                        retVal);
2466
0
                *name8 = '\0';
2467
0
            }
2468
            /* Make sure it is NULL Terminated */
2469
0
            else if ((uintptr_t) name8 >=
2470
0
                (uintptr_t) fs_name->name + sizeof(fs_name->name))
2471
0
                fs_name->name[sizeof(fs_name->name) - 1] = '\0';
2472
0
            else
2473
0
                *name8 = '\0';
2474
2475
0
            fs_name->par_inode = tsk_getu48(fs->endian, fname->par_ref);
2476
0
            fs_name->par_seq = tsk_getu16(fs->endian, fname->par_seq);
2477
0
        }
2478
2479
        /* If this is an attribute list than we need to process
2480
         * it to get the list of other entries to read.  But, because
2481
         * of the wierd scenario of the $MFT having an attribute list
2482
         * and not knowing where the other MFT entires are yet, we wait
2483
         * until the end of the attrseq to processes the list and then
2484
         * we should have the $Data attribute loaded
2485
         */
2486
0
        else if (type == NTFS_ATYPE_ATTRLIST) {
2487
0
            if (fs_attr_attrl) {
2488
0
                tsk_error_reset();
2489
0
                tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2490
0
                tsk_error_set_errstr
2491
0
                    ("Multiple instances of attribute lists in the same MFT\n"
2492
0
                    "I didn't realize that could happen, contact the developers");
2493
0
                return TSK_ERR;
2494
0
            }
2495
0
            fs_attr_attrl = tsk_fs_attrlist_get_id(fs_file->meta->attr,
2496
0
                (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_ATTRLIST, id_new);
2497
0
            if (fs_attr_attrl == NULL) {
2498
0
                tsk_error_errstr2_concat
2499
0
                    ("- proc_attrseq: getting attribute list");
2500
0
                return TSK_ERR;
2501
0
            }
2502
0
        }
2503
0
    }
2504
2505
2506
    /* Are we currently in the process of loading $MFT? */
2507
0
    if (ntfs->loading_the_MFT == 1) {
2508
2509
        /* If we don't even have a mini cached version, get it now
2510
         * Even if we are not done because of attribute lists, then we
2511
         * should at least have the head of the list
2512
         */
2513
0
        if (!ntfs->mft_data) {
2514
0
            int cnt, i;
2515
2516
            // cycle through the attributes
2517
0
            cnt = tsk_fs_file_attr_getsize(fs_file);
2518
0
            for (i = 0; i < cnt; i++) {
2519
0
                const TSK_FS_ATTR *fs_attr =
2520
0
                    tsk_fs_file_attr_get_idx(fs_file, i);
2521
0
                if (!fs_attr)
2522
0
                    continue;
2523
2524
                // get the default attribute
2525
0
                if ((fs_attr->type == NTFS_ATYPE_DATA) &&
2526
0
                    (fs_attr->name == NULL)) {
2527
0
                    ntfs->mft_data = fs_attr;
2528
0
                    break;
2529
0
                }
2530
0
            }
2531
2532
            // @@@ Is this needed here -- maybe it should be only in _open
2533
0
            if (!ntfs->mft_data) {
2534
0
                tsk_error_reset();
2535
0
                tsk_error_set_errno(TSK_ERR_FS_GENFS);
2536
0
                tsk_error_set_errstr
2537
0
                    ("$Data not found while loading the MFT");
2538
0
                return TSK_ERR;
2539
0
            }
2540
0
        }
2541
2542
        /* Update the inode count based on the current size
2543
         * IF $MFT has an attribute list, this value will increase each
2544
         * time
2545
         */
2546
0
        fs->inum_count = ntfs->mft_data->size / ntfs->mft_rsize_b;
2547
0
        fs->last_inum = fs->inum_count - 1;
2548
0
    }
2549
2550
    /* If there was an attribute list, process it now, we wait because
2551
     * the list can contain MFT entries that are described in $Data
2552
     * of this MFT entry.  For example, part of the $DATA attribute
2553
     * could follow the ATTRLIST entry, so we read it first and then
2554
     * process the attribute list
2555
     */
2556
0
    if (fs_attr_attrl) {
2557
0
    TSK_RETVAL_ENUM retval;
2558
0
        if (a_seen_inum_list != NULL) {
2559
0
            tsk_stack_push(a_seen_inum_list, a_attrinum);
2560
0
        }
2561
0
        if ((retval = ntfs_proc_attrlist(ntfs, fs_file, fs_attr_attrl, a_seen_inum_list)) != TSK_OK) {
2562
0
            return retval;
2563
0
        }
2564
0
    }
2565
2566
0
    fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED;
2567
0
    return TSK_OK;
2568
0
}
2569
2570
2571
2572
/********   Attribute List Action and Function ***********/
2573
2574
2575
2576
/*
2577
 * Attribute lists are used when all of the attribute  headers can not
2578
 * fit into one MFT entry.  This contains an entry for every attribute
2579
 * and where they are located.  We process this to get the locations
2580
 * and then call proc_attrseq on each of those, which adds the data
2581
 * to the fs_file structure.
2582
 *
2583
 * @param ntfs File system being analyzed
2584
 * @param fs_file Main file that will have attributes added to it.
2585
 * @param fs_attr_attrlist Attrlist attribute that needs to be parsed.
2586
 * @param a_seen_inum_list List of MFT entries (inums) previously
2587
 * processed for this file or NULL.
2588
 *
2589
 * @returns status of error, corrupt, or OK
2590
 */
2591
static TSK_RETVAL_ENUM
2592
ntfs_proc_attrlist(NTFS_INFO * ntfs,
2593
    TSK_FS_FILE * fs_file, const TSK_FS_ATTR * fs_attr_attrlist, TSK_STACK * processed_inum_list)
2594
0
{
2595
0
    ntfs_attrlist *list;
2596
0
    char *buf;
2597
0
    uintptr_t endaddr;
2598
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
2599
0
    ntfs_mft *mft;
2600
0
    TSK_FS_LOAD_FILE load_file;
2601
0
    TSK_INUM_T mftToDo[256];
2602
0
    uint16_t mftToDoCnt = 0;
2603
0
    NTFS_ATTRLIST_MAP *map;
2604
0
    uint16_t nextid = 0;
2605
0
    TSK_STACK * mftSeenList = NULL;
2606
0
    int a;
2607
2608
0
    if (tsk_verbose)
2609
0
        tsk_fprintf(stderr,
2610
0
            "ntfs_proc_attrlist: Processing entry %"
2611
0
            PRIuINUM "\n", fs_file->meta->addr);
2612
2613
0
    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
2614
0
        return TSK_ERR;
2615
0
    }
2616
2617
0
    if ((map =
2618
0
            (NTFS_ATTRLIST_MAP *) tsk_malloc(sizeof(NTFS_ATTRLIST_MAP))) ==
2619
0
        NULL) {
2620
0
        free(mft);
2621
0
        return TSK_ERR;
2622
0
    }
2623
2624
    /* Clear the contents of the todo buffer */
2625
0
    memset(mftToDo, 0, sizeof(mftToDo));
2626
2627
    /* Get a copy of the attribute list stream using the above action */
2628
0
    load_file.left = load_file.total = (size_t) fs_attr_attrlist->size;
2629
0
    load_file.base = load_file.cur = buf =
2630
0
        (char*) tsk_malloc((size_t) fs_attr_attrlist->size);
2631
0
    if (buf == NULL) {
2632
0
        free(mft);
2633
0
        free(map);
2634
0
        return TSK_ERR;
2635
0
    }
2636
0
    endaddr = (uintptr_t) buf + (uintptr_t) fs_attr_attrlist->size;
2637
0
    if (tsk_fs_attr_walk(fs_attr_attrlist, TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_load_file_action,
2638
0
            (void *) &load_file)) {
2639
0
        tsk_error_errstr2_concat("- processing attrlist");
2640
0
        free(mft);
2641
0
        free(buf);
2642
0
        free(map);
2643
0
        return TSK_ERR;
2644
0
    }
2645
2646
    /* this value should be zero, if not then we didn't read all of the
2647
     * buffer
2648
     */
2649
0
    if (load_file.left > 0) {
2650
0
        tsk_error_reset();
2651
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
2652
0
        tsk_error_set_errstr2("processing attrlist of entry %" PRIuINUM,
2653
0
            fs_file->meta->addr);
2654
0
        free(mft);
2655
0
        free(buf);
2656
0
        free(map);
2657
0
        return TSK_ERR;
2658
0
    }
2659
2660
    /* The TSK design requires that each attribute have its own ID.
2661
     * Therefore, we need to identify all of the unique attributes
2662
     * so that we can assign a unique ID to them.
2663
     * In this process, we will also identify the unique MFT entries to
2664
     * process. */
2665
0
    nextid = fs_attr_attrlist->id;      // we won't see this entry in the list
2666
0
    for (list = (ntfs_attrlist *) buf;
2667
0
        (list)
2668
        // ntfs_attrlist contains the first byte of the name, which might actually be 0-length
2669
0
        && (uintptr_t) list + sizeof(ntfs_attrlist) - 1 <= endaddr
2670
0
        && tsk_getu16(fs->endian, list->len) > 0
2671
0
        && (uintptr_t) list + tsk_getu16(fs->endian, list->len) <= endaddr
2672
0
        && (uintptr_t) list + sizeof(ntfs_attrlist) - 1 + 2 * list->nlen <= endaddr;
2673
0
        list =
2674
0
        (ntfs_attrlist *) ((uintptr_t) list + tsk_getu16(fs->endian,
2675
0
                list->len))) {
2676
0
        uint8_t found;
2677
0
        int i;
2678
2679
0
        TSK_INUM_T mftnum = tsk_getu48(fs->endian, list->file_ref);
2680
0
        uint32_t type = tsk_getu32(fs->endian, list->type);
2681
0
        uint16_t id = tsk_getu16(fs->endian, list->id);
2682
2683
0
        if (tsk_verbose)
2684
0
            tsk_fprintf(stderr,
2685
0
                "ntfs_proc_attrlist: mft: %" PRIuINUM
2686
0
                " type %" PRIu32 " id %" PRIu16
2687
0
                "  VCN: %" PRIu64 "\n", mftnum, type,
2688
0
                id, tsk_getu64(fs->endian, list->start_vcn));
2689
2690
2691
        // keep track of the biggest ID that we saw.
2692
0
        if (id > nextid)
2693
0
            nextid = id;
2694
2695
        /* First identify the unique attributes.
2696
         * we can have duplicate entries at different VCNs.  Ignore those. */
2697
0
        found = 0;
2698
0
        for (i = 0; i < map->num_used; i++) {
2699
0
            if ((map->type[i] == type)
2700
0
                && (memcmp(map->name[i], &list->name,
2701
0
                        list->nlen * 2) == 0)) {
2702
0
                found = 1;
2703
0
                break;
2704
0
            }
2705
0
        }
2706
2707
        // add it to the list
2708
0
        if (found == 0) {
2709
0
            map->extMft[map->num_used] = mftnum;
2710
0
            map->type[map->num_used] = type;
2711
0
            map->extId[map->num_used] = id;
2712
0
            memcpy(map->name[map->num_used], &list->name, list->nlen * 2);
2713
0
            if (map->num_used < 255)
2714
0
                map->num_used++;
2715
0
        }
2716
2717
        /* also check the todo list -- skip the base entry
2718
         * the goal here is to get a unique list of MFT entries
2719
         * to later process. */
2720
0
        if (mftnum != fs_file->meta->addr) {
2721
0
            found = 0;
2722
0
            for (i = 0; i < mftToDoCnt; i++) {
2723
0
                if (mftToDo[i] == mftnum) {
2724
0
                    found = 1;
2725
0
                    break;
2726
0
                }
2727
0
            }
2728
0
            if ((found == 0) && (mftToDoCnt < 256)) {
2729
0
                mftToDo[mftToDoCnt++] = mftnum;
2730
0
            }
2731
0
        }
2732
0
    }
2733
2734
    // update the map and assign unique IDs
2735
0
    for (a = 0; a < map->num_used; a++) {
2736
        // skip the base entry attributes -- they have unique attribute IDs
2737
0
        if (map->extMft[a] == fs_file->meta->addr)
2738
0
            continue;
2739
0
        map->newId[a] = ++nextid;
2740
0
    }
2741
2742
2743
    /* Process the ToDo list & and call ntfs_proc_attr */
2744
0
    for (a = 0; a < mftToDoCnt; a++) {
2745
0
        TSK_RETVAL_ENUM retval;
2746
2747
        /* Sanity check. */
2748
0
        if (mftToDo[a] < ntfs->fs_info.first_inum ||
2749
            // decrement the last_inum because the last value is a special value for the ORPHANS directory
2750
0
            mftToDo[a] > ntfs->fs_info.last_inum - 1 ||
2751
            // MFT 0 is for $MFT.  We had one system that we got a reference to it from parsing an allocated attribute list
2752
0
            mftToDo[a] == 0) {
2753
2754
0
            if (tsk_verbose) {
2755
                /* this case can easily occur if the attribute list was non-resident and the cluster has been reallocated */
2756
2757
0
                tsk_fprintf(stderr,
2758
0
                    "Invalid MFT file reference (%"
2759
0
                    PRIuINUM
2760
0
                    ") in the unallocated attribute list of MFT %"
2761
0
                    PRIuINUM "", mftToDo[a], fs_file->meta->addr);
2762
0
            }
2763
0
            continue;
2764
0
        }
2765
2766
0
        if ((retval =
2767
0
                ntfs_dinode_lookup(ntfs, (char *) mft,
2768
0
                    mftToDo[a], 0)) != TSK_OK) {
2769
            // if the entry is corrupt, then continue
2770
0
            if (retval == TSK_COR) {
2771
0
                if (tsk_verbose)
2772
0
                    tsk_error_print(stderr);
2773
0
                tsk_error_reset();
2774
0
                continue;
2775
0
            }
2776
2777
0
            free(mft);
2778
0
            free(map);
2779
0
            free(buf);
2780
0
            if (mftSeenList != NULL)
2781
0
                tsk_stack_free(mftSeenList);
2782
0
            tsk_error_errstr2_concat(" - proc_attrlist");
2783
0
            return TSK_ERR;
2784
0
        }
2785
2786
        /* verify that this entry refers to the original one */
2787
0
        if (tsk_getu48(fs->endian, mft->base_ref) != fs_file->meta->addr) {
2788
2789
            /* Before we raise alarms, check if the original was
2790
             * unallocated.  If so, then the list entry could
2791
             * have been reallocated, so we will just ignore it
2792
             */
2793
0
            if (((tsk_getu16(fs->endian,
2794
0
                            mft->flags) & NTFS_MFT_INUSE) == 0)
2795
0
                || (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) {
2796
0
                continue;
2797
0
            }
2798
0
            else {
2799
0
                tsk_error_reset();
2800
0
                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
2801
0
                tsk_error_set_errstr("ntfs_proc_attrlist: MFT %" PRIuINUM
2802
0
                    " is not an attribute list for %"
2803
0
                    PRIuINUM
2804
0
                    " (base file ref = %" PRIuINUM ")",
2805
0
                    mftToDo[a],
2806
0
                    fs_file->meta->addr,
2807
0
                    tsk_getu48(fs->endian, mft->base_ref));
2808
0
                free(mft);
2809
0
                free(map);
2810
0
                free(buf);
2811
0
                if (mftSeenList != NULL)
2812
0
                    tsk_stack_free(mftSeenList);
2813
0
                return TSK_COR;
2814
0
            }
2815
0
        }
2816
2817
        // bounds check
2818
0
        if (tsk_getu16(fs->endian, mft->attr_off) > ntfs->mft_rsize_b) {
2819
0
            if (tsk_verbose)
2820
0
                    tsk_fprintf(stderr, "ntfs_proc_attrlist: corrupt MFT entry attribute offsets\n");
2821
0
            continue;
2822
0
        }
2823
2824
        /* Process the attribute seq for this MFT entry and add them
2825
         * to the TSK_FS_META structure
2826
         */
2827
0
        if (processed_inum_list != NULL && tsk_stack_find(processed_inum_list, mftToDo[a])) {
2828
0
            tsk_error_reset();
2829
0
            tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
2830
0
            tsk_error_set_errstr("ntfs_proc_attrlist: MFT %" PRIuINUM
2831
0
                " seen in more than one attribute list for %"
2832
0
                PRIuINUM
2833
0
                " (base file ref = %" PRIuINUM ")",
2834
0
                mftToDo[a],
2835
0
                fs_file->meta->addr,
2836
0
                tsk_getu48(fs->endian, mft->base_ref));
2837
0
            free(mft);
2838
0
            free(map);
2839
0
            free(buf);
2840
0
            if (mftSeenList != NULL)
2841
0
                tsk_stack_free(mftSeenList);
2842
0
            return TSK_COR;
2843
0
        }
2844
2845
0
        if (processed_inum_list == NULL) {
2846
            /*
2847
             * Create a stack to keep track of inums already seen.
2848
             * The local mftSeenList variable is used to keep track
2849
             * of which iteration created the stack so that it can
2850
             * be correctly freed later.
2851
             */
2852
0
            processed_inum_list = mftSeenList = tsk_stack_create();
2853
0
        }
2854
2855
0
        if ((retval =
2856
0
                ntfs_proc_attrseq(ntfs, fs_file, (ntfs_attr *) ((uintptr_t)
2857
0
                        mft + tsk_getu16(fs->endian, mft->attr_off)),
2858
0
                    ntfs->mft_rsize_b - tsk_getu16(fs->endian,
2859
0
                        mft->attr_off), mftToDo[a], map, processed_inum_list)) != TSK_OK) {
2860
2861
0
            if (retval == TSK_COR) {
2862
0
                if (tsk_verbose)
2863
0
                    tsk_error_print(stderr);
2864
0
                tsk_error_reset();
2865
0
                continue;
2866
0
            }
2867
0
            tsk_error_errstr2_concat("- proc_attrlist");
2868
0
            free(mft);
2869
0
            free(map);
2870
0
            free(buf);
2871
0
            if (mftSeenList != NULL)
2872
0
                tsk_stack_free(mftSeenList);
2873
0
            return TSK_ERR;
2874
0
        }
2875
0
    }
2876
2877
0
    free(mft);
2878
0
    free(map);
2879
0
    free(buf);
2880
0
    if (mftSeenList != NULL)
2881
0
        tsk_stack_free(mftSeenList);
2882
0
    return TSK_OK;
2883
0
}
2884
2885
2886
2887
/**
2888
 * Copy the MFT entry saved in a_buf to the generic structure.
2889
 *
2890
 * @param ntfs File system structure that contains entry to copy
2891
 * @param fs_file Structure to copy processed data to.
2892
 * @param a_buf MFT structure to copy from. Must be of size NTFS_INFO.mft_rsize_b
2893
 * @param a_mnum MFT entry address
2894
 *
2895
 * @returns error code
2896
 */
2897
static TSK_RETVAL_ENUM
2898
ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file, char *a_buf,
2899
    TSK_INUM_T a_mnum)
2900
0
{
2901
0
    ntfs_attr *attr;
2902
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
2903
0
    TSK_RETVAL_ENUM retval;
2904
0
    ntfs_mft *mft = (ntfs_mft *) a_buf;
2905
2906
0
    if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) {
2907
0
        tsk_error_reset();
2908
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2909
0
        tsk_error_set_errstr("ntfs_dinode_copy: NULL fs_file given");
2910
0
        return TSK_ERR;
2911
0
    }
2912
2913
    /* if the attributes list has been used previously, then make sure the
2914
     * flags are cleared
2915
     */
2916
0
    if (a_fs_file->meta->attr) {
2917
0
        tsk_fs_attrlist_markunused(a_fs_file->meta->attr);
2918
0
    }
2919
0
    else {
2920
0
        a_fs_file->meta->attr = tsk_fs_attrlist_alloc();
2921
0
        if (a_fs_file->meta->attr == NULL)
2922
0
            return TSK_ERR;
2923
0
    }
2924
0
    a_fs_file->meta->attr_state = TSK_FS_META_ATTR_EMPTY;
2925
2926
    /* If there are any name structures allocated, then free 'em */
2927
0
    if (a_fs_file->meta->name2) {
2928
0
        TSK_FS_META_NAME_LIST *fs_name1, *fs_name2;
2929
0
        fs_name1 = a_fs_file->meta->name2;
2930
2931
0
        while (fs_name1) {
2932
0
            fs_name2 = fs_name1->next;
2933
0
            free(fs_name1);
2934
0
            fs_name1 = fs_name2;
2935
0
        }
2936
0
        a_fs_file->meta->name2 = NULL;
2937
0
    }
2938
2939
    /* Set the a_fs_file->meta values from mft */
2940
0
    a_fs_file->meta->nlink = tsk_getu16(fs->endian, mft->link);
2941
0
    a_fs_file->meta->seq = tsk_getu16(fs->endian, mft->seq);
2942
0
    a_fs_file->meta->addr = a_mnum;
2943
2944
    /* Set the mode for file or directory */
2945
0
    if (tsk_getu16(fs->endian, mft->flags) & NTFS_MFT_DIR)
2946
0
        a_fs_file->meta->type = TSK_FS_META_TYPE_DIR;
2947
0
    else
2948
0
        a_fs_file->meta->type = TSK_FS_META_TYPE_REG;
2949
0
    a_fs_file->meta->mode = TSK_FS_META_MODE_UNSPECIFIED;  // will be set by proc_attrseq
2950
2951
    /* the following will be changed once we find the correct attribute,
2952
     * but initialize them now just in case
2953
     */
2954
0
    a_fs_file->meta->uid = 0;
2955
0
    a_fs_file->meta->gid = 0;
2956
0
    a_fs_file->meta->size = 0;
2957
0
    a_fs_file->meta->mtime = 0;
2958
0
    a_fs_file->meta->mtime_nano = 0;
2959
0
    a_fs_file->meta->atime = 0;
2960
0
    a_fs_file->meta->atime_nano = 0;
2961
0
    a_fs_file->meta->ctime = 0;
2962
0
    a_fs_file->meta->ctime_nano = 0;
2963
0
    a_fs_file->meta->crtime = 0;
2964
0
    a_fs_file->meta->crtime_nano = 0;
2965
0
    a_fs_file->meta->time2.ntfs.fn_mtime = 0;
2966
0
    a_fs_file->meta->time2.ntfs.fn_mtime_nano = 0;
2967
0
    a_fs_file->meta->time2.ntfs.fn_atime = 0;
2968
0
    a_fs_file->meta->time2.ntfs.fn_atime_nano = 0;
2969
0
    a_fs_file->meta->time2.ntfs.fn_ctime = 0;
2970
0
    a_fs_file->meta->time2.ntfs.fn_ctime_nano = 0;
2971
0
    a_fs_file->meta->time2.ntfs.fn_crtime = 0;
2972
0
    a_fs_file->meta->time2.ntfs.fn_crtime_nano = 0;
2973
0
    a_fs_file->meta->time2.ntfs.fn_id = 0;
2974
2975
    /* add the flags */
2976
0
    a_fs_file->meta->flags =
2977
0
        ((tsk_getu16(fs->endian, mft->flags) &
2978
0
            NTFS_MFT_INUSE) ? TSK_FS_META_FLAG_ALLOC :
2979
0
        TSK_FS_META_FLAG_UNALLOC);
2980
2981
2982
    /* Process the attribute sequence to fill in the fs_meta->attr
2983
     * list and the other info such as size and times
2984
     */
2985
0
    if (tsk_getu16(fs->endian, mft->attr_off) > ntfs->mft_rsize_b) {
2986
0
        tsk_error_reset();
2987
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2988
0
        tsk_error_set_errstr("ntfs_dinode_copy: corrupt MFT entry attribute offsets");
2989
0
        return TSK_ERR;
2990
0
    }
2991
2992
0
    attr =
2993
0
        (ntfs_attr *) ((uintptr_t) mft + tsk_getu16(fs->endian,
2994
0
            mft->attr_off));
2995
0
    if ((retval = ntfs_proc_attrseq(ntfs, a_fs_file, attr,
2996
0
                ntfs->mft_rsize_b - tsk_getu16(fs->endian,
2997
0
                    mft->attr_off), a_fs_file->meta->addr,
2998
0
                NULL, NULL)) != TSK_OK) {
2999
0
        return retval;
3000
0
    }
3001
3002
    /* The entry has been 'used' if it has attributes */
3003
3004
0
    if ((a_fs_file->meta->attr == NULL)
3005
0
        || (a_fs_file->meta->attr->head == NULL)
3006
0
        || ((a_fs_file->meta->attr->head->flags & TSK_FS_ATTR_INUSE) == 0))
3007
0
        a_fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (a_fs_file->meta->flags | TSK_FS_META_FLAG_UNUSED);
3008
0
    else
3009
0
        a_fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (a_fs_file->meta->flags | TSK_FS_META_FLAG_USED);
3010
3011
0
    return TSK_OK;
3012
0
}
3013
3014
3015
3016
/** \internal
3017
 * Load the attributes.  In NTFS, the attributes are already loaded
3018
 * so return error values based on current state.
3019
 * @param a_fs_file File to load attributes for.
3020
 * @returns 1 on error
3021
 */
3022
static uint8_t
3023
ntfs_load_attrs(TSK_FS_FILE * a_fs_file)
3024
0
{
3025
0
    if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) {
3026
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3027
0
        tsk_error_set_errstr("ntfs_load_attrs: called with NULL pointers");
3028
0
        return 1;
3029
0
    }
3030
3031
    /* Verify the file has attributes */
3032
0
    if (a_fs_file->meta->attr == NULL) {
3033
0
        if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)
3034
0
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
3035
0
        else
3036
0
            tsk_error_set_errno(TSK_ERR_FS_ARG);
3037
0
        tsk_error_set_errstr("ntfs_load_attrs: attributes are NULL");
3038
0
        return 1;
3039
0
    }
3040
0
    return 0;
3041
0
}
3042
3043
/**
3044
 * Read an MFT entry and save it in the generic TSK_FS_META format.
3045
 *
3046
 * @param fs File system to read from.
3047
 * @param mftnum Address of mft entry to read
3048
 * @returns 1 on error
3049
 */
3050
static uint8_t
3051
ntfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
3052
    TSK_INUM_T mftnum)
3053
0
{
3054
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
3055
0
    char *mft;
3056
0
    uint8_t allocedMeta = 0;
3057
3058
    // clean up any error messages that are lying around
3059
0
    tsk_error_reset();
3060
3061
0
    if (a_fs_file == NULL) {
3062
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3063
0
        tsk_error_set_errstr("ntfs_inode_lookup: fs_file is NULL");
3064
0
        return 1;
3065
0
    }
3066
3067
0
    if (a_fs_file->meta == NULL) {
3068
0
        a_fs_file->meta = tsk_fs_meta_alloc(NTFS_FILE_CONTENT_LEN);
3069
0
        if (a_fs_file->meta == NULL)
3070
0
            return 1;
3071
0
        allocedMeta = 1;
3072
0
    }
3073
0
    else {
3074
0
        tsk_fs_meta_reset(a_fs_file->meta);
3075
0
    }
3076
3077
    // see if they are looking for the special "orphans" directory
3078
0
    if (mftnum == TSK_FS_ORPHANDIR_INUM(fs)) {
3079
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta))
3080
0
            return 1;
3081
0
        else
3082
0
            return 0;
3083
0
    }
3084
3085
0
    if ((mft = (char *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
3086
0
        return 1;
3087
0
    }
3088
3089
    /* Lookup inode and store it in the ntfs structure */
3090
0
    if (ntfs_dinode_lookup(ntfs, mft, mftnum, & (a_fs_file->meta->start_of_inode)) != TSK_OK) {
3091
0
        free(mft);
3092
0
        return 1;
3093
0
    }
3094
3095
    /* Copy the structure in ntfs to generic a_fs_file->meta */
3096
0
    if (ntfs_dinode_copy(ntfs, a_fs_file, mft, mftnum) != TSK_OK) {
3097
0
        free(mft);
3098
0
        return 1;
3099
0
    }
3100
3101
    /* Check if the metadata is the same sequence as the name - if it was already set.
3102
     * Note that this is not as efficient and elegant as desired, but works for now.
3103
     * Better design would be to pass sequence into dinode_lookup and have a more
3104
     * obvious way to pass the desired sequence in.  fs_dir_walk_lcl sets the name
3105
     * before calling this, which motivated this quick fix. */
3106
0
    if ((a_fs_file->name != NULL) && (a_fs_file->name->meta_addr == mftnum)) {
3107
3108
        /* NTFS Updates the sequence when an entry is deleted and not when
3109
         * it is allocated.  So, if we have a deleted MFT entry, then use
3110
         * its previous sequence number to compare with the name so that we
3111
         * still match them up (until the entry is allocated again). */
3112
0
        uint16_t seqToCmp = a_fs_file->meta->seq;
3113
0
        if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) {
3114
0
            if (a_fs_file->meta->seq > 0)
3115
0
                seqToCmp--;
3116
0
        }
3117
3118
0
        if (a_fs_file->name->meta_seq != seqToCmp) {
3119
0
            if (allocedMeta) {
3120
0
                tsk_fs_meta_close(a_fs_file->meta);
3121
0
                a_fs_file->meta = NULL;
3122
0
            }
3123
0
            else {
3124
0
                tsk_fs_meta_reset(a_fs_file->meta);
3125
0
            }
3126
0
        }
3127
0
    }
3128
3129
0
    free(mft);
3130
0
    return 0;
3131
0
}
3132
3133
3134
3135
3136
/**********************************************************************
3137
 *
3138
 *  Load special MFT structures into the NTFS_INFO structure
3139
 *
3140
 **********************************************************************/
3141
3142
/* The attrdef structure defines the types of attributes and gives a
3143
 * name value to the type number.
3144
 *
3145
 * We currently do not use this during the analysis (Because it has not
3146
 * historically changed, but we do display it in fsstat
3147
 *
3148
 * Return 1 on error and 0 on success
3149
 */
3150
static uint8_t
3151
ntfs_load_attrdef(NTFS_INFO * ntfs)
3152
0
{
3153
0
    const TSK_FS_ATTR *fs_attr;
3154
0
    TSK_FS_INFO *fs = &ntfs->fs_info;
3155
0
    TSK_FS_LOAD_FILE load_file;
3156
3157
    /* if already loaded, return now */
3158
0
    if (ntfs->attrdef)
3159
0
        return 1;
3160
3161
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
3162
0
        tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_ATTR),
3163
0
        tsk_fs_file_close
3164
0
    };
3165
3166
0
    if (!fs_file)
3167
0
        return 1;
3168
3169
0
    fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM
3170
0
) NTFS_ATYPE_DATA);
3171
0
    if (!fs_attr) {
3172
        //("Data attribute not found in $Attr");
3173
0
        return 1;
3174
0
    }
3175
3176
// @@@ We need to do a sanity check on the size of fs_attr->size
3177
3178
    /* Get a copy of the attribute list stream using the above action */
3179
0
    load_file.left = load_file.total = (size_t) fs_attr->size;
3180
0
    load_file.base = load_file.cur = (char*) tsk_malloc((size_t) fs_attr->size);
3181
0
    if (load_file.cur == NULL) {
3182
0
        return 1;
3183
0
    }
3184
0
    ntfs->attrdef = (ntfs_attrdef *) load_file.base;
3185
3186
0
    if (tsk_fs_attr_walk(fs_attr,
3187
0
            TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_load_file_action, (void *) &load_file)) {
3188
0
        tsk_error_errstr2_concat(" - load_attrdef");
3189
0
        free(ntfs->attrdef);
3190
0
        ntfs->attrdef = NULL;
3191
0
        return 1;
3192
0
    }
3193
0
    else if (load_file.left > 0) {
3194
0
        tsk_error_reset();
3195
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
3196
0
        tsk_error_set_errstr
3197
0
            ("load_attrdef: space still left after walking $Attr data");
3198
0
        free(ntfs->attrdef);
3199
0
        ntfs->attrdef = NULL;
3200
0
        return 1;
3201
0
    }
3202
3203
0
    ntfs->attrdef_len = (size_t) fs_attr->size;
3204
0
    return 0;
3205
0
}
3206
3207
3208
/*
3209
 * return the name of the attribute type.  If the attribute has not
3210
 * been loaded yet, it will be.
3211
 *
3212
 * Return 1 on error and 0 on success
3213
 */
3214
uint8_t
3215
ntfs_attrname_lookup(TSK_FS_INFO * fs, uint16_t type, char *name, int len)
3216
0
{
3217
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
3218
0
    ntfs_attrdef *attrdef;
3219
0
    if (!ntfs->attrdef) {
3220
0
        if (ntfs_load_attrdef(ntfs))
3221
0
            return 1;
3222
0
    }
3223
3224
0
    attrdef = ntfs->attrdef;
3225
0
    while (
3226
0
        (((uintptr_t) attrdef - (uintptr_t) ntfs->attrdef +
3227
0
                sizeof(ntfs_attrdef)) < ntfs->attrdef_len) &&
3228
0
        (tsk_getu32(fs->endian, attrdef->type))) {
3229
0
        if (tsk_getu32(fs->endian, attrdef->type) == type) {
3230
3231
0
            UTF16 *name16 = (UTF16 *) attrdef->label;
3232
0
            UTF8 *name8 = (UTF8 *) name;
3233
0
            int retVal;
3234
0
            retVal =
3235
0
                tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
3236
0
                (UTF16 *) ((uintptr_t) name16 +
3237
0
                    sizeof(attrdef->label)),
3238
0
                &name8,
3239
0
                (UTF8 *) ((uintptr_t) name8 + len), TSKlenientConversion);
3240
0
            if (retVal != TSKconversionOK) {
3241
0
                if (tsk_verbose)
3242
0
                    tsk_fprintf(stderr,
3243
0
                        "attrname_lookup: Error converting NTFS attribute def label to UTF8: %d",
3244
0
                        retVal);
3245
0
                break;
3246
0
            }
3247
3248
            /* Make sure it is NULL Terminated */
3249
0
            else if ((uintptr_t) name8 >= (uintptr_t) name + len)
3250
0
                name[len - 1] = '\0';
3251
0
            else
3252
0
                *name8 = '\0';
3253
0
            return 0;
3254
0
        }
3255
0
        attrdef++;
3256
0
    }
3257
    /* If we didn't find it, then call it '?' */
3258
0
    snprintf(name, len, "?");
3259
0
    return 0;
3260
0
}
3261
3262
3263
/* Load the block bitmap $Data run  and allocate a buffer for a cache
3264
 *
3265
 * return 1 on error and 0 on success
3266
 * */
3267
static uint8_t
3268
ntfs_load_bmap(NTFS_INFO * ntfs)
3269
0
{
3270
0
    ssize_t cnt = 0;
3271
0
    ntfs_attr *attr = NULL;
3272
0
    ntfs_attr *data_attr = NULL;
3273
0
    TSK_FS_INFO *fs = NULL;
3274
0
    ntfs_mft *mft = NULL;
3275
3276
0
    uint32_t attr_len = 0;
3277
0
    uint32_t attr_type = 0;
3278
3279
0
    uint64_t run_start_vcn;
3280
0
    uint16_t run_off;
3281
3282
0
    if (ntfs == NULL) {
3283
0
        goto on_error;
3284
0
    }
3285
0
    fs = &ntfs->fs_info;
3286
3287
0
    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
3288
0
        goto on_error;
3289
0
    }
3290
3291
    /* Get data on the bitmap */
3292
0
    if (ntfs_dinode_lookup(ntfs, (char *) mft, NTFS_MFT_BMAP, 0) != TSK_OK) {
3293
0
        goto on_error;
3294
0
    }
3295
3296
0
    attr = (ntfs_attr *) ((uintptr_t) mft +
3297
0
        tsk_getu16(fs->endian, mft->attr_off));
3298
0
    data_attr = NULL;
3299
3300
    /* cycle through them */
3301
0
    while ((uintptr_t) attr + sizeof (ntfs_attr) <=
3302
0
            ((uintptr_t) mft + (uintptr_t) ntfs->mft_rsize_b)) {
3303
3304
0
        attr_len = tsk_getu32(fs->endian, attr->len);
3305
0
        attr_type = tsk_getu32(fs->endian, attr->type);
3306
3307
0
        if ((attr_len == 0) || (attr_type == 0xffffffff)) {
3308
0
            break;
3309
0
        }
3310
3311
0
        if (attr_type == NTFS_ATYPE_DATA) {
3312
0
            data_attr = attr;
3313
0
            break;
3314
0
        }
3315
3316
0
        attr = (ntfs_attr *) ((uintptr_t) attr + attr_len);
3317
0
    }
3318
3319
    /* did we get it? */
3320
0
    if (data_attr == NULL) {
3321
0
        tsk_error_reset();
3322
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
3323
0
        tsk_error_set_errstr("Error Finding Bitmap Data Attribute");
3324
0
        goto on_error;
3325
0
    }
3326
0
    attr_len = tsk_getu32(fs->endian, data_attr->len);
3327
0
    if (attr_len > ntfs->mft_rsize_b) {
3328
0
        goto on_error;
3329
0
    }
3330
3331
0
    run_start_vcn = tsk_getu64(fs->endian, data_attr->c.nr.start_vcn);
3332
0
    run_off = tsk_getu16(fs->endian, data_attr->c.nr.run_off);
3333
3334
0
    if ((run_off < 48) ||
3335
0
        (run_off >= attr_len) ||
3336
0
        ((uintptr_t) data_attr + run_off) >= ((uintptr_t) mft + (uintptr_t) ntfs->mft_rsize_b)) {
3337
0
        tsk_error_reset();
3338
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
3339
0
        tsk_error_set_errstr("Invalid run_off of Bitmap Data Attribute - value out of bounds");
3340
0
        goto on_error;
3341
0
    }
3342
    /* convert data run to generic form */
3343
0
    if ((ntfs_make_data_run(ntfs,
3344
0
                run_start_vcn,
3345
0
                (ntfs_runlist *) ((uintptr_t) data_attr + run_off),
3346
0
                attr_len - run_off,
3347
0
                &(ntfs->bmap), NULL, NTFS_MFT_BMAP)) != TSK_OK) {
3348
0
        goto on_error;
3349
0
    }
3350
0
    ntfs->bmap_buf = (char *) tsk_malloc(fs->block_size);
3351
0
    if (ntfs->bmap_buf == NULL) {
3352
0
        goto on_error;
3353
0
    }
3354
3355
    /* Load the first cluster so that we have something there */
3356
0
    ntfs->bmap_buf_off = 0;
3357
3358
    // Check ntfs->bmap before it is accessed.
3359
0
    if (ntfs->bmap == NULL) {
3360
0
        goto on_error;
3361
0
    }
3362
0
    if (ntfs->bmap->addr > fs->last_block) {
3363
0
        tsk_error_reset();
3364
0
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
3365
0
        tsk_error_set_errstr
3366
0
            ("ntfs_load_bmap: Bitmap too large for image size: %" PRIuDADDR
3367
0
            "", ntfs->bmap->addr);
3368
0
        goto on_error;
3369
0
    }
3370
0
    cnt =
3371
0
        tsk_fs_read_block(fs,
3372
0
        ntfs->bmap->addr, ntfs->bmap_buf, fs->block_size);
3373
0
    if (cnt != fs->block_size) {
3374
0
        if (cnt >= 0) {
3375
0
            tsk_error_reset();
3376
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
3377
0
        }
3378
0
        tsk_error_set_errstr2("ntfs_load_bmap: Error reading block at %"
3379
0
            PRIuDADDR, ntfs->bmap->addr);
3380
0
        goto on_error;
3381
0
    }
3382
3383
0
    free (mft);
3384
0
    return 0;
3385
3386
0
on_error:
3387
0
    if (mft != NULL) {
3388
0
        free (mft);
3389
0
    }
3390
0
    return 1;
3391
0
}
3392
3393
3394
/*
3395
 * Load the VOLUME MFT entry and the VINFO attribute so that we
3396
 * can identify the volume version of this.
3397
 *
3398
 * Return 1 on error and 0 on success
3399
 */
3400
static uint8_t
3401
ntfs_load_ver(NTFS_INFO * ntfs)
3402
0
{
3403
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
3404
0
    const TSK_FS_ATTR *fs_attr;
3405
3406
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
3407
0
        tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_VOL),
3408
0
        tsk_fs_file_close
3409
0
    };
3410
3411
0
    if (!fs_file) {
3412
0
        return 1;
3413
0
    }
3414
3415
    /* cache the data attribute */
3416
0
    fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_VINFO);
3417
0
    if (!fs_attr) {
3418
0
        tsk_error_reset();
3419
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
3420
0
        tsk_error_set_errstr("Volume Info attribute not found in $Volume");
3421
0
        return 1;
3422
0
    }
3423
3424
0
    if ((fs_attr->flags & TSK_FS_ATTR_RES)
3425
0
        && (fs_attr->size)) {
3426
0
        ntfs_attr_vinfo *vinfo = (ntfs_attr_vinfo *) fs_attr->rd.buf;
3427
3428
0
        if ((vinfo->maj_ver == 1)
3429
0
            && (vinfo->min_ver == 2)) {
3430
0
            ntfs->ver = NTFS_VINFO_NT;
3431
0
        }
3432
0
        else if ((vinfo->maj_ver == 3)
3433
0
            && (vinfo->min_ver == 0)) {
3434
0
            ntfs->ver = NTFS_VINFO_2K;
3435
0
        }
3436
0
        else if ((vinfo->maj_ver == 3)
3437
0
            && (vinfo->min_ver == 1)) {
3438
0
            ntfs->ver = NTFS_VINFO_XP;
3439
0
        }
3440
0
        else {
3441
0
            tsk_error_reset();
3442
0
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
3443
0
            tsk_error_set_errstr("unknown version: %d.%d\n",
3444
0
                vinfo->maj_ver, vinfo->min_ver);
3445
0
            return 1;
3446
0
        }
3447
0
    }
3448
0
    else {
3449
0
        tsk_error_reset();
3450
0
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
3451
0
        tsk_error_set_errstr
3452
0
            ("load_version: VINFO is a non-resident attribute");
3453
0
        return 1;
3454
0
    }
3455
3456
0
    return 0;
3457
0
}
3458
3459
3460
#if TSK_USE_SID
3461
/** \internal
3462
 * Prints the value of sds into the a_sidstr string in ASCII form.  This will allocate a new buffer for the
3463
 * string, so a_sidstr should not point to a buffer. Output is in format of:
3464
 * S-R-I-S-S... with 'R' being revision, 'I' being the identifier authority, and 'S' being subauthority values.
3465
 *
3466
 * @param a_fs File system
3467
 * @param a_sds SDS
3468
 * @param a_sidstr [out] Pointer that will be assigned to the buffer allocated by this function to store the string.
3469
 * @returns 1 on error, 0 on success
3470
 */
3471
static uint8_t
3472
ntfs_sds_to_str(TSK_FS_INFO * a_fs, const ntfs_attr_sds * a_sds,
3473
    char **a_sidstr)
3474
0
{
3475
0
    ntfs_sid *sid = NULL;
3476
3477
0
    uint32_t owner_offset;
3478
0
    *a_sidstr = NULL;
3479
3480
0
    if ((a_fs == NULL) || (a_sds == NULL) || (a_sidstr == NULL)) {
3481
0
        tsk_error_reset();
3482
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3483
0
        tsk_error_set_errstr("Invalid argument");
3484
0
        return 1;
3485
0
    }
3486
3487
0
    owner_offset =
3488
0
        tsk_getu32(a_fs->endian, a_sds->self_rel_sec_desc.owner);
3489
3490
0
    if (((uintptr_t) & a_sds->self_rel_sec_desc + owner_offset) >
3491
0
        ((uintptr_t) a_sds + tsk_getu32(a_fs->endian, a_sds->ent_size))) {
3492
0
        tsk_error_reset();
3493
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
3494
0
        tsk_error_set_errstr
3495
0
            ("ntfs_sds_to_str: owner offset larger than a_sds length");
3496
0
        return 1;
3497
0
    }
3498
3499
0
    sid =
3500
0
        (ntfs_sid *) ((uint8_t *) & a_sds->self_rel_sec_desc +
3501
0
        owner_offset);
3502
3503
    //tsk_fprintf(stderr, "Revision: %i\n", sid->revision);
3504
3505
    // This check helps not process invalid data, which was noticed while testing
3506
    // a failing harddrive
3507
0
    if (sid->revision == 1) {
3508
0
        uint64_t authority = 0;
3509
0
        int i, len;
3510
0
        char *sid_str_offset = NULL;
3511
0
        char *sid_str = NULL;
3512
3513
        //tsk_fprintf(stderr, "Sub-Authority Count: %i\n", sid->sub_auth_count);
3514
0
        authority = 0;
3515
0
        for (i = 0; i < 6; i++)
3516
0
            authority += (uint64_t) sid->ident_auth[i] << ((5 - i) * 8);
3517
3518
        //tsk_fprintf(stderr, "NT Authority: %" PRIu64 "\n", authority);
3519
3520
        // "S-1-AUTH-SUBAUTH-SUBAUTH..."
3521
0
        const size_t sid_str_len = 4 + 13 + (1 + 10) * sid->sub_auth_count;
3522
3523
        // Allocate the buffer for the string representation of the SID.
3524
0
        if ((sid_str = (char *) tsk_malloc(sid_str_len + 1)) == NULL) {
3525
0
            return 1;
3526
0
        }
3527
3528
0
        len = snprintf(sid_str, sid_str_len + 1, "S-1-%" PRIu64, authority);
3529
0
        sid_str_offset = sid_str + len;
3530
3531
0
        for (i = 0; i < sid->sub_auth_count; i++) {
3532
0
            len = snprintf(sid_str_offset, sid_str_len + 1 - len, "-%" PRIu32, sid->sub_auth[i]);
3533
0
            sid_str_offset += len;
3534
0
        }
3535
0
        *a_sidstr = sid_str;
3536
        //tsk_fprintf(stderr, "SID: %s\n", sid_str);
3537
0
    }
3538
0
    else {
3539
0
        tsk_error_reset();
3540
0
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
3541
0
        tsk_error_set_errstr("ntfs_sds_to_str: Invalid SID revision (%d)",
3542
0
            sid->revision);
3543
0
        return 1;               // Invalid revision number in the SID.
3544
0
    }
3545
3546
0
    return 0;
3547
0
}
3548
3549
3550
3551
3552
/** \internal
3553
 * Maps a security id value from a file to its SDS structure
3554
 *
3555
 * Note: This routine assumes &ntfs->sid_lock is locked by the caller.
3556
 *
3557
 * @param fs File system
3558
 * @param secid Security Id to find SDS for.
3559
 * @returns NULL on error
3560
 */
3561
static const ntfs_attr_sds *
3562
ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
3563
0
{
3564
0
    uint32_t i = 0;
3565
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
3566
0
    ntfs_attr_sii *sii = NULL;
3567
0
    ntfs_attr_sds *sds = NULL;
3568
0
    uint32_t sii_secid = 0;
3569
0
    uint32_t sds_secid = 0;
3570
0
    uint32_t sii_sechash = 0;
3571
0
    uint32_t sds_sechash = 0;
3572
0
    uint64_t sds_file_off = 0;
3573
    //uint32_t sds_ent_size = 0;
3574
0
    uint64_t sii_sds_file_off = 0;
3575
0
    uint32_t sii_sds_ent_size = 0;
3576
3577
0
    if ((fs == NULL) || (secid == 0)) {
3578
0
        tsk_error_reset();
3579
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3580
0
        tsk_error_set_errstr("Invalid argument");
3581
0
        return NULL;
3582
0
    }
3583
3584
    // Loop through all the SII entries looking for the security id matching that found in the file.
3585
    // This lookup is obviously O(n^2) for all n files. However, since so many files have the exact
3586
    // same security identifier, it is not really that bad. In reality, 100,000 files may only map to
3587
    // 10,000 security identifiers. Since SII entries are 0x28 bytes each and security identifiers
3588
    // increase incrementally, we could go directly to the entry in question ((secid * 0x28) + 256).
3589
    // SII entries started at 256 on Vista; however, I did not look at the starting secid for other
3590
    // versions of NTFS.
3591
  //
3592
  // It appears that the file format may have changed since this was first written. There now appear to
3593
  // be multiple entries for each security ID. Some may no longer be valid, so we loop over all of them
3594
  // until we find one that looks valid.
3595
0
  for (i = 0; i < ntfs->sii_data.used; i++) {
3596
0
    if (! (tsk_getu32(fs->endian,
3597
0
      ((ntfs_attr_sii *)(ntfs->sii_data.buffer))[i].key_sec_id) == secid)) {
3598
0
      continue;
3599
0
    }
3600
3601
    // We found a potentially good SII entry
3602
0
    sii = &((ntfs_attr_sii *)(ntfs->sii_data.buffer))[i];
3603
0
    sii_secid = tsk_getu32(fs->endian, sii->key_sec_id);
3604
0
    sii_sechash = tsk_getu32(fs->endian, sii->data_hash_sec_desc);
3605
0
    sii_sds_file_off = tsk_getu64(fs->endian, sii->sec_desc_off);
3606
0
    sii_sds_ent_size = tsk_getu32(fs->endian, sii->sec_desc_size);
3607
3608
    // Check that we do not go out of bounds.
3609
0
    if (sii_sds_file_off > ntfs->sds_data.size) {
3610
0
      tsk_error_reset();
3611
0
      tsk_error_set_errno(TSK_ERR_FS_GENFS);
3612
0
      tsk_error_set_errstr("ntfs_get_sds: SII offset too large (%" PRIu64
3613
0
        ")", sii_sds_file_off);
3614
0
      continue;
3615
0
    }
3616
0
    else if (!sii_sds_ent_size) {
3617
0
      tsk_error_reset();
3618
0
      tsk_error_set_errno(TSK_ERR_FS_GENFS);
3619
0
      tsk_error_set_errstr("ntfs_get_sds: SII entry size is invalid (%"
3620
0
        PRIu32 ")", sii_sds_ent_size);
3621
0
      continue;
3622
0
    }
3623
3624
0
    sds =
3625
0
      (ntfs_attr_sds *)((uint8_t *)ntfs->sds_data.buffer +
3626
0
        sii_sds_file_off);
3627
0
    sds_secid = tsk_getu32(fs->endian, sds->sec_id);
3628
0
    sds_sechash = tsk_getu32(fs->endian, sds->hash_sec_desc);
3629
0
    sds_file_off = tsk_getu64(fs->endian, sds->file_off);
3630
3631
    // Sanity check to make sure the $SII entry points to
3632
    // the correct $SDS entry.
3633
0
    if ((sds_secid == sii_secid) &&
3634
0
      (sds_sechash == sii_sechash) && (sds_file_off == sii_sds_file_off)
3635
      //&& (sds_ent_size == sii_sds_ent_size)
3636
0
      ) {
3637
      // Clear any previous errors
3638
0
      tsk_error_reset();
3639
0
      return sds;
3640
0
    }
3641
0
    tsk_error_reset();
3642
0
    tsk_error_set_errno(TSK_ERR_FS_GENFS);
3643
0
    tsk_error_set_errstr("ntfs_get_sds: SII entry %" PRIu32 " not found", secid);
3644
0
  }
3645
3646
  // If we never even found an SII entry that matched our secid, update the error state.
3647
  // Otherwise leave it as the last error recorded.
3648
0
  if (sii == NULL) {
3649
0
    tsk_error_reset();
3650
0
    tsk_error_set_errno(TSK_ERR_FS_GENFS);
3651
0
    tsk_error_set_errstr("ntfs_get_sds: Got to end w/out data");
3652
0
  }
3653
0
    return NULL;
3654
0
}
3655
#endif
3656
3657
/** \internal
3658
 * NTFS-specific function (pointed to in FS_INFO) that maps a security ID
3659
 * to an ASCII printable string.
3660
 * Read the contents of the STANDARD_INFORMATION attribute of a file
3661
 * to get the security id. Once we have the security id, we will
3662
 * search $Secure:$SII to find a matching security id. That $SII entry
3663
 * will contain the offset within the $SDS stream for the $SDS entry,
3664
 * which contains the owner SID
3665
 *
3666
 * @param a_fs_file File to get security info on
3667
 * @param sid_str [out] location where string representation of security info will be stored.
3668
 Caller must free the string.
3669
 * @returns 1 on error
3670
 */
3671
static uint8_t
3672
ntfs_file_get_sidstr(TSK_FS_FILE * a_fs_file, char **sid_str)
3673
0
{
3674
0
#if TSK_USE_SID
3675
0
    const TSK_FS_ATTR *fs_data;
3676
0
    ntfs_attr_si *si;
3677
0
    const ntfs_attr_sds *sds;
3678
0
    NTFS_INFO *ntfs = (NTFS_INFO *) a_fs_file->fs_info;
3679
3680
0
    *sid_str = NULL;
3681
3682
0
    if (!a_fs_file->meta->attr) {
3683
0
        tsk_error_reset();
3684
0
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
3685
0
    tsk_error_set_errstr
3686
0
    ("ntfs_file_get_sidstr: file argument has no meta data");
3687
0
        return 1;
3688
0
    }
3689
3690
    // Read STANDARD_INFORMATION attribute for the security id of the file.
3691
0
    fs_data = tsk_fs_attrlist_get(a_fs_file->meta->attr,
3692
0
        TSK_FS_ATTR_TYPE_NTFS_SI);
3693
0
    if (!fs_data) {
3694
0
        tsk_error_set_errstr2("- ntfs_file_get_sidstr:SI attribute");
3695
0
        return 1;
3696
0
    }
3697
3698
0
    si = (ntfs_attr_si *) fs_data->rd.buf;
3699
0
    if (!si) {
3700
0
        tsk_error_reset();
3701
0
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
3702
0
        tsk_error_set_errstr("ntfs_file_get_sidstr: SI buf is NULL");
3703
0
        return 1;
3704
0
    }
3705
3706
0
    tsk_take_lock(&ntfs->sid_lock);
3707
    // sds points inside ntfs->sds_data, which we've just locked
3708
0
    sds =
3709
0
        ntfs_get_sds(a_fs_file->fs_info,
3710
0
        tsk_getu32(a_fs_file->fs_info->endian, si->sec_id));
3711
0
    if (!sds) {
3712
0
        tsk_release_lock(&ntfs->sid_lock);
3713
0
        tsk_error_set_errstr2("- ntfs_file_get_sidstr:SI attribute");
3714
0
        return 1;
3715
0
    }
3716
0
    if (ntfs_sds_to_str(a_fs_file->fs_info, sds, sid_str)) {
3717
0
        tsk_release_lock(&ntfs->sid_lock);
3718
0
        tsk_error_set_errstr2("- ntfs_file_get_sidstr:SI attribute");
3719
0
        return 1;
3720
0
    }
3721
0
    tsk_release_lock(&ntfs->sid_lock);
3722
0
    return 0;
3723
#else
3724
    *sid_str = NULL;
3725
    tsk_error_reset();
3726
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
3727
    tsk_error_set_errstr("Unsupported function");
3728
    return 1;
3729
#endif
3730
0
}
3731
3732
3733
#if TSK_USE_SID
3734
/** \internal
3735
 * Process all the $SII entries into a single array by removing all the Attribute Headers.
3736
 * Note: This routine assumes &ntfs->sid_lock is locked by the caller.
3737
 * @param fs File system structure to store results into
3738
 * @param sii_buffer Buffer of raw $SII entries to parse
3739
 */
3740
static void
3741
ntfs_proc_sii(TSK_FS_INFO * fs, NTFS_SXX_BUFFER * sii_buffer)
3742
0
{
3743
0
    unsigned int sii_buffer_offset = 0;
3744
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
3745
0
    ntfs_attr_sii *sii;
3746
3747
0
    if ((fs == NULL) || (sii_buffer == NULL)
3748
0
        || (ntfs->sii_data.buffer == NULL))
3749
0
        return;
3750
3751
    /* Loop by cluster size */
3752
0
    for (sii_buffer_offset = 0; sii_buffer_offset < sii_buffer->size;
3753
0
        sii_buffer_offset += ntfs->idx_rsize_b) {
3754
3755
0
        uint8_t* idx_buffer_end = 0;
3756
3757
0
        ntfs_idxrec *idxrec =
3758
0
            (ntfs_idxrec *) & sii_buffer->buffer[sii_buffer_offset];
3759
3760
        // stop processing if we hit corrupt data
3761
0
        if (tsk_getu32(fs->endian, idxrec->list.begin_off) > ntfs->idx_rsize_b) {
3762
0
            if (tsk_verbose)
3763
0
                tsk_fprintf(stderr, "ntfs_proc_sii: corrupt offset\n");
3764
0
            break;
3765
0
        }
3766
0
        else if (tsk_getu32(fs->endian, idxrec->list.bufend_off) > ntfs->idx_rsize_b) {
3767
0
            if (tsk_verbose)
3768
0
                tsk_fprintf(stderr, "ntfs_proc_sii: corrupt offset\n");
3769
0
            break;
3770
0
        }
3771
0
        else if (tsk_getu32(fs->endian, idxrec->list.begin_off) > tsk_getu32(fs->endian, idxrec->list.bufend_off)) {
3772
0
            if (tsk_verbose)
3773
0
                tsk_fprintf(stderr, "ntfs_proc_sii: corrupt offset\n");
3774
0
            break;
3775
0
        }
3776
3777
        // get pointer to first record
3778
0
    uint8_t* sii_data_ptr = ((uint8_t*)& idxrec->list +
3779
0
      tsk_getu32(fs->endian, idxrec->list.begin_off));
3780
3781
        // where last record ends
3782
0
        idx_buffer_end = (uint8_t*) & idxrec->list +
3783
0
            tsk_getu32(fs->endian, idxrec->list.bufend_off);
3784
3785
3786
        // copy records into NTFS_INFO
3787
0
    while (sii_data_ptr + sizeof(ntfs_attr_sii) <= idx_buffer_end) {
3788
      /* make sure we don't go over bounds of ntfs->sii_data.buffer */
3789
0
      if ((ntfs->sii_data.used + 1) * sizeof(ntfs_attr_sii) > ntfs->sii_data.size) {
3790
0
        if (tsk_verbose)
3791
0
          tsk_fprintf(stderr, "ntfs_proc_sii: data buffer too small\n");
3792
0
        return; // reached end of ntfs->sii_data.buffer
3793
0
      }
3794
3795
      // It appears that perhaps older versions of NTFS always had entries of length 0x28. Now it appears we also can
3796
      // have entries of length 0x30. And there are also some entries that take up 0x28 bytes but have their length set to 0x10.
3797
3798
      // 1400140000000000280004000000000002110000f233505302110000a026320000000000ec000000  // Normal entry of length 0x28
3799
      // 0000000000000000100000000200000003110000a65c02000311000090273200000000005c010000  // Possibly deleted? entry of length 0x28 but reporting length 0x10
3800
      // 140014000000000030000400010000001d150000abb032671d150000805a3a0000000000e80000006800000000000000  // Entry of length 0x30. Unclear what the eight final bytes are
3801
      // 00000000000000001800000003001b00540000000000000067110000a0823200000000003c0100005400000000000000  // I think this is the possibly deleted form of a long entry
3802
      //
3803
      // I haven't been able to find any documentation of what's going on - it's all old and says the entry length will be 0x28. The flags
3804
      // are also different across these three types but I also can't find any documentation on what they mean. So this is a best guess on
3805
      // how we should handle things:
3806
      // - If the length field is 0x30 or the first two fields are null and the length is 0x18, save the entry and advance 0x30 bytes.
3807
      //         The last eight bytes on the long entries will be ignored.
3808
      // - Otherwise save the entry and advance by 0x28 bytes.
3809
      //
3810
0
      sii = (ntfs_attr_sii*)sii_data_ptr;
3811
0
      int data_off = tsk_getu16(fs->endian, sii->data_off);
3812
0
      int data_size = tsk_getu16(fs->endian, sii->size);
3813
0
      int ent_size = tsk_getu16(fs->endian, sii->ent_size);
3814
3815
      // Copy the entry. It seems like we could have a check here that the first two fields are 0x14
3816
      // but we don't know for sure that not having those indicates an invalid entry.
3817
0
      memcpy(ntfs->sii_data.buffer +
3818
0
        (ntfs->sii_data.used * sizeof(ntfs_attr_sii)), sii_data_ptr,
3819
0
        sizeof(ntfs_attr_sii));
3820
0
      ntfs->sii_data.used++;
3821
3822
      // Advance the pointer
3823
0
      if (ent_size == 0x30 || (data_off == 0 && data_size == 0 && ent_size == 0x18)) {
3824
0
        sii_data_ptr += 0x30;
3825
0
      }
3826
0
      else {
3827
0
        sii_data_ptr += 0x28;
3828
0
      }
3829
3830
/*
3831
        printf("Security id %d is at offset 0x%I64x for 0x%x bytes\n", tsk_getu32(fs->endian,sii->key_sec_id),
3832
                                       tsk_getu64(fs->endian,sii->sec_desc_off),
3833
                                       tsk_getu32(fs->endian,sii->sec_desc_size));
3834
      }
3835
      else
3836
      {
3837
        printf("\n\tOffset to data %x Size of data %x Size of Index entry %x\n", tsk_getu16(fs->endian,sii->data_off),
3838
                                           tsk_getu16(fs->endian,sii->size),
3839
                                           tsk_getu16(fs->endian,sii->ent_size));
3840
        printf("\tSecurity id %d is at offset 0x%I64x for 0x%x bytes\n\n", tsk_getu32(fs->endian,sii->key_sec_id),
3841
                                       tsk_getu64(fs->endian,sii->sec_desc_off),
3842
                                       tsk_getu32(fs->endian,sii->sec_desc_size));
3843
      }
3844
*/
3845
0
        }
3846
0
    }
3847
0
}
3848
3849
3850
/*
3851
 * Load the $Secure attributes so that we can identify the user.
3852
 *
3853
 * Note: This routine is called only from ntfs_open and therefore does
3854
 * not need to lock ntfs->sid_lock.
3855
 *
3856
 * @returns 1 on error (which occurs only if malloc or other system error).
3857
 */
3858
static uint8_t
3859
ntfs_load_secure(NTFS_INFO * ntfs)
3860
0
{
3861
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
3862
0
    TSK_FS_META *fs_meta = NULL;
3863
0
    const TSK_FS_ATTR *fs_attr_sds = NULL;
3864
0
    const TSK_FS_ATTR *fs_attr_sii = NULL;
3865
0
    NTFS_SXX_BUFFER sii_buffer;
3866
0
    ssize_t cnt;
3867
3868
0
    ntfs->sii_data.buffer = NULL;
3869
0
    ntfs->sii_data.size = 0;
3870
0
    ntfs->sii_data.used = 0;
3871
0
    ntfs->sds_data.buffer = NULL;
3872
0
    ntfs->sds_data.size = 0;
3873
0
    ntfs->sds_data.used = 0;
3874
3875
3876
    // Open $Secure. The $SDS stream contains all the security descriptors
3877
    // and is indexed by $SII and $SDH.
3878
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> secure{
3879
0
        tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_SECURE),
3880
0
        tsk_fs_file_close
3881
0
    };
3882
3883
0
    if (!secure) {
3884
0
        if (tsk_verbose)
3885
0
            tsk_fprintf(stderr,
3886
0
                "ntfs_load_secure: error opening $Secure file: %s\n",
3887
0
                tsk_error_get_errstr());
3888
0
        tsk_error_reset();
3889
0
        return 0;
3890
0
    }
3891
3892
    // Make sure the TSK_FS_META is not NULL. We need it to get the
3893
    // $SII and $SDH attributes.
3894
0
    fs_meta = secure->meta;
3895
0
    if (!fs_meta) {
3896
0
        if (tsk_verbose)
3897
0
            tsk_fprintf(stderr,
3898
0
                "ntfs_load_secure: $Secure file has no attributes\n");
3899
0
        tsk_error_reset();
3900
0
        return 0;
3901
0
    }
3902
3903
    // Get the $SII attribute.
3904
0
    fs_attr_sii =
3905
0
        tsk_fs_attrlist_get_name_type(fs_meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_IDXALLOC,
3906
0
        "$SII\0");
3907
0
    if (!fs_attr_sii) {
3908
0
        if (tsk_verbose)
3909
0
            tsk_fprintf(stderr,
3910
0
                "ntfs_load_secure: error getting $Secure:$SII IDX_ALLOC attribute\n");
3911
0
        tsk_error_reset();
3912
0
        return 0;
3913
3914
0
    }
3915
3916
    // Get the $SDS attribute.
3917
0
    fs_attr_sds = tsk_fs_attrlist_get(fs_meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_DATA);
3918
0
    if (!fs_attr_sds) {
3919
0
        if (tsk_verbose)
3920
0
            tsk_fprintf(stderr,
3921
0
                "ntfs_load_secure: error getting $Secure:$SDS $Data attribute\n");
3922
0
        tsk_error_reset();
3923
0
        return 0;
3924
0
    }
3925
3926
    /* First we read in $SII to a local buffer adn then process it into NTFS_INFO */
3927
3928
    // Allocate local space for the entire $SII stream.
3929
0
    sii_buffer.size = (size_t) roundup(fs_attr_sii->size, fs->block_size);
3930
0
    sii_buffer.used = 0;
3931
3932
    // arbitrary check because we had problems before with alloc too much memory
3933
0
    if (sii_buffer.size > 64000000) {
3934
0
        if (tsk_verbose)
3935
0
            tsk_fprintf(stderr,
3936
0
                "ntfs_load_secure: sii_buffer.size is too large: %z\n",
3937
0
                sii_buffer.size);
3938
0
        return 0;
3939
0
    }
3940
0
    if ((sii_buffer.buffer = (char*) tsk_malloc(sii_buffer.size)) == NULL) {
3941
0
        return 1;
3942
0
    }
3943
3944
    // Read in the raw $SII stream.
3945
0
    cnt =
3946
0
        tsk_fs_attr_read(fs_attr_sii, 0, sii_buffer.buffer,
3947
0
        sii_buffer.size, TSK_FS_FILE_READ_FLAG_NONE);
3948
0
    if (cnt != (ssize_t)sii_buffer.size) {
3949
0
        if (tsk_verbose)
3950
0
            tsk_fprintf(stderr,
3951
0
                "ntfs_load_secure: error reading $Secure:$SII attribute: %s\n",
3952
0
                tsk_error_get_errstr());
3953
0
        tsk_error_reset();
3954
3955
0
        free(sii_buffer.buffer);
3956
0
        return 0;
3957
0
    }
3958
3959
    // allocate the structure for the processed version of the data
3960
0
    ntfs->sii_data.used = 0;    // use this to count the number of $SII entries
3961
0
    if ((ntfs->sii_data.buffer =
3962
0
            (char *) tsk_malloc(sii_buffer.size)) == NULL) {
3963
0
        free(sii_buffer.buffer);
3964
0
        return 1;
3965
0
    }
3966
0
    ntfs->sii_data.size = sii_buffer.size;
3967
3968
    // parse sii_buffer into ntfs->sii_data.
3969
0
    ntfs_proc_sii(fs, &sii_buffer);
3970
0
    free(sii_buffer.buffer);
3971
3972
    /* Now we copy $SDS into NTFS_INFO. We do not do any processing in this step. */
3973
3974
    // Allocate space for the entire $SDS stream with all the security
3975
    // descriptors. We should be able to use the $SII offset to index
3976
    // into the $SDS stream.
3977
0
    ntfs->sds_data.size = (size_t) fs_attr_sds->size;
3978
    // arbitrary check because we had problems before with alloc too much memory
3979
0
    if (ntfs->sds_data.size > 64000000) {
3980
0
        if (tsk_verbose)
3981
0
            tsk_fprintf(stderr,
3982
0
                "ntfs_load_secure: ntfs->sds_data.size is too large: %z\n",
3983
0
                ntfs->sds_data.size);
3984
0
        free(ntfs->sii_data.buffer);
3985
0
        ntfs->sii_data.buffer = NULL;
3986
0
        ntfs->sii_data.used = 0;
3987
0
        ntfs->sii_data.size = 0;
3988
0
        return 0;
3989
0
    }
3990
0
    ntfs->sds_data.used = 0;
3991
0
    if ((ntfs->sds_data.buffer =
3992
0
            (char *) tsk_malloc(ntfs->sds_data.size)) == NULL) {
3993
0
        free(ntfs->sii_data.buffer);
3994
0
        ntfs->sii_data.buffer = NULL;
3995
0
        ntfs->sii_data.used = 0;
3996
0
        ntfs->sii_data.size = 0;
3997
0
        return 1;
3998
0
    }
3999
4000
    // Read in the raw $SDS ($DATA) stream.
4001
0
    cnt =
4002
0
        tsk_fs_attr_read(fs_attr_sds, 0,
4003
0
        ntfs->sds_data.buffer, ntfs->sds_data.size,
4004
0
        TSK_FS_FILE_READ_FLAG_NONE);
4005
0
    if (cnt != (ssize_t)ntfs->sds_data.size) {
4006
0
        if (tsk_verbose)
4007
0
            tsk_fprintf(stderr,
4008
0
                "ntfs_load_secure: error reading $Secure:$SDS attribute: %s\n",
4009
0
                tsk_error_get_errstr());
4010
0
        tsk_error_reset();
4011
4012
0
        free(ntfs->sii_data.buffer);
4013
0
        ntfs->sii_data.buffer = NULL;
4014
0
        ntfs->sii_data.used = 0;
4015
0
        ntfs->sii_data.size = 0;
4016
0
        free(ntfs->sds_data.buffer);
4017
0
        ntfs->sds_data.buffer = NULL;
4018
0
        ntfs->sds_data.used = 0;
4019
0
        ntfs->sds_data.size = 0;
4020
0
        return 0;
4021
0
    }
4022
4023
0
    return 0;
4024
0
}
4025
4026
#endif
4027
4028
/**********************************************************************
4029
 *
4030
 *  Exported Walk Functions
4031
 *
4032
 **********************************************************************/
4033
4034
4035
static TSK_FS_BLOCK_FLAG_ENUM
4036
ntfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
4037
0
{
4038
0
    NTFS_INFO *ntfs = (NTFS_INFO *) a_fs;
4039
0
    int retval;
4040
0
    int flags = 0;
4041
4042
    /* identify if the cluster is allocated or not */
4043
0
    retval = is_clustalloc(ntfs, a_addr);
4044
0
    if (retval == 1)
4045
0
        flags = TSK_FS_BLOCK_FLAG_ALLOC;
4046
0
    else if (retval == 0)
4047
0
        flags = TSK_FS_BLOCK_FLAG_UNALLOC;
4048
4049
0
    return (TSK_FS_BLOCK_FLAG_ENUM) flags;
4050
0
}
4051
4052
4053
4054
/*
4055
 * flags: TSK_FS_BLOCK_FLAG_ALLOC and FS_FLAG_UNALLOC
4056
 *
4057
 * @@@ We should probably consider some data META, but it is tough with
4058
 * the NTFS design ...
4059
 */
4060
static uint8_t
4061
ntfs_block_walk(TSK_FS_INFO * fs,
4062
    TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk,
4063
    TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action,
4064
    void *a_ptr)
4065
0
{
4066
0
    const char *myname = "ntfs_block_walk";
4067
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
4068
0
    TSK_DADDR_T addr;
4069
0
    TSK_FS_BLOCK *fs_block;
4070
4071
    // clean up any error messages that are lying around
4072
0
    tsk_error_reset();
4073
4074
    /*
4075
     * Sanity checks.
4076
     */
4077
0
    if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
4078
0
        tsk_error_reset();
4079
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
4080
0
        tsk_error_set_errstr("%s: start block: %" PRIuDADDR "", myname,
4081
0
            a_start_blk);
4082
0
        return 1;
4083
0
    }
4084
0
    else if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) {
4085
0
        tsk_error_reset();
4086
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
4087
0
        tsk_error_set_errstr("%s: last block: %" PRIuDADDR "", myname,
4088
0
            a_end_blk);
4089
0
        return 1;
4090
0
    }
4091
4092
    /* Sanity check on a_flags -- make sure at least one ALLOC is set */
4093
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
4094
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
4095
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
4096
0
            (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC |
4097
0
            TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
4098
0
    }
4099
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
4100
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
4101
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
4102
0
            (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
4103
0
    }
4104
4105
4106
0
    if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) {
4107
0
        return 1;
4108
0
    }
4109
4110
    /* Cycle through the blocks */
4111
0
    for (addr = a_start_blk; addr <= a_end_blk; addr++) {
4112
0
        int retval;
4113
0
        int myflags;
4114
4115
        /* identify if the cluster is allocated or not */
4116
0
        retval = is_clustalloc(ntfs, addr);
4117
0
        if (retval == -1) {
4118
0
            tsk_fs_block_free(fs_block);
4119
0
            return 1;
4120
0
        }
4121
4122
0
        else if (retval == 1) {
4123
0
            myflags = TSK_FS_BLOCK_FLAG_ALLOC;
4124
0
        }
4125
0
        else {
4126
0
            myflags = TSK_FS_BLOCK_FLAG_UNALLOC;
4127
0
        }
4128
4129
        // test if we should call the callback with this one
4130
0
        if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
4131
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
4132
0
            continue;
4133
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
4134
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
4135
0
            continue;
4136
4137
0
        if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
4138
0
            myflags |= TSK_FS_BLOCK_FLAG_AONLY;
4139
4140
0
        if (tsk_fs_block_get_flag(fs, fs_block, addr,
4141
0
                (TSK_FS_BLOCK_FLAG_ENUM) myflags) == NULL) {
4142
0
            tsk_error_set_errstr2
4143
0
                ("ntfs_block_walk: Error reading block at %" PRIuDADDR,
4144
0
                addr);
4145
0
            tsk_fs_block_free(fs_block);
4146
0
            return 1;
4147
0
        }
4148
4149
0
        retval = a_action(fs_block, a_ptr);
4150
0
        if (retval == TSK_WALK_STOP) {
4151
0
            break;
4152
0
        }
4153
0
        else if (retval == TSK_WALK_ERROR) {
4154
0
            tsk_fs_block_free(fs_block);
4155
0
            return 1;
4156
0
        }
4157
0
    }
4158
4159
0
    tsk_fs_block_free(fs_block);
4160
0
    return 0;
4161
0
}
4162
4163
4164
4165
/*
4166
 * inode_walk
4167
 *
4168
 * Flags: TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC,
4169
 * TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED, TSK_FS_META_FLAG_ORPHAN
4170
 *
4171
 * Note that with ORPHAN, entries will be found that can also be
4172
 * found by searching based on parent directories (if parent directory is
4173
 * known)
4174
 */
4175
static uint8_t
4176
ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
4177
    TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags,
4178
    TSK_FS_META_WALK_CB a_action, void *ptr)
4179
0
{
4180
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
4181
0
    unsigned int myflags;
4182
0
    TSK_INUM_T mftnum;
4183
0
    TSK_INUM_T end_inum_tmp;
4184
0
    ntfs_mft *mft;
4185
    /*
4186
     * Sanity checks.
4187
     */
4188
0
    if (start_inum < fs->first_inum) {
4189
0
        tsk_error_reset();
4190
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
4191
0
        tsk_error_set_errstr
4192
0
            ("inode_walk: Starting inode number is too small (%" PRIuINUM
4193
0
            ")", start_inum);
4194
0
        return 1;
4195
0
    }
4196
0
    if (start_inum > fs->last_inum) {
4197
0
        tsk_error_reset();
4198
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
4199
0
        tsk_error_set_errstr
4200
0
            ("inode_walk: Starting inode number is too large (%" PRIuINUM
4201
0
            ")", start_inum);
4202
0
        return 1;
4203
0
    }
4204
0
    if (end_inum < fs->first_inum) {
4205
0
        tsk_error_reset();
4206
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
4207
0
        tsk_error_set_errstr
4208
0
            ("inode_walk: Ending inode number is too small (%" PRIuINUM
4209
0
            ")", end_inum);
4210
0
        return 1;
4211
0
    }
4212
0
    if (end_inum > fs->last_inum) {
4213
0
        tsk_error_reset();
4214
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
4215
0
        tsk_error_set_errstr("Ending inode number is too large (%" PRIuINUM
4216
0
            ")", end_inum);
4217
0
        return 1;
4218
0
    }
4219
4220
4221
    /* If ORPHAN is wanted, then make sure that the flags are correct */
4222
0
    if (flags & TSK_FS_META_FLAG_ORPHAN) {
4223
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_UNALLOC);
4224
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_ALLOC);
4225
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED);
4226
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_UNUSED);
4227
0
    }
4228
4229
0
    else {
4230
0
        if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
4231
0
            ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
4232
0
            flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
4233
0
        }
4234
4235
        /* If neither of the USED or UNUSED flags are set, then set them
4236
         * both
4237
         */
4238
0
        if (((flags & TSK_FS_META_FLAG_USED) == 0) &&
4239
0
            ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) {
4240
0
            flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
4241
0
        }
4242
0
    }
4243
4244
4245
    /* If we are looking for orphan files and have not yet filled
4246
     * in the list of unalloc inodes that are pointed to, then fill
4247
     * in the list
4248
     * */
4249
0
    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
4250
0
        if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
4251
0
            tsk_error_errstr2_concat
4252
0
                ("- ntfs_inode_walk: identifying inodes allocated by file names");
4253
0
            return 1;
4254
0
        }
4255
0
    }
4256
4257
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
4258
0
        tsk_fs_file_alloc(fs),
4259
0
        tsk_fs_file_close
4260
0
    };
4261
4262
0
    if (!fs_file)
4263
0
        return 1;
4264
4265
0
    if ((fs_file->meta = tsk_fs_meta_alloc(NTFS_FILE_CONTENT_LEN)) == NULL) {
4266
        // JRB: Coverity CID: 348
4267
0
        return 1;
4268
0
    }
4269
4270
0
    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
4271
0
        return 1;
4272
0
    }
4273
    // we need to handle fs->last_inum specially because it is for the
4274
    // virtual ORPHANS directory.  Handle it outside of the loop.
4275
0
    if (end_inum == TSK_FS_ORPHANDIR_INUM(fs))
4276
0
        end_inum_tmp = end_inum - 1;
4277
0
    else
4278
0
        end_inum_tmp = end_inum;
4279
4280
4281
0
    for (mftnum = start_inum; mftnum <= end_inum_tmp; mftnum++) {
4282
0
        int retval;
4283
0
        TSK_RETVAL_ENUM retval2;
4284
4285
        /* read MFT entry in to NTFS_INFO */
4286
0
        if ((retval2 =
4287
0
                ntfs_dinode_lookup(ntfs, (char *) mft,
4288
0
                    mftnum, & (fs_file->meta->start_of_inode))) != TSK_OK) {
4289
            // if the entry is corrupt, then skip to the next one
4290
0
            if (retval2 == TSK_COR) {
4291
0
                if (tsk_verbose)
4292
0
                    tsk_error_print(stderr);
4293
0
                tsk_error_reset();
4294
0
                continue;
4295
0
            }
4296
0
            free(mft);
4297
0
            return 1;
4298
0
        }
4299
4300
        /* we only want to look at base file records
4301
         * (extended are because the base could not fit into one)
4302
         */
4303
0
        if (tsk_getu48(fs->endian, mft->base_ref) != NTFS_MFT_BASE)
4304
0
            continue;
4305
4306
        /* NOTE: We could add a sanity check here with the MFT bitmap
4307
         * to validate of the INUSE flag and bitmap are in agreement
4308
         */
4309
        /* check flags */
4310
0
        myflags =
4311
0
            ((tsk_getu16(fs->endian, mft->flags) &
4312
0
                NTFS_MFT_INUSE) ? TSK_FS_META_FLAG_ALLOC :
4313
0
            TSK_FS_META_FLAG_UNALLOC);
4314
4315
        /* If we want only orphans, then check if this
4316
         * inode is in the seen list
4317
         * */
4318
0
        if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
4319
0
            (flags & TSK_FS_META_FLAG_ORPHAN) &&
4320
0
            (tsk_fs_dir_find_inum_named(fs, mftnum))) {
4321
0
            continue;
4322
0
        }
4323
4324
        /* copy into generic format */
4325
0
        if ((retval =
4326
0
                ntfs_dinode_copy(ntfs, fs_file.get(), (char *) mft,
4327
0
                    mftnum)) != TSK_OK) {
4328
            // continue on if there were only corruption problems
4329
0
            if (retval == TSK_COR) {
4330
0
                if (tsk_verbose)
4331
0
                    tsk_error_print(stderr);
4332
0
                tsk_error_reset();
4333
0
                continue;
4334
0
            }
4335
0
            free(mft);
4336
0
            return 1;
4337
0
        }
4338
4339
0
        myflags |=
4340
0
            (fs_file->meta->flags & (TSK_FS_META_FLAG_USED |
4341
0
                TSK_FS_META_FLAG_UNUSED));
4342
0
        if ((flags & myflags) != myflags)
4343
0
            continue;
4344
4345
        /* call action */
4346
0
        retval = a_action(fs_file.get(), ptr);
4347
0
        if (retval == TSK_WALK_STOP) {
4348
0
            free(mft);
4349
0
            return 0;
4350
0
        }
4351
0
        else if (retval == TSK_WALK_ERROR) {
4352
0
            free(mft);
4353
0
            return 1;
4354
0
        }
4355
0
    }
4356
4357
    // handle the virtual orphans folder if they asked for it
4358
0
    if ((end_inum == TSK_FS_ORPHANDIR_INUM(fs))
4359
0
        && (flags & TSK_FS_META_FLAG_ALLOC)
4360
0
        && (flags & TSK_FS_META_FLAG_USED)) {
4361
0
        int retval;
4362
4363
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
4364
0
            free(mft);
4365
0
            return 1;
4366
0
        }
4367
        /* call action */
4368
0
        retval = a_action(fs_file.get(), ptr);
4369
0
        if (retval == TSK_WALK_STOP) {
4370
0
            free(mft);
4371
0
            return 0;
4372
0
        }
4373
0
        else if (retval == TSK_WALK_ERROR) {
4374
0
            free(mft);
4375
0
            return 1;
4376
0
        }
4377
0
    }
4378
4379
0
    free(mft);
4380
0
    return 0;
4381
0
}
4382
4383
4384
4385
static uint8_t
4386
ntfs_fscheck(
4387
  [[maybe_unused]] TSK_FS_INFO * fs,
4388
  [[maybe_unused]] FILE * hFile)
4389
0
{
4390
0
    tsk_error_reset();
4391
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
4392
0
    tsk_error_set_errstr("fscheck not implemented for NTFS yet");
4393
0
    return 1;
4394
0
}
4395
4396
4397
/**
4398
 * Print details about the file system to a file handle.
4399
 *
4400
 * @param fs File system to print details on
4401
 * @param hFile File handle to print text to
4402
 *
4403
 * @returns 1 on error and 0 on success
4404
 */
4405
static uint8_t
4406
ntfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
4407
0
{
4408
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
4409
0
    const TSK_FS_ATTR *fs_attr;
4410
0
    char asc[512];
4411
0
    ntfs_attrdef *attrdeftmp;
4412
4413
0
    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
4414
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
4415
0
    tsk_fprintf(hFile, "File System Type: NTFS\n");
4416
0
    tsk_fprintf(hFile,
4417
0
        "Volume Serial Number: %.16" PRIX64
4418
0
        "\n", tsk_getu64(fs->endian, ntfs->fs->serial));
4419
0
    tsk_fprintf(hFile, "OEM Name: %c%c%c%c%c%c%c%c\n",
4420
0
        ntfs->fs->oemname[0],
4421
0
        ntfs->fs->oemname[1],
4422
0
        ntfs->fs->oemname[2],
4423
0
        ntfs->fs->oemname[3],
4424
0
        ntfs->fs->oemname[4],
4425
0
        ntfs->fs->oemname[5], ntfs->fs->oemname[6], ntfs->fs->oemname[7]);
4426
    /*
4427
     * Volume
4428
     */
4429
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
4430
0
        tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_VOL),
4431
0
        tsk_fs_file_close
4432
0
    };
4433
4434
0
    if (!fs_file) {
4435
0
        tsk_error_reset();
4436
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
4437
0
        tsk_error_errstr2_concat
4438
0
            (" - fsstat: Error finding Volume MFT Entry");
4439
0
        return 1;
4440
0
    }
4441
4442
0
    fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_VNAME);
4443
0
    if (!fs_attr) {
4444
0
        tsk_error_reset();
4445
0
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
4446
0
        tsk_error_set_errstr("Volume Name attribute not found in $Volume");
4447
0
        return 1;
4448
0
    }
4449
4450
0
    if ((fs_attr->flags & TSK_FS_ATTR_RES)
4451
0
        && (fs_attr->size)) {
4452
4453
0
        UTF16 *name16 = (UTF16 *) fs_attr->rd.buf;
4454
0
        UTF8 *name8 = (UTF8 *) asc;
4455
0
        int retVal;
4456
0
        retVal =
4457
0
            tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
4458
0
            (UTF16 *) ((uintptr_t) name16 +
4459
0
                (int) fs_attr->size), &name8,
4460
0
            (UTF8 *) ((uintptr_t) name8 + sizeof(asc)),
4461
0
            TSKlenientConversion);
4462
0
        if (retVal != TSKconversionOK) {
4463
0
            if (tsk_verbose)
4464
0
                tsk_fprintf(stderr,
4465
0
                    "fsstat: Error converting NTFS Volume label to UTF8: %d",
4466
0
                    retVal);
4467
0
            *name8 = '\0';
4468
0
        }
4469
4470
        /* Make sure it is NULL Terminated */
4471
0
        else if ((uintptr_t) name8 >= (uintptr_t) asc + sizeof(asc))
4472
0
            asc[sizeof(asc) - 1] = '\0';
4473
0
        else
4474
0
            *name8 = '\0';
4475
0
        tsk_fprintf(hFile, "Volume Name: %s\n", asc);
4476
0
    }
4477
4478
0
    fs_attr = NULL;
4479
0
    if (ntfs->ver == NTFS_VINFO_NT)
4480
0
        tsk_fprintf(hFile, "Version: Windows NT\n");
4481
0
    else if (ntfs->ver == NTFS_VINFO_2K)
4482
0
        tsk_fprintf(hFile, "Version: Windows 2000\n");
4483
0
    else if (ntfs->ver == NTFS_VINFO_XP)
4484
0
        tsk_fprintf(hFile, "Version: Windows XP\n");
4485
0
    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
4486
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
4487
0
    tsk_fprintf(hFile,
4488
0
        "First Cluster of MFT: %" PRIu64 "\n",
4489
0
        tsk_getu64(fs->endian, ntfs->fs->mft_clust));
4490
0
    tsk_fprintf(hFile,
4491
0
        "First Cluster of MFT Mirror: %"
4492
0
        PRIu64 "\n", tsk_getu64(fs->endian, ntfs->fs->mftm_clust));
4493
0
    tsk_fprintf(hFile,
4494
0
        "Size of MFT Entries: %" PRIu16 " bytes\n", ntfs->mft_rsize_b);
4495
0
    tsk_fprintf(hFile,
4496
0
        "Size of Index Records: %" PRIu16 " bytes\n", ntfs->idx_rsize_b);
4497
0
    tsk_fprintf(hFile,
4498
0
        "Range: %" PRIuINUM " - %" PRIuINUM
4499
0
        "\n", fs->first_inum, fs->last_inum);
4500
0
    tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum);
4501
0
    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
4502
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
4503
0
    tsk_fprintf(hFile, "Sector Size: %" PRIu16 "\n", ntfs->ssize_b);
4504
0
    tsk_fprintf(hFile, "Cluster Size: %" PRIu16 "\n", ntfs->csize_b);
4505
0
    tsk_fprintf(hFile,
4506
0
        "Total Cluster Range: %" PRIuDADDR
4507
0
        " - %" PRIuDADDR "\n", fs->first_block, fs->last_block);
4508
4509
0
    if (fs->last_block != fs->last_block_act)
4510
0
        tsk_fprintf(hFile,
4511
0
            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
4512
0
            fs->first_block, fs->last_block_act);
4513
4514
0
    tsk_fprintf(hFile,
4515
0
        "Total Sector Range: 0 - %" PRIu64
4516
0
        "\n", tsk_getu64(fs->endian, ntfs->fs->vol_size_s) - 1);
4517
    /*
4518
     * Attrdef Info
4519
     */
4520
0
    tsk_fprintf(hFile, "\n$AttrDef Attribute Values:\n");
4521
0
    if (!ntfs->attrdef) {
4522
0
        if (ntfs_load_attrdef(ntfs)) {
4523
0
            tsk_fprintf(hFile, "Error loading attribute definitions\n");
4524
0
            goto attrdef_egress;
4525
0
        }
4526
0
    }
4527
4528
0
    attrdeftmp = ntfs->attrdef;
4529
0
    while ((((uintptr_t) attrdeftmp - (uintptr_t) ntfs->attrdef +
4530
0
                sizeof(ntfs_attrdef)) < ntfs->attrdef_len) &&
4531
0
        (tsk_getu32(fs->endian, attrdeftmp->type))) {
4532
0
        UTF16 *name16 = (UTF16 *) attrdeftmp->label;
4533
0
        UTF8 *name8 = (UTF8 *) asc;
4534
0
        int retVal;
4535
0
        retVal =
4536
0
            tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
4537
0
            (UTF16 *) ((uintptr_t) name16 +
4538
0
                sizeof(attrdeftmp->label)),
4539
0
            &name8,
4540
0
            (UTF8 *) ((uintptr_t) name8 + sizeof(asc)),
4541
0
            TSKlenientConversion);
4542
0
        if (retVal != TSKconversionOK) {
4543
0
            if (tsk_verbose)
4544
0
                tsk_fprintf(stderr,
4545
0
                    "fsstat: Error converting NTFS attribute def label to UTF8: %d",
4546
0
                    retVal);
4547
0
            *name8 = '\0';
4548
0
        }
4549
4550
        /* Make sure it is NULL Terminated */
4551
0
        else if ((uintptr_t) name8 >= (uintptr_t) asc + sizeof(asc))
4552
0
            asc[sizeof(asc) - 1] = '\0';
4553
0
        else
4554
0
            *name8 = '\0';
4555
0
        tsk_fprintf(hFile, "%s (%" PRIu32 ")   ",
4556
0
            asc, tsk_getu32(fs->endian, attrdeftmp->type));
4557
0
        if ((tsk_getu64(fs->endian, attrdeftmp->minsize) == 0) &&
4558
0
            (tsk_getu64(fs->endian,
4559
0
                    attrdeftmp->maxsize) == 0xffffffffffffffffULL)) {
4560
4561
0
            tsk_fprintf(hFile, "Size: No Limit");
4562
0
        }
4563
0
        else {
4564
0
            tsk_fprintf(hFile, "Size: %" PRIu64 "-%" PRIu64,
4565
0
                tsk_getu64(fs->endian, attrdeftmp->minsize),
4566
0
                tsk_getu64(fs->endian, attrdeftmp->maxsize));
4567
0
        }
4568
4569
0
        tsk_fprintf(hFile, "   Flags: %s%s%s\n",
4570
0
            (tsk_getu32(fs->endian, attrdeftmp->flags) &
4571
0
                NTFS_ATTRDEF_FLAGS_RES ? "Resident" :
4572
0
                ""), (tsk_getu32(fs->endian,
4573
0
                    attrdeftmp->flags) &
4574
0
                NTFS_ATTRDEF_FLAGS_NONRES ?
4575
0
                "Non-resident" : ""),
4576
0
            (tsk_getu32(fs->endian, attrdeftmp->flags) &
4577
0
                NTFS_ATTRDEF_FLAGS_IDX ? ",Index" : ""));
4578
0
        attrdeftmp++;
4579
0
    }
4580
4581
0
  attrdef_egress:
4582
4583
0
    return 0;
4584
0
}
4585
4586
4587
/************************* istat *******************************/
4588
4589
0
#define NTFS_PRINT_WIDTH   8
4590
typedef struct {
4591
    FILE *hFile;
4592
    int idx;
4593
} NTFS_PRINT_ADDR;
4594
static TSK_WALK_RET_ENUM
4595
print_addr_act(
4596
  [[maybe_unused]] TSK_FS_FILE * fs_file,
4597
  [[maybe_unused]] TSK_OFF_T a_off,
4598
  TSK_DADDR_T addr,
4599
  [[maybe_unused]] char *buf,
4600
  [[maybe_unused]] size_t size,
4601
  [[maybe_unused]] TSK_FS_BLOCK_FLAG_ENUM flags,
4602
  void *ptr)
4603
0
{
4604
0
    NTFS_PRINT_ADDR *print = (NTFS_PRINT_ADDR *) ptr;
4605
0
    tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr);
4606
0
    if (++(print->idx) == NTFS_PRINT_WIDTH) {
4607
0
        tsk_fprintf(print->hFile, "\n");
4608
0
        print->idx = 0;
4609
0
    }
4610
4611
0
    return TSK_WALK_CONT;
4612
0
}
4613
4614
/**
4615
 * Print details on a specific file to a file handle.
4616
 *
4617
 * @param fs File system file is located in
4618
 * @param hFile File name to print text to
4619
 * @param inum Address of file in file system
4620
 * @param numblock The number of blocks in file to force print (can go beyond file size)
4621
 * @param sec_skew Clock skew in seconds to also print times in
4622
 *
4623
 * @returns 1 on error and 0 on success
4624
 */
4625
static uint8_t
4626
ntfs_istat(
4627
  TSK_FS_INFO * fs,
4628
  TSK_FS_ISTAT_FLAG_ENUM istat_flags,
4629
  FILE * hFile,
4630
  TSK_INUM_T inum,
4631
  [[maybe_unused]] TSK_DADDR_T numblock,
4632
  int32_t sec_skew)
4633
0
{
4634
0
    const TSK_FS_ATTR *fs_attr;
4635
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
4636
0
    ntfs_mft *mft;
4637
0
    char timeBuf[128];
4638
0
    int idx;
4639
4640
    // clean up any error messages that are lying around
4641
0
    tsk_error_reset();
4642
4643
0
    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
4644
0
        return 1;
4645
0
    }
4646
4647
0
    if (ntfs_dinode_lookup(ntfs, (char *) mft, inum, 0)) {
4648
0
        free(mft);
4649
0
        return 1;
4650
0
    }
4651
4652
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
4653
0
        tsk_fs_file_open_meta(fs, NULL, inum),
4654
0
        tsk_fs_file_close
4655
0
    };
4656
4657
0
    if (!fs_file) {
4658
0
        tsk_error_errstr2_concat(" - istat");
4659
0
        free(mft);
4660
0
        return 1;
4661
0
    }
4662
4663
0
    tsk_fprintf(hFile, "MFT Entry Header Values:\n");
4664
0
    tsk_fprintf(hFile,
4665
0
        "Entry: %" PRIuINUM
4666
0
        "        Sequence: %" PRIu32 "\n", inum, fs_file->meta->seq);
4667
0
    if (tsk_getu48(fs->endian, mft->base_ref) != 0) {
4668
0
        tsk_fprintf(hFile,
4669
0
            "Base File Record: %" PRIu64 "\n",
4670
0
            (uint64_t) tsk_getu48(fs->endian, mft->base_ref));
4671
0
    }
4672
4673
0
    tsk_fprintf(hFile,
4674
0
        "$LogFile Sequence Number: %" PRIu64
4675
0
        "\n", tsk_getu64(fs->endian, mft->lsn));
4676
0
    tsk_fprintf(hFile, "%sAllocated %s\n",
4677
0
        (fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" :
4678
0
        "Not ",
4679
0
        TSK_FS_IS_DIR_META(fs_file->meta->type) ? "Directory" : "File");
4680
0
    tsk_fprintf(hFile, "Links: %u\n", fs_file->meta->nlink);
4681
4682
    /* STANDARD_INFORMATION info */
4683
0
    fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_SI);
4684
0
    if (fs_attr) {
4685
0
        ntfs_attr_si *si = (ntfs_attr_si *) fs_attr->rd.buf;
4686
0
        char *sid_str;
4687
4688
0
        int a = 0;
4689
0
        tsk_fprintf(hFile, "\n$STANDARD_INFORMATION Attribute Values:\n");
4690
0
        tsk_fprintf(hFile, "Flags: ");
4691
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_RO)
4692
0
            tsk_fprintf(hFile, "%sRead Only", a++ == 0 ? "" : ", ");
4693
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_HID)
4694
0
            tsk_fprintf(hFile, "%sHidden", a++ == 0 ? "" : ", ");
4695
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_SYS)
4696
0
            tsk_fprintf(hFile, "%sSystem", a++ == 0 ? "" : ", ");
4697
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_ARCH)
4698
0
            tsk_fprintf(hFile, "%sArchive", a++ == 0 ? "" : ", ");
4699
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_DEV)
4700
0
            tsk_fprintf(hFile, "%sDevice", a++ == 0 ? "" : ", ");
4701
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_NORM)
4702
0
            tsk_fprintf(hFile, "%sNormal", a++ == 0 ? "" : ", ");
4703
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_TEMP)
4704
0
            tsk_fprintf(hFile, "%sTemporary", a++ == 0 ? "" : ", ");
4705
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_SPAR)
4706
0
            tsk_fprintf(hFile, "%sSparse", a++ == 0 ? "" : ", ");
4707
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_REP)
4708
0
            tsk_fprintf(hFile, "%sReparse Point", a++ == 0 ? "" : ", ");
4709
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_COMP)
4710
0
            tsk_fprintf(hFile, "%sCompressed", a++ == 0 ? "" : ", ");
4711
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_OFF)
4712
0
            tsk_fprintf(hFile, "%sOffline", a++ == 0 ? "" : ", ");
4713
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_NOIDX)
4714
0
            tsk_fprintf(hFile, "%sNot Content Indexed",
4715
0
                a++ == 0 ? "" : ", ");
4716
0
        if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_ENC)
4717
0
            tsk_fprintf(hFile, "%sEncrypted", a++ == 0 ? "" : ", ");
4718
0
        tsk_fprintf(hFile, "\n");
4719
0
        tsk_fprintf(hFile, "Owner ID: %" PRIu32 "\n",
4720
0
            tsk_getu32(fs->endian, si->own_id));
4721
4722
0
#if TSK_USE_SID
4723
0
        ntfs_file_get_sidstr(fs_file.get(), &sid_str);
4724
4725
0
        tsk_fprintf(hFile, "Security ID: %" PRIu32 "  (%s)\n",
4726
0
            tsk_getu32(fs->endian, si->sec_id), sid_str ? sid_str : "");
4727
0
        free(sid_str);
4728
0
        sid_str = NULL;
4729
0
#endif
4730
4731
4732
0
        if (tsk_getu32(fs->endian, si->maxver) != 0) {
4733
0
            tsk_fprintf(hFile,
4734
0
                "Version %" PRIu32 " of %" PRIu32
4735
0
                "\n", tsk_getu32(fs->endian, si->ver),
4736
0
                tsk_getu32(fs->endian, si->maxver));
4737
0
        }
4738
4739
0
        if (tsk_getu64(fs->endian, si->quota) != 0) {
4740
0
            tsk_fprintf(hFile, "Quota Charged: %" PRIu64 "\n",
4741
0
                tsk_getu64(fs->endian, si->quota));
4742
0
        }
4743
4744
0
        if (tsk_getu64(fs->endian, si->usn) != 0) {
4745
0
            tsk_fprintf(hFile,
4746
0
                "Last User Journal Update Sequence Number: %"
4747
0
                PRIu64 "\n", tsk_getu64(fs->endian, si->usn));
4748
0
        }
4749
4750
4751
        /* Times - take it from fs_file->meta instead of redoing the work */
4752
4753
0
        if (sec_skew != 0) {
4754
0
            tsk_fprintf(hFile, "\nAdjusted times:\n");
4755
0
            if (fs_file->meta->mtime)
4756
0
                fs_file->meta->mtime -= sec_skew;
4757
0
            if (fs_file->meta->atime)
4758
0
                fs_file->meta->atime -= sec_skew;
4759
0
            if (fs_file->meta->ctime)
4760
0
                fs_file->meta->ctime -= sec_skew;
4761
0
            if (fs_file->meta->crtime)
4762
0
                fs_file->meta->crtime -= sec_skew;
4763
4764
0
            tsk_fprintf(hFile, "Created:\t%s\n",
4765
0
                tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->crtime), timeBuf));
4766
0
            tsk_fprintf(hFile, "File Modified:\t%s\n",
4767
0
                tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->mtime), timeBuf));
4768
0
            tsk_fprintf(hFile, "MFT Modified:\t%s\n",
4769
0
                tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->ctime), timeBuf));
4770
0
            tsk_fprintf(hFile, "Accessed:\t%s\n",
4771
0
                tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->atime), timeBuf));
4772
4773
0
            if (fs_file->meta->mtime)
4774
0
                fs_file->meta->mtime += sec_skew;
4775
0
            if (fs_file->meta->atime)
4776
0
                fs_file->meta->atime += sec_skew;
4777
0
            if (fs_file->meta->ctime)
4778
0
                fs_file->meta->ctime += sec_skew;
4779
0
            if (fs_file->meta->crtime)
4780
0
                fs_file->meta->crtime += sec_skew;
4781
4782
0
            tsk_fprintf(hFile, "\nOriginal times:\n");
4783
0
        }
4784
4785
0
        tsk_fprintf(hFile, "Created:\t%s\n",
4786
0
            tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->crtime), timeBuf));
4787
0
        tsk_fprintf(hFile, "File Modified:\t%s\n",
4788
0
            tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->mtime), timeBuf));
4789
0
        tsk_fprintf(hFile, "MFT Modified:\t%s\n",
4790
0
            tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->ctime), timeBuf));
4791
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
4792
0
            tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->atime), timeBuf));
4793
0
    }
4794
4795
    /* $FILE_NAME Information */
4796
0
    for (idx = 0; idx < tsk_fs_attrlist_get_len(fs_file->meta->attr); idx++) {
4797
0
        ntfs_attr_fname *fname;
4798
0
        uint64_t flags;
4799
0
        int a = 0;
4800
0
        UTF16 *name16;
4801
0
        UTF8 *name8;
4802
0
        char name8buf[NTFS_MAXNAMLEN_UTF8 + 1];
4803
0
        int retVal;
4804
4805
0
        fs_attr = tsk_fs_attrlist_get_idx(fs_file->meta->attr, idx);
4806
0
        if (fs_attr->type != NTFS_ATYPE_FNAME) {
4807
0
            continue;
4808
0
        }
4809
0
        fname = (ntfs_attr_fname *) fs_attr->rd.buf;
4810
4811
0
        tsk_fprintf(hFile, "\n$FILE_NAME Attribute Values:\n");
4812
0
        flags = tsk_getu64(fs->endian, fname->flags);
4813
0
        tsk_fprintf(hFile, "Flags: ");
4814
0
        if (flags & NTFS_FNAME_FLAGS_DIR)
4815
0
            tsk_fprintf(hFile, "%sDirectory", a++ == 0 ? "" : ", ");
4816
0
        if (flags & NTFS_FNAME_FLAGS_DEV)
4817
0
            tsk_fprintf(hFile, "%sDevice", a++ == 0 ? "" : ", ");
4818
0
        if (flags & NTFS_FNAME_FLAGS_NORM)
4819
0
            tsk_fprintf(hFile, "%sNormal", a++ == 0 ? "" : ", ");
4820
0
        if (flags & NTFS_FNAME_FLAGS_RO)
4821
0
            tsk_fprintf(hFile, "%sRead Only", a++ == 0 ? "" : ", ");
4822
0
        if (flags & NTFS_FNAME_FLAGS_HID)
4823
0
            tsk_fprintf(hFile, "%sHidden", a++ == 0 ? "" : ", ");
4824
0
        if (flags & NTFS_FNAME_FLAGS_SYS)
4825
0
            tsk_fprintf(hFile, "%sSystem", a++ == 0 ? "" : ", ");
4826
0
        if (flags & NTFS_FNAME_FLAGS_ARCH)
4827
0
            tsk_fprintf(hFile, "%sArchive", a++ == 0 ? "" : ", ");
4828
0
        if (flags & NTFS_FNAME_FLAGS_TEMP)
4829
0
            tsk_fprintf(hFile, "%sTemp", a++ == 0 ? "" : ", ");
4830
0
        if (flags & NTFS_FNAME_FLAGS_SPAR)
4831
0
            tsk_fprintf(hFile, "%sSparse", a++ == 0 ? "" : ", ");
4832
0
        if (flags & NTFS_FNAME_FLAGS_REP)
4833
0
            tsk_fprintf(hFile, "%sReparse Point", a++ == 0 ? "" : ", ");
4834
0
        if (flags & NTFS_FNAME_FLAGS_COMP)
4835
0
            tsk_fprintf(hFile, "%sCompressed", a++ == 0 ? "" : ", ");
4836
0
        if (flags & NTFS_FNAME_FLAGS_ENC)
4837
0
            tsk_fprintf(hFile, "%sEncrypted", a++ == 0 ? "" : ", ");
4838
0
        if (flags & NTFS_FNAME_FLAGS_OFF)
4839
0
            tsk_fprintf(hFile, "%sOffline", a++ == 0 ? "" : ", ");
4840
0
        if (flags & NTFS_FNAME_FLAGS_NOIDX)
4841
0
            tsk_fprintf(hFile, "%sNot Content Indexed",
4842
0
                a++ == 0 ? "" : ", ");
4843
0
        if (flags & NTFS_FNAME_FLAGS_IDXVIEW)
4844
0
            tsk_fprintf(hFile, "%sIndex View", a++ == 0 ? "" : ", ");
4845
0
        tsk_fprintf(hFile, "\n");
4846
4847
4848
0
        name16 = (UTF16 *) & fname->name;
4849
0
        name8 = (UTF8 *) name8buf;
4850
4851
0
        retVal =
4852
0
            tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
4853
0
            (UTF16 *) ((uintptr_t) name16 +
4854
0
                fname->nlen * 2),
4855
0
            &name8,
4856
0
            (UTF8 *) ((uintptr_t) name8 + NTFS_MAXNAMLEN_UTF8),
4857
0
            TSKlenientConversion);
4858
0
        if (retVal != TSKconversionOK) {
4859
0
            if (tsk_verbose)
4860
0
                tsk_fprintf(stderr,
4861
0
                    "ntfs_istat: Error converting NTFS name in $FNAME to UTF8: %d",
4862
0
                    retVal);
4863
0
            *name8 = '\0';
4864
0
        }
4865
        /* Make sure it is NULL Terminated */
4866
0
        else if ((uintptr_t) name8 >=
4867
0
            (uintptr_t) name8buf + NTFS_MAXNAMLEN_UTF8)
4868
0
            name8buf[NTFS_MAXNAMLEN_UTF8] = '\0';
4869
0
        else
4870
0
            *name8 = '\0';
4871
4872
4873
0
        tsk_fprintf(hFile, "Name: %s\n", name8buf);
4874
4875
0
        tsk_fprintf(hFile,
4876
0
            "Parent MFT Entry: %" PRIu64
4877
0
            " \tSequence: %" PRIu16 "\n",
4878
0
            (uint64_t) tsk_getu48(fs->endian, fname->par_ref),
4879
0
            tsk_getu16(fs->endian, fname->par_seq));
4880
0
        tsk_fprintf(hFile,
4881
0
            "Allocated Size: %" PRIu64
4882
0
            "   \tActual Size: %" PRIu64 "\n",
4883
0
            tsk_getu64(fs->endian, fname->alloc_fsize),
4884
0
            tsk_getu64(fs->endian, fname->real_fsize));
4885
        /*
4886
         * Times
4887
         */
4888
4889
        /* Times - take it from fs_file->meta instead of redoing the work */
4890
4891
0
        if (sec_skew != 0) {
4892
0
            tsk_fprintf(hFile, "\nAdjusted times:\n");
4893
4894
0
            tsk_fprintf(hFile, "Created:\t%s\n",
4895
0
                        tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->crtime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->crtime)), timeBuf));
4896
0
            tsk_fprintf(hFile, "File Modified:\t%s\n",
4897
0
                        tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->mtime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->mtime)), timeBuf));
4898
0
            tsk_fprintf(hFile, "MFT Modified:\t%s\n",
4899
0
                        tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->ctime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->ctime)), timeBuf));
4900
0
            tsk_fprintf(hFile, "Accessed:\t%s\n",
4901
0
                        tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->atime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->atime)), timeBuf));
4902
4903
0
            tsk_fprintf(hFile, "\nOriginal times:\n");
4904
0
        }
4905
4906
0
        tsk_fprintf(hFile, "Created:\t%s\n",
4907
0
                    tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->crtime)), nt2nano(tsk_getu64(fs->endian, fname->crtime)), timeBuf));
4908
0
        tsk_fprintf(hFile, "File Modified:\t%s\n",
4909
0
                    tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->mtime)), nt2nano(tsk_getu64(fs->endian, fname->mtime)), timeBuf));
4910
0
        tsk_fprintf(hFile, "MFT Modified:\t%s\n",
4911
0
                    tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->ctime)), nt2nano(tsk_getu64(fs->endian, fname->ctime)), timeBuf));
4912
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
4913
0
                    tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->atime)), nt2nano(tsk_getu64(fs->endian, fname->atime)), timeBuf));
4914
0
    }
4915
4916
4917
    /* $OBJECT_ID Information */
4918
0
    fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_OBJID);
4919
0
    if (fs_attr) {
4920
0
        ntfs_attr_objid *objid = (ntfs_attr_objid *) fs_attr->rd.buf;
4921
0
        uint64_t id1, id2;
4922
0
        tsk_fprintf(hFile, "\n$OBJECT_ID Attribute Values:\n");
4923
0
        id1 = tsk_getu64(fs->endian, objid->objid1);
4924
0
        id2 = tsk_getu64(fs->endian, objid->objid2);
4925
0
        tsk_fprintf(hFile,
4926
0
            "Object Id: %.8" PRIx32 "-%.4" PRIx16
4927
0
            "-%.4" PRIx16 "-%.4" PRIx16 "-%.4"
4928
0
            PRIx16 "%.8" PRIx32 "\n",
4929
0
            tsk_getu32(fs->endian, objid->objid1),
4930
0
            tsk_getu16(fs->endian, objid->objid2),
4931
0
            tsk_getu16(fs->endian, objid->objid3),
4932
0
            tsk_getu16(TSK_BIG_ENDIAN, objid->objid4),
4933
0
            tsk_getu16(TSK_BIG_ENDIAN, objid->objid5),
4934
0
            tsk_getu32(TSK_BIG_ENDIAN, objid->objid6));
4935
4936
        /* The rest of the  fields do not always exist.  Check the attr size */
4937
0
        if (fs_attr->size > 16) {
4938
0
            id1 = tsk_getu64(fs->endian, objid->orig_volid1);
4939
0
            id2 = tsk_getu64(fs->endian, objid->orig_volid2);
4940
0
            tsk_fprintf(hFile,
4941
0
                "Birth Volume Id: %.8" PRIx32 "-%.4"
4942
0
                PRIx16 "-%.4" PRIx16 "-%.4" PRIx16
4943
0
                "-%.12" PRIx64 "\n",
4944
0
                (uint32_t) (id2 >> 32) & 0xffffffff,
4945
0
                (uint16_t) (id2 >> 16) & 0xffff,
4946
0
                (uint16_t) (id2 & 0xffff),
4947
0
                (uint16_t) (id1 >> 48) & 0xffff,
4948
0
                (uint64_t) (id1 & (uint64_t)
4949
0
                    0x0000ffffffffffffULL));
4950
0
        }
4951
4952
0
        if (fs_attr->size > 32) {
4953
0
            id1 = tsk_getu64(fs->endian, objid->orig_objid1);
4954
0
            id2 = tsk_getu64(fs->endian, objid->orig_objid2);
4955
0
            tsk_fprintf(hFile,
4956
0
                "Birth Object Id: %.8" PRIx32 "-%.4"
4957
0
                PRIx16 "-%.4" PRIx16 "-%.4" PRIx16
4958
0
                "-%.12" PRIx64 "\n",
4959
0
                (uint32_t) (id2 >> 32) & 0xffffffff,
4960
0
                (uint16_t) (id2 >> 16) & 0xffff,
4961
0
                (uint16_t) (id2 & 0xffff),
4962
0
                (uint16_t) (id1 >> 48) & 0xffff,
4963
0
                (uint64_t) (id1 & (uint64_t)
4964
0
                    0x0000ffffffffffffULL));
4965
0
        }
4966
4967
0
        if (fs_attr->size > 48) {
4968
0
            id1 = tsk_getu64(fs->endian, objid->orig_domid1);
4969
0
            id2 = tsk_getu64(fs->endian, objid->orig_domid2);
4970
0
            tsk_fprintf(hFile,
4971
0
                "Birth Domain Id: %.8" PRIx32 "-%.4"
4972
0
                PRIx16 "-%.4" PRIx16 "-%.4" PRIx16
4973
0
                "-%.12" PRIx64 "\n",
4974
0
                (uint32_t) (id2 >> 32) & 0xffffffff,
4975
0
                (uint16_t) (id2 >> 16) & 0xffff,
4976
0
                (uint16_t) (id2 & 0xffff),
4977
0
                (uint16_t) (id1 >> 48) & 0xffff,
4978
0
                (uint64_t) (id1 & (uint64_t)
4979
0
                    0x0000ffffffffffffULL));
4980
0
        }
4981
0
    }
4982
4983
    /* Attribute List Information */
4984
0
    fs_attr =
4985
0
        tsk_fs_attrlist_get(fs_file->meta->attr,  (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_ATTRLIST);
4986
0
    if (fs_attr) {
4987
0
        char *buf;
4988
0
        ntfs_attrlist *list;
4989
0
        uintptr_t endaddr;
4990
0
        TSK_FS_LOAD_FILE load_file;
4991
4992
0
        tsk_fprintf(hFile, "\n$ATTRIBUTE_LIST Attribute Values:\n");
4993
4994
        /* Get a copy of the attribute list stream  */
4995
0
        load_file.total = load_file.left = (size_t) fs_attr->size;
4996
0
        load_file.cur = load_file.base = buf =
4997
0
            (char*) tsk_malloc((size_t) fs_attr->size);
4998
0
        if (buf == NULL) {
4999
0
            free(mft);
5000
0
            return 1;
5001
0
        }
5002
5003
0
        endaddr = (uintptr_t) buf + (uintptr_t) fs_attr->size;
5004
0
        if (tsk_fs_attr_walk(fs_attr,
5005
0
                TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_load_file_action, (void *) &load_file)) {
5006
0
            tsk_fprintf(hFile, "error reading attribute list buffer\n");
5007
0
            tsk_error_reset();
5008
0
            goto egress;
5009
0
        }
5010
5011
        /* this value should be zero, if not then we didn't read all of the
5012
         * buffer
5013
         */
5014
0
        if (load_file.left > 0) {
5015
0
            tsk_fprintf(hFile, "error reading attribute list buffer\n");
5016
0
            goto egress;
5017
0
        }
5018
5019
        /* Process the list & print the details */
5020
0
        for (list = (ntfs_attrlist *) buf;
5021
0
            (list) && ((uintptr_t) list < endaddr)
5022
0
            && (tsk_getu16(fs->endian, list->len) > 0);
5023
0
            list =
5024
0
            (ntfs_attrlist *) ((uintptr_t) list + tsk_getu16(fs->endian,
5025
0
                    list->len))) {
5026
0
            tsk_fprintf(hFile,
5027
0
                "Type: %" PRIu32 "-%" PRIu16 " \tMFT Entry: %" PRIu64
5028
0
                " \tVCN: %" PRIu64 "\n", tsk_getu32(fs->endian,
5029
0
                    list->type), tsk_getu16(fs->endian, list->id),
5030
0
                (uint64_t) tsk_getu48(fs->endian, list->file_ref),
5031
0
                tsk_getu64(fs->endian, list->start_vcn));
5032
0
        }
5033
0
      egress:
5034
0
        free(buf);
5035
0
    }
5036
5037
    /* Print all of the attributes */
5038
0
    tsk_fprintf(hFile, "\nAttributes: \n");
5039
0
    if (fs_file->meta->attr) {
5040
0
        int cnt, i;
5041
5042
        // cycle through the attributes
5043
0
        cnt = tsk_fs_file_attr_getsize(fs_file.get());
5044
0
        for (i = 0; i < cnt; i++) {
5045
0
            char type[512];
5046
5047
0
            const TSK_FS_ATTR *fs_attr =
5048
0
                tsk_fs_file_attr_get_idx(fs_file.get(), i);
5049
0
            if (!fs_attr)
5050
0
                continue;
5051
5052
0
            if (ntfs_attrname_lookup(fs, fs_attr->type, type, 512)) {
5053
0
                tsk_fprintf(hFile, "error looking attribute name\n");
5054
0
                break;
5055
0
            }
5056
5057
            /* print the layout if it is non-resident and not "special" */
5058
0
            if (fs_attr->flags & TSK_FS_ATTR_NONRES) {
5059
0
                NTFS_PRINT_ADDR print_addr;
5060
5061
0
                tsk_fprintf(hFile,
5062
0
                    "Type: %s (%" PRIu32 "-%" PRIu16
5063
0
                    ")   Name: %s   Non-Resident%s%s%s   size: %"
5064
0
          PRIdOFF "  init_size: %" PRIdOFF "\n", type,
5065
0
                    fs_attr->type, fs_attr->id,
5066
0
                    (fs_attr->name) ? fs_attr->name : "N/A",
5067
0
                    (fs_attr->flags & TSK_FS_ATTR_ENC) ? ", Encrypted" :
5068
0
                    "",
5069
0
                    (fs_attr->flags & TSK_FS_ATTR_COMP) ? ", Compressed" :
5070
0
                    "",
5071
0
                    (fs_attr->flags & TSK_FS_ATTR_SPARSE) ? ", Sparse" :
5072
0
                    "", fs_attr->size, fs_attr->nrd.initsize);
5073
0
                if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
5074
0
                    if (tsk_fs_attr_print(fs_attr, hFile)) {
5075
0
                        tsk_fprintf(hFile, "\nError creating run lists\n");
5076
0
                        tsk_error_print(hFile);
5077
0
                        tsk_error_reset();
5078
0
                    }
5079
0
                }
5080
0
                else {
5081
0
                    print_addr.idx = 0;
5082
0
                    print_addr.hFile = hFile;
5083
0
                    if (tsk_fs_file_walk_type(fs_file.get(), fs_attr->type,
5084
0
                        fs_attr->id,
5085
0
                        (TSK_FS_FILE_WALK_FLAG_ENUM)
5086
0
                        (TSK_FS_FILE_WALK_FLAG_AONLY |
5087
0
                            TSK_FS_FILE_WALK_FLAG_SLACK),
5088
0
                        print_addr_act, (void *)&print_addr)) {
5089
0
                        tsk_fprintf(hFile, "\nError walking file\n");
5090
0
                        tsk_error_print(hFile);
5091
0
                        tsk_error_reset();
5092
0
                    }
5093
0
                    if (print_addr.idx != 0)
5094
0
                        tsk_fprintf(hFile, "\n");
5095
0
                }
5096
5097
0
            }
5098
0
            else {
5099
0
                tsk_fprintf(hFile,
5100
0
                    "Type: %s (%" PRIu32 "-%" PRIu16
5101
0
                    ")   Name: %s   Resident%s%s%s   size: %"
5102
0
          PRIdOFF "\n", type, fs_attr->type,
5103
0
                    fs_attr->id,
5104
0
                    (fs_attr->name) ? fs_attr->name : "N/A",
5105
0
                    (fs_attr->flags & TSK_FS_ATTR_ENC) ? ", Encrypted"
5106
0
                    : "",
5107
0
                    (fs_attr->flags & TSK_FS_ATTR_COMP) ?
5108
0
                    ", Compressed" : "",
5109
0
                    (fs_attr->flags & TSK_FS_ATTR_SPARSE) ? ", Sparse" :
5110
0
                    "", fs_attr->size);
5111
5112
0
            }
5113
0
        }
5114
0
    }
5115
5116
0
    free(mft);
5117
0
    return 0;
5118
0
}
5119
5120
5121
5122
/* JOURNAL CODE - MOVE TO NEW FILE AT SOME POINT */
5123
5124
static uint8_t
5125
ntfs_jopen(
5126
  [[maybe_unused]] TSK_FS_INFO * fs,
5127
  [[maybe_unused]] TSK_INUM_T inum)
5128
0
{
5129
0
    tsk_error_reset();
5130
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
5131
0
    tsk_error_set_errstr("NTFS Journal is not yet supported\n");
5132
0
    return 1;
5133
0
}
5134
5135
static uint8_t
5136
ntfs_jentry_walk(
5137
  [[maybe_unused]] TSK_FS_INFO * fs,
5138
  [[maybe_unused]] int flags,
5139
  [[maybe_unused]] TSK_FS_JENTRY_WALK_CB a_action,
5140
  [[maybe_unused]] void *ptr)
5141
0
{
5142
0
    tsk_error_reset();
5143
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
5144
0
    tsk_error_set_errstr("NTFS Journal is not yet supported\n");
5145
0
    return 1;
5146
0
}
5147
5148
static uint8_t
5149
ntfs_jblk_walk(
5150
  [[maybe_unused]] TSK_FS_INFO * fs,
5151
  [[maybe_unused]] TSK_DADDR_T start,
5152
  [[maybe_unused]] TSK_DADDR_T end,
5153
  [[maybe_unused]] int flags,
5154
  [[maybe_unused]] TSK_FS_JBLK_WALK_CB a_action,
5155
  [[maybe_unused]] void *ptr)
5156
0
{
5157
0
    tsk_error_reset();
5158
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
5159
0
    tsk_error_set_errstr("NTFS Journal is not yet supported\n");
5160
0
    return 1;
5161
0
}
5162
5163
5164
static TSK_FS_ATTR_TYPE_ENUM
5165
ntfs_get_default_attr_type(const TSK_FS_FILE * a_file)
5166
0
{
5167
0
    if ((a_file == NULL) || (a_file->meta == NULL))
5168
0
        return TSK_FS_ATTR_TYPE_DEFAULT;
5169
5170
    /* Use DATA for files and IDXROOT for dirs */
5171
0
    if (TSK_FS_IS_DIR_META(a_file->meta->type))
5172
0
        return TSK_FS_ATTR_TYPE_NTFS_IDXROOT;
5173
0
    else
5174
0
        return TSK_FS_ATTR_TYPE_NTFS_DATA;
5175
5176
0
}
5177
5178
5179
static void
5180
ntfs_close(TSK_FS_INFO * fs)
5181
0
{
5182
0
    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
5183
5184
0
    if (fs == NULL)
5185
0
        return;
5186
5187
0
#if TSK_USE_SID
5188
0
    free(ntfs->sii_data.buffer);
5189
0
    ntfs->sii_data.buffer = NULL;
5190
5191
0
    free(ntfs->sds_data.buffer);
5192
0
    ntfs->sds_data.buffer = NULL;
5193
5194
0
#endif
5195
5196
0
    fs->tag = 0;
5197
0
    free(ntfs->fs);
5198
0
    tsk_fs_attr_run_free(ntfs->bmap);
5199
0
    free(ntfs->bmap_buf);
5200
0
    tsk_fs_file_close(ntfs->mft_file);
5201
5202
0
    if (ntfs->orphan_map)
5203
0
        ntfs_orphan_map_free(ntfs);
5204
5205
0
    tsk_deinit_lock(&ntfs->lock);
5206
0
    tsk_deinit_lock(&ntfs->orphan_map_lock);
5207
0
#if TSK_USE_SID
5208
0
    tsk_deinit_lock(&ntfs->sid_lock);
5209
0
#endif
5210
5211
0
    tsk_fs_free(fs);
5212
0
}
5213
5214
/**
5215
 * Check if the boot format matches that produced in KAPE VHDs
5216
 * that are missing the 0x55AA marker.
5217
 * Will also set the endianness.
5218
 *
5219
 * @param ntfs_info File system info
5220
 * @returns 0 if format appeares valid, 1 otherwise
5221
 */
5222
static int
5223
0
process_kape_boot_format(NTFS_INFO* ntfs_info) {
5224
5225
    // Check that we have a VHD
5226
0
    if (ntfs_info->fs_info.img_info->itype != TSK_IMG_TYPE_VHD_VHD) {
5227
0
        return 1;
5228
0
    }
5229
5230
    // Check that expected name is present
5231
0
    if (strncmp(ntfs_info->fs->oemname, "NTFS    ", 8) != 0) {
5232
0
        return 1;
5233
0
    }
5234
5235
    // Check endianness using the sector size
5236
0
    uint16_t ssize = tsk_getu16(TSK_LIT_ENDIAN, ntfs_info->fs->ssize);
5237
0
    if ((ssize != 0) && (ssize % 512 == 0)) {
5238
0
        ntfs_info->fs_info.endian = TSK_LIT_ENDIAN;
5239
0
        return 0;
5240
0
    }
5241
0
    ssize = tsk_getu16(TSK_BIG_ENDIAN, ntfs_info->fs->ssize);
5242
0
    if ((ssize != 0) && (ssize % 512 == 0)) {
5243
0
        ntfs_info->fs_info.endian = TSK_BIG_ENDIAN;
5244
0
        return 0;
5245
0
    }
5246
5247
0
    return 1;
5248
0
}
5249
5250
/**
5251
 * Open part of a disk image as an NTFS file system.
5252
 *
5253
 * @param img_info Disk image to analyze
5254
 * @param offset Byte offset where NTFS file system starts
5255
 * @param ftype Specific type of NTFS file system
5256
 * @param a_pass (Optional) bitlocker password
5257
 * @param test NOT USED
5258
 * @returns NULL on error or if data is not an NTFS file system
5259
 */
5260
TSK_FS_INFO *
5261
ntfs_open(
5262
  TSK_IMG_INFO * img_info,
5263
  TSK_OFF_T offset,
5264
  TSK_FS_TYPE_ENUM ftype,
5265
  const char* a_pass,
5266
  [[maybe_unused]] uint8_t test)
5267
0
{
5268
0
    const char *myname = "ntfs_open";
5269
0
    NTFS_INFO *ntfs = NULL;
5270
0
    TSK_FS_INFO *fs = NULL;
5271
0
    unsigned int len = 0;
5272
0
    ssize_t cnt = 0;
5273
5274
    // clean up any error messages that are lying around
5275
0
    tsk_error_reset();
5276
5277
0
    if (TSK_FS_TYPE_ISNTFS(ftype) == 0) {
5278
0
        tsk_error_reset();
5279
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
5280
0
        tsk_error_set_errstr("Invalid FS type in ntfs_open");
5281
0
        return NULL;
5282
0
    }
5283
5284
0
    if (img_info->sector_size == 0) {
5285
0
        tsk_error_reset();
5286
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
5287
0
        tsk_error_set_errstr("ntfs_open: sector size is 0");
5288
0
        return NULL;
5289
0
    }
5290
5291
0
    uint32_t csize;
5292
5293
0
    if ((ntfs = (NTFS_INFO *) tsk_fs_malloc(sizeof(*ntfs))) == NULL) {
5294
0
        goto on_error;
5295
0
    }
5296
0
    fs = &(ntfs->fs_info);
5297
5298
0
    fs->ftype = TSK_FS_TYPE_NTFS;
5299
0
    fs->duname = "Cluster";
5300
0
    fs->flags = TSK_FS_INFO_FLAG_HAVE_SEQ;
5301
0
    fs->tag = TSK_FS_INFO_TAG;
5302
5303
0
    fs->img_info = img_info;
5304
0
    fs->offset = offset;
5305
5306
0
    ntfs->loading_the_MFT = 0;
5307
0
    ntfs->bmap = NULL;
5308
0
    ntfs->bmap_buf = NULL;
5309
5310
    // Check for any volume encryption and initialize if found.
5311
    // A non-zero value will only be returned if we are very confident encryption was found
5312
    // but encountered an error and should not continue trying to open the volume.
5313
    // In this case we should also have a specific error to get back to the user, such as reporting an incorrect password.
5314
0
    if (0 != handleVolumeEncryption((TSK_FS_INFO*)ntfs, a_pass)) {
5315
0
        goto on_error;
5316
0
    }
5317
5318
    /* Read the boot sector */
5319
0
    len = roundup(sizeof(ntfs_sb), img_info->sector_size);
5320
0
    ntfs->fs = (ntfs_sb *) tsk_malloc(len);
5321
0
    if (ntfs->fs == NULL) {
5322
0
        goto on_error;
5323
0
    }
5324
5325
0
    cnt = tsk_fs_read(fs, (TSK_OFF_T) 0, (char *) ntfs->fs, len);
5326
0
    if (cnt != len) {
5327
0
        if (cnt >= 0) {
5328
0
            tsk_error_reset();
5329
0
            tsk_error_set_errno(TSK_ERR_FS_READ);
5330
0
        }
5331
0
        tsk_error_set_errstr2("%s: Error reading boot sector.", myname);
5332
0
        goto on_error;
5333
0
    }
5334
5335
    /* Check the magic value */
5336
0
    if (tsk_fs_guessu16(fs, ntfs->fs->magic, NTFS_FS_MAGIC)) {
5337
0
        if (process_kape_boot_format(ntfs)) {
5338
0
            tsk_error_reset();
5339
0
            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5340
0
            tsk_error_set_errstr("Not a NTFS file system (magic)");
5341
0
            if (tsk_verbose)
5342
0
                fprintf(stderr, "ntfs_open: Incorrect NTFS magic\n");
5343
0
            goto on_error;
5344
0
        }
5345
0
    }
5346
5347
5348
    /*
5349
     * block calculations : although there are no blocks in ntfs,
5350
     * we are using a cluster as a "block"
5351
     */
5352
5353
0
    ntfs->ssize_b = tsk_getu16(fs->endian, ntfs->fs->ssize);
5354
0
    if ((ntfs->ssize_b == 0) || (ntfs->ssize_b % 512)) {
5355
0
        tsk_error_reset();
5356
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5357
0
        tsk_error_set_errstr
5358
0
            ("Not a NTFS file system (invalid sector size %d))",
5359
0
            ntfs->ssize_b);
5360
0
        if (tsk_verbose)
5361
0
            fprintf(stderr, "ntfs_open: invalid sector size: %d\n",
5362
0
                ntfs->ssize_b);
5363
0
        goto on_error;
5364
0
    }
5365
5366
0
    csize = ntfs->fs->csize;
5367
0
    if (ntfs->fs->csize > 0x80) {
5368
0
      csize = 1 << -(int8_t)ntfs->fs->csize;
5369
0
    }
5370
5371
0
    if ((csize != 0x01) &&
5372
0
      (csize != 0x02) &&
5373
0
      (csize != 0x04) &&
5374
0
      (csize != 0x08) &&
5375
0
      (csize != 0x10) &&
5376
0
      (csize != 0x20) &&
5377
0
      (csize != 0x40) &&
5378
0
      (csize != 0x80) &&
5379
0
      (csize != 0x100) &&
5380
0
      (csize != 0x200) &&
5381
0
      (csize != 0x400) &&
5382
0
      (csize != 0x800) &&
5383
0
      (csize != 0x1000)
5384
0
      ) {
5385
0
        tsk_error_reset();
5386
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5387
0
        tsk_error_set_errstr
5388
0
            ("Not a NTFS file system (invalid cluster size %d)",
5389
0
            ntfs->fs->csize);
5390
0
        if (tsk_verbose)
5391
0
            fprintf(stderr, "ntfs_open: invalid cluster size: %d\n",
5392
0
                ntfs->fs->csize);
5393
0
        goto on_error;
5394
0
    }
5395
5396
0
    ntfs->csize_b = csize * ntfs->ssize_b;
5397
0
    fs->first_block = 0;
5398
    /* This field is defined as 64-bits but according to the
5399
     * NTFS drivers in Linux, old Windows versions used only 32-bits
5400
     */
5401
0
    fs->block_count =
5402
0
        (TSK_DADDR_T) tsk_getu64(fs->endian,
5403
0
        ntfs->fs->vol_size_s) / csize;
5404
0
    if (fs->block_count == 0) {
5405
0
        tsk_error_reset();
5406
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5407
0
        tsk_error_set_errstr("Not a NTFS file system (volume size is 0)");
5408
0
        if (tsk_verbose)
5409
0
            fprintf(stderr, "ntfs_open: invalid volume size: 0\n");
5410
0
        goto on_error;
5411
0
    }
5412
5413
0
    fs->last_block = fs->last_block_act = fs->block_count - 1;
5414
0
    fs->block_size = ntfs->csize_b;
5415
0
    fs->dev_bsize = img_info->sector_size;
5416
5417
    // determine the last block we have in this image
5418
0
    if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) <
5419
0
        fs->block_count)
5420
0
        fs->last_block_act =
5421
0
            (img_info->size - offset) / fs->block_size - 1;
5422
5423
0
    ntfs->mft_rsize_b = 0;
5424
0
    if (ntfs->fs->mft_rsize_c > 0) {
5425
0
        ntfs->mft_rsize_b = ntfs->fs->mft_rsize_c * ntfs->csize_b;
5426
0
    }
5427
0
    else if (ntfs->fs->mft_rsize_c > -32) {
5428
        /* if the mft_rsize_c is not > 0, then it is -log2(rsize_b) */
5429
0
        ntfs->mft_rsize_b = 1 << -ntfs->fs->mft_rsize_c;
5430
0
    }
5431
5432
0
    if ((ntfs->mft_rsize_b == 0) || (ntfs->mft_rsize_b % 512)) {
5433
0
        tsk_error_reset();
5434
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5435
0
        tsk_error_set_errstr
5436
0
            ("Not a NTFS file system (invalid MFT entry size)");
5437
0
        if (tsk_verbose)
5438
0
            fprintf(stderr, "ntfs_open: invalid MFT entry size\n");
5439
0
        goto on_error;
5440
0
    }
5441
5442
0
    ntfs->idx_rsize_b = 0;
5443
0
    if (ntfs->fs->idx_rsize_c > 0) {
5444
0
        ntfs->idx_rsize_b = ntfs->fs->idx_rsize_c * ntfs->csize_b;
5445
0
    }
5446
0
    else if (ntfs->fs->idx_rsize_c > -32) {
5447
        /* if the idx_rsize_c is not > 0, then it is -log2(rsize_b) */
5448
0
        ntfs->idx_rsize_b = 1 << -ntfs->fs->idx_rsize_c;
5449
0
    }
5450
5451
0
    if ((ntfs->idx_rsize_b == 0) || (ntfs->idx_rsize_b % 512)) {
5452
0
        tsk_error_reset();
5453
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5454
0
        tsk_error_set_errstr
5455
0
            ("Not a NTFS file system (invalid idx record size %d)",
5456
0
            ntfs->idx_rsize_b);
5457
0
        if (tsk_verbose)
5458
0
            fprintf(stderr, "ntfs_open: invalid idx record size %d\n",
5459
0
                ntfs->idx_rsize_b);
5460
0
        goto on_error;
5461
0
    }
5462
5463
0
    ntfs->root_mft_addr =
5464
0
        tsk_getu64(fs->endian, ntfs->fs->mft_clust) * ntfs->csize_b;
5465
0
    if (tsk_getu64(fs->endian, ntfs->fs->mft_clust) > fs->last_block) {
5466
0
        tsk_error_reset();
5467
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
5468
0
        tsk_error_set_errstr
5469
0
            ("Not a NTFS file system (invalid starting MFT clust)");
5470
0
        if (tsk_verbose)
5471
0
            fprintf(stderr, "ntfs_open: invalid starting MFT cluster\n");
5472
0
        goto on_error;
5473
0
    }
5474
5475
    /*
5476
     * Set the function pointers (before we start calling internal functions)
5477
     */
5478
0
    fs->inode_walk = ntfs_inode_walk;
5479
0
    fs->block_walk = ntfs_block_walk;
5480
0
    fs->block_getflags = ntfs_block_getflags;
5481
5482
0
    fs->get_default_attr_type = ntfs_get_default_attr_type;
5483
0
    fs->load_attrs = ntfs_load_attrs;
5484
5485
0
    fs->file_add_meta = ntfs_inode_lookup;
5486
0
    fs->dir_open_meta = ntfs_dir_open_meta;
5487
0
    fs->fsstat = ntfs_fsstat;
5488
0
    fs->fscheck = ntfs_fscheck;
5489
0
    fs->istat = ntfs_istat;
5490
0
    fs->close = ntfs_close;
5491
0
    fs->name_cmp = ntfs_name_cmp;
5492
5493
0
    fs->fread_owner_sid = ntfs_file_get_sidstr;
5494
0
    fs->jblk_walk = ntfs_jblk_walk;
5495
0
    fs->jentry_walk = ntfs_jentry_walk;
5496
0
    fs->jopen = ntfs_jopen;
5497
0
    fs->journ_inum = 0;
5498
5499
5500
5501
    // set up locks
5502
0
    tsk_init_lock(&ntfs->lock);
5503
0
    tsk_init_lock(&ntfs->orphan_map_lock);
5504
0
#if TSK_USE_SID
5505
0
    tsk_init_lock(&ntfs->sid_lock);
5506
0
#endif
5507
5508
    /*
5509
     * inode
5510
     */
5511
5512
0
    fs->root_inum = NTFS_ROOTINO;
5513
0
    fs->first_inum = NTFS_FIRSTINO;
5514
0
    fs->last_inum = NTFS_LAST_DEFAULT_INO;
5515
0
    ntfs->mft_data = NULL;
5516
5517
    /* load the data run for the MFT table into ntfs->mft */
5518
0
    ntfs->loading_the_MFT = 1;
5519
0
    if ((ntfs->mft_file =
5520
0
            tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_MFT)) == NULL) {
5521
0
        if (tsk_verbose)
5522
0
            fprintf(stderr,
5523
0
                "ntfs_open: Error opening $MFT (%s)\n", tsk_error_get());
5524
0
        goto on_error;
5525
0
    }
5526
5527
    /* cache the data attribute
5528
     *
5529
     * This will likely be done already by proc_attrseq, but this
5530
     * should be quick
5531
     */
5532
0
    ntfs->mft_data =
5533
0
        tsk_fs_attrlist_get(ntfs->mft_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_DATA);
5534
0
    if (!ntfs->mft_data) {
5535
0
        tsk_error_errstr2_concat(" - Data Attribute not found in $MFT");
5536
0
        if (tsk_verbose)
5537
0
            fprintf(stderr,
5538
0
                "ntfs_open: Data attribute not found in $MFT (%s)\n",
5539
0
                tsk_error_get());
5540
0
        goto on_error;
5541
0
    }
5542
5543
    /* Get the inode count based on the table size */
5544
0
    fs->inum_count = ntfs->mft_data->size / ntfs->mft_rsize_b + 1;      // we are adding 1 in this calc to account for Orphans directory
5545
0
    fs->last_inum = fs->inum_count - 1;
5546
5547
    /* reset the flag that we are no longer loading $MFT */
5548
0
    ntfs->loading_the_MFT = 0;
5549
5550
    /* Volume ID */
5551
0
    for (fs->fs_id_used = 0; fs->fs_id_used < 8; fs->fs_id_used++) {
5552
0
        fs->fs_id[fs->fs_id_used] = ntfs->fs->serial[fs->fs_id_used];
5553
0
    }
5554
5555
    /* load the version of the file system */
5556
0
    if (ntfs_load_ver(ntfs)) {
5557
0
        if (tsk_verbose)
5558
0
            fprintf(stderr,
5559
0
                "ntfs_open: Error loading file system version ((%s)\n",
5560
0
                tsk_error_get());
5561
0
        goto on_error;
5562
0
    }
5563
5564
    /* load the data block bitmap data run into ntfs_info */
5565
0
    if (ntfs_load_bmap(ntfs)) {
5566
0
        if (tsk_verbose)
5567
0
            fprintf(stderr, "ntfs_open: Error loading block bitmap (%s)\n",
5568
0
                tsk_error_get());
5569
0
        goto on_error;
5570
0
    }
5571
5572
    /* load the SID data into ntfs_info ($Secure - $SDS, $SDH, $SII */
5573
5574
5575
0
#if TSK_USE_SID
5576
0
    if (ntfs_load_secure(ntfs)) {
5577
0
        if (tsk_verbose)
5578
0
            fprintf(stderr, "ntfs_open: Error loading Secure Info (%s)\n",
5579
0
                tsk_error_get());
5580
0
        goto on_error;
5581
0
    }
5582
0
#endif
5583
5584
    // initialize the caches
5585
0
    ntfs->attrdef = NULL;
5586
0
    ntfs->orphan_map = NULL;
5587
5588
    // initialize the number of allocated files
5589
0
    ntfs->alloc_file_count = 0;
5590
5591
0
    if (tsk_verbose) {
5592
0
        tsk_fprintf(stderr,
5593
0
            "ssize: %" PRIu16
5594
0
            " csize: %d serial: %" PRIx64 "\n",
5595
0
            tsk_getu16(fs->endian, ntfs->fs->ssize),
5596
0
            csize, tsk_getu64(fs->endian, ntfs->fs->serial));
5597
0
        tsk_fprintf(stderr,
5598
0
            "mft_rsize: %d idx_rsize: %d vol: %d mft: %"
5599
0
            PRIu64 " mft_mir: %" PRIu64 "\n",
5600
0
            ntfs->mft_rsize_b, ntfs->idx_rsize_b,
5601
0
            (int) fs->block_count, tsk_getu64(fs->endian,
5602
0
                ntfs->fs->mft_clust), tsk_getu64(fs->endian,
5603
0
                ntfs->fs->mftm_clust));
5604
0
    }
5605
0
    return fs;
5606
5607
0
on_error:
5608
0
    ntfs_close(fs);
5609
    return NULL;
5610
0
}