Coverage Report

Created: 2025-07-23 06:41

/src/sleuthkit/tsk/fs/btrfs.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
** The Sleuth Kit
3
**
4
** Brian Carrier [carrier <at> sleuthkit [dot] org]
5
** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
6
**
7
** TASK
8
** Copyright (c) 2015 Stefan Pöschel.  All rights reserved
9
**
10
** This software is distributed under the Common Public License 1.0
11
*/
12
13
/**
14
 * \file btrfs.cpp
15
 * Contains the internal TSK Btrfs file system functions
16
 */
17
18
#include "tsk_fs_i.h"
19
#include "tsk_btrfs.h"
20
21
#include <cassert>
22
#include <memory>
23
24
25
/*
26
 * general stuff
27
 */
28
29
// enable for debug messages
30
//#define BTRFS_DEBUG
31
32
// enable to also check tree node checksums (otherwise only the superblock checksum is checked)
33
#define BTRFS_CHECK_TREENODE_CSUM
34
35
// size of treenode cache
36
0
#define BTRFS_TREENODE_CACHE_SIZE 50
37
38
39
40
#ifdef BTRFS_DEBUG
41
#define BTRFS_DEBUG_PRINT 1
42
#else
43
0
#define BTRFS_DEBUG_PRINT 0
44
#endif
45
46
#define btrfs_debug(format, ...) \
47
0
    do { if (BTRFS_DEBUG_PRINT) tsk_fprintf(stderr, "[btrfs] " format, ##__VA_ARGS__); } while (0)
48
49
50
#if 0
51
static void
52
btrfs_debug_hexdump(const uint8_t * data, const int len,
53
    const char *caption)
54
{
55
    if (caption)
56
        tsk_fprintf(stderr, "----- Hexdump of '%s' (%d bytes) -----", caption, len);
57
    for (int i = 0; i < len; i++) {
58
        if (i % 24 == 0)
59
            tsk_fprintf(stderr, "\n 0x%04x", i);
60
        tsk_fprintf(stderr, " %02x", *(data + i));
61
    }
62
    tsk_fprintf(stderr, "\n");
63
}
64
#endif
65
66
#ifndef MIN
67
#define MIN(a,b) (((a)<(b))?(a):(b))
68
#endif
69
70
71
72
73
/**
74
 * Resets error and sets error number/string
75
 * @param a_errno error number
76
 * @param a_format error string
77
 */
78
static void
79
btrfs_error(const uint32_t a_errno, const char *a_format, ...)
80
0
{
81
0
    tsk_error_reset();
82
0
    tsk_error_set_errno(a_errno);
83
84
0
    va_list args;
85
0
    va_start(args, a_format);
86
0
    tsk_error_vset_errstr(a_format, args);
87
0
    va_end(args);
88
0
}
89
90
91
92
93
94
95
/*
96
 * structure parsing
97
 */
98
99
100
static void
101
btrfs_key_rawparse(const uint8_t * a_raw, BTRFS_KEY * a_key)
102
0
{
103
0
    a_key->object_id    = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
104
0
    a_key->item_type    = a_raw[0x08];
105
0
    a_key->offset       = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x09);
106
0
}
107
108
109
static void
110
btrfs_time_rawparse(const uint8_t * a_raw, BTRFS_TIME * a_time)
111
0
{
112
0
    a_time->seconds     = tsk_gets64(BTRFS_ENDIAN, a_raw + 0x00);
113
0
    a_time->nanoseconds = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x08);
114
0
}
115
116
117
static void
118
btrfs_inode_rawparse(const uint8_t * a_raw, BTRFS_INODE_ITEM * a_ii)
119
0
{
120
0
    a_ii->generation        = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
121
0
    a_ii->transid           = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x08);
122
0
    a_ii->size              = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x10);
123
0
    a_ii->blocks            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x18);
124
0
    a_ii->block_group       = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x20);
125
0
    a_ii->nlink             = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x28);
126
0
    a_ii->uid               = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x2C);
127
0
    a_ii->gid               = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x30);
128
0
    a_ii->mode              = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x34);
129
0
    a_ii->rdev              = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x38);
130
0
    a_ii->flags             = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x40);
131
0
    a_ii->sequence          = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x48);
132
0
    memcpy(a_ii->_reserved  , a_raw + 0x50, sizeof(a_ii->_reserved));
133
0
    btrfs_time_rawparse(a_raw + 0x70, &a_ii->atime);
134
0
    btrfs_time_rawparse(a_raw + 0x7C, &a_ii->ctime);
135
0
    btrfs_time_rawparse(a_raw + 0x88, &a_ii->mtime);
136
0
    btrfs_time_rawparse(a_raw + 0x94, &a_ii->otime);
137
0
}
138
139
140
static void
141
btrfs_root_item_rawparse(const uint8_t * a_raw, BTRFS_ROOT_ITEM * a_ri)
142
0
{
143
0
    btrfs_inode_rawparse(a_raw + 0x00, &a_ri->inode);
144
0
    a_ri->expected_generation       = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xA0);
145
0
    a_ri->root_dir_object_id        = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xA8);
146
0
    a_ri->root_node_block_number    = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xB0);
147
0
    a_ri->byte_limit                = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xB8);
148
0
    a_ri->bytes_used                = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xC0);
149
0
    a_ri->last_snapshot_generation  = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xC8);
150
0
    a_ri->flags                     = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xD0);
151
0
    a_ri->number_of_references      = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xD8);
152
0
    btrfs_key_rawparse(a_raw + 0xDC, &a_ri->drop_progress);
153
0
    a_ri->drop_level                = a_raw[0xED];
154
0
    a_ri->root_node_level           = a_raw[0xEE];
155
0
}
156
157
158
static void
159
btrfs_dev_item_rawparse(const uint8_t * a_raw, BTRFS_DEV_ITEM * a_di)
160
0
{
161
0
    a_di->device_id             = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
162
0
    a_di->total_bytes           = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x08);
163
0
    a_di->bytes_used            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x10);
164
0
    a_di->optimal_io_align      = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x18);
165
0
    a_di->optimal_io_width      = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x1C);
166
0
    a_di->minimal_io_size       = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x20);
167
0
    a_di->type                  = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x24);
168
0
    a_di->generation            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x2C);
169
0
    a_di->start_offset          = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x34);
170
0
    a_di->dev_group             = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x3C);
171
0
    a_di->seek_speed            = a_raw[0x40];
172
0
    a_di->bandwidth             = a_raw[0x41];
173
0
    memcpy(a_di->device_uuid    , a_raw + 0x42, sizeof(a_di->device_uuid));
174
0
    memcpy(a_di->fs_uuid        , a_raw + 0x52, sizeof(a_di->fs_uuid));
175
0
}
176
177
178
#ifdef BTRFS_DEBUG
179
static BTRFS_INODE_REF *
180
btrfs_inode_ref_fromraw(const uint8_t * a_raw, const uint32_t a_len)
181
{
182
    BTRFS_INODE_REF *result = NULL;
183
    BTRFS_INODE_REF *prev_ir = NULL;
184
    for (uint32_t offset = 0; offset < a_len;) {
185
        BTRFS_INODE_REF *curr_ir = new BTRFS_INODE_REF;
186
        if (prev_ir)
187
            prev_ir->next = curr_ir;
188
        else
189
            result = curr_ir;
190
191
        curr_ir->index_in_dir       = tsk_getu64(BTRFS_ENDIAN, a_raw + offset + 0x00);
192
        uint16_t name_len           = tsk_getu16(BTRFS_ENDIAN, a_raw + offset + 0x08);
193
194
        curr_ir->name_in_dir = new char[name_len + 1];
195
        memcpy(curr_ir->name_in_dir , a_raw + offset + 0x0A, name_len);
196
        curr_ir->name_in_dir[name_len] = 0x00;  // terminator
197
198
        offset += 10 + name_len;
199
        prev_ir = curr_ir;
200
    }
201
    if (prev_ir)
202
        prev_ir->next = NULL;
203
    return result;
204
}
205
#endif
206
207
208
#ifdef BTRFS_DEBUG
209
static void
210
btrfs_inode_ref_free(BTRFS_INODE_REF * a_ir)
211
{
212
    BTRFS_INODE_REF *old_ir;
213
    while (a_ir) {
214
        old_ir = a_ir;
215
        a_ir = a_ir->next;
216
217
        delete[] old_ir->name_in_dir;
218
        delete old_ir;
219
    }
220
}
221
#endif
222
223
224
static inline int
225
btrfs_dir_entry_single_rawlen(const uint8_t * a_raw)
226
0
{
227
0
    return 0x1E + tsk_getu16(BTRFS_ENDIAN, a_raw + 0x19) + tsk_getu16(BTRFS_ENDIAN, a_raw + 0x1B);
228
0
}
229
230
static BTRFS_DIR_ENTRY *
231
btrfs_dir_entry_fromraw_single(const uint8_t * a_raw)
232
0
{
233
0
    BTRFS_DIR_ENTRY *de = new BTRFS_DIR_ENTRY;
234
    // de->next must be set later!
235
236
0
    btrfs_key_rawparse(a_raw + 0x00, &de->child);
237
0
    de->transid         = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x11);
238
0
    de->data_len        = tsk_getu16(BTRFS_ENDIAN, a_raw + 0x19);
239
0
    uint16_t name_len   = tsk_getu16(BTRFS_ENDIAN, a_raw + 0x1B);
240
0
    de->type            = a_raw[0x1D];
241
242
0
    de->name = new char[name_len + 1];
243
0
    memcpy(de->name     , a_raw + 0x1E, name_len);
244
0
    de->name[name_len] = 0x00;  // terminator
245
246
0
    de->data = new uint8_t[de->data_len];
247
0
    memcpy(de->data     , a_raw + 0x1E + name_len, de->data_len);
248
249
0
    return de;
250
0
}
251
252
static BTRFS_DIR_ENTRY *
253
btrfs_dir_entry_fromraw(const uint8_t * a_raw, const uint32_t a_len)
254
0
{
255
0
    BTRFS_DIR_ENTRY *first_de = NULL;
256
0
    BTRFS_DIR_ENTRY *prev_de = NULL;
257
258
0
    for (const uint8_t *p = a_raw; p < a_raw + a_len; p += btrfs_dir_entry_single_rawlen(p)) {
259
0
        BTRFS_DIR_ENTRY *curr_de = btrfs_dir_entry_fromraw_single(p);
260
261
0
        if (!first_de)
262
0
            first_de = curr_de;
263
264
0
        if (prev_de)
265
0
            prev_de->next = curr_de;
266
0
        prev_de = curr_de;
267
0
    }
268
0
    prev_de->next = NULL;
269
270
0
    return first_de;
271
0
}
272
273
274
static void
275
btrfs_dir_entry_free(BTRFS_DIR_ENTRY * a_de)
276
0
{
277
0
    while (a_de) {
278
0
        BTRFS_DIR_ENTRY *next_de = a_de->next;
279
0
        delete[] a_de->name;
280
0
        delete[] a_de->data;
281
282
0
        delete a_de;
283
284
0
        a_de = next_de;
285
0
    }
286
0
}
287
288
289
static void
290
btrfs_extent_data_free(BTRFS_EXTENT_DATA * a_ed)
291
0
{
292
0
    if (!a_ed)
293
0
        return;
294
295
0
    if (a_ed->type == BTRFS_EXTENT_DATA_TYPE_INLINE)
296
0
        delete[] a_ed->rd.data;
297
298
0
    delete a_ed;
299
0
}
300
301
302
static BTRFS_EXTENT_DATA *
303
btrfs_extent_data_fromraw(const uint8_t * a_raw, const uint32_t a_len)
304
0
{
305
0
    BTRFS_EXTENT_DATA *ed = new BTRFS_EXTENT_DATA;
306
307
0
    ed->generation      = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
308
0
    ed->size_decoded    = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x08);
309
0
    ed->compression     = a_raw[0x10];
310
0
    ed->encryption      = a_raw[0x11];
311
0
    ed->other_encoding  = tsk_getu16(BTRFS_ENDIAN, a_raw + 0x12);
312
0
    ed->type            = a_raw[0x14];
313
314
0
    switch (ed->type) {
315
0
    case BTRFS_EXTENT_DATA_TYPE_INLINE:
316
0
        ed->rd.data_len = a_len - 0x15;
317
0
        ed->rd.data = new uint8_t[ed->rd.data_len];
318
0
        memcpy(ed->rd.data      , a_raw + 0x15, ed->rd.data_len);
319
0
        return ed;
320
0
    case BTRFS_EXTENT_DATA_TYPE_REGULAR:
321
0
    case BTRFS_EXTENT_DATA_TYPE_PREALLOC:
322
0
        ed->nrd.extent_address  = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x15);
323
0
        ed->nrd.extent_size     = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x1D);
324
0
        ed->nrd.file_offset     = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x25);
325
0
        ed->nrd.file_bytes      = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x2D);
326
0
        return ed;
327
0
    }
328
329
0
    btrfs_error(TSK_ERR_FS_INODE_COR, "btrfs_extent_data_fromraw: unknown type");
330
0
    btrfs_extent_data_free(ed);
331
0
    return NULL;
332
0
}
333
334
335
static uint64_t
336
btrfs_extent_data_size(BTRFS_EXTENT_DATA * a_ed)
337
0
{
338
0
    return a_ed->type == BTRFS_EXTENT_DATA_TYPE_INLINE ? a_ed->size_decoded : a_ed->nrd.file_bytes;
339
0
}
340
341
342
static void
343
btrfs_extent_item_rawparse(const uint8_t * a_raw, BTRFS_EXTENT_ITEM * a_ei)
344
0
{
345
0
    a_ei->reference_count   = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
346
0
    a_ei->generation        = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x08);
347
0
    a_ei->flags             = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x10);
348
    // depending on the flags, different fields follow - ATM they are not needed and therefore ignored
349
0
}
350
351
352
static void
353
btrfs_chunk_item_stripe_rawparse(const uint8_t * a_raw,
354
    BTRFS_CHUNK_ITEM_STRIPE * a_cis)
355
0
{
356
0
    a_cis->device_id            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
357
0
    a_cis->offset               = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x08);
358
0
    memcpy(a_cis->device_uuid   , a_raw + 0x10, sizeof(a_cis->device_uuid));
359
0
}
360
361
362
static void
363
btrfs_chunk_item_free(BTRFS_CHUNK_ITEM * a_ci)
364
0
{
365
0
    if (!a_ci)
366
0
        return;
367
368
0
    delete[] a_ci->stripes;
369
0
    delete a_ci;
370
0
}
371
372
static int
373
btrfs_chunk_item_rawlen(const uint8_t * a_raw)
374
0
{
375
0
    return 0x30 + tsk_getu16(BTRFS_ENDIAN, a_raw + 0x2C) * 0x20;
376
0
}
377
378
static BTRFS_CHUNK_ITEM *
379
btrfs_chunk_item_fromraw(const uint8_t * a_raw)
380
0
{
381
0
    BTRFS_CHUNK_ITEM *ci = new BTRFS_CHUNK_ITEM;
382
383
0
    ci->chunk_size          = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x00);
384
0
    ci->referencing_root    = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x08);
385
0
    ci->stripe_length       = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x10);
386
0
    ci->type                = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x18);
387
0
    ci->optimal_io_align    = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x20);
388
0
    ci->optimal_io_width    = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x24);
389
0
    ci->minimal_io_size     = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x28);
390
0
    ci->number_of_stripes   = tsk_getu16(BTRFS_ENDIAN, a_raw + 0x2C);
391
0
    ci->sub_stripes         = tsk_getu16(BTRFS_ENDIAN, a_raw + 0x2E);
392
393
0
    ci->stripes = new BTRFS_CHUNK_ITEM_STRIPE[ci->number_of_stripes];
394
395
0
    for (uint16_t i = 0; i < ci->number_of_stripes; i++)
396
0
        btrfs_chunk_item_stripe_rawparse(a_raw + 0x30 + i * 0x20, &ci->stripes[i]);
397
398
0
    return ci;
399
0
}
400
401
402
static void
403
btrfs_superblock_rawparse(const uint8_t * a_raw, BTRFS_SUPERBLOCK * a_sb)
404
0
{
405
    // csum ignored (checked on raw item)
406
0
    memcpy(a_sb->uuid           , a_raw + 0x20, sizeof(a_sb->uuid));
407
0
    a_sb->physical_address      = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x30);
408
0
    a_sb->flags                 = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x38);
409
    // magic ignored (checked on raw item)
410
0
    a_sb->generation            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x48);
411
0
    a_sb->root_tree_root        = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x50);
412
0
    a_sb->chunk_tree_root       = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x58);
413
0
    a_sb->log_tree_root         = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x60);
414
0
    a_sb->log_root_transid      = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x68);
415
0
    a_sb->total_bytes           = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x70);
416
0
    a_sb->bytes_used            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x78);
417
0
    a_sb->root_dir_objectid     = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x80);
418
0
    a_sb->num_devices           = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x88);
419
0
    a_sb->sectorsize            = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x90);
420
0
    a_sb->nodesize              = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x94);
421
0
    a_sb->leafsize              = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x98);
422
0
    a_sb->stripesize            = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x9C);
423
0
    a_sb->n                     = tsk_getu32(BTRFS_ENDIAN, a_raw + 0xA0);
424
0
    a_sb->chunk_root_generation = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xA4);
425
0
    a_sb->compat_flags          = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xAC);
426
0
    a_sb->compat_ro_flags       = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xB4);
427
0
    a_sb->incompat_flags        = tsk_getu64(BTRFS_ENDIAN, a_raw + 0xBC);
428
0
    a_sb->csum_type             = tsk_getu16(BTRFS_ENDIAN, a_raw + 0xC4);
429
0
    a_sb->root_level            = a_raw[0xC6];
430
0
    a_sb->chunk_root_level      = a_raw[0xC7];
431
0
    a_sb->log_root_level        = a_raw[0xC8];
432
0
    btrfs_dev_item_rawparse(a_raw + 0xC9, &a_sb->dev_item);
433
0
    memcpy(a_sb->label          , a_raw + 0x12B, sizeof(a_sb->label));
434
0
    memcpy(a_sb->reserved       , a_raw + 0x22B, sizeof(a_sb->reserved));
435
0
    memcpy(a_sb->system_chunks  , a_raw + 0x32B, sizeof(a_sb->system_chunks));
436
0
    memcpy(a_sb->_unused        , a_raw + 0xB2B, sizeof(a_sb->_unused));
437
0
}
438
439
440
static void
441
btrfs_key_pointer_rest_rawparse(const uint8_t * a_raw,
442
    BTRFS_KEY_POINTER_REST * a_kp)
443
0
{
444
0
    a_kp->block_number  = tsk_getu64(BTRFS_ENDIAN, a_raw + (0x11 - BTRFS_KEY_RAWLEN));
445
0
    a_kp->generation    = tsk_getu64(BTRFS_ENDIAN, a_raw + (0x19 - BTRFS_KEY_RAWLEN));
446
0
}
447
448
449
static void
450
btrfs_item_rest_rawparse(const uint8_t * a_raw, BTRFS_ITEM_REST * a_item)
451
0
{
452
0
    a_item->data_offset = tsk_getu32(BTRFS_ENDIAN, a_raw + (0x11 - BTRFS_KEY_RAWLEN));
453
0
    a_item->data_size   = tsk_getu32(BTRFS_ENDIAN, a_raw + (0x15 - BTRFS_KEY_RAWLEN));
454
0
}
455
456
457
static void
458
btrfs_tree_header_rawparse(const uint8_t * a_raw, BTRFS_TREE_HEADER * a_th)
459
0
{
460
    // csum ignored (checked on raw item)
461
0
    memcpy(a_th->uuid               , a_raw + 0x20, sizeof(a_th->uuid));
462
0
    a_th->logical_address           = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x30);
463
0
    a_th->flags                     = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x38) & 0x00FFFFFFFFFFFFFF;  // 7 bytes
464
0
    a_th->backref_rev               = a_raw[0x3F];
465
0
    memcpy(a_th->chunk_tree_uuid    , a_raw + 0x40, sizeof(a_th->chunk_tree_uuid));
466
0
    a_th->generation                = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x50);
467
0
    a_th->parent_tree_id            = tsk_getu64(BTRFS_ENDIAN, a_raw + 0x58);
468
0
    a_th->number_of_items           = tsk_getu32(BTRFS_ENDIAN, a_raw + 0x60);
469
0
    a_th->level                     = a_raw[0x64];
470
0
}
471
472
473
474
/*
475
 * structure printing
476
 */
477
478
479
#ifdef BTRFS_DEBUG
480
static inline const char *
481
btrfs_decode_item_type(const uint8_t a_item_type)
482
{
483
    switch (a_item_type) {
484
    case BTRFS_ITEM_TYPE_INODE_ITEM:
485
        return "INODE_ITEM";
486
    case BTRFS_ITEM_TYPE_INODE_REF:
487
        return "INODE_REF";
488
    case BTRFS_ITEM_TYPE_DIR_ITEM:
489
        return "DIR_ITEM";
490
    case BTRFS_ITEM_TYPE_DIR_INDEX:
491
        return "DIR_INDEX";
492
    case BTRFS_ITEM_TYPE_EXTENT_DATA:
493
        return "EXTENT_DATA";
494
    case BTRFS_ITEM_TYPE_ROOT_ITEM:
495
        return "ROOT_ITEM";
496
    case BTRFS_ITEM_TYPE_EXTENT_ITEM:
497
        return "EXTENT_ITEM";
498
    case BTRFS_ITEM_TYPE_METADATA_ITEM:
499
        return "METADATA_ITEM";
500
    case BTRFS_ITEM_TYPE_DEV_ITEM:
501
        return "DEV_ITEM";
502
    case BTRFS_ITEM_TYPE_CHUNK_ITEM:
503
        return "CHUNK_ITEM";
504
    default:
505
        return "(unknown)";
506
    }
507
}
508
#endif
509
510
511
#ifdef BTRFS_DEBUG
512
static void
513
btrfs_key_debugprint(const BTRFS_KEY * a_key)
514
{
515
    btrfs_debug("key: object ID/item type/offset: 0x%16" PRIx64 " / 0x%02" PRIx8 " / 0x%16" PRIx64 " = %s\n",
516
            a_key->object_id, a_key->item_type, a_key->offset, btrfs_decode_item_type(a_key->item_type));
517
}
518
519
520
static void
521
btrfs_time_debugprint(const BTRFS_TIME * a_time)
522
{
523
    btrfs_debug("time: seconds/nanoseconds: %" PRId64 " / %" PRId32 "\n", a_time->seconds, a_time->nanoseconds);
524
}
525
526
527
static void
528
btrfs_inode_debugprint(const BTRFS_INODE_ITEM * a_ii)
529
{
530
    btrfs_debug("inode: generation:  %"         PRId64 "\n", a_ii->generation);
531
    btrfs_debug("inode: transid:     %"         PRId64 "\n", a_ii->transid);
532
    btrfs_debug("inode: size:        %"         PRId64 "\n", a_ii->size);
533
    btrfs_debug("inode: blocks:      %"         PRId64 "\n", a_ii->blocks);
534
    btrfs_debug("inode: block group: %"         PRId64 "\n", a_ii->block_group);
535
    btrfs_debug("inode: nlink:       %"         PRId32 "\n", a_ii->nlink);
536
    btrfs_debug("inode: uid:         %"         PRId32 "\n", a_ii->uid);
537
    btrfs_debug("inode: gid:         %"         PRId32 "\n", a_ii->gid);
538
    btrfs_debug("inode: mode:        0x%08"     PRIx32 "\n", a_ii->mode);
539
    btrfs_debug("inode: rdev:        0x%"       PRIx64 "\n", a_ii->rdev);
540
    btrfs_debug("inode: flags:       0x%016"    PRIx64 "\n", a_ii->flags);
541
    btrfs_debug("inode: sequence:    %"         PRId64 "\n", a_ii->sequence);
542
    btrfs_time_debugprint(&a_ii->atime);
543
    btrfs_time_debugprint(&a_ii->ctime);
544
    btrfs_time_debugprint(&a_ii->mtime);
545
    btrfs_time_debugprint(&a_ii->otime);
546
}
547
548
549
static void
550
btrfs_root_item_debugprint(const BTRFS_ROOT_ITEM * a_ri)
551
{
552
    btrfs_inode_debugprint(&a_ri->inode);
553
    btrfs_debug("root item: expected generation:      %"        PRId64 "\n", a_ri->expected_generation);
554
    btrfs_debug("root item: root dir object ID:       0x%"      PRIx64 "\n", a_ri->root_dir_object_id);
555
    btrfs_debug("root item: root node block number:   0x%"      PRIx64 "\n", a_ri->root_node_block_number);
556
    btrfs_debug("root item: byte limit:               %"        PRId64 "\n", a_ri->byte_limit);
557
    btrfs_debug("root item: bytes used:               %"        PRId64 "\n", a_ri->bytes_used);
558
    btrfs_debug("root item: last snapshot generation: %"        PRId64 "\n", a_ri->last_snapshot_generation);
559
    btrfs_debug("root item: flags:                    0x%016"   PRIx64 "\n", a_ri->flags);
560
    btrfs_debug("root item: number of references:     %"        PRId32 "\n", a_ri->number_of_references);
561
    btrfs_key_debugprint(&a_ri->drop_progress);
562
    btrfs_debug("root item: drop level:               %"        PRId8  "\n", a_ri->drop_level);
563
    btrfs_debug("root item: root node level:          %"        PRId8  "\n", a_ri->root_node_level);
564
}
565
566
567
static void
568
btrfs_dev_item_debugprint(const BTRFS_DEV_ITEM * a_di)
569
{
570
    btrfs_debug("dev item: device_id:        %"     PRId64 "\n", a_di->device_id);
571
    btrfs_debug("dev item: total bytes:      %"     PRId64 "\n", a_di->total_bytes);
572
    btrfs_debug("dev item: bytes used:       %"     PRId64 "\n", a_di->bytes_used);
573
    btrfs_debug("dev item: optimal_io_align: 0x%"   PRIx32 "\n", a_di->optimal_io_align);
574
    btrfs_debug("dev item: optimal_io_width: 0x%"   PRIx32 "\n", a_di->optimal_io_width);
575
    btrfs_debug("dev item: minimal_io_size:  0x%"   PRIx32 "\n", a_di->minimal_io_size);
576
    btrfs_debug("dev item: type:             0x%"   PRIx64 "\n", a_di->type);
577
    btrfs_debug("dev item: generation:       %"     PRId64 "\n", a_di->generation);
578
    btrfs_debug("dev item: start_offset:     0x%"   PRIx64 "\n", a_di->start_offset);
579
    btrfs_debug("dev item: dev_group:        0x%"   PRIx32 "\n", a_di->dev_group);
580
    btrfs_debug("dev item: seek_speed:       %"     PRId8  "\n", a_di->seek_speed);
581
    btrfs_debug("dev item: bandwidth:        %"     PRId8  "\n", a_di->bandwidth);
582
}
583
584
585
static void
586
btrfs_inode_ref_debugprint(BTRFS_INODE_REF * a_ir)
587
{
588
    for (int index = 0; a_ir; index++) {
589
        btrfs_debug("inode ref #%d: index in dir: %"    PRId64  "\n", index, a_ir->index_in_dir);
590
        btrfs_debug("inode ref #%d: name in dir:  '%s'"         "\n", index, a_ir->name_in_dir);
591
        a_ir = a_ir->next;
592
    }
593
}
594
595
596
static void
597
btrfs_dir_entry_debugprint(BTRFS_DIR_ENTRY * a_de)
598
{
599
    for (int index = 0; a_de; index++) {
600
        btrfs_key_debugprint(&a_de->child);
601
        btrfs_debug("dir entry #%d: transid:  %"    PRId64  "\n", index, a_de->transid);
602
        btrfs_debug("dir entry #%d: type:     %"    PRId8   "\n", index, a_de->type);
603
        btrfs_debug("dir entry #%d: name:     '%s'"         "\n", index, a_de->name);
604
        btrfs_debug("dir entry #%d: data_len: %"    PRId16  "\n", index, a_de->data_len);
605
        a_de = a_de->next;
606
    }
607
}
608
609
610
static void
611
btrfs_extent_data_debugprint(const BTRFS_EXTENT_DATA * a_ed)
612
{
613
    btrfs_debug("extent data: generation:     %"        PRId64  "\n", a_ed->generation);
614
    btrfs_debug("extent data: size_decoded:   %"        PRId64  "\n", a_ed->size_decoded);
615
    btrfs_debug("extent data: compression:    0x%02"    PRIx8   "\n", a_ed->compression);
616
    btrfs_debug("extent data: encryption:     0x%02"    PRIx8   "\n", a_ed->encryption);
617
    btrfs_debug("extent data: other_encoding: 0x%04"    PRIx16  "\n", a_ed->other_encoding);
618
    btrfs_debug("extent data: type:           0x%02"    PRIx8   "\n", a_ed->type);
619
620
    switch (a_ed->type) {
621
    case BTRFS_EXTENT_DATA_TYPE_INLINE:
622
        btrfs_debug("extent data: resident data_len: %" PRId32 "\n", a_ed->rd.data_len);
623
        break;
624
    case BTRFS_EXTENT_DATA_TYPE_REGULAR:
625
    case BTRFS_EXTENT_DATA_TYPE_PREALLOC:
626
        btrfs_debug("extent data: non-resident extent address: 0x%" PRIx64 "\n", a_ed->nrd.extent_address);
627
        btrfs_debug("extent data: non-resident extent size:    %"   PRId64 "\n", a_ed->nrd.extent_size);
628
        btrfs_debug("extent data: non-resident file offset:    0x%" PRIx64 "\n", a_ed->nrd.file_offset);
629
        btrfs_debug("extent data: non-resident file size:      %"   PRId64 "\n", a_ed->nrd.file_bytes);
630
        break;
631
    default:
632
        btrfs_debug("extent data: - unknown type -\n");
633
    }
634
}
635
636
637
static void
638
btrfs_extent_item_debugprint(const BTRFS_EXTENT_ITEM * a_ei)
639
{
640
    btrfs_debug("extent item: reference count: %"       PRId64 "\n", a_ei->reference_count);
641
    btrfs_debug("extent item: generation:      %"       PRId64 "\n", a_ei->generation);
642
    btrfs_debug("extent item: flags:           0x%016"  PRIx64 "\n", a_ei->flags);
643
}
644
645
646
static void
647
btrfs_chunk_item_debugprint(const BTRFS_CHUNK_ITEM * a_ci)
648
{
649
    btrfs_debug("chunk item: chunk size:        0x%"    PRIx64 "\n", a_ci->chunk_size);
650
    btrfs_debug("chunk item: referencing root:  0x%"    PRIx64 "\n", a_ci->referencing_root);
651
    btrfs_debug("chunk item: stripe length:     0x%"    PRIx64 "\n", a_ci->stripe_length);
652
    btrfs_debug("chunk item: type:              0x%"    PRIx64 "\n", a_ci->type);
653
    btrfs_debug("chunk item: optimal_io_align:  0x%"    PRIx32 "\n", a_ci->optimal_io_align);
654
    btrfs_debug("chunk item: optimal_io_width:  0x%"    PRIx32 "\n", a_ci->optimal_io_width);
655
    btrfs_debug("chunk item: minimal_io_size:   0x%"    PRIx32 "\n", a_ci->minimal_io_size);
656
    btrfs_debug("chunk item: sub stripes:       %"      PRId16 "\n", a_ci->sub_stripes);
657
658
    for (uint16_t index = 0; index < a_ci->number_of_stripes; index++) {
659
        BTRFS_CHUNK_ITEM_STRIPE *a_cis = &a_ci->stripes[index];
660
        btrfs_debug("chunk item stripe #%d: device_id: %"   PRId64 "\n", index, a_cis->device_id);
661
        btrfs_debug("chunk item stripe #%d: offset:    0x%" PRIx64 "\n", index, a_cis->offset);
662
    }
663
}
664
665
666
static void
667
btrfs_superblock_debugprint(const BTRFS_SUPERBLOCK * a_sb)
668
{
669
    btrfs_debug("superblock: physical address:      0x%"    PRIx64 "\n", a_sb->physical_address);
670
    btrfs_debug("superblock: flags:                 0x%016" PRIx64 "\n", a_sb->flags);
671
    btrfs_debug("superblock: generation:            %"      PRId64 "\n", a_sb->generation);
672
    btrfs_debug("superblock: root tree root:        0x%"    PRIx64 "\n", a_sb->root_tree_root);
673
    btrfs_debug("superblock: chunk tree root:       0x%"    PRIx64 "\n", a_sb->chunk_tree_root);
674
    btrfs_debug("superblock: log tree root:         0x%"    PRIx64 "\n", a_sb->log_tree_root);
675
    btrfs_debug("superblock: log_root_transid:      0x%"    PRIx64 "\n", a_sb->log_root_transid);
676
    btrfs_debug("superblock: total bytes:           %"      PRId64 "\n", a_sb->total_bytes);
677
    btrfs_debug("superblock: bytes used:            %"      PRId64 "\n", a_sb->bytes_used);
678
    btrfs_debug("superblock: root_dir_objectid:     0x%"    PRIx64 "\n", a_sb->root_dir_objectid);
679
    btrfs_debug("superblock: num_devices:           %"      PRId64 "\n", a_sb->num_devices);
680
    btrfs_debug("superblock: sectorsize:            %"      PRId32 "\n", a_sb->sectorsize);
681
    btrfs_debug("superblock: nodesize:              %"      PRId32 "\n", a_sb->nodesize);
682
    btrfs_debug("superblock: leafsize:              %"      PRId32 "\n", a_sb->leafsize);
683
    btrfs_debug("superblock: stripesize:            %"      PRId32 "\n", a_sb->stripesize);
684
    btrfs_debug("superblock: n:                     %"      PRId32 "\n", a_sb->n);
685
    btrfs_debug("superblock: chunk_root_generation: %"      PRId64 "\n", a_sb->chunk_root_generation);
686
    btrfs_debug("superblock: compat_flags:          0x%016" PRIx64 "\n", a_sb->compat_flags);
687
    btrfs_debug("superblock: compat_ro_flags:       0x%016" PRIx64 "\n", a_sb->compat_ro_flags);
688
    btrfs_debug("superblock: incompat_flags:        0x%016" PRIx64 "\n", a_sb->incompat_flags);
689
    btrfs_debug("superblock: csum_type:             %"      PRId16 "\n", a_sb->csum_type);
690
    btrfs_debug("superblock: root_level:            %"      PRId8  "\n", a_sb->root_level);
691
    btrfs_debug("superblock: chunk_root_level:      %"      PRId8  "\n", a_sb->chunk_root_level);
692
    btrfs_debug("superblock: log_root_level:        %"      PRId8  "\n", a_sb->log_root_level);
693
    btrfs_dev_item_debugprint(&a_sb->dev_item);
694
    btrfs_debug("superblock: label:                 '%s'\n"            , a_sb->label);
695
//  btrfs_debug_hexdump(a_sb->system_chunks, a_sb->n, "SYSTEM chunks");
696
}
697
698
699
static void
700
btrfs_key_pointer_rest_debugprint(const BTRFS_KEY_POINTER_REST * a_kp)
701
{
702
    btrfs_debug("key pointer: block number: 0x%"    PRIx64 "\n", a_kp->block_number);
703
    btrfs_debug("key pointer: generation:   %"      PRId64 "\n", a_kp->generation);
704
}
705
706
707
static void
708
btrfs_item_rest_debugprint(const BTRFS_ITEM_REST * a_item)
709
{
710
    btrfs_debug("item: data offset: %"  PRId32 "\n", a_item->data_offset);
711
    btrfs_debug("item: data size:   %"  PRId32 "\n", a_item->data_size);
712
}
713
714
715
static void
716
btrfs_tree_header_debugprint(const BTRFS_TREE_HEADER * a_th)
717
{
718
    btrfs_debug("tree header: logical address: 0x%"     PRIx64 "\n", a_th->logical_address);
719
    btrfs_debug("tree header: flags:           0x%014"  PRIx64 "\n", a_th->flags);  // 7 bytes
720
    btrfs_debug("tree header: backref_rev:     %"       PRId8  "\n", a_th->backref_rev);
721
    btrfs_debug("tree header: generation:      %"       PRId64 "\n", a_th->generation);
722
    btrfs_debug("tree header: parent_tree_id:  0x%"     PRIx64 "\n", a_th->parent_tree_id);
723
    btrfs_debug("tree header: number_of_items: %"       PRId32 "\n", a_th->number_of_items);
724
    btrfs_debug("tree header: level:           %"       PRId8  "\n", a_th->level);
725
}
726
#endif
727
728
729
730
/*
731
 * checksums
732
 */
733
734
735
/**
736
 * Checks if the specified checksum type is supported.
737
 * @param a_csum_type checksum type
738
 * @return true if supported, otherwise false
739
 */
740
static bool
741
btrfs_csum_supported(const uint16_t a_csum_type)
742
0
{
743
0
    switch (a_csum_type) {
744
0
    case BTRFS_CSUM_TYPE_CRC32C:
745
        // CRC-32C
746
0
        return true;
747
0
    }
748
0
    return false;
749
0
}
750
751
752
/**
753
 * Returns a string description of the specified checksum type.
754
 * @param a_csum_type checksum type
755
 * @return description
756
 */
757
static const char *
758
btrfs_csum_description(const uint16_t a_csum_type)
759
0
{
760
0
    switch (a_csum_type) {
761
0
    case BTRFS_CSUM_TYPE_CRC32C:
762
        // CRC-32C
763
0
        return "CRC-32C";
764
0
    }
765
0
    return "unknown";
766
0
}
767
768
769
/**
770
 * Validates the checksum of a specific amount of data.
771
 * @param a_csum_type checksum type (must be supported)
772
 * @param a_data pointer to data
773
 * @param a_len data len
774
 * @return true if the checksum is valid, otherwise false
775
 *
776
 * It looks like the BTRFS checksums start with the checksum and are then followed by the data being summed.
777
 * So if the data size is < BTRFS_CSUM_RAWLEN, the checksum cannot be valid
778
 */
779
static bool
780
btrfs_csum_valid(const uint16_t a_csum_type, const uint8_t * a_data,
781
    const int a_len)
782
0
{
783
#ifdef BTRFS_DEBUG
784
    btrfs_debug("btrfs_csum_valid a_csum_type=%d BTRFS_CSUM_TYPE_CRC32C=%d a_data=%p a_len=%d BTRFS_CSUM_RAWLEN=%d\n",a_csum_type,BTRFS_CSUM_TYPE_CRC32C,a_data,a_len,BTRFS_CSUM_RAWLEN);
785
#endif
786
0
    if (a_len < BTRFS_CSUM_RAWLEN){
787
#ifdef BTRFS_DEBUG
788
        btrfs_debug("a_data is too small\n");
789
#endif
790
0
        return false;
791
0
    }
792
793
0
    unsigned long v1=0, v2=0;
794
0
    switch (a_csum_type) {
795
0
    case BTRFS_CSUM_TYPE_CRC32C:
796
        // CRC-32C
797
0
        v1 = btrfs_csum_crc32c(a_data + BTRFS_CSUM_RAWLEN, a_len - BTRFS_CSUM_RAWLEN) ;
798
#ifdef BTRFS_DEBUG
799
        btrfs_debug("v1=%ld\n",v1);
800
#endif
801
0
        v2 = tsk_getu32(BTRFS_ENDIAN, a_data);
802
#ifdef BTRFS_DEBUG
803
        btrfs_debug("v2=%ld\n",v2);
804
#endif
805
0
        return v1 == v2;
806
0
    default:
807
#ifdef BTRFS_DEBUG
808
        btrfs_debug("default\n");
809
#endif
810
0
        return false;
811
0
    }
812
0
}
813
814
815
816
/*
817
 * superblock
818
 */
819
820
821
/**
822
 * Returns the physical address of a specific superblock mirror.
823
 * @param a_index mirror index in the range of 0 to (BTRFS_SUPERBLOCK_MIRRORS_MAX - 1)
824
 * @return physical superblock mirror address
825
 */
826
static TSK_DADDR_T
827
btrfs_superblock_address(const int a_index)
828
0
{
829
0
    return 1ULL << (a_index ? (14 + a_index * 12) : 16);
830
0
}
831
832
833
/**
834
 * Checks if a specific physical address is included by any superblock mirror.
835
 * @param a_btrfs Btrfs info
836
 * @param a_address physical address
837
 * @return true if the address is included, otherwise false
838
 */
839
static bool
840
btrfs_superblock_includes_address(const TSK_DADDR_T a_address)
841
0
{
842
0
    for (int i = 0; i < BTRFS_SUPERBLOCK_MIRRORS_MAX; i++) {
843
0
        TSK_DADDR_T sb_start = btrfs_superblock_address(i);
844
0
        if (a_address >= sb_start && a_address < sb_start + BTRFS_SUPERBLOCK_RAWLEN)
845
0
            return true;
846
0
    }
847
0
    return false;
848
0
}
849
850
851
/**
852
 * Tries to read the superblock at a specific physical address.
853
 * @param a_btrfs Btrfs info
854
 * @param a_offset physical address
855
 * @return pointer to the superblock if no error occured, otherwise NULL
856
 */
857
static BTRFS_SUPERBLOCK *
858
btrfs_superblock_read(BTRFS_INFO * a_btrfs, const TSK_DADDR_T a_offset)
859
0
{
860
0
    uint8_t raw[BTRFS_SUPERBLOCK_RAWLEN];
861
862
0
    btrfs_debug("trying to read superblock at offset 0x%" PRIxDADDR "\n", a_offset);
863
864
    // try to read raw superblock
865
0
    ssize_t result = tsk_fs_read(&a_btrfs->fs_info, a_offset, (char*) raw, sizeof(raw));
866
0
    if (result != (signed) sizeof(raw)) {
867
0
        tsk_error_reset();  // maybe the request was out of range, so reset error
868
0
        btrfs_debug("could not read superblock - tsk_fs_read result: %zd\n", result);
869
0
        if (tsk_verbose && !a_btrfs->test)
870
0
            tsk_fprintf(stderr, "btrfs_superblock_read: Could not read superblock - tsk_fs_read result: %zd\n", result);
871
0
        return NULL;
872
0
    }
873
874
    // check for magic
875
0
    if (memcmp(raw + BTRFS_SUPERBLOCK_MAGIC_OFFSET, BTRFS_SUPERBLOCK_MAGIC_VALUE, strlen(BTRFS_SUPERBLOCK_MAGIC_VALUE))) {
876
0
        btrfs_debug("superblock magic not found\n");
877
0
        if (tsk_verbose && !a_btrfs->test)
878
0
            tsk_fprintf(stderr, "btrfs_superblock_read: Superblock magic not found\n");
879
0
        return NULL;
880
0
    }
881
882
0
    BTRFS_SUPERBLOCK *sb = new BTRFS_SUPERBLOCK;
883
0
    btrfs_superblock_rawparse(raw, sb);
884
885
    // validate checksum
886
0
    if (!btrfs_csum_supported(sb->csum_type)) {
887
0
        btrfs_debug("superblock checksum type unknown - skipping\n");
888
0
        if (tsk_verbose && !a_btrfs->test)
889
0
            tsk_fprintf(stderr, "btrfs_superblock_read: Superblock checksum type unknown - skipping\n");
890
0
        delete sb;
891
0
        return NULL;
892
0
    }
893
0
    if (!btrfs_csum_valid(sb->csum_type, raw, sizeof(raw))) {
894
0
        btrfs_debug("superblock checksum invalid - skipping\n");
895
0
        if (tsk_verbose && !a_btrfs->test)
896
0
            tsk_fprintf(stderr, "btrfs_superblock_read: Superblock checksum invalid - skipping\n");
897
0
        delete sb;
898
0
        return NULL;
899
0
    }
900
901
    // ensure that the superblock belongs to the current filesystem
902
0
    if (sb->physical_address != a_offset) {
903
0
        btrfs_debug("superblock does not belong to the current filesystem\n");
904
0
        if (tsk_verbose && !a_btrfs->test)
905
0
            tsk_fprintf(stderr, "btrfs_superblock_read: Superblock does not belong to the current filesystem\n");
906
0
        delete sb;
907
0
        return NULL;
908
0
    }
909
910
0
    btrfs_debug("found valid superblock having generation: %" PRId64 "\n", sb->generation);
911
0
    if (tsk_verbose && !a_btrfs->test)
912
0
        tsk_fprintf(stderr, "btrfs_superblock_read: Found valid superblock having generation: %" PRId64 "\n", sb->generation);
913
0
    return sb;
914
0
}
915
916
917
/**
918
 * Searches for the valid superblock with the highest generation.
919
 * @param a_btrfs Btrfs info
920
 * @return true if a valid superblock was found, otherwise false
921
 */
922
static bool
923
btrfs_superblock_search(BTRFS_INFO * a_btrfs)
924
0
{
925
0
    a_btrfs->sb = NULL;
926
0
    for (int i = 0; i < BTRFS_SUPERBLOCK_MIRRORS_MAX; i++) {
927
0
        if (tsk_verbose && !a_btrfs->test)
928
0
            tsk_fprintf(stderr, "btrfs_superblock_search: Trying to read superblock mirror %d\n", i);
929
930
0
        BTRFS_SUPERBLOCK *tmp_sb = btrfs_superblock_read(a_btrfs, btrfs_superblock_address(i));
931
932
        // continue on invalid superblock mirror
933
0
        if (!tmp_sb)
934
0
            continue;
935
936
        // apply superblock (use highest generation)
937
0
        if (!a_btrfs->sb || a_btrfs->sb->generation < tmp_sb->generation) {
938
0
            delete a_btrfs->sb;
939
0
            a_btrfs->sb = tmp_sb;
940
0
            a_btrfs->sb_mirror_index = i;
941
0
        } else {
942
0
            delete tmp_sb;
943
0
        }
944
0
    }
945
0
    return a_btrfs->sb;
946
0
}
947
948
949
950
/*
951
 * chunks 1/2
952
 */
953
954
955
/**
956
 * Processes a chunk item and possibly adds it to a cached chunk mapping
957
 * @param a_btrfs Btrfs info
958
 * @param a_chunks pointer to cached chunk mapping
959
 * @param a_source_address source address belonging to the chunk item
960
 * @param a_ci_raw pointer to raw chunk item
961
 */
962
static void
963
btrfs_chunks_process_chunk_item(BTRFS_INFO * a_btrfs,
964
    BTRFS_CACHED_CHUNK_MAPPING * a_chunks, TSK_DADDR_T a_source_address,
965
    const uint8_t * a_ci_raw)
966
0
{
967
    // the chunks describe a 1:n log <-> phys relation, so we adopt only one stripe in log -> phys direction
968
0
    bool log2phys_added = false;
969
970
0
    BTRFS_CHUNK_ITEM *ci = btrfs_chunk_item_fromraw(a_ci_raw);
971
972
#ifdef BTRFS_DEBUG
973
    btrfs_debug("Processing chunk for logical address 0x%"  PRIx64 "...\n", a_source_address);
974
    btrfs_chunk_item_debugprint(ci);
975
#endif
976
977
    // check all stripes for affecting our device
978
0
    for (uint16_t i = 0; i < ci->number_of_stripes; i++) {
979
0
        BTRFS_CHUNK_ITEM_STRIPE *cis = &ci->stripes[i];
980
0
        if (cis->device_id != a_btrfs->sb->dev_item.device_id)
981
0
            continue;
982
983
984
        // convert info to internal format
985
0
        BTRFS_CACHED_CHUNK cc;
986
0
        cc.source_address = a_source_address;
987
0
        cc.size = ci->chunk_size;
988
0
        cc.target_address = cis->offset;
989
990
        // add to log -> phys mapping (only once)
991
0
        if (!log2phys_added) {
992
0
            a_chunks->log2phys.insert(a_chunks->log2phys.end(), cc);
993
0
            log2phys_added = true;
994
0
        }
995
996
        // add to phys -> log mapping
997
0
        cc.source_address = cis->offset;
998
0
        cc.target_address = a_source_address;
999
0
        a_chunks->phys2log.insert(cc);
1000
0
    }
1001
0
    btrfs_chunk_item_free(ci);
1002
0
}
1003
1004
1005
/**
1006
 * Processes all chunks embedded into superblock into a newly created cached chunk mapping
1007
 * @param a_btrfs Btrfs info
1008
 * @return pointer to new cached chunk mapping
1009
 */
1010
static BTRFS_CACHED_CHUNK_MAPPING *
1011
btrfs_chunks_from_superblock(BTRFS_INFO * a_btrfs)
1012
0
{
1013
0
    BTRFS_CACHED_CHUNK_MAPPING *chunks = new BTRFS_CACHED_CHUNK_MAPPING;
1014
0
    BTRFS_KEY key;
1015
1016
    // iterate over all system chunks embedded into superblock
1017
0
    btrfs_debug("Parsing superblock system chunks...\n");
1018
0
    for (uint8_t *p = a_btrfs->sb->system_chunks; p < a_btrfs->sb->system_chunks + a_btrfs->sb->n;) {
1019
0
        btrfs_key_rawparse(p, &key);
1020
0
        p += BTRFS_KEY_RAWLEN;
1021
1022
0
        btrfs_chunks_process_chunk_item(a_btrfs, chunks, key.offset, p);
1023
0
        p += btrfs_chunk_item_rawlen(p);
1024
0
    }
1025
0
    return chunks;
1026
0
}
1027
1028
1029
/**
1030
 * Maps an address by using a cached chunk
1031
 * @param a_cc pointer to a cached chunk
1032
 * @param a_source_addr source address
1033
 * @param a_target_addr pointer for target address
1034
 * @return true if the source address could be mapped, otherwise false
1035
 */
1036
static bool
1037
btrfs_chunk_map(const BTRFS_CACHED_CHUNK * a_cc,
1038
    const TSK_DADDR_T a_source_addr, TSK_DADDR_T * a_target_addr)
1039
0
{
1040
#ifdef BTRFS_DEBUG
1041
    btrfs_debug("btrfs_chunk_map [enter] a_cc=%x a_source_addr=%x a_target_addr=%x\n",a_cc,a_source_addr,a_target_addr);
1042
#endif
1043
1044
0
    TSK_OFF_T offset = a_source_addr - a_cc->source_address;
1045
0
    if (!(offset >= 0 && offset < a_cc->size))
1046
0
        return false;
1047
1048
0
    *a_target_addr = a_cc->target_address + offset;
1049
#ifdef BTRFS_DEBUG
1050
    btrfs_debug("btrfs_chunk_map [exit] Mapping address 0x%" PRIxDADDR " to address 0x%" PRIxDADDR "\n", a_source_addr, *a_target_addr);
1051
#endif
1052
0
    return true;
1053
0
}
1054
1055
1056
/**
1057
 * Returns the remaining bytes of a source address regarding a specific cached chunk (ignoring chunk range)
1058
 * @param a_cc pointer to a cached chunk pointer (or NULL)
1059
 * @param a_address source address
1060
 * @return remaining bytes
1061
 */
1062
static inline TSK_OFF_T
1063
btrfs_chunk_remaining_bytes(const BTRFS_CACHED_CHUNK * a_cc,
1064
    const TSK_DADDR_T a_source_addr)
1065
0
{
1066
0
    return a_cc->source_address + a_cc->size - a_source_addr;
1067
0
}
1068
1069
1070
/**
1071
 * Maps an address with regard to a specified mapping and gets a pointer to a cached chunk related to it, which is:
1072
 *   a) a current chunk (including the address) => true  returned + *a_cc set + *a_target_addr set
1073
 *   b) no current chunk, but the next chunk    => false returned + *a_cc set
1074
 *   c) neither a current nor the next chunk    => false returned
1075
 * @param a_mapping mapping out of cached chunks
1076
 * @param a_cc pointer to a cached chunk pointer (or NULL)
1077
 * @param a_source_addr source address
1078
 * @param a_target_addr pointer for target address
1079
 * @return true if the source address could be mapped, otherwise false
1080
 */
1081
static bool
1082
btrfs_address_map(const btrfs_cached_chunks_t * a_mapping,
1083
    const BTRFS_CACHED_CHUNK ** a_cc, const TSK_DADDR_T a_source_addr,
1084
    TSK_DADDR_T * a_target_addr)
1085
0
{
1086
#ifdef BTRFS_DEBUG
1087
    btrfs_debug("btrfs_address_map [enter] a_mapping=%x a_cc=%x a_source_addr=%x a_target_addr=%x\n",
1088
                a_mapping,a_cc,a_source_addr,a_target_addr);
1089
#endif
1090
1091
    // resolve to matching chunk, if possible
1092
0
    BTRFS_CACHED_CHUNK cc;
1093
0
    cc.source_address = a_source_addr;
1094
0
    cc.size = 1;
1095
0
    btrfs_cached_chunks_t::iterator result = a_mapping->lower_bound(cc);
1096
1097
    // if neither current nor next chunk, abort
1098
0
    if (result == a_mapping->end())
1099
0
        return false;
1100
1101
0
    const BTRFS_CACHED_CHUNK *result_cc = &(*result);
1102
0
    if (a_cc)
1103
0
        *a_cc = result_cc;
1104
1105
    // check for a) or b)
1106
0
    return btrfs_chunk_map(result_cc, a_source_addr, a_target_addr);
1107
0
}
1108
1109
1110
1111
/*
1112
 * tree node stuff
1113
 */
1114
1115
1116
/**
1117
 * Try to get a raw tree node from the treenode cache (lock must be taken!).
1118
 * @param a_btrfs Btrfs info
1119
 * @param a_address logical tree node address
1120
 * @param a_data pointer to data buffer
1121
 * @return true on cache hit, false otherwise
1122
 */
1123
static bool
1124
btrfs_treenode_cache_get(BTRFS_INFO * a_btrfs, const TSK_DADDR_T a_address,
1125
    uint8_t * a_data)
1126
0
{
1127
0
    btrfs_treenode_cache_map_t::iterator map_it = a_btrfs->treenode_cache_map->find(a_address);
1128
0
    bool hit = map_it != a_btrfs->treenode_cache_map->end();
1129
0
    if (hit) {
1130
0
        memcpy(a_data, map_it->second, a_btrfs->sb->nodesize);
1131
1132
        // if not already at LRU list front, move to front
1133
0
        if (a_btrfs->treenode_cache_lru->front() != a_address) {
1134
0
            for (btrfs_treenode_cache_lru_t::iterator lru_it = a_btrfs->treenode_cache_lru->begin();
1135
0
                    lru_it != a_btrfs->treenode_cache_lru->end(); lru_it++) {
1136
0
                if (*lru_it == a_address) {
1137
0
                    a_btrfs->treenode_cache_lru->erase(lru_it);
1138
0
                    break;
1139
0
                }
1140
0
            }
1141
0
            a_btrfs->treenode_cache_lru->push_front(a_address);
1142
0
        }
1143
0
    }
1144
1145
0
    btrfs_debug("cache %s at address 0x%" PRIxDADDR " (entry count: %zu)\n", hit ? "hit" : "miss", a_address, a_btrfs->treenode_cache_lru->size());
1146
0
    return hit;
1147
0
}
1148
1149
1150
/**
1151
 * Puts a raw tree node into the treenode cache (lock must be taken; node must not yet be in cache!).
1152
 * @param a_btrfs Btrfs info
1153
 * @param a_address logical tree node address
1154
 * @param a_data pointer to data buffer
1155
 */
1156
static void
1157
btrfs_treenode_cache_put(BTRFS_INFO * a_btrfs, const TSK_DADDR_T a_address,
1158
    const uint8_t * a_data)
1159
0
{
1160
#ifdef BTRFS_DEBUG
1161
    btrfs_debug("btrfs_treenode_cache_put a_btrfs=%x data=%x\n",a_btrfs,a_data);
1162
#endif
1163
0
    uint8_t *target_data;
1164
0
    size_t cache_size = a_btrfs->treenode_cache_lru->size();
1165
0
    if (cache_size < BTRFS_TREENODE_CACHE_SIZE) {
1166
        // add new entry
1167
0
        target_data = new uint8_t[a_btrfs->sb->nodesize];
1168
0
        btrfs_debug("caching address 0x%" PRIxDADDR " (entry count: %zu; entry was new)\n", a_address, cache_size + 1);
1169
0
    } else {
1170
        // replace old entry
1171
0
        TSK_DADDR_T old_address = a_btrfs->treenode_cache_lru->back();
1172
0
        a_btrfs->treenode_cache_lru->pop_back();
1173
1174
0
        btrfs_treenode_cache_map_t::iterator map_it = a_btrfs->treenode_cache_map->find(old_address);
1175
0
        target_data = map_it->second;
1176
0
        a_btrfs->treenode_cache_map->erase(map_it);
1177
1178
0
        btrfs_debug("caching address 0x%" PRIxDADDR " (entry count: %zu; entry replaced address 0x%" PRIxDADDR ")\n", a_address, cache_size, old_address);
1179
0
    }
1180
1181
#ifdef BTRFS_DEBUG
1182
    btrfs_debug("starting memcpy...\n");
1183
#endif
1184
0
    memcpy(target_data, a_data, a_btrfs->sb->nodesize);
1185
#ifdef BTRFS_DEBUG
1186
    btrfs_debug("done...\n");
1187
#endif
1188
0
    a_btrfs->treenode_cache_map->insert(btrfs_treenode_cache_map_t::value_type(a_address, target_data));
1189
0
    a_btrfs->treenode_cache_lru->push_front(a_address);
1190
0
}
1191
1192
1193
/**
1194
 * Goes one tree level up by removing the bottom node
1195
 * @param a_node pointer to treenode structure pointer
1196
 */
1197
static void
1198
btrfs_treenode_pop(BTRFS_TREENODE ** a_node)
1199
0
{
1200
0
    BTRFS_TREENODE *node = *a_node;
1201
0
    *a_node = node->prev;
1202
1203
0
    delete[] node->data;
1204
0
    delete node;
1205
0
}
1206
1207
1208
/**
1209
 * Frees a complete treenode structure
1210
 * @param a_node pointer to treenode structure
1211
 */
1212
static void
1213
btrfs_treenode_free(BTRFS_TREENODE * a_node)
1214
0
{
1215
#ifdef BTRFS_DEBUG
1216
    btrfs_debug("btrfs_treenode_free...\n");
1217
#endif
1218
0
    while (a_node)
1219
0
        btrfs_treenode_pop(&a_node);
1220
0
}
1221
1222
1223
/**
1224
 * Compares two BTRFS_KEYs.
1225
 * @param a_key_a key A
1226
 * @param a_key_b key B
1227
 * @param a_flags flags
1228
 * @return relation between key A and key B
1229
 */
1230
static int
1231
btrfs_cmp(const BTRFS_KEY * a_key_a, const BTRFS_KEY * a_key_b,
1232
    const int a_flags)
1233
0
{
1234
    // compare key fields one after each other
1235
1236
0
    if (!(a_flags & BTRFS_CMP_IGNORE_OBJID)) {
1237
0
        if (a_key_a->object_id > a_key_b->object_id)
1238
0
            return 1;
1239
0
        if (a_key_a->object_id < a_key_b->object_id)
1240
0
            return -1;
1241
0
    }
1242
1243
0
    if (!(a_flags & BTRFS_CMP_IGNORE_TYPE)) {
1244
        // special flag to cover two types which only differ in LSB
1245
0
        int shift = a_flags & BTRFS_CMP_IGNORE_LSB_TYPE ? 1 : 0;
1246
1247
0
        if ((a_key_a->item_type >> shift) > (a_key_b->item_type >> shift))
1248
0
            return 1;
1249
0
        if ((a_key_a->item_type >> shift) < (a_key_b->item_type >> shift))
1250
0
            return -1;
1251
0
    }
1252
1253
0
    if (!(a_flags & BTRFS_CMP_IGNORE_OFFSET)) {
1254
0
        if (a_key_a->offset > a_key_b->offset)
1255
0
            return 1;
1256
0
        if (a_key_a->offset < a_key_b->offset)
1257
0
            return -1;
1258
0
    }
1259
1260
0
    return 0;
1261
0
}
1262
1263
1264
/**
1265
 * Selects the current item of a node (the resulting index must be valid!)
1266
 * @param a_node pointer to treenode structure
1267
 * @param a_absolute TRUE, if an absolute index is specified, otherwise it is treatened as relative index
1268
 * @param a_index index
1269
 */
1270
static void
1271
btrfs_treenode_set_index(BTRFS_TREENODE * a_node, const bool a_absolute,
1272
    const int a_index)
1273
0
{
1274
0
    a_node->index = (a_absolute ? 0 : a_node->index) + a_index;
1275
1276
    // update values
1277
0
    uint8_t *raw = a_node->data + a_node->index *
1278
0
            (a_node->header.level ? BTRFS_KEY_POINTER_RAWLEN : BTRFS_ITEM_RAWLEN);
1279
0
    btrfs_key_rawparse(raw, &a_node->key);
1280
0
    raw += BTRFS_KEY_RAWLEN;
1281
1282
0
    if (a_node->header.level)
1283
0
        btrfs_key_pointer_rest_rawparse(raw, &a_node->kp);
1284
0
    else
1285
0
        btrfs_item_rest_rawparse(raw, &a_node->item);
1286
0
}
1287
1288
1289
/**
1290
 * Returns a pointer to the raw item data of the current index of the current node
1291
 * @param a_node pointer to treenode structure
1292
 * @return pointer to raw data
1293
 */
1294
static inline uint8_t *
1295
btrfs_treenode_itemdata(const BTRFS_TREENODE * a_node)
1296
0
{
1297
0
    return a_node->data + a_node->item.data_offset;
1298
0
}
1299
1300
1301
/**
1302
 * Returns the size of the raw item data of the current index of the current node
1303
 * @param a_node pointer to treenode structure
1304
 * @return raw data size
1305
 */
1306
static inline uint32_t
1307
btrfs_treenode_itemsize(const BTRFS_TREENODE * a_node)
1308
0
{
1309
0
    return a_node->item.data_size;
1310
0
}
1311
1312
1313
/**
1314
 * Goes one tree level down by adding a new node
1315
 * @param a_btrfs Btrfs info
1316
 * @param a_node pointer to treenode structure pointer (treenode structure pointer itself may be NULL)
1317
 * @param a_address logical node address
1318
 * @param a_initial_index initial index to be set
1319
 * @return true if no error occured, false otherwise
1320
 */
1321
static bool
1322
btrfs_treenode_push(BTRFS_INFO * a_btrfs, BTRFS_TREENODE ** a_node,
1323
    const TSK_DADDR_T a_address, const BTRFS_DIRECTION a_initial_index)
1324
0
{
1325
#ifdef BTRFS_DEBUG
1326
    btrfs_debug(" btrfs_treenode_push a_btrfs=%x\n",a_btrfs);
1327
    btrfs_debug(" btrfs_treenode_push a_btrfs->sb=%x\n",a_btrfs->sb);
1328
    btrfs_debug(" btrfs_treenode_push a_btrfs->sb->nodesize=%d\n",a_btrfs->sb->nodesize);
1329
#endif
1330
0
    const size_t nodesize = a_btrfs->sb->nodesize;
1331
0
    if (nodesize<=0) return false;
1332
    //uint8_t raw[nodesize];
1333
0
    uint8_t *raw = new uint8_t[nodesize];
1334
1335
    // lock remains taken between cache get and a possible put in order to prevent an possible meanwhile cache put by another thread
1336
0
    tsk_take_lock(&a_btrfs->treenode_cache_lock);
1337
1338
    // on treenode cache miss fetch node from image
1339
0
    if (!btrfs_treenode_cache_get(a_btrfs, a_address, raw)) {
1340
        // map address
1341
#ifdef BTRFS_DEBUG
1342
        btrfs_debug("in loop. raw=%x\n",raw);
1343
#endif
1344
1345
0
        TSK_DADDR_T phys_address;
1346
0
        if (!btrfs_address_map(&a_btrfs->chunks->log2phys, NULL, a_address, &phys_address)) {
1347
0
            btrfs_error(TSK_ERR_FS_BLK_NUM,"btrfs_treenode_push: Could not map logical address: 0x%" PRIxDADDR, a_address);
1348
0
            tsk_release_lock(&a_btrfs->treenode_cache_lock);
1349
#ifdef BTRFS_DEBUG
1350
            btrfs_debug("return point 1\n");
1351
#endif
1352
0
            delete[] raw;
1353
0
            return false;
1354
0
        }
1355
1356
#ifdef BTRFS_DEBUG
1357
        btrfs_debug("progress point 1\n");
1358
#endif
1359
1360
        // get node data
1361
0
        ssize_t result = tsk_fs_read(&a_btrfs->fs_info, phys_address, (char*) raw, nodesize);
1362
0
        if (result != (signed) nodesize) {
1363
0
            if (result >= 0)
1364
0
                btrfs_error(TSK_ERR_FS_READ, "btrfs_treenode_push: Error reading treenode at physical address: 0x%" PRIxDADDR, phys_address);
1365
0
            else
1366
0
                tsk_error_set_errstr2("btrfs_treenode_push: Error reading treenode at physical address: 0x%" PRIxDADDR, phys_address);
1367
0
            tsk_release_lock(&a_btrfs->treenode_cache_lock);
1368
#ifdef BTRFS_DEBUG
1369
            btrfs_debug("return point 2\n");
1370
#endif
1371
0
            delete[] raw;
1372
0
            return false;
1373
0
        }
1374
1375
#ifdef BTRFS_DEBUG
1376
        btrfs_debug("progress point 2\n");
1377
#endif
1378
0
#ifdef BTRFS_CHECK_TREENODE_CSUM
1379
        // validate checksum
1380
0
        if (!btrfs_csum_valid(a_btrfs->sb->csum_type, raw, nodesize)) {
1381
0
            btrfs_error(TSK_ERR_FS_INODE_COR,
1382
0
                    "btrfs_treenode_push: treenode checksum invalid at logical / physical address: 0x%" PRIxDADDR " / 0x%" PRIxDADDR, a_address, phys_address);
1383
0
            tsk_release_lock(&a_btrfs->treenode_cache_lock);
1384
#ifdef BTRFS_DEBUG
1385
            btrfs_debug("return point 3\n");
1386
#endif
1387
0
            delete[] raw;
1388
0
            return false;
1389
0
        }
1390
0
        btrfs_debug("treenode checksum valid\n");
1391
0
#endif
1392
#ifdef BTRFS_DEBUG
1393
        btrfs_debug("progress point 3\n");
1394
#endif
1395
0
        btrfs_treenode_cache_put(a_btrfs, a_address, raw);
1396
0
    }
1397
#ifdef BTRFS_DEBUG
1398
    btrfs_debug("loop done\n");
1399
#endif
1400
0
    tsk_release_lock(&a_btrfs->treenode_cache_lock);
1401
    // append node
1402
0
    btrfs_debug("treenode push at address 0x%" PRIxDADDR " (logical)\n", a_address);
1403
0
    BTRFS_TREENODE *node = new BTRFS_TREENODE;
1404
0
    node->prev = *a_node;
1405
1406
0
    btrfs_tree_header_rawparse(raw, &node->header);
1407
1408
    // validate header address
1409
0
    if (node->header.logical_address != a_address) {
1410
0
        btrfs_error(TSK_ERR_FS_INODE_COR,
1411
0
                "btrfs_treenode_push: logical address different to header: 0x%" PRIxDADDR " / 0x%" PRIxDADDR, a_address, node->header.logical_address);
1412
0
        btrfs_treenode_pop(&node);  // NOT btrfs_treenode_free - otherwise the upper levels would also be freed!
1413
0
        delete[] raw;
1414
0
        return false;
1415
0
    }
1416
1417
0
    size_t data_size = nodesize - BTRFS_TREE_HEADER_RAWLEN;
1418
0
    node->data = new uint8_t[data_size];
1419
0
    memcpy(node->data, raw + BTRFS_TREE_HEADER_RAWLEN, data_size);
1420
1421
0
    btrfs_treenode_set_index(node, true, a_initial_index == BTRFS_FIRST ? 0 : node->header.number_of_items - 1);
1422
1423
0
    *a_node = node;
1424
0
    delete[] raw;
1425
0
    return true;
1426
0
}
1427
1428
1429
/**
1430
 * Returns the first/last item of a tree
1431
 * @param a_btrfs Btrfs info
1432
 * @param a_address logical root node address
1433
 * @param a_direction extremum to be returned
1434
 * @return requested tree item, or NULL on error
1435
 */
1436
static BTRFS_TREENODE *
1437
btrfs_treenode_extremum(BTRFS_INFO * a_btrfs, TSK_DADDR_T a_address,
1438
    const BTRFS_DIRECTION a_direction)
1439
0
{
1440
0
    BTRFS_TREENODE *node = NULL;
1441
0
    for (;;) {
1442
#ifdef BTRFS_DEBUG
1443
        btrfs_debug(" btrfs_treenode_extremum node==%x\n",node);
1444
#endif
1445
0
        if (!btrfs_treenode_push(a_btrfs, &node, a_address, a_direction)) {
1446
0
            btrfs_treenode_free(node);
1447
0
            return NULL;
1448
0
        }
1449
0
        btrfs_debug("btrfs_treenode_extremum looking for %s at level %d (address: 0x%" PRIxDADDR ")\n",
1450
0
                a_direction == BTRFS_LAST ? "maximum" : "minimum", node->header.level, a_address);
1451
1452
0
        if (!node->header.level)
1453
0
            break;
1454
1455
        // go downwards
1456
0
        a_address = node->kp.block_number;
1457
0
    }
1458
0
    return node;
1459
0
}
1460
1461
1462
/**
1463
 * Searches a tree for a specific leaf node. If more than one leaf node matches the considered key parts, the one with the HIGHEST key is chosen.
1464
 * @param a_btrfs Btrfs info
1465
 * @param a_node pointer to treenode structure pointer
1466
 * @param a_address logical root node address
1467
 * @param a_key key
1468
 * @param a_cmp_flags cmp flags
1469
 * @param a_flags flags
1470
 * @return result
1471
 */
1472
static BTRFS_TREENODE_RESULT
1473
btrfs_treenode_search(BTRFS_INFO * a_btrfs, BTRFS_TREENODE ** a_node,
1474
    TSK_DADDR_T a_address, const BTRFS_KEY * a_key, const int a_cmp_flags,
1475
    const int a_flags)
1476
0
{
1477
#ifdef BTRFS_DEBUG
1478
    btrfs_debug("### search key ###\n");
1479
    btrfs_key_debugprint(a_key);
1480
#endif
1481
1482
0
    BTRFS_TREENODE *node = NULL;
1483
0
    for (;;) {
1484
0
        if (!btrfs_treenode_push(a_btrfs, &node, a_address, BTRFS_FIRST)) {
1485
0
            btrfs_treenode_free(node);
1486
0
            return BTRFS_TREENODE_ERROR;
1487
0
        }
1488
1489
0
        uint32_t index_min = 0;
1490
0
        uint32_t index_max = node->header.number_of_items - 1;
1491
0
        while (index_min != index_max) {
1492
0
            btrfs_treenode_set_index(node, true, index_max - (index_max - index_min) / 2);  // rounding up - needed for correct selection of inside nodes!
1493
#ifdef BTRFS_DEBUG
1494
//          btrfs_debug("min = %d, max = %d, node->index = %d\n", index_min, index_max, node->index);
1495
            btrfs_debug("### level %d node - key (loop  cmp @ index %" PRId32 " of %" PRId32 ") ###\n",
1496
                    node->header.level, node->index, node->header.number_of_items);
1497
            btrfs_key_debugprint(&node->key);
1498
#endif
1499
1500
0
            if (btrfs_cmp(a_key, &node->key, a_cmp_flags) < 0)
1501
0
                index_max = node->index - 1;
1502
0
            else
1503
0
                index_min = node->index;
1504
0
        }
1505
0
        btrfs_treenode_set_index(node, true, index_min);
1506
1507
#ifdef BTRFS_DEBUG
1508
        btrfs_debug("### level %d node - key (final cmp @ index %" PRId32 " of %" PRId32 ") ###\n",
1509
                node->header.level, node->index, node->header.number_of_items);
1510
        btrfs_key_debugprint(&node->key);
1511
#endif
1512
1513
0
        int cmp = btrfs_cmp(a_key, &node->key, a_cmp_flags);
1514
0
        if (node->header.level) {
1515
            // ***** INSIDE NODE *****
1516
0
            if (cmp >= 0) {
1517
0
                a_address = node->kp.block_number;
1518
0
                continue;
1519
0
            }
1520
0
        } else {
1521
            // *****     LEAF    *****
1522
0
            if (cmp == 0 || (a_flags & BTRFS_SEARCH_ALLOW_LEFT_NEIGHBOUR)) {
1523
0
                *a_node = node;
1524
0
                return BTRFS_TREENODE_FOUND;
1525
0
            }
1526
0
        }
1527
0
        break;
1528
0
    }
1529
1530
    // node not found
1531
0
    btrfs_treenode_free(node);
1532
0
    return BTRFS_TREENODE_NOT_FOUND;
1533
0
}
1534
1535
1536
/**
1537
 * Goes a single step within a tree
1538
 * @param a_btrfs Btrfs info
1539
 * @param a_node pointer to treenode structure pointer
1540
 * @param a_direction direction to go
1541
 * @return result
1542
 */
1543
static BTRFS_TREENODE_RESULT
1544
btrfs_treenode_single_step(BTRFS_INFO * a_btrfs, BTRFS_TREENODE ** a_node,
1545
    const BTRFS_DIRECTION a_direction)
1546
0
{
1547
0
    BTRFS_TREENODE *node = *a_node;
1548
1549
    // check if first/last tree node + count necessary pops
1550
0
    int pop_count = 0;
1551
0
    while (node->index == (a_direction == BTRFS_LAST ? node->header.number_of_items - 1 : 0)) {
1552
0
        node = node->prev;
1553
0
        if (!node)
1554
0
            return BTRFS_TREENODE_NOT_FOUND;    // abort due to first/last item
1555
0
        pop_count++;
1556
0
    }
1557
1558
    // do the step
1559
0
    btrfs_treenode_set_index(node, false, a_direction == BTRFS_LAST ? 1 : -1);
1560
1561
    // while not yet at leaf level, do a push
1562
0
    for (int push_count = 0; node->header.level; push_count++) {
1563
0
        if (!btrfs_treenode_push(a_btrfs, &node, node->kp.block_number,
1564
0
                a_direction == BTRFS_LAST ? BTRFS_FIRST : BTRFS_LAST)) {
1565
1566
            // undo pushes and step (the old leaf sub-path is still intact, so leave *a_node unaltered)
1567
0
            while (push_count--)
1568
0
                btrfs_treenode_pop(&node);
1569
0
            btrfs_treenode_set_index(node, false, a_direction == BTRFS_LAST ? -1 : 1);
1570
1571
0
            return BTRFS_TREENODE_ERROR;
1572
0
        }
1573
0
    }
1574
1575
    // do the pops (on the old leaf sub-path)
1576
0
    while (pop_count--)
1577
0
        btrfs_treenode_pop(a_node);
1578
1579
0
    *a_node = node;
1580
0
    return BTRFS_TREENODE_FOUND;
1581
0
}
1582
1583
1584
/**
1585
 * Goes steps within a tree
1586
 * @param a_btrfs Btrfs info
1587
 * @param a_node pointer to treenode structure pointer
1588
 * @param a_key key
1589
 * @param a_cmp_flags cmp flags
1590
 * @param a_direction direction to go
1591
 * @param a_flags flags
1592
 * @return result
1593
 */
1594
static BTRFS_TREENODE_RESULT
1595
btrfs_treenode_step(BTRFS_INFO * a_btrfs, BTRFS_TREENODE ** a_node,
1596
    const BTRFS_KEY * a_key, const int a_cmp_flags,
1597
    const BTRFS_DIRECTION a_direction, const int a_flags)
1598
0
{
1599
    // if requested, try to do an initial step to ensure that not the original item is returned
1600
0
    if (a_flags & BTRFS_STEP_INITIAL) {
1601
0
        BTRFS_TREENODE_RESULT result = btrfs_treenode_single_step(a_btrfs, a_node, a_direction);
1602
0
        if (result != BTRFS_TREENODE_FOUND)
1603
0
            return result;
1604
0
    }
1605
1606
    // while key mismatch
1607
0
    while (btrfs_cmp((const BTRFS_KEY*) &(*a_node)->key, a_key, a_cmp_flags)) {
1608
        // if multiple steps not wanted, return
1609
0
        if (!(a_flags & BTRFS_STEP_REPEAT))
1610
0
            return BTRFS_TREENODE_NOT_FOUND;
1611
1612
        // try to do single step
1613
0
        BTRFS_TREENODE_RESULT result = btrfs_treenode_single_step(a_btrfs, a_node, a_direction);
1614
0
        if (result != BTRFS_TREENODE_FOUND)
1615
0
            return result;
1616
0
    }
1617
0
    return BTRFS_TREENODE_FOUND;
1618
0
}
1619
1620
1621
/**
1622
 * Searches a tree for a specific leaf node. If more than one leaf node matches the considered key parts, the one with the LOWEST key is chosen.
1623
 * @param a_btrfs Btrfs info
1624
 * @param a_node pointer to treenode structure pointer
1625
 * @param a_address logical root node address
1626
 * @param a_key key (all ignored key parts must be zeroed!)
1627
 * @param a_cmp_flags cmp flags
1628
 * @return result
1629
 */
1630
static BTRFS_TREENODE_RESULT
1631
btrfs_treenode_search_lowest(BTRFS_INFO * a_btrfs,
1632
    BTRFS_TREENODE ** a_node, TSK_DADDR_T a_address,
1633
    const BTRFS_KEY * a_key, const int a_cmp_flags)
1634
0
{
1635
0
    BTRFS_TREENODE *node = NULL;
1636
1637
    // get either the desired node itself or its left neighbour
1638
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_search(a_btrfs, &node, a_address, a_key,
1639
0
            0, BTRFS_SEARCH_ALLOW_LEFT_NEIGHBOUR);
1640
0
    if (node_result == BTRFS_TREENODE_ERROR)
1641
0
        return BTRFS_TREENODE_ERROR;
1642
0
    if (node_result == BTRFS_TREENODE_NOT_FOUND) {
1643
        // neither exists, so it only could be the first tree node
1644
0
        node = btrfs_treenode_extremum(a_btrfs, a_address, BTRFS_FIRST);
1645
0
        if (!node)
1646
0
            return BTRFS_TREENODE_ERROR;
1647
1648
0
        if (!btrfs_cmp(a_key, &node->key, a_cmp_flags)) {
1649
0
            *a_node = node;
1650
0
            return BTRFS_TREENODE_FOUND;
1651
0
        }
1652
0
        btrfs_treenode_free(node);
1653
0
        return BTRFS_TREENODE_NOT_FOUND;
1654
0
    }
1655
1656
    // check if desired node
1657
0
    if (!btrfs_cmp(a_key, &node->key, a_cmp_flags)) {
1658
0
        *a_node = node;
1659
0
        return BTRFS_TREENODE_FOUND;
1660
0
    }
1661
1662
    // left neighbour, so it only could be the next node
1663
0
    node_result = btrfs_treenode_step(a_btrfs, &node, a_key,
1664
0
            a_cmp_flags, BTRFS_LAST, BTRFS_STEP_INITIAL);
1665
0
    if (node_result == BTRFS_TREENODE_FOUND) {
1666
0
        *a_node = node;
1667
0
        return BTRFS_TREENODE_FOUND;
1668
0
    }
1669
0
    btrfs_treenode_free(node);
1670
0
    return node_result;
1671
0
}
1672
1673
1674
/**
1675
 * Derives the logical root node address of a specific subtree from the root tree
1676
 * @param a_btrfs Btrfs info
1677
 * @param a_obj_id object ID of the subtree
1678
 * @param a_node_tree_address pointer to the logical root node address
1679
 * @return true if no error occured, false otherwise
1680
 */
1681
static bool
1682
btrfs_root_tree_derive_subtree_address(BTRFS_INFO * a_btrfs,
1683
    uint64_t a_obj_id, uint64_t * a_node_tree_address)
1684
0
{
1685
0
    BTRFS_KEY key;
1686
0
    key.object_id = a_obj_id;
1687
0
    key.item_type = BTRFS_ITEM_TYPE_ROOT_ITEM;
1688
0
    key.offset = 0; // not used, except at debug output
1689
1690
0
    BTRFS_TREENODE *node = NULL;
1691
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_search(a_btrfs, &node, a_btrfs->sb->root_tree_root, &key,
1692
0
            BTRFS_CMP_IGNORE_OFFSET, 0);
1693
0
    if (node_result == BTRFS_TREENODE_ERROR)
1694
0
        return false;
1695
0
    if (node_result == BTRFS_TREENODE_NOT_FOUND) {
1696
0
        btrfs_error(TSK_ERR_FS_CORRUPT,
1697
0
                "btrfs_root_tree_derive_node_tree_address: Could not find ROOT_ITEM of object ID 0x%" PRIu64 " in root tree", a_obj_id);
1698
0
        return false;
1699
0
    }
1700
1701
0
    BTRFS_ROOT_ITEM root_item;
1702
0
    btrfs_root_item_rawparse(btrfs_treenode_itemdata(node), &root_item);
1703
1704
#ifdef BTRFS_DEBUG
1705
    btrfs_debug("#####\n");
1706
    btrfs_debug("ROOT_ITEM of object ID 0x%" PRIu64 ":\n", a_obj_id);
1707
    btrfs_root_item_debugprint(&root_item);
1708
#endif
1709
1710
0
    *a_node_tree_address = root_item.root_node_block_number;
1711
1712
0
    btrfs_treenode_free(node);
1713
0
    return true;
1714
0
}
1715
1716
1717
1718
/*
1719
 * chunks 2/2
1720
 */
1721
1722
1723
/**
1724
 * Processes all chunks of the chunk tree into a newly created cached chunk mapping
1725
 * @param a_btrfs Btrfs info
1726
 * @return pointer to new cached chunk mapping if no error occurs, otherwise NULL
1727
 */
1728
static BTRFS_CACHED_CHUNK_MAPPING *
1729
btrfs_chunks_from_chunktree(BTRFS_INFO * a_btrfs)
1730
0
{
1731
    // superblock system chunks must already have been derived!
1732
1733
0
    BTRFS_KEY key;
1734
0
    key.object_id = BTRFS_OBJID_CHUNK_ITEM;
1735
0
    key.item_type = BTRFS_ITEM_TYPE_CHUNK_ITEM;
1736
0
    key.offset = 0; // not used, except at debug output
1737
1738
    // iterate through chunk tree
1739
#ifdef BTRFS_DEBUG
1740
    btrfs_debug("Parsing chunk tree chunks...\n");
1741
#endif
1742
0
    BTRFS_TREENODE *node = btrfs_treenode_extremum(a_btrfs, a_btrfs->sb->chunk_tree_root, BTRFS_FIRST);
1743
#ifdef BTRFS_DEBUG
1744
    btrfs_debug(" node==%x\n",node);
1745
#endif
1746
0
    if (!node) {
1747
0
        return NULL;
1748
0
    }
1749
1750
    // first CHUNK_ITEM
1751
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_step(a_btrfs, &node, &key,
1752
0
            BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_REPEAT);
1753
0
    if (node_result != BTRFS_TREENODE_FOUND) {
1754
0
        if (node_result == BTRFS_TREENODE_NOT_FOUND)
1755
0
            btrfs_error(TSK_ERR_FS_CORRUPT,
1756
0
                    "btrfs_chunks_from_chunktree: Could not find any CHUNK_ITEM in chunk tree");
1757
0
        btrfs_treenode_free(node);
1758
0
        return NULL;
1759
0
    }
1760
1761
#ifdef BTRFS_DEBUG
1762
    btrfs_debug("Parsing chunk mapping...\n");
1763
#endif
1764
0
    BTRFS_CACHED_CHUNK_MAPPING *chunks = new BTRFS_CACHED_CHUNK_MAPPING;
1765
0
    do {
1766
0
        btrfs_chunks_process_chunk_item(a_btrfs, chunks, node->key.offset, btrfs_treenode_itemdata(node));
1767
1768
        // next CHUNK_ITEM
1769
0
        node_result = btrfs_treenode_step(a_btrfs, &node, &key,
1770
0
                BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_INITIAL | BTRFS_STEP_REPEAT);
1771
0
        if (node_result == BTRFS_TREENODE_ERROR) {
1772
0
            btrfs_treenode_free(node);
1773
0
            delete chunks;
1774
0
            return NULL;
1775
0
        }
1776
0
    } while (node_result == BTRFS_TREENODE_FOUND);
1777
1778
0
    btrfs_treenode_free(node);
1779
0
    return chunks;
1780
0
}
1781
1782
1783
1784
/*
1785
 * subvolumes
1786
 */
1787
1788
1789
/**
1790
 * Add the subvolume described by the specified ROOT_ITEM
1791
 * @param a_btrfs Btrfs info
1792
 * @param a_node pointer to treenode structure with a selected ROOT_ITEM
1793
 * @return true if no error occured, false otherwise
1794
 */
1795
static bool
1796
btrfs_parse_subvolume(BTRFS_INFO * a_btrfs, BTRFS_TREENODE * a_node)
1797
0
{
1798
    // create subvolume
1799
0
    uint64_t subvol_id = a_node->key.object_id;
1800
0
    BTRFS_SUBVOLUME *subvol = &((*a_btrfs->subvolumes)[subvol_id]);
1801
0
    btrfs_root_item_rawparse(btrfs_treenode_itemdata(a_node), &subvol->ri);
1802
1803
0
    BTRFS_KEY key;
1804
0
    key.object_id = 0;  // not used, except at debug output
1805
0
    key.item_type = BTRFS_ITEM_TYPE_INODE_ITEM;
1806
0
    key.offset = 0;
1807
1808
    // iterate over all inodes
1809
0
    BTRFS_TREENODE *node = btrfs_treenode_extremum(a_btrfs, subvol->ri.root_node_block_number, BTRFS_FIRST);
1810
0
    if (!node)
1811
0
        return false;
1812
1813
    // first INODE_ITEM
1814
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_step(a_btrfs, &node, &key,
1815
0
            BTRFS_CMP_IGNORE_OBJID, BTRFS_LAST, BTRFS_STEP_REPEAT);
1816
0
    if (node_result != BTRFS_TREENODE_FOUND) {
1817
0
        if (node_result == BTRFS_TREENODE_NOT_FOUND)
1818
0
            btrfs_error(TSK_ERR_FS_CORRUPT,
1819
0
                    "btrfs_parse_subvolume: Could not find any INODE_ITEM in subvolume tree 0x%" PRIx64, subvol_id);
1820
0
        btrfs_treenode_free(node);
1821
0
        return false;
1822
0
    }
1823
1824
0
    do {
1825
        // add to virt->real mapping
1826
0
        TSK_INUM_T inum = node->key.object_id;
1827
0
        a_btrfs->virt2real_inums->push_back(btrfs_virt2real_inums_t::value_type(subvol_id, inum));
1828
1829
        // add to real->virt mapping
1830
0
        TSK_INUM_T vinum = a_btrfs->virt2real_inums->size() - 1;
1831
0
        subvol->real2virt_inums.insert(subvol->real2virt_inums.end(),
1832
0
                btrfs_real2virt_inums_t::value_type(inum, vinum));
1833
1834
        // next INODE_ITEM
1835
0
        node_result = btrfs_treenode_step(a_btrfs, &node, &key,
1836
0
                BTRFS_CMP_IGNORE_OBJID, BTRFS_LAST, BTRFS_STEP_INITIAL | BTRFS_STEP_REPEAT);
1837
0
        if (node_result == BTRFS_TREENODE_ERROR) {
1838
0
            btrfs_treenode_free(node);
1839
0
            return false;
1840
0
        }
1841
0
    } while (node_result == BTRFS_TREENODE_FOUND);
1842
1843
0
    btrfs_treenode_free(node);
1844
0
    btrfs_debug("########## subvolume 0x%" PRIx64 " with %zd inodes ##########\n",
1845
0
            subvol_id, subvol->real2virt_inums.size());
1846
0
    if (tsk_verbose)
1847
0
        tsk_fprintf(stderr, "btrfs_parse_subvolume: inodes in subvolume 0x%" PRIx64 "%s: %zd\n",
1848
0
                subvol_id, subvol_id == BTRFS_OBJID_FS_TREE ? " (FS_TREE)" : "", subvol->real2virt_inums.size());
1849
0
    return true;
1850
0
}
1851
1852
1853
/**
1854
 * Add all subvolumes
1855
 * @param a_btrfs Btrfs info
1856
 * @return true if no error occured, false otherwise
1857
 */
1858
static bool
1859
btrfs_parse_subvolumes(BTRFS_INFO * a_btrfs)
1860
0
{
1861
0
    BTRFS_KEY key;
1862
0
    key.object_id = BTRFS_OBJID_FS_TREE;
1863
0
    key.item_type = BTRFS_ITEM_TYPE_ROOT_ITEM;
1864
0
    key.offset = 0; // not used, except at debug output
1865
1866
    // iterate through all tree roots
1867
0
    BTRFS_TREENODE *node = NULL;
1868
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_search(a_btrfs, &node, a_btrfs->sb->root_tree_root, &key,
1869
0
            BTRFS_CMP_IGNORE_OFFSET, 0);
1870
0
    if (node_result == BTRFS_TREENODE_ERROR)
1871
0
        return false;
1872
0
    if (node_result == BTRFS_TREENODE_NOT_FOUND) {
1873
0
        btrfs_error(TSK_ERR_FS_CORRUPT,
1874
0
                "btrfs_parse_subvolumes: Could not find FS_TREE in root tree");
1875
0
        return false;
1876
0
    }
1877
1878
0
    do {
1879
        // only process FS_TREE and subvolumes
1880
0
        uint64_t subvol = node->key.object_id;
1881
0
        if (subvol == BTRFS_OBJID_FS_TREE || (subvol >= BTRFS_OBJID_MIN && subvol <= BTRFS_OBJID_MAX)) {
1882
0
            if (!btrfs_parse_subvolume(a_btrfs, node)) {
1883
0
                btrfs_treenode_free(node);
1884
0
                return false;
1885
0
            }
1886
0
        }
1887
1888
        // next ROOT_ITEM
1889
0
        node_result = btrfs_treenode_step(a_btrfs, &node, &key,
1890
0
                BTRFS_CMP_IGNORE_OBJID | BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_INITIAL | BTRFS_STEP_REPEAT);
1891
0
        if (node_result == BTRFS_TREENODE_ERROR) {
1892
0
            btrfs_treenode_free(node);
1893
0
            return false;
1894
0
        }
1895
0
    } while (node_result == BTRFS_TREENODE_FOUND);
1896
1897
0
    btrfs_treenode_free(node);
1898
0
    return true;
1899
0
}
1900
1901
1902
/**
1903
 * Maps a virtual inum to a real inum
1904
 * @param a_btrfs Btrfs info
1905
 * @param a_vinum virtual inum
1906
 * @param a_subvol pointer to subvolume ID
1907
 * @param a_inum pointer to real inum
1908
 * @return true if no error occured, false otherwise
1909
 */
1910
static bool
1911
btrfs_inum_virt2real_map(BTRFS_INFO * a_btrfs, const TSK_INUM_T a_vinum,
1912
    uint64_t * a_subvol, TSK_INUM_T * a_inum)
1913
0
{
1914
    // ignore exceeded range (and thereby special virtual inums)
1915
0
    if (a_vinum >= a_btrfs->virt2real_inums->size()) {
1916
0
        btrfs_error(TSK_ERR_FS_ARG,
1917
0
                "btrfs_inum_virt2real_map: invalid virtual inum: %" PRIuINUM, a_vinum);
1918
0
        return false;
1919
0
    }
1920
1921
0
    btrfs_virt2real_inums_t::value_type result = (*a_btrfs->virt2real_inums)[a_vinum];
1922
1923
0
    *a_subvol = result.first;
1924
0
    *a_inum = result.second;
1925
0
    return true;
1926
0
}
1927
1928
1929
/**
1930
 * Maps a real inum to a virtual inum
1931
 * @param a_btrfs Btrfs info
1932
 * @param a_subvol subvolume ID
1933
 * @param a_inum real inum
1934
 * @param a_vinum pointer to virtual inum
1935
 * @return true if no error occured, false otherwise
1936
 */
1937
static bool
1938
btrfs_inum_real2virt_map(BTRFS_INFO * a_btrfs, const uint64_t a_subvol,
1939
    const TSK_INUM_T a_inum, TSK_INUM_T * a_vinum)
1940
0
{
1941
0
    btrfs_subvolumes_t::iterator subvol_it = a_btrfs->subvolumes->find(a_subvol);
1942
0
    if (subvol_it == a_btrfs->subvolumes->end()) {
1943
0
        btrfs_error(TSK_ERR_FS_ARG,
1944
0
                "btrfs_inum_real2virt_map: invalid subvolume ID: 0x%" PRIx64, a_subvol);
1945
0
        return false;
1946
0
    }
1947
1948
0
    btrfs_real2virt_inums_t::iterator inode_it = subvol_it->second.real2virt_inums.find(a_inum);
1949
0
    if (inode_it == subvol_it->second.real2virt_inums.end()) {
1950
0
        btrfs_error(TSK_ERR_FS_ARG,
1951
0
                "btrfs_inum_real2virt_map: invalid real inum: %" PRIuINUM, a_inum);
1952
0
        return false;
1953
0
    }
1954
1955
0
    *a_vinum = inode_it->second;
1956
0
    return true;
1957
0
}
1958
1959
1960
/**
1961
 * Derives the set default subvolume.
1962
 * @param a_btrfs Btrfs info
1963
 * @return subvolume ID if no error occured, otherwise 0
1964
 */
1965
static uint64_t
1966
btrfs_subvol_default(BTRFS_INFO * a_btrfs)
1967
0
{
1968
0
    BTRFS_KEY key;
1969
0
    key.object_id = a_btrfs->sb->root_dir_objectid;
1970
0
    key.item_type = BTRFS_ITEM_TYPE_DIR_ITEM;
1971
0
    key.offset = 0; // not used, except at debug output
1972
1973
0
    BTRFS_TREENODE *node = NULL;
1974
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_search(a_btrfs, &node, a_btrfs->sb->root_tree_root, &key,
1975
0
            BTRFS_CMP_IGNORE_OFFSET, 0);
1976
0
    if (node_result == BTRFS_TREENODE_ERROR)
1977
0
        return 0;
1978
0
    if (node_result == BTRFS_TREENODE_NOT_FOUND) {
1979
        // default to FS_TREE
1980
0
        return BTRFS_OBJID_FS_TREE;
1981
0
    }
1982
1983
    // ensure this is the only entry
1984
0
    BTRFS_DIR_ENTRY *de = btrfs_dir_entry_fromraw(btrfs_treenode_itemdata(node), btrfs_treenode_itemsize(node));
1985
0
    if (de->next) {
1986
0
        btrfs_error(TSK_ERR_FS_CORRUPT,
1987
0
                "btrfs_subvol_default: DIR_ITEM item with more than one entry");
1988
0
        btrfs_treenode_free(node);
1989
0
        btrfs_dir_entry_free(de);
1990
0
        return 0;
1991
0
    }
1992
#ifdef BTRFS_DEBUG
1993
    btrfs_debug("### DIR_ITEM ###\n");
1994
    btrfs_dir_entry_debugprint(de);
1995
#endif
1996
1997
    // ensure expected name
1998
0
    if (strcmp(de->name, "default")) {
1999
0
        btrfs_error(TSK_ERR_FS_CORRUPT,
2000
0
                "btrfs_subvol_default: DIR_ITEM has wrong name: %s", de->name);
2001
0
        btrfs_treenode_free(node);
2002
0
        btrfs_dir_entry_free(de);
2003
0
        return 0;
2004
0
    }
2005
2006
    // success
2007
0
    uint64_t subvol = de->child.object_id;
2008
0
    btrfs_treenode_free(node);
2009
0
    btrfs_dir_entry_free(de);
2010
0
    return subvol;
2011
0
}
2012
2013
2014
/**
2015
 * Returns the logical root node address of a subvolume (which must exist)
2016
 * @param a_btrfs Btrfs info
2017
 * @param a_subvol subvolume ID
2018
 * @return tree address
2019
 */
2020
static TSK_DADDR_T
2021
btrfs_subvol_tree_address(BTRFS_INFO * a_btrfs, const uint64_t a_subvol)
2022
0
{
2023
0
    return (*a_btrfs->subvolumes)[a_subvol].ri.root_node_block_number;
2024
0
}
2025
2026
2027
/**
2028
 * Returns the real root inum of a subvolume (which must exist)
2029
 * @param a_btrfs Btrfs info
2030
 * @param a_subvol subvolume ID
2031
 * @return root inum
2032
 */
2033
static TSK_INUM_T
2034
btrfs_subvol_root_inum(BTRFS_INFO * a_btrfs, const uint64_t a_subvol)
2035
0
{
2036
0
    return (*a_btrfs->subvolumes)[a_subvol].ri.root_dir_object_id;
2037
0
}
2038
2039
2040
2041
/*
2042
 * block walk
2043
 */
2044
2045
2046
/**
2047
 * Allocates a blockwalk structure
2048
 * @param a_btrfs Btrfs info
2049
 * @param a_start_block physical block to start the blockwalk with
2050
 * @return pointer to blockwalk structure
2051
 */
2052
static BTRFS_BLOCKWALK *
2053
btrfs_blockwalk_alloc(BTRFS_INFO * a_btrfs, const uint64_t a_start_block)
2054
0
{
2055
0
    BTRFS_BLOCKWALK *bw = new BTRFS_BLOCKWALK;
2056
0
    bw->btrfs = a_btrfs;
2057
0
    bw->block = a_start_block;
2058
2059
0
    bw->no_more_ei = false;
2060
0
    bw->ei_key.item_type = BTRFS_ITEM_TYPE_EXTENT_ITEM & BTRFS_ITEM_TYPE_METADATA_ITEM; // via BTRFS_CMP_IGNORE_LSB_TYPE this includes both types!
2061
0
    bw->ei_key.offset = 0;  // not used, except at debug output
2062
0
    bw->ei_node = NULL;
2063
0
    bw->ei_start = 0;
2064
0
    bw->ei_end = 0;
2065
2066
0
    bw->no_more_cc = false;
2067
0
    bw->cc = NULL;
2068
0
    return bw;
2069
0
}
2070
2071
2072
/**
2073
 * Frees a blockwalk structure
2074
 * @param a_bw pointer to blockwalk structure
2075
 */
2076
static void
2077
btrfs_blockwalk_free(BTRFS_BLOCKWALK * a_bw)
2078
0
{
2079
0
    if (!a_bw)
2080
0
        return;
2081
2082
0
    if (a_bw->ei_node)
2083
0
        btrfs_treenode_free(a_bw->ei_node);
2084
0
    delete a_bw;
2085
0
}
2086
2087
2088
/**
2089
 * Applies the values of the current selected extent item to the blockwalk structure
2090
 * @param a_bw pointer to blockwalk structure
2091
 */
2092
static void
2093
btrfs_blockwalk_apply_extent_item(BTRFS_BLOCKWALK * a_bw)
2094
0
{
2095
0
    BTRFS_EXTENT_ITEM ei;
2096
0
    btrfs_extent_item_rawparse(btrfs_treenode_itemdata(a_bw->ei_node), &ei);
2097
2098
0
    a_bw->ei_start = a_bw->ei_node->key.object_id;
2099
2100
    // skinny/normal extent item
2101
0
    if (a_bw->ei_node->key.item_type == BTRFS_ITEM_TYPE_METADATA_ITEM)
2102
0
        a_bw->ei_end = a_bw->ei_start + a_bw->btrfs->sb->leafsize;
2103
0
    else
2104
0
        a_bw->ei_end = a_bw->ei_start + a_bw->ei_node->key.offset;
2105
2106
0
    a_bw->ei_flags = TSK_FS_BLOCK_FLAG_ALLOC;
2107
0
    if (ei.flags & BTRFS_EXTENT_ITEM_FLAGS_DATA)
2108
0
        a_bw->ei_flags = (TSK_FS_BLOCK_FLAG_ENUM) (a_bw->ei_flags | TSK_FS_BLOCK_FLAG_CONT);
2109
0
    if (ei.flags & BTRFS_EXTENT_ITEM_FLAGS_TREE_BLOCK)
2110
0
        a_bw->ei_flags = (TSK_FS_BLOCK_FLAG_ENUM) (a_bw->ei_flags | TSK_FS_BLOCK_FLAG_META);
2111
0
}
2112
2113
2114
/**
2115
 * Ensures that the current extent data covers a logical address or otherwise lies before or after it
2116
 * @param a_bw pointer to blockwalk structure
2117
 * @param a_block_address logical address
2118
 * @return true if no error occured, otherwise false
2119
 */
2120
static bool
2121
btrfs_blockwalk_ensure_extent_data(BTRFS_BLOCKWALK * a_bw,
2122
    const TSK_DADDR_T a_block_address)
2123
0
{
2124
    // if we already have a node
2125
0
    if (a_bw->ei_node) {
2126
        // if the next extent item is needed, fetch it (if existing)
2127
0
        if (!a_bw->no_more_ei && a_block_address >= a_bw->ei_end) {
2128
0
            BTRFS_TREENODE_RESULT node_result = btrfs_treenode_step(a_bw->btrfs, &a_bw->ei_node, &a_bw->ei_key,
2129
0
                    BTRFS_CMP_IGNORE_OBJID | BTRFS_CMP_IGNORE_LSB_TYPE | BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_INITIAL | BTRFS_STEP_REPEAT);
2130
0
            if (node_result == BTRFS_TREENODE_ERROR) {
2131
0
                tsk_error_errstr2_concat("- btrfs_blockwalk_invoke: stepping to next extent item");
2132
0
                return false;
2133
0
            }
2134
0
            if (node_result == BTRFS_TREENODE_NOT_FOUND)
2135
0
                a_bw->no_more_ei = true;
2136
0
            if (node_result == BTRFS_TREENODE_FOUND)
2137
0
                btrfs_blockwalk_apply_extent_item(a_bw);
2138
0
        }
2139
0
        return true;
2140
0
    }
2141
2142
2143
    /* try to get an extent item
2144
     *   a) whose address (= object ID) equals the block's address OR OTHERWISE
2145
     *   b) being the next left neighbour of a (non-existing) a)
2146
     * which of both exactly applies will be handled by the final address comparison
2147
     */
2148
0
    a_bw->ei_key.object_id = a_block_address;
2149
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_search(a_bw->btrfs, &a_bw->ei_node, a_bw->btrfs->extent_tree_root_node_address, &a_bw->ei_key,
2150
0
            BTRFS_CMP_IGNORE_LSB_TYPE | BTRFS_CMP_IGNORE_OFFSET, BTRFS_SEARCH_ALLOW_LEFT_NEIGHBOUR);
2151
0
    if (node_result == BTRFS_TREENODE_ERROR) {
2152
0
        tsk_error_errstr2_concat("- btrfs_blockwalk_retrieve_initial_node: loading extent item");
2153
0
        return false;
2154
0
    }
2155
0
    if (node_result == BTRFS_TREENODE_FOUND) {
2156
        // ensure that in case b) the selected item is an extent item
2157
0
        node_result = btrfs_treenode_step(a_bw->btrfs, &a_bw->ei_node, &a_bw->ei_key,
2158
0
                BTRFS_CMP_IGNORE_OBJID | BTRFS_CMP_IGNORE_LSB_TYPE | BTRFS_CMP_IGNORE_OFFSET, BTRFS_FIRST, BTRFS_STEP_REPEAT);
2159
0
        if (node_result == BTRFS_TREENODE_ERROR) {
2160
0
            tsk_error_errstr2_concat("- btrfs_blockwalk_retrieve_initial_node: stepping to previous extent item");
2161
0
            return false;
2162
0
        }
2163
0
        if (node_result == BTRFS_TREENODE_FOUND) {
2164
0
            btrfs_blockwalk_apply_extent_item(a_bw);
2165
0
            return true;
2166
0
        }
2167
0
    }
2168
2169
    /* neither a) or b) applies, so we know that the current address is not covered by any extent item - therefore prepare for next invokation:
2170
     * now we can only get an extent item
2171
     *   c) being the next right neighbour of a (non-existing) a)
2172
     * this is exactly fulfilled by the very first extent item in the tree, so fetch it
2173
     * (such an item definitely exists, as there are at least the default trees using allocated space)
2174
     */
2175
0
    btrfs_treenode_free(a_bw->ei_node);
2176
0
    a_bw->ei_node = btrfs_treenode_extremum(a_bw->btrfs, a_bw->btrfs->extent_tree_root_node_address, BTRFS_FIRST);
2177
0
    if (!a_bw->ei_node)
2178
0
        return false;
2179
2180
0
    node_result = btrfs_treenode_step(a_bw->btrfs, &a_bw->ei_node, &a_bw->ei_key,
2181
0
            BTRFS_CMP_IGNORE_OBJID | BTRFS_CMP_IGNORE_LSB_TYPE | BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_REPEAT);
2182
0
    if (node_result == BTRFS_TREENODE_ERROR) {
2183
0
        tsk_error_errstr2_concat("- btrfs_blockwalk_retrieve_initial_node: stepping to first extent item");
2184
0
        return false;
2185
0
    }
2186
0
    if (node_result == BTRFS_TREENODE_NOT_FOUND) {
2187
0
        btrfs_error(TSK_ERR_FS_CORRUPT, "btrfs_blockwalk_retrieve_initial_node: no extent items found");
2188
0
        return false;
2189
0
    }
2190
2191
0
    btrfs_blockwalk_apply_extent_item(a_bw);
2192
0
    return true;
2193
0
}
2194
2195
2196
/**
2197
 * Tries to map a physical address to a logical address.
2198
 * @param a_bw pointer to blockwalk structure
2199
 * @param a_block_address pointer to address
2200
 * @return true if the address could be mapped, otherwise false
2201
 */
2202
static bool
2203
btrfs_blockwalk_apply_mapping(BTRFS_BLOCKWALK * a_bw,
2204
    TSK_DADDR_T * a_block_address)
2205
0
{
2206
    // if no more cached chunks abort
2207
0
    if (a_bw->no_more_cc)
2208
0
        return false;
2209
2210
    // if valid cached chunk is current or next, try to map
2211
0
    if (a_bw->cc && btrfs_chunk_remaining_bytes(a_bw->cc, *a_block_address) > 0)
2212
0
        return btrfs_chunk_map(a_bw->cc, *a_block_address, a_block_address);
2213
2214
2215
    // derive next cached chunk (thereby try to map)
2216
0
    a_bw->cc = NULL;
2217
0
    bool result = btrfs_address_map(&a_bw->btrfs->chunks->phys2log, &a_bw->cc, *a_block_address, a_block_address);
2218
2219
    // reset extent data (in case of current logical address smaller than previous one)
2220
0
    btrfs_treenode_free(a_bw->ei_node);
2221
0
    a_bw->ei_node = NULL;
2222
0
    a_bw->no_more_ei = false;
2223
2224
    // check if no more cached chunks
2225
0
    if (!a_bw->cc)
2226
0
        a_bw->no_more_cc = true;
2227
2228
0
    return result;
2229
0
}
2230
2231
2232
/**
2233
 * Returns the block flags of the next block.
2234
 * @param a_bw pointer to blockwalk structure
2235
 * @return flags of the current block if no error occured, otherwise TSK_FS_BLOCK_FLAG_UNUSED
2236
 */
2237
static TSK_FS_BLOCK_FLAG_ENUM
2238
btrfs_blockwalk_invoke(BTRFS_BLOCKWALK * a_bw)
2239
0
{
2240
    // early block increment for next invokation
2241
0
    TSK_DADDR_T block_address = a_bw->block++ * a_bw->btrfs->fs_info.block_size;
2242
2243
    // check for superblocks (which are not covered by extent tree)
2244
0
    if (btrfs_superblock_includes_address(block_address))
2245
0
        return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_ALLOC | TSK_FS_BLOCK_FLAG_META);
2246
2247
    // handle phys->log mapping
2248
//  btrfs_debug("### address before: 0x%" PRIxDADDR "\n", block_address);
2249
0
    if (!btrfs_blockwalk_apply_mapping(a_bw, &block_address))
2250
0
        return TSK_FS_BLOCK_FLAG_UNALLOC;
2251
//  btrfs_debug("### address after: 0x%" PRIxDADDR "\n", block_address);
2252
2253
    // ensure correct extent data
2254
0
    if (!btrfs_blockwalk_ensure_extent_data(a_bw, block_address))
2255
0
        return TSK_FS_BLOCK_FLAG_UNUSED;
2256
2257
    // if block address within extent item range, return regarding flags
2258
0
    return (block_address >= a_bw->ei_start && block_address < a_bw->ei_end) ?
2259
0
            a_bw->ei_flags : TSK_FS_BLOCK_FLAG_UNALLOC;
2260
0
}
2261
2262
2263
/**
2264
 * Returns the block flags of the specified physical block
2265
 * @param a_fs FS info
2266
 * @param a_addr physical block
2267
 * @return flags of the block if no error occured, otherwise TSK_FS_BLOCK_FLAG_UNUSED
2268
 */
2269
static TSK_FS_BLOCK_FLAG_ENUM
2270
btrfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
2271
0
{
2272
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
2273
2274
    // single blockwalk invokation
2275
0
    BTRFS_BLOCKWALK *bw = btrfs_blockwalk_alloc(btrfs, a_addr);
2276
0
    TSK_FS_BLOCK_FLAG_ENUM result = btrfs_blockwalk_invoke(bw);
2277
0
    btrfs_blockwalk_free(bw);
2278
2279
0
    return result;
2280
0
}
2281
2282
2283
/**
2284
 * Iterates through a range of physical blocks and calls the callback with the content and the allocation status of each desired block.
2285
 * @param a_fs FS info
2286
 * @param a_start_blk physical start block
2287
 * @param a_end_blk physical end block
2288
 * @param a_flags flags
2289
 * @param a_action pointer to callback
2290
 * @param a_ptr pointer to opaque callback data
2291
 * @return 0 if no error occured, otherwise 1
2292
 */
2293
static uint8_t
2294
btrfs_block_walk(TSK_FS_INFO * a_fs, TSK_DADDR_T a_start_blk,
2295
    TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags,
2296
    TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr)
2297
0
{
2298
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
2299
0
    TSK_FS_BLOCK *block;
2300
0
    uint8_t result = 0;
2301
2302
    // clean up any error messages that are lying around
2303
0
    tsk_error_reset();
2304
2305
    // sanity checks
2306
0
    if (a_start_blk < a_fs->first_block || a_start_blk > a_fs->last_block) {
2307
0
        btrfs_error(TSK_ERR_FS_WALK_RNG,
2308
0
                "btrfs_block_walk: start block: %" PRIuDADDR, a_start_blk);
2309
0
        return 1;
2310
0
    }
2311
0
    if (a_end_blk < a_fs->first_block || a_end_blk > a_fs->last_block || a_end_blk < a_start_blk) {
2312
0
        btrfs_error(TSK_ERR_FS_WALK_RNG,
2313
0
                "btrfs_block_walk: end block: %" PRIuDADDR, a_end_blk);
2314
0
        return 1;
2315
0
    }
2316
2317
    // sanity check on a_flags
2318
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) && ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0))
2319
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC | TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
2320
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) && ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0))
2321
0
        a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
2322
2323
0
    block = tsk_fs_block_alloc(a_fs);
2324
0
    if (!block)
2325
0
        return 1;
2326
2327
    // iterate through block range
2328
0
    BTRFS_BLOCKWALK *bw = btrfs_blockwalk_alloc(btrfs, a_start_blk);
2329
0
    for (TSK_DADDR_T addr = a_start_blk; addr <= a_end_blk; addr++) {
2330
0
        TSK_FS_BLOCK_FLAG_ENUM block_flags = btrfs_blockwalk_invoke(bw);
2331
0
        if (block_flags == TSK_FS_BLOCK_FLAG_UNUSED) {
2332
0
            tsk_error_errstr2_concat("- btrfs_block_walk: block %" PRIuDADDR, addr);
2333
0
            result = 1;
2334
0
            goto end;
2335
0
        }
2336
2337
        // test if we should call the callback with this one
2338
0
        if ((block_flags & TSK_FS_BLOCK_FLAG_META)      && !(a_flags & TSK_FS_BLOCK_WALK_FLAG_META))
2339
0
            continue;
2340
0
        if ((block_flags & TSK_FS_BLOCK_FLAG_CONT)      && !(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT))
2341
0
            continue;
2342
0
        if ((block_flags & TSK_FS_BLOCK_FLAG_ALLOC)     && !(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC))
2343
0
            continue;
2344
0
        if ((block_flags & TSK_FS_BLOCK_FLAG_UNALLOC)   && !(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC))
2345
0
            continue;
2346
2347
0
        if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
2348
0
            block_flags = (TSK_FS_BLOCK_FLAG_ENUM) (block_flags | TSK_FS_BLOCK_FLAG_AONLY);
2349
2350
0
        if (!tsk_fs_block_get_flag(a_fs, block, addr, block_flags)) {
2351
0
            tsk_error_set_errstr2("btrfs_block_walk: block %" PRIuDADDR, addr);
2352
0
            result = 1;
2353
0
            goto end;
2354
0
        }
2355
2356
        // invoke callback
2357
0
        int retval = a_action(block, a_ptr);
2358
0
        if (retval == TSK_WALK_STOP)
2359
0
            break;
2360
0
        if (retval == TSK_WALK_ERROR) {
2361
0
            result = 1;
2362
0
            break;
2363
0
        }
2364
0
    }
2365
2366
    // cleanup
2367
0
end:
2368
0
    btrfs_blockwalk_free(bw);
2369
0
    tsk_fs_block_free(block);
2370
0
    return result;
2371
0
}
2372
2373
2374
2375
/*
2376
 * EXTENT_DATA walk
2377
 */
2378
2379
2380
/**
2381
 * Frees an EXTENT_DATA walk structure.
2382
 * @param a_edw pointer to EXTENT_DATA walk structure
2383
 */
2384
static void
2385
btrfs_extent_datawalk_free(BTRFS_EXTENT_DATAWALK * a_edw)
2386
0
{
2387
0
    if (!a_edw)
2388
0
        return;
2389
2390
0
    if (a_edw->node)
2391
0
        btrfs_treenode_free(a_edw->node);
2392
0
    delete a_edw;
2393
0
}
2394
2395
2396
/**
2397
 * Allocates an EXTENT_DATA walk structure.
2398
 * @param a_btrfs Btrfs info
2399
 * @param a_meta pointer to file meta structure
2400
 * @return pointer to EXTENT_DATA walk structure if no error occured, otherwise NULL
2401
 */
2402
static BTRFS_EXTENT_DATAWALK *
2403
btrfs_extent_datawalk_alloc(BTRFS_INFO * a_btrfs,
2404
    const TSK_FS_META * a_meta)
2405
0
{
2406
0
    BTRFS_EXTENT_DATAWALK *edw = new BTRFS_EXTENT_DATAWALK;
2407
0
    edw->btrfs = a_btrfs;
2408
0
    edw->size = a_meta->size;
2409
0
    edw->offset = 0;
2410
2411
0
    edw->node = NULL;
2412
2413
0
    uint64_t subvol;
2414
0
    TSK_INUM_T inum;
2415
0
    if (!btrfs_inum_virt2real_map(a_btrfs, a_meta->addr, &subvol, &inum)) {
2416
0
        btrfs_extent_datawalk_free(edw);
2417
0
        return NULL;
2418
0
    }
2419
2420
0
    edw->key.object_id = inum;
2421
0
    edw->key.item_type = BTRFS_ITEM_TYPE_EXTENT_DATA;
2422
0
    edw->key.offset = 0;
2423
2424
    // get first item (if it exists)
2425
0
    BTRFS_TREENODE_RESULT node_result = btrfs_treenode_search_lowest(edw->btrfs, &edw->node, btrfs_subvol_tree_address(edw->btrfs, subvol), &edw->key,
2426
0
            BTRFS_CMP_IGNORE_OFFSET);
2427
0
    if (node_result == BTRFS_TREENODE_ERROR) {
2428
0
        tsk_error_set_errstr2("- btrfs_extentdatawalk_alloc: loading EXTENT_DATA");
2429
0
        btrfs_extent_datawalk_free(edw);
2430
0
        return NULL;
2431
0
    }
2432
2433
0
    return edw;
2434
0
}
2435
2436
2437
/**
2438
 * Gets the next (possibly emulated) EXTENT_DATA item.
2439
 * @param a_edw pointer to EXTENT_DATA walk structure
2440
 * @param a_ed pointer to EXTENT_DATA structure pointer
2441
 * @param a_offset pointer to current offset (or NULL)
2442
 * @return result
2443
 */
2444
static BTRFS_TREENODE_RESULT
2445
btrfs_extent_datawalk_get(BTRFS_EXTENT_DATAWALK * a_edw, BTRFS_EXTENT_DATA ** a_ed, TSK_DADDR_T * a_offset)
2446
0
{
2447
    // return, if file content is already completely covered
2448
0
    if (a_edw->offset >= a_edw->size)
2449
0
        return BTRFS_TREENODE_NOT_FOUND;
2450
2451
    // if no more item, ensure block size alignment
2452
0
    size_t hole_end = a_edw->node ?
2453
0
            a_edw->node->key.offset : roundup(a_edw->size, a_edw->btrfs->fs_info.block_size);
2454
0
    size_t hole_size = hole_end - a_edw->offset;
2455
0
    BTRFS_EXTENT_DATA *ed;
2456
2457
    // if hole present, return emulated sparse block, otherwise real item
2458
0
    if (hole_size) {
2459
0
        ed = new BTRFS_EXTENT_DATA;
2460
0
        memset(ed, 0x00, sizeof(BTRFS_EXTENT_DATA));
2461
2462
0
        ed->size_decoded = hole_size;
2463
0
        ed->compression = BTRFS_EXTENT_DATA_COMPRESSION_NONE;
2464
0
        ed->encryption = BTRFS_EXTENT_DATA_ENCRYPTION_NONE;
2465
0
        ed->other_encoding = BTRFS_EXTENT_DATA_OTHER_ENCODING_NONE;
2466
0
        ed->type = BTRFS_EXTENT_DATA_TYPE_REGULAR;
2467
2468
0
        ed->nrd.extent_address = 0; // sparse
2469
0
        ed->nrd.extent_size = hole_size;
2470
0
        ed->nrd.file_offset = 0;
2471
0
        ed->nrd.file_bytes = hole_size;
2472
2473
0
        if (tsk_verbose)
2474
0
            tsk_fprintf(stderr, "btrfs_extent_datawalk_get: emulated sparse run at offset %zd: n: %" PRId64 "\n",
2475
0
                    a_edw->offset, ed->size_decoded);
2476
0
    } else {
2477
0
        ed = btrfs_extent_data_fromraw(btrfs_treenode_itemdata(a_edw->node), btrfs_treenode_itemsize(a_edw->node));
2478
0
        if (!ed)
2479
0
            return BTRFS_TREENODE_ERROR;
2480
2481
0
        if (tsk_verbose) {
2482
0
            if (ed->type == BTRFS_EXTENT_DATA_TYPE_INLINE)
2483
0
                tsk_fprintf(stderr, "btrfs_extent_datawalk_get: inline run at offset %zd: "
2484
0
                        "n: %" PRId64 ", comp: 0x%" PRIx8 ", encr: 0x%" PRIx8 ", o_enc: 0x%" PRIx16 ", "
2485
0
                        "data len: %" PRId32 "\n",
2486
0
                        a_edw->offset, ed->size_decoded, ed->compression, ed->encryption, ed->other_encoding,
2487
0
                        ed->rd.data_len);
2488
0
            else {
2489
0
                if (ed->nrd.extent_address)
2490
0
                    tsk_fprintf(stderr, "btrfs_extent_datawalk_get: regular run at offset %zd: "
2491
0
                            "n: %" PRId64 ", comp: 0x%" PRIx8 ", encr: 0x%" PRIx8 ", o_enc: 0x%" PRIx16 ", "
2492
0
                            "ea: 0x%" PRIx64 ", es: %" PRId64 ", o: 0x%" PRIx64 ", s: %" PRId64 "\n",
2493
0
                            a_edw->offset, ed->size_decoded, ed->compression, ed->encryption, ed->other_encoding,
2494
0
                            ed->nrd.extent_address, ed->nrd.extent_size, ed->nrd.file_offset, ed->nrd.file_bytes);
2495
0
                else
2496
0
                    tsk_fprintf(stderr, "btrfs_extent_datawalk_get: sparse run at offset %zd: "
2497
0
                            "n: %" PRId64 ", comp: 0x%" PRIx8 ", encr: 0x%" PRIx8 ", o_enc: 0x%" PRIx16 ", "
2498
0
                            "es: %" PRId64 ", o: 0x%" PRIx64 ", s: %" PRId64 "\n",
2499
0
                            a_edw->offset, ed->size_decoded, ed->compression, ed->encryption, ed->other_encoding,
2500
0
                            ed->nrd.extent_size, ed->nrd.file_offset, ed->nrd.file_bytes);
2501
0
            }
2502
0
        }
2503
2504
        // step to next item
2505
0
        BTRFS_TREENODE_RESULT node_result = btrfs_treenode_step(a_edw->btrfs, &a_edw->node, &a_edw->key,
2506
0
                BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_INITIAL);
2507
0
        if (node_result == BTRFS_TREENODE_ERROR) {
2508
0
            tsk_error_errstr2_concat("- btrfs_extentdatawalk_get: stepping to next EXTENT_DATA item");
2509
0
            btrfs_extent_data_free(ed);
2510
0
            return BTRFS_TREENODE_ERROR;
2511
0
        }
2512
0
        if (node_result == BTRFS_TREENODE_NOT_FOUND) {
2513
0
            btrfs_treenode_free(a_edw->node);
2514
0
            a_edw->node = NULL;
2515
0
        }
2516
0
    }
2517
2518
0
    *a_ed = ed;
2519
0
    if (a_offset)
2520
0
        *a_offset = a_edw->offset;
2521
2522
0
    a_edw->offset += btrfs_extent_data_size(ed);
2523
0
    return BTRFS_TREENODE_FOUND;
2524
0
}
2525
2526
2527
2528
/*
2529
 * inode walk
2530
 */
2531
2532
2533
/**
2534
 * Maps the stored inode file type to a TSK_FS_META_TYPE
2535
 * @param a_mode inode file type
2536
 * @return result
2537
 */
2538
static inline TSK_FS_META_TYPE_ENUM
2539
btrfs_mode2metatype(const uint32_t a_mode)
2540
0
{
2541
    // type is embedded into mode field like defined in stat.h
2542
0
    switch (a_mode & BTRFS_S_IFMT) {
2543
0
    case BTRFS_S_IFSOCK:
2544
0
        return TSK_FS_META_TYPE_SOCK;
2545
0
    case BTRFS_S_IFLNK:
2546
0
        return TSK_FS_META_TYPE_LNK;
2547
0
    case BTRFS_S_IFREG:
2548
0
        return TSK_FS_META_TYPE_REG;
2549
0
    case BTRFS_S_IFBLK:
2550
0
        return TSK_FS_META_TYPE_BLK;
2551
0
    case BTRFS_S_IFDIR:
2552
0
        return TSK_FS_META_TYPE_DIR;
2553
0
    case BTRFS_S_IFCHR:
2554
0
        return TSK_FS_META_TYPE_CHR;
2555
0
    case BTRFS_S_IFIFO:
2556
0
        return TSK_FS_META_TYPE_FIFO;
2557
0
    default:
2558
0
        return TSK_FS_META_TYPE_UNDEF;
2559
0
    }
2560
0
}
2561
2562
2563
/**
2564
 * Maps the stored inode file mode to a TSK_FS_META_MODE
2565
 * @param a_mode inode file mode
2566
 * @return result
2567
 */
2568
static inline TSK_FS_META_MODE_ENUM
2569
btrfs_mode2metamode(const uint32_t a_mode)
2570
0
{
2571
0
    TSK_FS_META_MODE_ENUM result = (TSK_FS_META_MODE_ENUM) 0;
2572
2573
0
    if (a_mode & BTRFS_S_ISUID)
2574
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_ISUID);
2575
0
    if (a_mode & BTRFS_S_ISGID)
2576
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_ISGID);
2577
0
    if (a_mode & BTRFS_S_ISVTX)
2578
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_ISVTX);
2579
2580
0
    if (a_mode & BTRFS_S_IRUSR)
2581
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IRUSR);
2582
0
    if (a_mode & BTRFS_S_IWUSR)
2583
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IWUSR);
2584
0
    if (a_mode & BTRFS_S_IXUSR)
2585
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IXUSR);
2586
2587
0
    if (a_mode & BTRFS_S_IRGRP)
2588
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IRGRP);
2589
0
    if (a_mode & BTRFS_S_IWGRP)
2590
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IWGRP);
2591
0
    if (a_mode & BTRFS_S_IXGRP)
2592
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IXGRP);
2593
2594
0
    if (a_mode & BTRFS_S_IROTH)
2595
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IROTH);
2596
0
    if (a_mode & BTRFS_S_IWOTH)
2597
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IWOTH);
2598
0
    if (a_mode & BTRFS_S_IXOTH)
2599
0
        result = (TSK_FS_META_MODE_ENUM) (result | TSK_FS_META_MODE_IXOTH);
2600
2601
0
    return result;
2602
0
}
2603
2604
2605
/**
2606
 * Allocates an inodewalk structure
2607
 * @param a_btrfs Btrfs info
2608
 * @param a_start_vinum virtual inum to start the inodewalk with
2609
 * @return pointer to inodewalk structure
2610
 */
2611
static BTRFS_INODEWALK *
2612
btrfs_inodewalk_alloc(BTRFS_INFO * a_btrfs, const uint64_t a_start_vinum)
2613
0
{
2614
0
    BTRFS_INODEWALK *iw = new BTRFS_INODEWALK;
2615
0
    iw->btrfs = a_btrfs;
2616
0
    iw->vinum = a_start_vinum;
2617
0
    iw->subvol = 0;
2618
2619
0
    iw->key.item_type = BTRFS_ITEM_TYPE_INODE_ITEM;
2620
0
    iw->key.offset = 0;
2621
0
    iw->node = NULL;
2622
0
    return iw;
2623
0
}
2624
2625
2626
/**
2627
 * Frees an inodewalk structure
2628
 * @param a_iw pointer to inodewalk structure
2629
 */
2630
static void
2631
btrfs_inodewalk_free(BTRFS_INODEWALK * a_iw)
2632
0
{
2633
0
    if (!a_iw)
2634
0
        return;
2635
2636
0
    if (a_iw->node)
2637
0
        btrfs_treenode_free(a_iw->node);
2638
0
    delete a_iw;
2639
0
}
2640
2641
2642
/**
2643
 * Returns the inode flags (except TSK_FS_META_FLAG_COMP) of the next inode
2644
 * @param a_iw pointer to inodewalk structure
2645
 * @return flags of the current inode if no error occured, otherwise 0
2646
 */
2647
static TSK_FS_META_FLAG_ENUM
2648
btrfs_inodewalk_invoke(BTRFS_INODEWALK * a_iw)
2649
0
{
2650
    // early virtual inum increment for next invokation
2651
0
    TSK_INUM_T current_vinum = a_iw->vinum++;
2652
2653
    // handle special virtual inums
2654
0
    if (current_vinum > a_iw->btrfs->fs_info.last_inum - BTRFS_VINUM_COUNT_SPECIAL)
2655
0
        return (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED);
2656
2657
0
    uint64_t subvol;
2658
0
    TSK_INUM_T inum;
2659
0
    if (!btrfs_inum_virt2real_map(a_iw->btrfs, current_vinum, &subvol, &inum)) {
2660
0
        tsk_error_set_errstr2("btrfs_inodewalk_invoke: mapping inum of virtual inum: %" PRIuINUM, current_vinum);
2661
0
        return (TSK_FS_META_FLAG_ENUM) 0;
2662
0
    }
2663
0
    a_iw->key.object_id = inum;
2664
2665
    // if subvol changed, reset treenode
2666
0
    if (a_iw->subvol != subvol) {
2667
0
        a_iw->subvol = subvol;
2668
2669
0
        btrfs_treenode_free(a_iw->node);
2670
0
        a_iw->node = NULL;
2671
0
    }
2672
2673
    // if no node, retrieve it, otherwise step to next INODE_ITEM - all this works due to the continuous virtual inum mapping
2674
0
    BTRFS_TREENODE_RESULT node_result;
2675
0
    if (!a_iw->node)
2676
0
        node_result = btrfs_treenode_search(a_iw->btrfs, &a_iw->node, btrfs_subvol_tree_address(a_iw->btrfs, a_iw->subvol), &a_iw->key,
2677
0
                0, 0);
2678
0
    else
2679
0
        node_result = btrfs_treenode_step(a_iw->btrfs, &a_iw->node, &a_iw->key,
2680
0
                0, BTRFS_LAST, BTRFS_STEP_INITIAL | BTRFS_STEP_REPEAT);
2681
0
    if (node_result == BTRFS_TREENODE_ERROR) {
2682
0
        tsk_error_errstr2_concat("- btrfs_inodewalk_invoke: %s INODE_ITEM item of virtual inum: %" PRIuINUM,
2683
0
                a_iw->node ? "stepping to current" : "loading", current_vinum);
2684
0
        return (TSK_FS_META_FLAG_ENUM) 0;
2685
0
    }
2686
0
    if (node_result == BTRFS_TREENODE_NOT_FOUND) {
2687
0
        btrfs_error(TSK_ERR_FS_INODE_COR, "btrfs_inodewalk_invoke: could not %s virtual inum: %" PRIuINUM,
2688
0
                a_iw->node ? "step to" : "find", current_vinum);
2689
0
        return (TSK_FS_META_FLAG_ENUM) 0;
2690
0
    }
2691
2692
    // retrieve inode data
2693
0
    btrfs_inode_rawparse(btrfs_treenode_itemdata(a_iw->node), &a_iw->ii);
2694
#ifdef BTRFS_DEBUG
2695
    btrfs_inode_debugprint(&a_iw->ii);
2696
#endif
2697
2698
0
    return (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_USED |
2699
0
            (a_iw->ii.nlink ? TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC));
2700
0
}
2701
2702
2703
/**
2704
 * Fills the meta structure with the regarding data of the current inode (possibly adds TSK_FS_META_FLAG_COMP to the flags)
2705
 * @param a_iw pointer to inodewalk structure
2706
 * @param a_flags inode flags derived from previous btrfs_inodewalk_invoke call
2707
 * @param a_meta pointer to file meta structure
2708
 * @return true if no error occured, otherwise false
2709
 */
2710
static bool
2711
btrfs_inodewalk_fillmeta(BTRFS_INODEWALK * a_iw,
2712
    const TSK_FS_META_FLAG_ENUM a_flags, TSK_FS_META * a_meta)
2713
0
{
2714
0
    TSK_FS_INFO *fs = &a_iw->btrfs->fs_info;
2715
0
    TSK_INUM_T current_vinum = a_iw->vinum - 1; // -1 to undo the early increment
2716
2717
0
    if (tsk_verbose)
2718
0
        tsk_fprintf(stderr, "btrfs_inodewalk_fillmeta: Filling meta structure of inum: %" PRIuINUM "\n", current_vinum);
2719
2720
    // handle orphan files dir
2721
0
    if (current_vinum == TSK_FS_ORPHANDIR_INUM(fs))
2722
0
        return tsk_fs_dir_make_orphan_dir_meta(fs, a_meta) == 0;
2723
2724
0
    a_meta->addr = current_vinum;
2725
0
    a_meta->flags = a_flags;
2726
2727
0
    a_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
2728
0
    if (a_meta->attr)
2729
0
        tsk_fs_attrlist_markunused(a_meta->attr);
2730
2731
0
    if (a_meta->link) {
2732
0
        free(a_meta->link);
2733
0
        a_meta->link = NULL;
2734
0
    }
2735
2736
    // init custom content
2737
0
    if (a_meta->content_len != BTRFS_FILE_CONTENT_LEN) {
2738
0
        a_meta = tsk_fs_meta_realloc(a_meta, BTRFS_FILE_CONTENT_LEN);
2739
0
        if (!a_meta)
2740
0
            return false;
2741
0
    }
2742
2743
2744
    // handle superblock
2745
0
    if (a_meta->addr == BTRFS_SUPERBLOCK_VINUM(fs)) {
2746
0
        memset(a_meta->content_ptr, 0x00, a_meta->content_len);
2747
0
        a_meta->size = BTRFS_SUPERBLOCK_RAWLEN;
2748
0
        a_meta->type = TSK_FS_META_TYPE_VIRT;
2749
0
        return true;
2750
0
    }
2751
2752
    // store inode data for later
2753
0
    memcpy(a_meta->content_ptr, &a_iw->ii, a_meta->content_len);
2754
2755
2756
0
    a_meta->nlink = a_iw->ii.nlink;
2757
0
    a_meta->gid = a_iw->ii.gid;
2758
0
    a_meta->uid = a_iw->ii.uid;
2759
2760
0
    a_meta->type = btrfs_mode2metatype(a_iw->ii.mode);
2761
0
    a_meta->mode = btrfs_mode2metamode(a_iw->ii.mode);
2762
2763
    // stored dir size is twice the total char number of all entries filenames, so leave it at 0
2764
0
    if (a_meta->type != TSK_FS_META_TYPE_DIR)
2765
0
        a_meta->size = a_iw->ii.size;
2766
2767
0
    a_meta->atime = a_iw->ii.atime.seconds;
2768
0
    a_meta->atime_nano = a_iw->ii.atime.nanoseconds;
2769
0
    a_meta->ctime = a_iw->ii.ctime.seconds;
2770
0
    a_meta->ctime_nano = a_iw->ii.ctime.nanoseconds;
2771
0
    a_meta->mtime = a_iw->ii.mtime.seconds;
2772
0
    a_meta->mtime_nano = a_iw->ii.mtime.nanoseconds;
2773
2774
2775
    /*
2776
     * - if non-empty regular file, check for at least one non-raw extent
2777
     * - if symlink, derive link name
2778
     * => early exit, if neither applies
2779
     */
2780
0
    if (!((a_meta->type == TSK_FS_META_TYPE_REG && a_meta->size) || a_meta->type == TSK_FS_META_TYPE_LNK))
2781
0
        return true;
2782
2783
0
    if (tsk_verbose)
2784
0
        tsk_fprintf(stderr, "btrfs_inodewalk_fillmeta: Checking EXTENT_DATA item(s) of inum: %" PRIuINUM "\n", current_vinum);
2785
2786
    // iterate over all EXTENT_DATA items
2787
0
    BTRFS_EXTENT_DATAWALK *edw = btrfs_extent_datawalk_alloc(a_iw->btrfs, a_meta);
2788
0
    if (!edw)
2789
0
        return false;
2790
2791
0
    for (;;) {
2792
        // next EXTENT_DATA
2793
0
        BTRFS_EXTENT_DATA *ed;
2794
0
        BTRFS_TREENODE_RESULT node_result = btrfs_extent_datawalk_get(edw, &ed, NULL);
2795
0
        if (node_result == BTRFS_TREENODE_ERROR) {
2796
0
            tsk_error_set_errstr2("btrfs_inodewalk_fillmeta: getting next EXTENT_DATA item");
2797
0
            btrfs_extent_datawalk_free(edw);
2798
0
            return false;
2799
0
        }
2800
0
        if (node_result == BTRFS_TREENODE_NOT_FOUND)
2801
0
            break;
2802
2803
#ifdef BTRFS_DEBUG
2804
        btrfs_extent_data_debugprint(ed);
2805
#endif
2806
2807
0
        bool ed_is_raw = BTRFS_EXTENT_DATA_IS_RAW(ed);
2808
2809
        // if symlink, handle target + break
2810
0
        if (a_meta->type == TSK_FS_META_TYPE_LNK) {
2811
0
            if (!ed_is_raw) {
2812
0
                btrfs_error(TSK_ERR_FS_INODE_COR,
2813
0
                        "btrfs_inodewalk_fillmeta: non-raw symlink target of virtual inum: %" PRIuINUM, current_vinum);
2814
0
                btrfs_extent_data_free(ed);
2815
0
                btrfs_extent_datawalk_free(edw);
2816
0
                return false;
2817
0
            }
2818
2819
0
            size_t target_len = ed->rd.data_len;
2820
0
            a_meta->link = (char*) tsk_malloc(target_len + 1);
2821
0
            if (!a_meta->link) {
2822
0
                tsk_error_set_errstr2("btrfs_inodewalk_fillmeta: setting target of virtual inum: %" PRIuINUM, current_vinum);
2823
0
                btrfs_extent_data_free(ed);
2824
0
                btrfs_extent_datawalk_free(edw);
2825
0
                return false;
2826
0
            }
2827
0
            memcpy(a_meta->link, ed->rd.data, target_len);
2828
0
            a_meta->link[target_len] = 0x00;    // terminator
2829
2830
0
            btrfs_debug("symlink target of inode 0x%" PRIxINUM " is '%s'\n", a_meta->addr, a_meta->link);
2831
2832
0
            btrfs_extent_data_free(ed);
2833
0
            break;
2834
0
        }
2835
2836
0
        btrfs_extent_data_free(ed);
2837
2838
        // set flag + break
2839
0
        if (!ed_is_raw) {
2840
0
            a_meta->flags = (TSK_FS_META_FLAG_ENUM) (a_meta->flags | TSK_FS_META_FLAG_COMP);
2841
0
            break;
2842
0
        }
2843
0
    };
2844
2845
0
    btrfs_extent_datawalk_free(edw);
2846
0
    return true;
2847
0
}
2848
2849
2850
/**
2851
 * Populates the meta structure of a file
2852
 * @param a_fs FS info
2853
 * @param a_fs_file pointer to file structure
2854
 * @param a_addr virtual inum
2855
 * @return 0 if no error occured, otherwise 1
2856
 */
2857
static uint8_t
2858
btrfs_file_add_meta(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file,
2859
    TSK_INUM_T a_addr)
2860
0
{
2861
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
2862
2863
    // clean up any error messages that are lying around
2864
0
    tsk_error_reset();
2865
2866
    // sanity check
2867
0
    if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
2868
0
        btrfs_error(TSK_ERR_FS_INODE_NUM,
2869
0
                "btrfs_file_add_meta: 0x%" PRIxINUM " too large/small", a_addr);
2870
0
        return 1;
2871
0
    }
2872
2873
0
    if (!a_fs_file) {
2874
0
        btrfs_error(TSK_ERR_FS_ARG,
2875
0
                "btrfs_file_add_meta: a_fs_file is NULL");
2876
0
        return 1;
2877
0
    }
2878
0
    if (!a_fs_file->meta) {
2879
0
        a_fs_file->meta = tsk_fs_meta_alloc(BTRFS_FILE_CONTENT_LEN);
2880
0
        if (!a_fs_file->meta)
2881
0
            return 1;
2882
0
    } else {
2883
0
        tsk_fs_meta_reset(a_fs_file->meta);
2884
0
    }
2885
2886
    // load inode info
2887
0
    BTRFS_INODEWALK *iw = btrfs_inodewalk_alloc(btrfs, a_addr);
2888
2889
0
    TSK_FS_META_FLAG_ENUM inode_flags = btrfs_inodewalk_invoke(iw);
2890
0
    if (inode_flags == (TSK_FS_META_FLAG_ENUM) 0) {
2891
0
        btrfs_inodewalk_free(iw);
2892
0
        return 1;
2893
0
    }
2894
2895
0
    if (!btrfs_inodewalk_fillmeta(iw, inode_flags, a_fs_file->meta)) {
2896
0
        btrfs_inodewalk_free(iw);
2897
0
        return 1;
2898
0
    }
2899
2900
0
    btrfs_inodewalk_free(iw);
2901
0
    return 0;
2902
0
}
2903
2904
2905
/**
2906
 * Iterates through a range of inodes and calls the callback with the inode of each desired inode.
2907
 * @param a_fs FS info
2908
 * @param a_start_inum start virtual inum
2909
 * @param a_end_inum end virtual inum
2910
 * @param a_flags flags
2911
 * @param a_action pointer to callback
2912
 * @param a_ptr pointer to opaque callback data
2913
 * @return 0 if no error occured, otherwise 1
2914
 */
2915
static uint8_t
2916
btrfs_inode_walk(TSK_FS_INFO * a_fs, TSK_INUM_T a_start_inum,
2917
    TSK_INUM_T a_end_inum, TSK_FS_META_FLAG_ENUM a_flags,
2918
    TSK_FS_META_WALK_CB a_action, void *a_ptr)
2919
0
{
2920
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
2921
2922
    // clean up any error messages that are lying around
2923
0
    tsk_error_reset();
2924
2925
    // sanity checks
2926
0
    if (a_start_inum < a_fs->first_inum || a_start_inum > a_fs->last_inum) {
2927
0
        btrfs_error(TSK_ERR_FS_WALK_RNG,
2928
0
                "btrfs_inode_walk: start inode: %" PRIuINUM "", a_start_inum);
2929
0
        return 1;
2930
0
    }
2931
2932
0
    if (a_end_inum < a_fs->first_inum || a_end_inum > a_fs->last_inum || a_end_inum < a_start_inum) {
2933
0
        btrfs_error(TSK_ERR_FS_WALK_RNG,
2934
0
                "btrfs_inode_walk: end inode: %" PRIuINUM "", a_end_inum);
2935
0
        return 1;
2936
0
    }
2937
2938
    // if ORPHAN is wanted, then make sure that the flags are correct
2939
0
    if (a_flags & TSK_FS_META_FLAG_ORPHAN) {
2940
0
        a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED);
2941
0
        a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags & ~TSK_FS_META_FLAG_ALLOC & ~TSK_FS_META_FLAG_UNUSED);
2942
2943
0
        if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) {
2944
0
            tsk_error_errstr2_concat("- btrfs_inode_walk: identifying inodes allocated by file names");
2945
0
            return 1;
2946
0
        }
2947
0
    } else {
2948
        // sanity check on flags
2949
0
        if (((a_flags & TSK_FS_META_FLAG_ALLOC) == 0) && ((a_flags & TSK_FS_META_FLAG_UNALLOC) == 0))
2950
0
            a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
2951
0
        if (((a_flags & TSK_FS_META_FLAG_USED) == 0) && ((a_flags & TSK_FS_META_FLAG_UNUSED) == 0))
2952
0
            a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
2953
0
    }
2954
2955
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> file{
2956
0
        tsk_fs_file_alloc(a_fs),
2957
0
        tsk_fs_file_close
2958
0
    };
2959
2960
0
    if (!file) {
2961
0
        return 1;
2962
0
    }
2963
2964
0
    file->meta = tsk_fs_meta_alloc(BTRFS_FILE_CONTENT_LEN);
2965
0
    if (!file->meta) {
2966
0
        return 1;
2967
0
    }
2968
2969
    // iterate through inode range
2970
0
    uint8_t result = 0;
2971
0
    BTRFS_INODEWALK *iw = btrfs_inodewalk_alloc(btrfs, a_start_inum);
2972
0
    for (TSK_INUM_T inum = a_start_inum; inum <= a_end_inum; inum++) {
2973
0
        TSK_FS_META_FLAG_ENUM inode_flags = btrfs_inodewalk_invoke(iw);
2974
0
        if (inode_flags == (TSK_FS_META_FLAG_ENUM) 0) {
2975
0
            result = 1;
2976
0
            break;
2977
0
        }
2978
2979
        // test if we should call the callback with this one
2980
0
        if ((inode_flags & TSK_FS_META_FLAG_ALLOC)      && !(a_flags & TSK_FS_META_FLAG_ALLOC))
2981
0
            continue;
2982
0
        if ((inode_flags & TSK_FS_META_FLAG_UNALLOC)        && !(a_flags & TSK_FS_META_FLAG_UNALLOC))
2983
0
            continue;
2984
0
        if ((inode_flags & TSK_FS_META_FLAG_USED)       && !(a_flags & TSK_FS_META_FLAG_USED))
2985
0
            continue;
2986
0
        if ((inode_flags & TSK_FS_META_FLAG_UNUSED)     && !(a_flags & TSK_FS_META_FLAG_UNUSED))
2987
0
            continue;
2988
2989
        // if we want only orphans, then check if this inode is in the seen list
2990
0
        if ((inode_flags & TSK_FS_META_FLAG_UNALLOC) && (a_flags & TSK_FS_META_FLAG_ORPHAN) &&
2991
0
                tsk_fs_dir_find_inum_named(a_fs, inum))
2992
0
            continue;
2993
2994
0
        tsk_fs_meta_reset(file->meta);
2995
0
        if (!btrfs_inodewalk_fillmeta(iw, inode_flags, file->meta)) {
2996
0
            result = 1;
2997
0
            break;
2998
0
        }
2999
3000
        // invoke callback
3001
0
        int retval = a_action(file.get(), a_ptr);
3002
0
        if (retval == TSK_WALK_STOP)
3003
0
            break;
3004
0
        if (retval == TSK_WALK_ERROR) {
3005
0
            result = 1;
3006
0
            break;
3007
0
        }
3008
0
    }
3009
3010
    // cleanup
3011
0
    btrfs_inodewalk_free(iw);
3012
0
    return result;
3013
0
}
3014
3015
/*
3016
 * directory
3017
 */
3018
3019
3020
// maps the stored dir file type to a TSK_FS_NAME_TYPE
3021
0
#define BTRFS_TYPE2NAMETYPE_COUNT 8
3022
static const TSK_FS_NAME_TYPE_ENUM btrfs_type2nametype[BTRFS_TYPE2NAMETYPE_COUNT] = {
3023
    TSK_FS_NAME_TYPE_UNDEF,
3024
    TSK_FS_NAME_TYPE_REG,
3025
    TSK_FS_NAME_TYPE_DIR,
3026
    TSK_FS_NAME_TYPE_CHR,
3027
    TSK_FS_NAME_TYPE_BLK,
3028
    TSK_FS_NAME_TYPE_FIFO,
3029
    TSK_FS_NAME_TYPE_SOCK,
3030
    TSK_FS_NAME_TYPE_LNK,
3031
};
3032
3033
3034
/**
3035
 * Opens a directory by virtual inum
3036
 * @param a_fs FS info
3037
 * @param a_fs_dir pointer to a directory pointer (directory pointer may be NULL)
3038
 * @param a_addr virtual inum
3039
 * @param recursion_depth - ignored
3040
 * @return result
3041
 */
3042
static TSK_RETVAL_ENUM
3043
btrfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr, [[maybe_unused]] int recursion_depth)
3044
0
{
3045
0
    if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
3046
0
        btrfs_error(TSK_ERR_FS_WALK_RNG,
3047
0
                "btrfs_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr);
3048
0
        return TSK_ERR;
3049
0
    }
3050
0
    if (!a_fs_dir) {
3051
0
        btrfs_error(TSK_ERR_FS_ARG,
3052
0
                "btrfs_dir_open_meta: NULL fs_dir argument given");
3053
0
        return TSK_ERR;
3054
0
    }
3055
3056
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
3057
0
    TSK_FS_DIR *fs_dir = *a_fs_dir;
3058
3059
0
    bool dir_alloced;
3060
0
    TSK_FS_NAME *fs_name = NULL;
3061
0
    TSK_DADDR_T tree_address;
3062
0
    BTRFS_TREENODE *node = NULL;
3063
0
    BTRFS_TREENODE_RESULT node_result;
3064
0
    BTRFS_DIR_ENTRY *de = NULL;
3065
3066
0
    if (fs_dir) {
3067
0
        tsk_fs_dir_reset(fs_dir);
3068
0
        fs_dir->addr = a_addr;
3069
0
        dir_alloced = false;
3070
0
    } else {
3071
0
        *a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128);
3072
0
        if (!fs_dir)
3073
0
            return TSK_ERR;
3074
0
        dir_alloced = true;
3075
0
    }
3076
3077
0
    if (tsk_verbose)
3078
0
        tsk_fprintf(stderr, "btrfs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr);
3079
3080
    // handle the orphan directory if its contents were requested
3081
0
    if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs))
3082
0
        return tsk_fs_dir_find_orphans(a_fs, fs_dir);
3083
3084
0
    fs_name = tsk_fs_name_alloc(BTRFS_NAME_LEN_MAX, 0);
3085
0
    if (!fs_name)
3086
0
        return TSK_ERR;
3087
3088
0
    fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr);
3089
0
    if (!fs_dir->fs_file) {
3090
0
        tsk_error_errstr2_concat(" - btrfs_dir_open_meta");
3091
0
        goto on_error;
3092
0
    }
3093
3094
    // abort, if not a dir
3095
0
    if (fs_dir->fs_file->meta->type != TSK_FS_META_TYPE_DIR) {
3096
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_dir_open_meta: not a directory");
3097
0
        goto on_error;
3098
0
    }
3099
3100
3101
0
    uint64_t subvol;
3102
0
    TSK_INUM_T inum;
3103
0
    if (!btrfs_inum_virt2real_map(btrfs, fs_dir->addr, &subvol, &inum)) {
3104
0
        tsk_error_set_errstr2("btrfs_dir_open_meta: mapping inum of dir");
3105
0
        goto on_error;
3106
0
    }
3107
0
    tree_address = btrfs_subvol_tree_address(btrfs, subvol);
3108
3109
3110
3111
0
    if (tsk_verbose)
3112
0
        tsk_fprintf(stderr, "btrfs_dir_open_meta: Creating . and .. entries\n");
3113
3114
    // add "." entry
3115
0
    fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
3116
0
    fs_name->meta_addr = fs_dir->addr;
3117
0
    strcpy(fs_name->name, ".");
3118
0
    fs_name->type = TSK_FS_NAME_TYPE_DIR;
3119
3120
0
    if (tsk_fs_dir_add(fs_dir, fs_name)) {
3121
0
        tsk_error_set_errstr2("btrfs_dir_open_meta: adding '.' dir entry");
3122
0
        goto on_error;
3123
0
    }
3124
3125
3126
    // add ".." entry
3127
0
    fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
3128
0
    fs_name->meta_addr = fs_dir->addr;  // fallback value, if no matching INODE_REF exists (e.g. orphan dir)
3129
0
    strcpy(fs_name->name, "..");
3130
0
    fs_name->type = TSK_FS_NAME_TYPE_DIR;
3131
3132
    // search INODE_REF - as dirs have no hardlinks, this retrieves the one and only INODE_REF (if it exists at all)
3133
0
    BTRFS_KEY key;
3134
0
    key.object_id = inum;
3135
0
    key.item_type = BTRFS_ITEM_TYPE_INODE_REF;
3136
0
    key.offset = 0; // not used, except at debug output
3137
3138
0
    node_result = btrfs_treenode_search(btrfs, &node, tree_address, &key,
3139
0
            BTRFS_CMP_IGNORE_OFFSET, 0);
3140
0
    if (node_result == BTRFS_TREENODE_ERROR) {
3141
0
        tsk_error_set_errstr2("btrfs_dir_open_meta: loading INODE_REF item");
3142
0
        goto on_error;
3143
0
    }
3144
0
    if (node_result == BTRFS_TREENODE_FOUND) {
3145
0
        if (!btrfs_inum_real2virt_map(btrfs, subvol, node->key.offset, &fs_name->meta_addr)) {
3146
0
            tsk_error_set_errstr2("btrfs_dir_open_meta: mapping inum of INODE_REF item");
3147
0
            goto on_error;
3148
0
        }
3149
3150
0
        btrfs_treenode_free(node);
3151
0
        node = NULL;
3152
0
    }
3153
3154
0
    if (tsk_fs_dir_add(fs_dir, fs_name)) {
3155
0
        tsk_error_set_errstr2("btrfs_dir_open_meta: adding '..' dir entry");
3156
0
        goto on_error;
3157
0
    }
3158
3159
3160
    // get first DIR_INDEX item
3161
0
    key.item_type = BTRFS_ITEM_TYPE_DIR_INDEX;
3162
0
    key.offset = 0;
3163
3164
0
    node_result = btrfs_treenode_search_lowest(btrfs, &node, tree_address, &key,
3165
0
            BTRFS_CMP_IGNORE_OFFSET);
3166
0
    if (node_result == BTRFS_TREENODE_ERROR) {
3167
0
        tsk_error_set_errstr2("btrfs_dir_open_meta: loading DIR_INDEX item");
3168
0
        goto on_error;
3169
0
    }
3170
3171
    // iterate
3172
0
    while (node_result == BTRFS_TREENODE_FOUND) {
3173
0
        de = btrfs_dir_entry_fromraw(btrfs_treenode_itemdata(node), btrfs_treenode_itemsize(node));
3174
0
        if (de->next) {
3175
0
            btrfs_error(TSK_ERR_FS_INODE_COR,
3176
0
                    "btrfs_dir_open_meta: DIR_INDEX item with more than one entry");
3177
0
            goto on_error;
3178
0
        }
3179
3180
0
        if (tsk_verbose)
3181
0
            tsk_fprintf(stderr, "btrfs_dir_open_meta: Processing DIR_INDEX: %" PRId64 "\n", node->key.offset);
3182
#ifdef BTRFS_DEBUG
3183
        btrfs_debug("### DIR_INDEX ###\n");
3184
        btrfs_dir_entry_debugprint(de);
3185
#endif
3186
3187
        // apply data
3188
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
3189
0
        if (strlen(de->name) > fs_name->name_size) {
3190
0
            memcpy(fs_name->name, de->name, fs_name->name_size);
3191
0
            fs_name->name[fs_name->name_size] = 0;  // terminator
3192
0
        } else {
3193
0
            strcpy(fs_name->name, de->name);
3194
0
        }
3195
0
        fs_name->type = btrfs_type2nametype[de->type < BTRFS_TYPE2NAMETYPE_COUNT ? de->type : 0];
3196
3197
        // derive target virtual inum
3198
0
        switch (de->child.item_type) {
3199
0
        case BTRFS_ITEM_TYPE_INODE_ITEM:
3200
            // ordinary file/dir
3201
0
            if (!btrfs_inum_real2virt_map(btrfs, subvol, de->child.object_id, &fs_name->meta_addr)) {
3202
0
                tsk_error_set_errstr2("btrfs_dir_open_meta: mapping inum of INODE_ITEM item");
3203
0
                goto on_error;
3204
0
            }
3205
0
            break;
3206
0
        case BTRFS_ITEM_TYPE_ROOT_ITEM: {
3207
            // subvolume
3208
0
            uint64_t new_subvol = de->child.object_id;
3209
0
            if (!btrfs_inum_real2virt_map(btrfs, new_subvol, btrfs_subvol_root_inum(btrfs, new_subvol), &fs_name->meta_addr)) {
3210
0
                tsk_error_set_errstr2("btrfs_dir_open_meta: mapping inum of ROOT_ITEM item");
3211
0
                goto on_error;
3212
0
            }
3213
0
            break; }
3214
0
        default:
3215
0
            btrfs_error(TSK_ERR_FS_INODE_COR,
3216
0
                    "btrfs_dir_open_meta: DIR_INDEX item with unsupported child item type: 0x%" PRIx8, de->child.item_type);
3217
0
            goto on_error;
3218
0
        }
3219
3220
0
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
3221
0
            tsk_error_set_errstr2("btrfs_dir_open_meta: adding dir entry");
3222
0
            goto on_error;
3223
0
        }
3224
3225
0
        btrfs_dir_entry_free(de);
3226
0
        de = NULL;
3227
3228
        // next DIR_INDEX
3229
0
        node_result = btrfs_treenode_step(btrfs, &node, &key,
3230
0
                BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_INITIAL);
3231
0
        if (node_result == BTRFS_TREENODE_ERROR) {
3232
0
            tsk_error_set_errstr2("btrfs_dir_open_meta: stepping to next DIR_INDEX item");
3233
0
            goto on_error;
3234
0
        }
3235
0
    }
3236
3237
0
    btrfs_treenode_free(node);
3238
0
    node = NULL;
3239
3240
3241
    // if root virtual inum, add special virtual inums
3242
0
    if (fs_dir->addr == btrfs->fs_info.root_inum) {
3243
0
        if (tsk_verbose)
3244
0
            tsk_fprintf(stderr, "btrfs_dir_open_meta: Creating superblock file and orphan files dir entries\n");
3245
3246
        // superblock
3247
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
3248
0
        fs_name->meta_addr = BTRFS_SUPERBLOCK_VINUM(a_fs);
3249
0
        strcpy(fs_name->name, BTRFS_SUPERBLOCK_NAME);
3250
0
        fs_name->type = TSK_FS_NAME_TYPE_VIRT;
3251
3252
0
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
3253
0
            tsk_error_set_errstr2("btrfs_dir_open_meta: adding superblock dir entry");
3254
0
            goto on_error;
3255
0
        }
3256
3257
        // orphan files
3258
0
        if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) {
3259
0
            tsk_error_set_errstr2("btrfs_dir_open_meta: making orphan files dir entry");
3260
0
            goto on_error;
3261
0
        }
3262
0
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
3263
0
            tsk_error_set_errstr2("btrfs_dir_open_meta: adding orphan files dir entry");
3264
0
            goto on_error;
3265
0
        }
3266
0
    }
3267
3268
0
    tsk_fs_name_free(fs_name);
3269
0
    return TSK_OK;
3270
3271
0
on_error:
3272
0
    if (dir_alloced) {
3273
0
        tsk_fs_dir_close(fs_dir);
3274
0
        *a_fs_dir = NULL;
3275
0
    } else {
3276
0
        tsk_fs_file_close(fs_dir->fs_file);
3277
0
    }
3278
0
    tsk_fs_name_free(fs_name);
3279
0
    btrfs_treenode_free(node);
3280
0
    btrfs_dir_entry_free(de);
3281
0
    return TSK_ERR;
3282
0
}
3283
3284
3285
/**
3286
 * Compares two names.
3287
 * @param a_fs_info FS info
3288
 * @param a_name_a name A
3289
 * @param a_name_b name B
3290
 * @return relation between name A and name B
3291
 */
3292
static int
3293
btrfs_name_cmp([[maybe_unused]] TSK_FS_INFO * a_fs_info, const char *a_name_a,
3294
    const char *a_name_b)
3295
0
{
3296
0
    return strcmp(a_name_a, a_name_b);
3297
0
}
3298
3299
3300
3301
/*
3302
 * attribute data
3303
 */
3304
3305
3306
#ifdef BTRFS_COMP_SUPPORT
3307
/**
3308
 * Tries to read a (non-)resident block into the input buffer
3309
 * @param a_dw pointer to datawalk structure
3310
 * @return amount of read bytes if no error occured, otherwise -1
3311
 */
3312
static ssize_t
3313
btrfs_datawalk_ed_read_rawblock(BTRFS_DATAWALK * a_dw)
3314
0
{
3315
0
    TSK_DADDR_T block_size = a_dw->btrfs->fs_info.block_size;
3316
0
    size_t read_bytes = MIN(a_dw->ed_raw_size - a_dw->ed_raw_offset, block_size);
3317
3318
0
    if (read_bytes == 0)
3319
0
        return -1;
3320
3321
0
    if (a_dw->ed_resident) {
3322
        // resident
3323
0
        memcpy(a_dw->in_blockbuffer, a_dw->ed->rd.data + a_dw->ed_raw_offset, read_bytes);
3324
0
    } else {
3325
        // non-resident
3326
0
        TSK_DADDR_T address_log = a_dw->ed->nrd.extent_address + a_dw->ed_raw_offset;
3327
0
        TSK_DADDR_T address_phys;
3328
3329
        // if logical address not in cached chunk range, derive new cached chunk
3330
0
        if (!(a_dw->cc && btrfs_chunk_map(a_dw->cc, address_log, &address_phys))) {
3331
0
            if (!btrfs_address_map(&a_dw->btrfs->chunks->log2phys, &a_dw->cc, address_log, &address_phys)) {
3332
0
                btrfs_error(TSK_ERR_FS_BLK_NUM,
3333
0
                        "btrfs_datawalk_ed_read_rawblock: Could not map logical address: 0x%" PRIxDADDR, address_log);
3334
0
                return -1;
3335
0
            }
3336
0
        }
3337
3338
0
        ssize_t result = tsk_fs_read(&a_dw->btrfs->fs_info, address_phys, (char*) a_dw->in_blockbuffer, read_bytes);
3339
0
        if (result != (ssize_t) read_bytes) {
3340
0
            if (result != -1)
3341
0
                btrfs_error(TSK_ERR_FS_READ,
3342
0
                        "btrfs_datawalk_ed_read_rawblock: Got less bytes than requested: %zd of %zd", result, read_bytes);
3343
0
            return -1;
3344
0
        }
3345
3346
0
        a_dw->last_raw_addr = address_phys;
3347
0
    }
3348
0
    a_dw->ed_raw_offset += read_bytes;
3349
0
    return read_bytes;
3350
0
}
3351
3352
3353
#ifdef HAVE_LIBZ
3354
/**
3355
 * Tries to read a specific byte amount at the current offset within the zlib compressed EXTENT_ITEM
3356
 * @param a_dw pointer to datawalk structure
3357
 * @param a_data pointer to data
3358
 * @param a_len data len
3359
 * @return amount of read bytes if no error occured, otherwise -1
3360
 */
3361
static ssize_t
3362
btrfs_datawalk_ed_read_zlib(BTRFS_DATAWALK * a_dw, uint8_t * a_data,
3363
    const size_t a_len)
3364
0
{
3365
0
    size_t read_bytes = MIN(a_len, a_dw->ed_out_size - a_dw->ed_out_offset);
3366
3367
0
    a_dw->zlib_state.next_out = a_data;
3368
0
    a_dw->zlib_state.avail_out = read_bytes;
3369
3370
0
    while (a_dw->zlib_state.avail_out) {
3371
        // if necessary, refill input buffer
3372
0
        if (a_dw->zlib_state.avail_in == 0) {
3373
0
            ssize_t result = btrfs_datawalk_ed_read_rawblock(a_dw);
3374
0
            if (result == -1)
3375
0
                return -1;
3376
0
            if (result == 0)
3377
0
                break;
3378
3379
0
            a_dw->zlib_state.next_in = a_dw->in_blockbuffer;
3380
0
            a_dw->zlib_state.avail_in = result;
3381
0
        }
3382
3383
        // inflate
3384
0
        int zlib_result = inflate(&a_dw->zlib_state, Z_SYNC_FLUSH);
3385
0
        if (zlib_result == Z_STREAM_END)
3386
0
            break;
3387
0
        if (zlib_result != Z_OK) {
3388
0
            btrfs_error(TSK_ERR_FS_READ, "btrfs_datawalk_ed_read_zlib: zlib error: %s (%d)",
3389
0
                    a_dw->zlib_state.msg ? a_dw->zlib_state.msg : "", zlib_result);
3390
0
            return -1;
3391
0
        }
3392
0
    }
3393
3394
0
    return read_bytes - a_dw->zlib_state.avail_out;
3395
0
}
3396
#endif
3397
3398
3399
/**
3400
 * Tries to read a specific byte amount at the current offset within the EXTENT_ITEM
3401
 * @param a_dw pointer to datawalk structure
3402
 * @param a_data pointer to data (or NULL to just skip)
3403
 * @param a_len data len
3404
 * @return amount of read bytes if no error occured, otherwise -1
3405
 */
3406
static ssize_t
3407
btrfs_datawalk_ed_read(BTRFS_DATAWALK * a_dw, uint8_t * a_data,
3408
    const size_t a_len)
3409
0
{
3410
0
    TSK_DADDR_T block_size = a_dw->btrfs->fs_info.block_size;
3411
0
    size_t available_bytes = a_dw->ed_out_size - a_dw->ed_out_offset;
3412
0
    size_t read_bytes = MIN(a_len, available_bytes);
3413
3414
0
    if (tsk_verbose)
3415
0
        tsk_fprintf(stderr, "btrfs_datawalk_ed_read: %s %zd bytes of data at offset: %zd\n",
3416
0
                a_data ? "Reading" : "Skipping", read_bytes, a_dw->ed_offset + a_dw->ed_out_offset);
3417
3418
    // shortcut return, if we should skip the whole EXTENT_ITEM - also at unsupported compression/encryption/encoding!
3419
0
    if (!a_data && read_bytes == available_bytes) {
3420
0
        a_dw->ed_out_offset += read_bytes;
3421
0
        return read_bytes;
3422
0
    }
3423
3424
0
    size_t read_result = 0;
3425
0
    switch (a_dw->ed_type) {
3426
0
    case BTRFS_ED_TYPE_SPARSE:
3427
0
        if (a_data)
3428
0
            memset(a_data, 0x00, read_bytes);
3429
0
        read_result = read_bytes;
3430
0
        break;
3431
0
    case BTRFS_ED_TYPE_RAW: {
3432
0
        if (!a_data)
3433
0
            read_result = read_bytes;
3434
0
        while (read_result < read_bytes) {
3435
            // round down to corresponding block address
3436
0
            size_t inblock_offset = (a_dw->ed_out_offset + read_result) % block_size;
3437
0
            a_dw->ed_raw_offset = (a_dw->ed_out_offset + read_result) - inblock_offset;
3438
3439
0
            ssize_t result = btrfs_datawalk_ed_read_rawblock(a_dw);
3440
0
            if (result == -1)
3441
0
                return -1;
3442
3443
0
            size_t needed_bytes_part = read_bytes - read_result;
3444
0
            size_t read_bytes_part = MIN(needed_bytes_part, result - inblock_offset);
3445
0
            memcpy(a_data + read_result, a_dw->in_blockbuffer + inblock_offset, read_bytes_part);
3446
0
            read_result += read_bytes_part;
3447
3448
0
            if (read_bytes_part < needed_bytes_part)
3449
0
                break;
3450
0
        }
3451
0
        break;  }
3452
0
#ifdef HAVE_LIBZ
3453
0
    case BTRFS_ED_TYPE_COMP_ZLIB: {
3454
0
        while (read_result < read_bytes) {
3455
            // skipping is done blockwise into a temporary buffer
3456
0
            size_t read_bytes_part = a_data ? read_bytes : MIN(read_bytes - read_result, block_size);
3457
3458
0
            ssize_t result = btrfs_datawalk_ed_read_zlib(a_dw, a_data ? a_data : a_dw->tmp_blockbuffer, read_bytes_part);
3459
0
            if (result == -1)
3460
0
                return -1;
3461
0
            read_result += result;
3462
3463
0
            if (result != (ssize_t) read_bytes_part)
3464
0
                break;
3465
0
        }
3466
0
        break;  }
3467
0
#endif
3468
0
    default:
3469
0
        btrfs_error(TSK_ERR_FS_MAGIC,
3470
0
                "btrfs_datawalk_ed_read: EXTENT_ITEM with unsupported compression/encryption/encoding mode: 0x%x 0x%x 0x%x",
3471
0
                a_dw->ed->compression, a_dw->ed->encryption, a_dw->ed->other_encoding);
3472
0
        return -1;
3473
0
    }
3474
3475
    // success
3476
0
    a_dw->ed_out_offset += read_result;
3477
0
    return read_result;
3478
0
}
3479
3480
3481
/**
3482
 * Initializes internal values with the current EXTENT_DATA item
3483
 * @param a_dw pointer to a datawalk structure
3484
 * @return true if no error occured, otherwise false
3485
 */
3486
static bool
3487
btrfs_datawalk_ed_init(BTRFS_DATAWALK * a_dw)
3488
0
{
3489
#ifdef BTRFS_DEBUG
3490
    btrfs_extent_data_debugprint(a_dw->ed);
3491
#endif
3492
3493
0
    a_dw->ed_resident = a_dw->ed->type == BTRFS_EXTENT_DATA_TYPE_INLINE;
3494
3495
    // retrieve type
3496
0
    if (BTRFS_EXTENT_DATA_IS_RAW(a_dw->ed)) {
3497
0
        a_dw->ed_type = (!a_dw->ed_resident && a_dw->ed->nrd.extent_address == 0) ?
3498
0
                BTRFS_ED_TYPE_SPARSE : BTRFS_ED_TYPE_RAW;
3499
0
    } else {
3500
0
        a_dw->ed_type = BTRFS_ED_TYPE_UNKNOWN;  // we don't abort here, because later maybe the whole EXTENT_ITEM is skipped
3501
0
#ifdef HAVE_LIBZ
3502
0
        if (    a_dw->ed->compression == BTRFS_EXTENT_DATA_COMPRESSION_ZLIB &&
3503
0
                a_dw->ed->encryption == BTRFS_EXTENT_DATA_ENCRYPTION_NONE &&
3504
0
                a_dw->ed->other_encoding == BTRFS_EXTENT_DATA_OTHER_ENCODING_NONE)
3505
0
            a_dw->ed_type = BTRFS_ED_TYPE_COMP_ZLIB;
3506
0
#endif
3507
0
    }
3508
3509
0
    a_dw->ed_raw_offset = 0;
3510
0
    a_dw->ed_out_offset = 0;
3511
3512
0
    if (a_dw->ed_resident) {
3513
0
        a_dw->ed_raw_size = a_dw->ed->rd.data_len;
3514
0
        a_dw->ed_out_size = a_dw->ed->size_decoded;
3515
0
    } else {
3516
0
        a_dw->ed_raw_size = a_dw->ed->nrd.extent_size;
3517
0
        a_dw->ed_out_size = MIN(a_dw->ed->nrd.file_bytes, a_dw->size - a_dw->ed_offset);
3518
0
    }
3519
3520
0
#ifdef HAVE_LIBZ
3521
    // if needed, (re)init zlib
3522
0
    if (a_dw->ed_type == BTRFS_ED_TYPE_COMP_ZLIB) {
3523
0
        a_dw->zlib_state.next_in = Z_NULL;
3524
0
        a_dw->zlib_state.avail_in = 0;
3525
3526
0
        int zlib_result;
3527
0
        if (a_dw->zlib_state_used)
3528
0
            zlib_result = inflateReset(&a_dw->zlib_state);
3529
0
        else {
3530
0
            zlib_result = inflateInit(&a_dw->zlib_state);
3531
0
            a_dw->zlib_state_used = true;
3532
0
        }
3533
3534
0
        if (zlib_result != Z_OK) {
3535
0
            btrfs_error(TSK_ERR_FS_READ, "btrfs_datawalk_ed_init: zlib error: %s (%d)",
3536
0
                    a_dw->zlib_state.msg ? a_dw->zlib_state.msg : "", zlib_result);
3537
0
            return false;
3538
0
        }
3539
0
    }
3540
0
#endif
3541
3542
    // skip offset within extent
3543
0
    size_t skip_offset = a_dw->ed->nrd.file_offset;
3544
0
    if (!a_dw->ed_resident && skip_offset) {
3545
0
        a_dw->ed_out_size += skip_offset;
3546
3547
0
        ssize_t result = btrfs_datawalk_ed_read(a_dw, NULL, skip_offset);
3548
0
        if (result != (ssize_t) skip_offset) {
3549
0
            if (result != -1)
3550
0
                btrfs_error(TSK_ERR_FS_READ,
3551
0
                        "btrfs_datawalk_ed_init: Got less bytes than requested: %zd of %zd", result, skip_offset);
3552
0
            return false;
3553
0
        }
3554
0
    }
3555
3556
0
    return true;
3557
0
}
3558
3559
3560
/**
3561
 * Frees a datawalk structure
3562
 * @param a_dw pointer to a datawalk structure
3563
 */
3564
static void
3565
btrfs_datawalk_free(BTRFS_DATAWALK * a_dw)
3566
0
{
3567
0
    if (!a_dw)
3568
0
        return;
3569
3570
0
    btrfs_extent_data_free(a_dw->ed);
3571
0
    btrfs_extent_datawalk_free(a_dw->edw);
3572
3573
0
#ifdef HAVE_LIBZ
3574
0
    if (a_dw->zlib_state_used)
3575
0
        inflateEnd(&a_dw->zlib_state);  // ignore possible error
3576
0
#endif
3577
3578
0
    delete[] a_dw->tmp_blockbuffer;
3579
0
    delete[] a_dw->in_blockbuffer;
3580
3581
0
    delete a_dw;
3582
0
}
3583
3584
3585
/**
3586
 * Allocates a datawalk structure
3587
 * @param a_fs_attr pointer to a file attribute
3588
 * @return pointer to datawalk structure if no error occured, otherwise NULL
3589
 */
3590
static BTRFS_DATAWALK *
3591
btrfs_datawalk_alloc(const TSK_FS_ATTR * a_fs_attr)
3592
0
{
3593
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs_attr->fs_file->fs_info;
3594
3595
0
    BTRFS_DATAWALK *dw = new BTRFS_DATAWALK;
3596
0
    dw->attr = a_fs_attr;
3597
0
    dw->btrfs = btrfs;
3598
0
    dw->size = dw->attr->fs_file->meta->size;   // attr->size can't be used, because compressed resident attributes have wrong size
3599
0
    dw->cc = NULL;
3600
3601
0
    dw->in_blockbuffer = new uint8_t[btrfs->fs_info.block_size];
3602
0
    dw->tmp_blockbuffer = new uint8_t[btrfs->fs_info.block_size];
3603
3604
0
#ifdef HAVE_LIBZ
3605
0
    dw->zlib_state_used = false;
3606
0
    dw->zlib_state.zalloc = Z_NULL;
3607
0
    dw->zlib_state.zfree = Z_NULL;
3608
0
    dw->zlib_state.opaque = Z_NULL;
3609
0
#endif
3610
3611
0
    dw->ed = NULL;
3612
0
    dw->edw = btrfs_extent_datawalk_alloc(btrfs, dw->attr->fs_file->meta);
3613
0
    if (!dw->edw) {
3614
0
        btrfs_datawalk_free(dw);
3615
0
        return NULL;
3616
0
    }
3617
3618
0
    return dw;
3619
0
}
3620
3621
3622
/**
3623
 * Tries to read a specific byte amount at the current offset within the attribute data
3624
 * @param a_dw pointer to datawalk structure
3625
 * @param a_data pointer to data (or NULL to just skip)
3626
 * @param a_len data len
3627
 * @return amount of read bytes if no error occured, otherwise -1
3628
 */
3629
static ssize_t
3630
btrfs_datawalk_read(BTRFS_DATAWALK * a_dw, uint8_t * a_data, const size_t a_len)
3631
0
{
3632
0
    size_t written = 0;
3633
0
    while (written < a_len) {
3634
        // if no EXTENT_DATA item yet or end of current one reached, get next one
3635
0
        if (!a_dw->ed || a_dw->ed_out_offset == a_dw->ed_out_size) {
3636
0
            btrfs_extent_data_free(a_dw->ed);
3637
0
            a_dw->ed = NULL;
3638
3639
0
            BTRFS_TREENODE_RESULT node_result = btrfs_extent_datawalk_get(a_dw->edw, &a_dw->ed, &a_dw->ed_offset);
3640
0
            if (node_result == BTRFS_TREENODE_ERROR) {
3641
0
                tsk_error_set_errstr2("- btrfs_datawalk_read: getting next EXTENT_DATA item");
3642
0
                return -1;
3643
0
            }
3644
0
            if (node_result == BTRFS_TREENODE_NOT_FOUND)
3645
0
                break;
3646
3647
0
            if (!btrfs_datawalk_ed_init(a_dw))
3648
0
                return -1;
3649
0
        }
3650
3651
0
        ssize_t result = btrfs_datawalk_ed_read(a_dw, a_data + written, a_len - written);
3652
0
        if (result == -1)
3653
0
            return -1;
3654
3655
0
        written += result;
3656
0
    }
3657
0
    return written;
3658
0
}
3659
3660
3661
/**
3662
 * Reads a specific byte amount at a specific byte offset within the attribute data
3663
 * @param a_fs_attr pointer to a file attribute
3664
 * @param a_offset offset
3665
 * @param a_buf pointer to buffer
3666
 * @param a_len data len   (>=0 due to size_t being unsigned)
3667
 * @return amount of read bytes if no error occured, otherwise -1
3668
 */
3669
static ssize_t
3670
btrfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
3671
    char *a_buf, size_t a_len)
3672
0
{
3673
    // check params
3674
0
    if (!a_fs_attr || !a_fs_attr->fs_file || !a_fs_attr->fs_file->meta || !a_fs_attr->fs_file->fs_info || !a_buf) {
3675
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_file_read_special: called with NULL pointers");
3676
0
        return -1;
3677
0
    }
3678
0
    if (!(a_fs_attr->flags & TSK_FS_ATTR_COMP)) {
3679
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_file_read_special: called with non-special attribute");
3680
0
        return -1;
3681
0
    }
3682
0
    if (a_offset >= a_fs_attr->size || a_offset < 0) {
3683
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_file_read_special: called with read offset out of range");
3684
0
        return -1;
3685
0
    }
3686
0
    if (a_offset + (TSK_OFF_T) a_len > a_fs_attr->size ) {
3687
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_file_read_special: called with read len out of range");
3688
0
        return -1;
3689
0
    }
3690
3691
3692
0
    BTRFS_DATAWALK *dw = btrfs_datawalk_alloc(a_fs_attr);
3693
0
    if (!dw) {
3694
0
        return -1;
3695
0
    }
3696
3697
    // skip offset
3698
0
    ssize_t result;
3699
3700
0
    if (a_offset) {
3701
0
        result = btrfs_datawalk_read(dw, NULL, a_offset);
3702
0
        if (result != (ssize_t) a_offset) {
3703
0
            if (result != -1)
3704
0
                btrfs_error(TSK_ERR_FS_READ,
3705
0
                        "btrfs_file_read_special: Got less offset bytes than requested: %zd of %" PRIdOFF,
3706
0
                        result, a_offset);
3707
0
            btrfs_datawalk_free(dw);
3708
0
            return -1;
3709
0
        }
3710
0
    }
3711
3712
    // read into buffer
3713
0
    result = btrfs_datawalk_read(dw, (uint8_t*) a_buf, a_len);
3714
3715
0
    btrfs_datawalk_free(dw);
3716
0
    return result;
3717
0
}
3718
3719
3720
/**
3721
 * Maps the stored EXTENT_DATA type to a TSK_FS_BLOCK_FLAG
3722
 * @param a_ed_type EXTENT_DATA type
3723
 * @return result
3724
 */
3725
static inline TSK_FS_BLOCK_FLAG_ENUM
3726
btrfs_edtype2blockflag(const BTRFS_ED_TYPE a_ed_type)
3727
0
{
3728
0
    switch (a_ed_type) {
3729
0
    case BTRFS_ED_TYPE_RAW:
3730
0
        return TSK_FS_BLOCK_FLAG_RAW;
3731
0
    case BTRFS_ED_TYPE_SPARSE:
3732
0
        return TSK_FS_BLOCK_FLAG_SPARSE;
3733
0
#ifdef HAVE_LIBZ
3734
0
    case BTRFS_ED_TYPE_COMP_ZLIB:
3735
0
        return TSK_FS_BLOCK_FLAG_COMP;
3736
0
#endif
3737
0
    default:
3738
0
        return (TSK_FS_BLOCK_FLAG_ENUM) 0;
3739
0
    }
3740
0
}
3741
3742
3743
/**
3744
 * Iterates through all blocks of an attribute and calls the callback with each block (block size less/equal FS block size)
3745
 * @param a_fs_attr pointer to a file attribute
3746
 * @param a_flags flags
3747
 * @param a_action pointer to callback
3748
 * @param a_ptr pointer to opaque callback data
3749
 * @return 0 if no error occured, otherwise 1
3750
 */
3751
static uint8_t
3752
btrfs_attr_walk_special(const TSK_FS_ATTR * a_fs_attr, int a_flags,
3753
    TSK_FS_FILE_WALK_CB a_action, void *a_ptr)
3754
0
{
3755
    // check params
3756
0
    if (!a_fs_attr || !a_fs_attr->fs_file || !a_fs_attr->fs_file->meta || !a_fs_attr->fs_file->fs_info || !a_action) {
3757
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_attr_walk_special: called with NULL pointers");
3758
0
        return 1;
3759
0
    }
3760
0
    if (!(a_fs_attr->flags & TSK_FS_ATTR_COMP)) {
3761
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_attr_walk_special: called with non-special attribute");
3762
0
        return 1;
3763
0
    }
3764
3765
3766
0
    BTRFS_DATAWALK *dw = btrfs_datawalk_alloc(a_fs_attr);
3767
0
    if (!dw)
3768
0
        return 1;
3769
3770
0
    const size_t block_size = a_fs_attr->fs_file->fs_info->block_size;
3771
    //uint8_t block[a_fs_attr->fs_file->fs_info->block_size];
3772
0
    uint8_t *block = new uint8_t[block_size];
3773
0
    uint8_t *used_block = a_flags & TSK_FS_FILE_WALK_FLAG_AONLY ? NULL : block;
3774
3775
0
    ssize_t result;
3776
0
    for (TSK_OFF_T offset = 0; offset < dw->size; offset += result) {
3777
0
        size_t read_bytes = 0;
3778
0
        if (dw->size < offset ) {
3779
0
            read_bytes = 0;
3780
0
        }
3781
0
        else {
3782
0
            read_bytes = dw->size - offset;
3783
0
        }
3784
0
        if (read_bytes > block_size) {
3785
0
            read_bytes = block_size;
3786
0
        }
3787
3788
        // read block
3789
0
        result = btrfs_datawalk_read(dw, used_block, read_bytes);
3790
0
        if (result != (ssize_t) read_bytes) {
3791
0
            if (result != -1)
3792
0
                btrfs_error(TSK_ERR_FS_READ,
3793
0
                        "btrfs_attr_walk_special: Got less bytes than requested: %zd of %zd", result, read_bytes);
3794
0
            btrfs_datawalk_free(dw);
3795
0
            return 1;
3796
0
        }
3797
3798
0
        TSK_FS_BLOCK_FLAG_ENUM flags = (TSK_FS_BLOCK_FLAG_ENUM)
3799
0
                (TSK_FS_BLOCK_FLAG_ALLOC | TSK_FS_BLOCK_FLAG_CONT | btrfs_edtype2blockflag(dw->ed_type));
3800
0
        if (dw->ed_resident)
3801
0
            flags = (TSK_FS_BLOCK_FLAG_ENUM) (flags | TSK_FS_BLOCK_FLAG_RES);
3802
3803
        // if sparse block and sparse blocks unwanted, skip block
3804
0
        if ((flags & TSK_FS_BLOCK_FLAG_SPARSE) && (a_flags & TSK_FS_FILE_WALK_FLAG_NOSPARSE))
3805
0
            continue;
3806
3807
        // invoke callback
3808
0
        TSK_DADDR_T raw_addr = !(flags & TSK_FS_BLOCK_FLAG_RES) && (flags & TSK_FS_BLOCK_FLAG_RAW) ?
3809
0
                dw->last_raw_addr : 0;
3810
0
        TSK_WALK_RET_ENUM cb_result = a_action(a_fs_attr->fs_file, offset, raw_addr, (char*) used_block, result, flags, a_ptr);
3811
0
        if (cb_result == TSK_WALK_ERROR) {
3812
0
            btrfs_datawalk_free(dw);
3813
0
            delete[] block;
3814
0
            return 1;
3815
0
        }
3816
0
        if (cb_result == TSK_WALK_STOP)
3817
0
            break;
3818
0
    }
3819
3820
0
    btrfs_datawalk_free(dw);
3821
0
    delete[] block;
3822
0
    return 0;
3823
0
}
3824
#else
3825
static ssize_t
3826
btrfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
3827
    char *a_buf, size_t a_len)
3828
{
3829
    btrfs_error(TSK_ERR_FS_UNSUPFUNC,
3830
            "btrfs_file_read_special: no supported compression available");
3831
    return -1;
3832
}
3833
3834
3835
static uint8_t
3836
btrfs_attr_walk_special(const TSK_FS_ATTR * a_fs_attr, int a_flags,
3837
    TSK_FS_FILE_WALK_CB a_action, void *a_ptr)
3838
{
3839
    btrfs_error(TSK_ERR_FS_UNSUPFUNC,
3840
            "btrfs_attr_walk_special: no supported compression available");
3841
    return 1;
3842
}
3843
#endif
3844
3845
3846
/**
3847
 * Returns the default attribute type
3848
 * @param a_file FS info
3849
 * @return default attribute type
3850
 */
3851
static TSK_FS_ATTR_TYPE_ENUM
3852
btrfs_get_default_attr_type([[maybe_unused]] const TSK_FS_FILE * a_file)
3853
0
{
3854
0
    return TSK_FS_ATTR_TYPE_DEFAULT;
3855
0
}
3856
3857
3858
/**
3859
 * Loads the attributes of a file
3860
 * @param a_fs_file pointer to file
3861
 * @return 0 if no error occured, otherwise 1
3862
 */
3863
static uint8_t
3864
btrfs_load_attrs(TSK_FS_FILE * a_fs_file)
3865
0
{
3866
0
    if (!a_fs_file || !a_fs_file->meta || !a_fs_file->fs_info) {
3867
0
        btrfs_error(TSK_ERR_FS_ARG, "btrfs_load_attrs: called with NULL pointers");
3868
0
        return 1;
3869
0
    }
3870
3871
0
    TSK_FS_INFO *fs = a_fs_file->fs_info;
3872
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) fs;
3873
0
    TSK_FS_META *meta = a_fs_file->meta;
3874
0
    bool comp = meta->flags & TSK_FS_META_FLAG_COMP;
3875
3876
0
    TSK_FS_ATTR *attr = NULL;
3877
0
    BTRFS_TREENODE *node = NULL;
3878
0
    BTRFS_TREENODE_RESULT node_result;
3879
0
    BTRFS_DIR_ENTRY *de = NULL;
3880
0
    BTRFS_EXTENT_DATAWALK *edw = NULL;
3881
0
    BTRFS_EXTENT_DATA *ed = NULL;
3882
0
    TSK_FS_ATTR_RUN *run = NULL;
3883
3884
0
    if (meta->attr && meta->attr_state == TSK_FS_META_ATTR_STUDIED)
3885
0
        return 0;
3886
0
    if (meta->attr_state == TSK_FS_META_ATTR_ERROR)
3887
0
        return 1;
3888
3889
0
    if (meta->attr)
3890
0
        tsk_fs_attrlist_markunused(meta->attr);
3891
0
    else {
3892
0
        meta->attr = tsk_fs_attrlist_alloc();
3893
0
        if (!meta->attr)
3894
0
            return 1;
3895
0
    }
3896
3897
0
    if (tsk_verbose)
3898
0
        tsk_fprintf(stderr, "btrfs_load_attrs: Loading attributes of inum: %" PRIuINUM "\n", meta->addr);
3899
3900
3901
    // handle special virtual inums
3902
0
    if (meta->addr == BTRFS_SUPERBLOCK_VINUM(fs)) {
3903
0
        TSK_DADDR_T sb_address = btrfs_superblock_address(btrfs->sb_mirror_index);
3904
        // uint8_t tmp_sb[meta->size];
3905
0
        uint8_t *tmp_sb = new uint8_t[meta->size];
3906
3907
0
        ssize_t result = tsk_fs_read(fs, sb_address, (char*) tmp_sb, meta->size);
3908
0
        if (result != (signed) meta->size) {
3909
0
            if (result >= 0)
3910
0
                btrfs_error(TSK_ERR_FS_READ, "btrfs_load_attrs: Error reading superblock at physical address: 0x%" PRIxDADDR, sb_address);
3911
0
            else
3912
0
                tsk_error_set_errstr2("btrfs_load_attrs: Error reading superblock at physical address: 0x%" PRIxDADDR, sb_address);
3913
0
            delete[] tmp_sb;
3914
0
            goto on_error;
3915
0
        }
3916
3917
0
        attr = tsk_fs_attrlist_getnew(meta->attr, TSK_FS_ATTR_RES);
3918
0
        if (!attr) {
3919
0
            tsk_error_set_errstr2("btrfs_load_attrs: Error getting attribute for superblock");
3920
0
            delete[] tmp_sb;
3921
0
            goto on_error;
3922
0
        }
3923
0
        if (tsk_fs_attr_set_str(a_fs_file, attr, NULL,
3924
0
                fs->get_default_attr_type(a_fs_file), TSK_FS_ATTR_ID_DEFAULT, tmp_sb, meta->size)) {
3925
0
            tsk_error_set_errstr2("btrfs_load_attrs: Error setting attribute for superblock");
3926
0
            delete[] tmp_sb;
3927
0
            goto on_error;
3928
0
        }
3929
0
        delete[] tmp_sb;
3930
3931
0
        if (tsk_verbose)
3932
0
            tsk_fprintf(stderr, "btrfs_load_attrs: Added superblock standard attribute (%" PRIdOFF " bytes)\n", meta->size);
3933
0
        return 0;
3934
0
    }
3935
0
    if (meta->addr == TSK_FS_ORPHANDIR_INUM(fs)) {
3936
0
        meta->attr_state = TSK_FS_META_ATTR_STUDIED;
3937
0
        return 0;
3938
0
    }
3939
3940
3941
0
    uint64_t subvol;
3942
0
    TSK_INUM_T inum;
3943
0
    if (!btrfs_inum_virt2real_map(btrfs, meta->addr, &subvol, &inum)) {
3944
0
        tsk_error_set_errstr2("btrfs_load_attrs: mapping inum of file");
3945
0
        goto on_error;
3946
0
    }
3947
3948
3949
    // derive XATTR_ITEM items, if existing
3950
0
    BTRFS_KEY key;
3951
0
    key.object_id = inum;
3952
0
    key.item_type = BTRFS_ITEM_TYPE_XATTR_ITEM;
3953
0
    key.offset = 0;
3954
3955
0
    node_result = btrfs_treenode_search_lowest(btrfs, &node, btrfs_subvol_tree_address(btrfs, subvol), &key,
3956
0
            BTRFS_CMP_IGNORE_OFFSET);
3957
0
    if (node_result == BTRFS_TREENODE_ERROR) {
3958
0
        tsk_error_errstr2_concat("- btrfs_load_attrs: loading XATTR_ITEM item");
3959
0
        goto on_error;
3960
0
    }
3961
0
    if (node_result == BTRFS_TREENODE_FOUND) {
3962
0
        uint8_t dummy[1];
3963
3964
        // iterate over all XATTR_ITEM items
3965
0
        do {
3966
0
            de = btrfs_dir_entry_fromraw(btrfs_treenode_itemdata(node), btrfs_treenode_itemsize(node));
3967
#ifdef BTRFS_DEBUG
3968
            btrfs_debug("### XATTR_ITEM ###\n");
3969
            btrfs_dir_entry_debugprint(de);
3970
#endif
3971
3972
            // iterate over all entries
3973
0
            for (BTRFS_DIR_ENTRY *de_entry = de; de_entry; de_entry = de_entry->next) {
3974
0
                attr = tsk_fs_attrlist_getnew(meta->attr, TSK_FS_ATTR_RES);
3975
0
                if (!attr) {
3976
0
                    tsk_error_set_errstr2("btrfs_load_attrs: Error getting attribute for extended attribute");
3977
0
                    goto on_error;
3978
0
                }
3979
3980
0
                uint8_t *res_data = de_entry->data_len ? de_entry->data : dummy;
3981
0
                if (tsk_fs_attr_set_str(a_fs_file, attr, de_entry->name,
3982
0
                        TSK_FS_ATTR_TYPE_UNIX_XATTR, TSK_FS_ATTR_ID_DEFAULT,
3983
0
                        res_data, de_entry->data_len)) {
3984
0
                    tsk_error_set_errstr2("btrfs_load_attrs: Error setting attribute for extended attribute");
3985
0
                    goto on_error;
3986
0
                }
3987
3988
0
                if (tsk_verbose)
3989
0
                    tsk_fprintf(stderr, "btrfs_load_attrs: Added extended attribute '%s' (%" PRIdOFF " bytes)\n",
3990
0
                            attr->name, attr->size);
3991
0
                attr = NULL;
3992
0
            }
3993
3994
0
            btrfs_dir_entry_free(de);
3995
0
            de = NULL;
3996
3997
            // next XATTR_ITEM
3998
0
            node_result = btrfs_treenode_step(btrfs, &node, &key,
3999
0
                    BTRFS_CMP_IGNORE_OFFSET, BTRFS_LAST, BTRFS_STEP_INITIAL);
4000
0
            if (node_result == BTRFS_TREENODE_ERROR) {
4001
0
                tsk_error_set_errstr2("btrfs_load_attrs: stepping to next XATTR_ITEM item");
4002
0
                goto on_error;
4003
0
            }
4004
0
        } while (node_result == BTRFS_TREENODE_FOUND);
4005
4006
0
        btrfs_treenode_free(node);
4007
0
        node = NULL;
4008
0
    }
4009
4010
4011
    // derive EXTENT_DATA items, if existing
4012
0
    edw = btrfs_extent_datawalk_alloc(btrfs, meta);
4013
0
    if (!edw)
4014
0
        goto on_error;
4015
4016
0
    for (;;) {
4017
        // next EXTENT_DATA
4018
0
        TSK_DADDR_T ed_offset;
4019
0
        node_result = btrfs_extent_datawalk_get(edw, &ed, &ed_offset);
4020
0
        if (node_result == BTRFS_TREENODE_ERROR) {
4021
0
            tsk_error_set_errstr2("btrfs_load_attrs: getting next EXTENT_DATA item");
4022
0
            goto on_error;
4023
0
        }
4024
0
        if (node_result == BTRFS_TREENODE_NOT_FOUND)
4025
0
            break;
4026
4027
#ifdef BTRFS_DEBUG
4028
        btrfs_extent_data_debugprint(ed);
4029
#endif
4030
4031
        // create attribute at first iteration
4032
0
        if (!attr) {
4033
0
            bool resident = ed->type == BTRFS_EXTENT_DATA_TYPE_INLINE;
4034
0
            attr = tsk_fs_attrlist_getnew(meta->attr,
4035
0
                    resident ? TSK_FS_ATTR_RES : TSK_FS_ATTR_NONRES);
4036
0
            if (!attr) {
4037
0
                tsk_error_set_errstr2("btrfs_load_attrs: Error getting attribute");
4038
0
                goto on_error;
4039
0
            }
4040
4041
0
            if (resident) {
4042
                // init for resident file + add data
4043
0
                uint8_t dummy[1];
4044
0
                uint8_t *res_data = comp ? dummy : ed->rd.data;
4045
0
                size_t len = comp ? 0 : ed->rd.data_len;
4046
0
                if (tsk_fs_attr_set_str(a_fs_file, attr, NULL,
4047
0
                        fs->get_default_attr_type(a_fs_file), TSK_FS_ATTR_ID_DEFAULT,
4048
0
                        res_data, len)) {
4049
0
                    tsk_error_set_errstr2("btrfs_load_attrs: Error setting resident attribute");
4050
0
                    goto on_error;
4051
0
                }
4052
0
            } else {
4053
                // init for non-resident file (no slack space at compressed files)
4054
0
                TSK_OFF_T alloc_size = comp ? meta->size : roundup(meta->size, fs->block_size);
4055
0
                if (tsk_fs_attr_set_run(a_fs_file, attr, NULL, NULL,
4056
0
                        fs->get_default_attr_type(a_fs_file), TSK_FS_ATTR_ID_DEFAULT,
4057
0
                        meta->size, meta->size, alloc_size, TSK_FS_ATTR_FLAG_NONE, 0)) {
4058
0
                    tsk_error_set_errstr2("btrfs_load_attrs: Error setting non-resident attribute");
4059
0
                    goto on_error;
4060
0
                }
4061
0
            }
4062
4063
0
            if (comp) {
4064
0
                attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (attr->flags | TSK_FS_ATTR_COMP);
4065
0
                attr->r = btrfs_file_read_special;
4066
0
                attr->w = btrfs_attr_walk_special;
4067
0
            }
4068
4069
0
            if (resident) {
4070
0
                btrfs_extent_data_free(ed);
4071
0
                ed = NULL;
4072
0
                break;
4073
0
            }
4074
0
        }
4075
4076
4077
        // non-resident file
4078
4079
0
        bool sparse_run = ed->nrd.extent_address == 0;
4080
0
        if (sparse_run)
4081
0
            attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (attr->flags | TSK_FS_ATTR_SPARSE);
4082
4083
4084
        // if compressed attribute, abort after first sparse run (so that the attribute's sparse flag gets set)
4085
0
        if (comp) {
4086
0
            if (sparse_run) {
4087
0
                btrfs_extent_data_free(ed);
4088
0
                ed = NULL;
4089
0
                break;
4090
0
            }
4091
0
        } else {
4092
            // add run (respecting chunk range)
4093
4094
0
            TSK_DADDR_T run_offset = ed_offset;
4095
0
            TSK_OFF_T run_len = ed->nrd.file_bytes;
4096
0
            while (run_len) {
4097
0
                TSK_DADDR_T run_phys_address;
4098
0
                TSK_OFF_T remaining_bytes;
4099
0
                TSK_FS_ATTR_RUN_FLAG_ENUM run_flag;
4100
4101
                // handle sparse runs
4102
0
                if (sparse_run) {
4103
0
                    run_phys_address = 0;
4104
0
                    remaining_bytes = run_len;
4105
0
                    run_flag = TSK_FS_ATTR_RUN_FLAG_SPARSE;
4106
0
                } else {
4107
0
                    TSK_DADDR_T run_log_address = ed->nrd.extent_address + ed->nrd.file_offset;
4108
0
                    const BTRFS_CACHED_CHUNK *cc;
4109
0
                    if (!btrfs_address_map(&btrfs->chunks->log2phys, &cc, run_log_address, &run_phys_address)) {
4110
0
                        btrfs_error(TSK_ERR_FS_BLK_NUM,
4111
0
                                "btrfs_load_attrs: Could not map logical address: 0x%" PRIxDADDR, run_log_address);
4112
0
                        goto on_error;
4113
0
                    }
4114
0
                    remaining_bytes = btrfs_chunk_remaining_bytes(cc, run_log_address);
4115
0
                    run_flag = TSK_FS_ATTR_RUN_FLAG_NONE;
4116
0
                }
4117
4118
0
                TSK_OFF_T current_run_len = MIN(run_len, remaining_bytes);
4119
4120
0
                run = tsk_fs_attr_run_alloc();
4121
0
                if (!run) {
4122
0
                    tsk_error_set_errstr2("btrfs_load_attrs: Error allocating run");
4123
0
                    goto on_error;
4124
0
                }
4125
4126
0
                if (run_offset % fs->block_size) {
4127
0
                    btrfs_error(TSK_ERR_FS_INODE_COR,
4128
0
                            "btrfs_load_attrs: run offset not divisible by block size: 0x%" PRIxDADDR, run_offset);
4129
0
                    goto on_error;
4130
0
                }
4131
0
                run->offset = run_offset / fs->block_size;
4132
4133
0
                if (run_phys_address % fs->block_size) {
4134
0
                    btrfs_error(TSK_ERR_FS_INODE_COR,
4135
0
                            "btrfs_load_attrs: run physical address not divisible by block size: 0x%" PRIxDADDR, run_phys_address);
4136
0
                    goto on_error;
4137
0
                }
4138
0
                run->addr = run_phys_address / fs->block_size;
4139
4140
0
                if (current_run_len % fs->block_size) {
4141
0
                    btrfs_error(TSK_ERR_FS_INODE_COR,
4142
0
                            "btrfs_load_attrs: run len not divisible by block size: %" PRIdOFF, current_run_len);
4143
0
                    goto on_error;
4144
0
                }
4145
0
                run->len = current_run_len / fs->block_size;
4146
4147
0
                run->flags = run_flag;
4148
0
                if (tsk_fs_attr_add_run(fs, attr, run)) {
4149
0
                    tsk_error_set_errstr2("btrfs_load_attrs: Error adding run");
4150
0
                    goto on_error;
4151
0
                }
4152
0
                run = NULL;
4153
4154
0
                run_offset += current_run_len;
4155
0
                run_len -= current_run_len;
4156
0
            }
4157
0
        }
4158
4159
0
        btrfs_extent_data_free(ed);
4160
0
        ed = NULL;
4161
0
    };
4162
4163
0
    btrfs_extent_datawalk_free(edw);
4164
0
    edw = NULL;
4165
4166
0
    if (tsk_verbose)
4167
0
        tsk_fprintf(stderr, "btrfs_load_attrs: Added standard attribute (%" PRIdOFF " bytes)\n", meta->size);
4168
4169
0
    meta->attr_state = TSK_FS_META_ATTR_STUDIED;
4170
0
    return 0;
4171
4172
0
on_error:
4173
0
    tsk_fs_attrlist_markunused(meta->attr);
4174
0
    btrfs_treenode_free(node);
4175
0
    btrfs_dir_entry_free(de);
4176
0
    btrfs_extent_data_free(ed);
4177
0
    btrfs_extent_datawalk_free(edw);
4178
0
    tsk_fs_attr_run_free(run);
4179
4180
0
    meta->attr_state = TSK_FS_META_ATTR_ERROR;
4181
0
    return 1;
4182
0
}
4183
4184
4185
4186
/*
4187
 * status
4188
 */
4189
4190
4191
/**
4192
 * Prints data in hex notation into a file.
4193
 * @param a_file file
4194
 * @param a_prefix prefixed description
4195
 * @param a_data pointer to data
4196
 * @param a_len data len
4197
 */
4198
static void
4199
btrfs_stat_output_hex(FILE * a_file, const char *a_prefix,
4200
    const uint8_t * a_data, int a_len)
4201
0
{
4202
0
    tsk_fprintf(a_file, "%s: ", a_prefix);
4203
0
    for (int i = 0; i < a_len; i++)
4204
0
        tsk_fprintf(a_file, "%02x", *(a_data + i));
4205
0
    tsk_fprintf(a_file, "\n");
4206
0
}
4207
4208
4209
/**
4210
 * Prints a flag description for each set superblock compat_flags flag into a file.
4211
 * @param a_file file
4212
 * @param a_flags flags
4213
 */
4214
static void
4215
btrfs_fsstat_print_compat_flags(FILE * a_file, uint64_t a_flags)
4216
0
{
4217
0
    for (int i = 0; i < 64; i++) {
4218
0
        if (!(a_flags & (1ULL << i)))
4219
0
            continue;
4220
4221
        // there are no such flags defined ATM!
4222
0
        tsk_fprintf(a_file, "unknown (1 << %d)\n", i);
4223
0
    }
4224
0
}
4225
4226
4227
/**
4228
 * Prints a flag description for each set superblock compat_ro_flags flag into a file.
4229
 * @param a_file file
4230
 * @param a_flags flags
4231
 */
4232
static void
4233
btrfs_fsstat_print_compat_ro_flags(FILE * a_file, uint64_t a_flags)
4234
0
{
4235
0
    for (int i = 0; i < 64; i++) {
4236
0
        if (!(a_flags & (1ULL << i)))
4237
0
            continue;
4238
4239
        // there are no such flags defined ATM!
4240
0
        tsk_fprintf(a_file, "unknown (1 << %d)\n", i);
4241
0
    }
4242
0
}
4243
4244
4245
/**
4246
 * Prints a flag description for each set superblock incompat_flags flag into a file.
4247
 * @param a_file file
4248
 * @param a_flags flags
4249
 */
4250
static void
4251
btrfs_fsstat_print_incompat_flags(FILE * a_file, uint64_t a_flags)
4252
0
{
4253
0
    static const char *general_flags[10] = {
4254
0
        "MIXED_BACKREF",
4255
0
        "DEFAULT_SUBVOL",
4256
0
        "MIXED_GROUPS",
4257
0
        "COMPRESS_LZO",
4258
0
        "COMPRESS_ZSTD",
4259
0
        "BIG_METADATA",
4260
0
        "EXTENDED_IREF",
4261
0
        "RAID56",
4262
0
        "SKINNY_METADATA",
4263
0
        "NO_HOLES"
4264
0
    };
4265
4266
0
    for (int i = 0; i < 64; i++) {
4267
0
        if (!(a_flags & (1ULL << i)))
4268
0
            continue;
4269
4270
        // handle general/unknown case
4271
0
        if (i < 10) {
4272
0
            tsk_fprintf(a_file, "%s\n", general_flags[i]);
4273
0
            continue;
4274
0
        }
4275
0
        tsk_fprintf(a_file, "unknown (1 << %d)\n", i);
4276
0
    }
4277
0
}
4278
4279
4280
/**
4281
 * Prints information about a file system into a file.
4282
 * @param a_fs FS info
4283
 * @param a_file file
4284
 * @return 0 if no error occured, otherwise 1
4285
 */
4286
static uint8_t
4287
btrfs_fsstat(TSK_FS_INFO * a_fs, FILE * a_file)
4288
0
{
4289
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
4290
4291
    // clean up any error messages that are lying around
4292
0
    tsk_error_reset();
4293
4294
0
    tsk_fprintf(a_file, "FILE SYSTEM INFORMATION\n");
4295
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4296
0
    tsk_fprintf(a_file, "File System Type: Btrfs\n");
4297
0
    tsk_fprintf(a_file, "File System Name: %s\n", btrfs->sb->label);
4298
0
    btrfs_stat_output_hex(a_file, "File System UUID",
4299
0
            btrfs->sb->uuid, sizeof(btrfs->sb->uuid));
4300
0
    tsk_fprintf(a_file, "\n");
4301
4302
0
    tsk_fprintf(a_file, "Used Superblock: ");
4303
0
    if (!btrfs->sb_mirror_index)
4304
0
        tsk_fprintf(a_file, "Original\n");
4305
0
    else
4306
0
        tsk_fprintf(a_file, "Mirror #%d\n", btrfs->sb_mirror_index);
4307
4308
0
    tsk_fprintf(a_file, "Flags: 0x%016" PRIx64 "\n", btrfs->sb->flags);
4309
0
    tsk_fprintf(a_file, "Generation: %" PRId64 "\n", btrfs->sb->generation);
4310
0
    tsk_fprintf(a_file, "\n");
4311
0
    tsk_fprintf(a_file, "Total Bytes: %" PRId64 "\n", btrfs->sb->total_bytes);
4312
0
    tsk_fprintf(a_file, "Bytes used: %" PRId64 "\n", btrfs->sb->bytes_used);
4313
0
    tsk_fprintf(a_file, "Number of Devices: %" PRId64 "\n", btrfs->sb->num_devices);
4314
0
    tsk_fprintf(a_file, "Stripe Size: %" PRId32 "\n", btrfs->sb->stripesize);
4315
0
    tsk_fprintf(a_file, "\n");
4316
0
    tsk_fprintf(a_file, "Checksum type: %" PRId16 " (%s)\n",
4317
0
            btrfs->sb->csum_type, btrfs_csum_description(btrfs->sb->csum_type));
4318
0
    tsk_fprintf(a_file, "\n");
4319
4320
0
    tsk_fprintf(a_file, "COMPATIBILITY FLAGS\n");
4321
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4322
0
    tsk_fprintf(a_file, "compat_flags:\n");
4323
0
    btrfs_fsstat_print_compat_flags(a_file, btrfs->sb->compat_flags);
4324
0
    tsk_fprintf(a_file, "\n");
4325
0
    tsk_fprintf(a_file, "compat_ro_flags:\n");
4326
0
    btrfs_fsstat_print_compat_ro_flags(a_file, btrfs->sb->compat_ro_flags);
4327
0
    tsk_fprintf(a_file, "\n");
4328
0
    tsk_fprintf(a_file, "incompat_flags:\n");
4329
0
    btrfs_fsstat_print_incompat_flags(a_file, btrfs->sb->incompat_flags);
4330
0
    tsk_fprintf(a_file, "\n");
4331
4332
0
    tsk_fprintf(a_file, "METADATA INFORMATION\n");
4333
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4334
0
    tsk_fprintf(a_file, "Inode Range: %" PRIuINUM " - %" PRIuINUM "\n",
4335
0
            a_fs->first_inum, a_fs->last_inum);
4336
4337
4338
0
    tsk_fprintf(a_file, "Root Directory Inode (virtual): %" PRIuINUM "\n", a_fs->root_inum);
4339
4340
0
    uint64_t subvol;
4341
0
    TSK_INUM_T inum;
4342
0
    if (!btrfs_inum_virt2real_map(btrfs, a_fs->root_inum, &subvol, &inum)) {
4343
0
        tsk_error_set_errstr2("btrfs_fsstat: mapping root inum");
4344
0
        return 1;
4345
0
    }
4346
4347
0
    tsk_fprintf(a_file, "Root Directory Subvolume: 0x%" PRIx64 "\n", subvol);
4348
0
    tsk_fprintf(a_file, "Root Directory Inode (real): %" PRIuINUM "\n", inum);
4349
4350
4351
0
    tsk_fprintf(a_file, "Node Size: %" PRId32 "\n", btrfs->sb->nodesize);
4352
0
    tsk_fprintf(a_file, "Leaf Size: %" PRId32 "\n", btrfs->sb->leafsize);
4353
0
    tsk_fprintf(a_file, "\n");
4354
4355
0
    tsk_fprintf(a_file, "CONTENT INFORMATION\n");
4356
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4357
0
    tsk_fprintf(a_file, "Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
4358
0
            a_fs->first_block, a_fs->last_block);
4359
0
    if (a_fs->last_block != a_fs->last_block_act)
4360
0
        tsk_fprintf(a_file, "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
4361
0
                a_fs->first_block, a_fs->last_block_act);
4362
0
    tsk_fprintf(a_file, "Block Size: %u\n", a_fs->block_size);
4363
0
    tsk_fprintf(a_file, "\n");
4364
4365
0
    tsk_fprintf(a_file, "TREE INFORMATION\n");
4366
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4367
0
    tsk_fprintf(a_file, "Logical Address of Root Tree Root: 0x%" PRIx64 "\n", btrfs->sb->root_tree_root);
4368
0
    tsk_fprintf(a_file, "Root Tree Root Level: %" PRId8  "\n", btrfs->sb->root_level);
4369
0
    tsk_fprintf(a_file, "\n");
4370
0
    tsk_fprintf(a_file, "Logical Address of Chunk Tree Root: 0x%" PRIx64 "\n", btrfs->sb->chunk_tree_root);
4371
0
    tsk_fprintf(a_file, "Chunk Root Level: %" PRId8  "\n", btrfs->sb->chunk_root_level);
4372
0
    tsk_fprintf(a_file, "Chunk Root Generation: %" PRId64 "\n", btrfs->sb->chunk_root_generation);
4373
0
    tsk_fprintf(a_file, "\n");
4374
0
    tsk_fprintf(a_file, "Logical Address of Log Tree Root: 0x%" PRIx64 "\n", btrfs->sb->log_tree_root);
4375
0
    tsk_fprintf(a_file, "Log Root Level: %" PRId8  "\n", btrfs->sb->log_root_level);
4376
0
    tsk_fprintf(a_file, "Log Root Transaction ID: 0x%" PRIx64 "\n", btrfs->sb->log_root_transid);
4377
0
    tsk_fprintf(a_file, "\n");
4378
4379
0
    tsk_fprintf(a_file, "VOLUME INFORMATION\n");
4380
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4381
0
    tsk_fprintf(a_file, "Device ID: %"  PRId64 "\n", btrfs->sb->dev_item.device_id);
4382
0
    tsk_fprintf(a_file, "Total Bytes: %" PRId64 "\n", btrfs->sb->dev_item.total_bytes);
4383
0
    tsk_fprintf(a_file, "Bytes used: %" PRId64 "\n", btrfs->sb->dev_item.bytes_used);
4384
0
    tsk_fprintf(a_file, "Type: 0x%" PRIx64 "\n", btrfs->sb->dev_item.type);
4385
0
    tsk_fprintf(a_file, "Generation: %" PRId64 "\n", btrfs->sb->dev_item.generation);
4386
0
    tsk_fprintf(a_file, "Start Offset: 0x%" PRIx64 "\n", btrfs->sb->dev_item.start_offset);
4387
0
    btrfs_stat_output_hex(a_file, "Device UUID",
4388
0
            btrfs->sb->dev_item.device_uuid, sizeof(btrfs->sb->dev_item.device_uuid));
4389
0
    btrfs_stat_output_hex(a_file, "File System UUID",
4390
0
            btrfs->sb->dev_item.fs_uuid, sizeof(btrfs->sb->dev_item.fs_uuid));
4391
0
    tsk_fprintf(a_file, "\n");
4392
4393
0
    uint64_t default_subvol = btrfs_subvol_default(btrfs);
4394
0
    if (!default_subvol)
4395
0
        return 1;
4396
4397
0
    tsk_fprintf(a_file, "SUBVOLUME INFORMATION\n");
4398
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4399
0
    tsk_fprintf(a_file, "Default subvolume: 0x%" PRIx64 "%s\n",
4400
0
            default_subvol, default_subvol == BTRFS_OBJID_FS_TREE ? " (FS_TREE)" : "");
4401
0
    tsk_fprintf(a_file, "\n");
4402
4403
0
    for (btrfs_subvolumes_t::iterator it = btrfs->subvolumes->begin();
4404
0
            it != btrfs->subvolumes->end(); it++) {
4405
0
        inum = btrfs_subvol_root_inum(btrfs, it->first);
4406
0
        TSK_INUM_T vinum;
4407
0
        if (!btrfs_inum_real2virt_map(btrfs, it->first, inum, &vinum)) {
4408
0
            tsk_error_set_errstr2("btrfs_fsstat: mapping root inum of subvolume: 0x%" PRIx64, it->first);
4409
0
            return 1;
4410
0
        }
4411
4412
0
        tsk_fprintf(a_file, "Subvolume: 0x%" PRIx64 "%s\n",
4413
0
                it->first, it->first == BTRFS_OBJID_FS_TREE ? " (FS_TREE)" : "");
4414
0
        tsk_fprintf(a_file, "Root Directory Inode (real): %" PRIuINUM "\n", inum);
4415
0
        tsk_fprintf(a_file, "Root Directory Inode (virtual): %" PRIuINUM "\n", vinum);
4416
0
        tsk_fprintf(a_file, "Root address: 0x%" PRIx64 "\n", btrfs_subvol_tree_address(btrfs, it->first));
4417
0
        tsk_fprintf(a_file, "Inode count: %zd\n", it->second.real2virt_inums.size());
4418
0
        tsk_fprintf(a_file, "\n");
4419
0
    }
4420
4421
0
    tsk_fprintf(a_file, "CACHED CHUNK INFORMATION - LOG -> PHYS\n");
4422
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4423
4424
0
    for (btrfs_cached_chunks_t::iterator it = btrfs->chunks->log2phys.begin();
4425
0
            it != btrfs->chunks->log2phys.end(); it++) {
4426
0
        tsk_fprintf(a_file, "Logical Address: 0x%" PRIx64 "\n", it->source_address);
4427
0
        tsk_fprintf(a_file, "Size: 0x%" PRIx64 "\n", it->size);
4428
0
        tsk_fprintf(a_file, "Physical Address: 0x%" PRIx64 "\n", it->target_address);
4429
0
        tsk_fprintf(a_file, "\n");
4430
0
    }
4431
4432
0
    tsk_fprintf(a_file, "CACHED CHUNK INFORMATION - PHYS -> LOG\n");
4433
0
    tsk_fprintf(a_file, "--------------------------------------------\n");
4434
4435
0
    for (btrfs_cached_chunks_t::iterator it = btrfs->chunks->phys2log.begin();
4436
0
            it != btrfs->chunks->phys2log.end(); it++) {
4437
0
        if (it != btrfs->chunks->phys2log.begin())
4438
0
            tsk_fprintf(a_file, "\n");
4439
0
        tsk_fprintf(a_file, "Physical Address: 0x%" PRIx64 "\n", it->source_address);
4440
0
        tsk_fprintf(a_file, "Size: 0x%" PRIx64 "\n", it->size);
4441
0
        tsk_fprintf(a_file, "Logical Address: 0x%" PRIx64 "\n", it->target_address);
4442
0
    }
4443
4444
0
    return 0;
4445
0
}
4446
4447
4448
// use helper callback to output used blocks
4449
typedef struct {
4450
    FILE *file;
4451
    int index;
4452
} BTRFS_ISTAT_FILEWALK_CB_HELPER;
4453
4454
4455
static TSK_WALK_RET_ENUM
4456
btrfs_istat_filewalk_cb([[maybe_unused]] TSK_FS_FILE * a_fs_file, [[maybe_unused]] TSK_OFF_T a_off,
4457
    TSK_DADDR_T a_addr, [[maybe_unused]] char *a_buf, [[maybe_unused]] size_t a_len,
4458
    TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr)
4459
0
{
4460
    // skip resident or non-raw or blocks
4461
0
    if ((a_flags & TSK_FS_BLOCK_FLAG_RES) || !(a_flags & TSK_FS_BLOCK_FLAG_RAW))
4462
0
        return TSK_WALK_CONT;
4463
4464
0
    BTRFS_ISTAT_FILEWALK_CB_HELPER *helper = (BTRFS_ISTAT_FILEWALK_CB_HELPER*) a_ptr;
4465
4466
0
    tsk_fprintf(helper->file, "%" PRIuDADDR " ", a_addr);
4467
4468
0
    helper->index++;
4469
0
    if (helper->index == 8) {
4470
0
        tsk_fprintf(helper->file, "\n");
4471
0
        helper->index = 0;
4472
0
    }
4473
4474
0
    return TSK_WALK_CONT;
4475
0
}
4476
4477
4478
/**
4479
 * Prints a flag description for each set inode flag into a file.
4480
 * @param a_file file
4481
 * @param a_flags flags
4482
 */
4483
static void
4484
btrfs_istat_print_flags(FILE * a_file, uint64_t a_flags)
4485
0
{
4486
0
    static const char *general_flags[12] = {
4487
0
        "NODATASUM",
4488
0
        "NODATACOW",
4489
0
        "READONLY",
4490
0
        "NOCOMPRESS",
4491
0
        "PREALLOC",
4492
0
        "SYNC",
4493
0
        "IMMUTABLE",
4494
0
        "APPEND",
4495
0
        "NODUMP",
4496
0
        "NOATIME",
4497
0
        "DIRSYNC",
4498
0
        "COMPRESS"
4499
0
    };
4500
4501
0
    for (int i = 0; i < 64; i++) {
4502
0
        if (!(a_flags & (1ULL << i)))
4503
0
            continue;
4504
4505
        // handle general/special/unknown case
4506
0
        if (i < 12) {
4507
0
            tsk_fprintf(a_file, "%s\n", general_flags[i]);
4508
0
            continue;
4509
0
        }
4510
0
        if (i == 31) {
4511
0
            tsk_fprintf(a_file, "ROOT_ITEM_INIT\n");
4512
0
            continue;
4513
0
        }
4514
0
        tsk_fprintf(a_file, "unknown (1 << %d)\n", i);
4515
0
    }
4516
0
}
4517
4518
4519
/**
4520
 * Prints information about an inode into a file.
4521
 * @param a_fs FS info
4522
 * @param istat_flags (ignored)
4523
 * @param a_file file
4524
 * @param a_inum virtual inum
4525
 * @param a_numblock number of blocks in file to force print (can go beyond file size)
4526
 * @param a_sec_skew clock skew in seconds to also print times in
4527
 * @return 0 if no error occured, otherwise 1
4528
 */
4529
static uint8_t
4530
btrfs_istat(TSK_FS_INFO * a_fs, [[maybe_unused]] TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * a_file, TSK_INUM_T a_inum,
4531
            [[maybe_unused]] TSK_DADDR_T a_numblock, int32_t a_sec_skew)
4532
0
{
4533
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
4534
0
    char ls[12];
4535
0
    char time_buffer[128];
4536
4537
    // clean up any error messages that are lying around
4538
0
    tsk_error_reset();
4539
4540
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> file{
4541
0
        tsk_fs_file_open_meta(a_fs, NULL, a_inum),
4542
0
        tsk_fs_file_close
4543
0
    };
4544
4545
0
    if (!file) {
4546
0
        return 1;
4547
0
    }
4548
4549
0
    TSK_FS_META *meta = file->meta;
4550
4551
0
    bool normal_inode = a_inum <= (a_fs->last_inum - BTRFS_VINUM_COUNT_SPECIAL);
4552
0
    BTRFS_INODE_ITEM *ii = (BTRFS_INODE_ITEM*) meta->content_ptr;
4553
4554
4555
0
    tsk_fprintf(a_file, "Inode (virtual): %" PRIuINUM "\n", a_inum);
4556
4557
0
    if (normal_inode) {
4558
0
        uint64_t subvol;
4559
0
        TSK_INUM_T inum;
4560
0
        if (!btrfs_inum_virt2real_map(btrfs, a_inum, &subvol, &inum)) {
4561
0
            return 1;
4562
0
        }
4563
4564
0
        tsk_fprintf(a_file, "Subvolume: 0x%" PRIx64 "\n", subvol);
4565
0
        tsk_fprintf(a_file, "Inode (real): %" PRIuINUM "\n", inum);
4566
0
    }
4567
0
    tsk_fprintf(a_file, "Allocated: %s\n",
4568
0
            meta->flags & TSK_FS_META_FLAG_ALLOC ? "yes" : "no");
4569
0
    tsk_fprintf(a_file, "Compressed: %s\n",
4570
0
            meta->flags & TSK_FS_META_FLAG_COMP ? "yes" : "no");
4571
4572
0
    if (normal_inode)
4573
0
        tsk_fprintf(a_file, "Generation: %" PRId64 "\n", ii->generation);
4574
0
    if (meta->link)
4575
0
        tsk_fprintf(a_file, "Symbolic Link to: %s\n", meta->link);
4576
0
    tsk_fprintf(a_file, "UID / GID: %" PRIuUID " / %" PRIuGID "\n",
4577
0
            meta->uid, meta->gid);
4578
4579
0
    tsk_fs_meta_make_ls(meta, ls, sizeof(ls));
4580
0
    tsk_fprintf(a_file, "Mode: %s\n", ls);
4581
4582
    // device ids
4583
0
    if (normal_inode && (meta->type == TSK_FS_META_TYPE_BLK || meta->type == TSK_FS_META_TYPE_CHR))
4584
0
        tsk_fprintf(a_file, "Device Major: %" PRIu64 "   Minor: %" PRIu64 "\n",
4585
0
                ii->rdev >> 20, ii->rdev & 0xFFFFF);
4586
4587
0
    tsk_fprintf(a_file, "Size: %zu\n", meta->size);
4588
0
    tsk_fprintf(a_file, "Num of Links: %d\n", meta->nlink);
4589
0
    tsk_fprintf(a_file, "\n");
4590
4591
4592
    // print flags
4593
0
    tsk_fprintf(a_file, "Flags:\n");
4594
0
    if (normal_inode)
4595
0
        btrfs_istat_print_flags(a_file, ii->flags);
4596
0
    tsk_fprintf(a_file, "\n");
4597
4598
4599
    // print times
4600
0
    if (a_sec_skew) {
4601
0
        tsk_fprintf(a_file, "Adjusted Inode Times:\n");
4602
4603
0
        if (meta->atime)
4604
0
            meta->atime -= a_sec_skew;
4605
0
        if (meta->ctime)
4606
0
            meta->ctime -= a_sec_skew;
4607
0
        if (meta->mtime)
4608
0
            meta->mtime -= a_sec_skew;
4609
4610
0
        tsk_fprintf(a_file, "Accessed:\t%s\n",
4611
0
                tsk_fs_time_to_str_subsecs(meta->atime, meta->atime_nano, time_buffer));
4612
0
        tsk_fprintf(a_file, "Created:\t%s\n",
4613
0
                tsk_fs_time_to_str_subsecs(meta->ctime, meta->ctime_nano, time_buffer));
4614
0
        tsk_fprintf(a_file, "Modified:\t%s\n",
4615
0
                tsk_fs_time_to_str_subsecs(meta->mtime, meta->mtime_nano, time_buffer));
4616
4617
0
        if (meta->atime)
4618
0
            meta->atime += a_sec_skew;
4619
0
        if (meta->ctime)
4620
0
            meta->ctime += a_sec_skew;
4621
0
        if (meta->mtime)
4622
0
            meta->mtime += a_sec_skew;
4623
4624
0
        tsk_fprintf(a_file, "\n");
4625
0
        tsk_fprintf(a_file, "Original Inode Times:\n");
4626
0
    } else {
4627
0
        tsk_fprintf(a_file, "Inode Times:\n");
4628
0
    }
4629
4630
0
    tsk_fprintf(a_file, "Accessed:\t%s\n",
4631
0
            tsk_fs_time_to_str_subsecs(meta->atime, meta->atime_nano, time_buffer));
4632
0
    tsk_fprintf(a_file, "Created:\t%s\n",
4633
0
            tsk_fs_time_to_str_subsecs(meta->ctime, meta->ctime_nano, time_buffer));
4634
0
    tsk_fprintf(a_file, "Modified:\t%s\n",
4635
0
            tsk_fs_time_to_str_subsecs(meta->mtime, meta->mtime_nano, time_buffer));
4636
0
    tsk_fprintf(a_file, "\n");
4637
4638
4639
    // print extended attributes
4640
0
    tsk_fprintf(a_file, "Extended attributes:\n");
4641
0
    int attribute_count = tsk_fs_file_attr_getsize(file.get());
4642
0
    for (int i = 0; i < attribute_count; i++) {
4643
0
        const TSK_FS_ATTR *attr = tsk_fs_file_attr_get_idx(file.get(), i);
4644
0
        if (!attr) {
4645
0
            return 1;
4646
0
        }
4647
0
        if (attr->type == TSK_FS_ATTR_TYPE_UNIX_XATTR)
4648
0
            tsk_fprintf(a_file, "%s (%d bytes)\n", attr->name, attr->size);
4649
0
    }
4650
0
    tsk_fprintf(a_file, "\n");
4651
4652
4653
0
    if (meta->type == TSK_FS_META_TYPE_REG || meta->type == TSK_FS_META_TYPE_VIRT) {
4654
        // print blocks
4655
0
        tsk_fprintf(a_file, "Blocks:\n");
4656
4657
0
        BTRFS_ISTAT_FILEWALK_CB_HELPER helper;
4658
0
        helper.file = a_file;
4659
0
        helper.index = 0;
4660
4661
0
        if (tsk_fs_file_walk(file.get(), TSK_FS_FILE_WALK_FLAG_AONLY, btrfs_istat_filewalk_cb, &helper)) {
4662
0
            return 1;
4663
0
        }
4664
0
        if (helper.index)
4665
0
            tsk_fprintf(a_file, "\n");
4666
0
    }
4667
4668
0
    return 0;
4669
0
}
4670
4671
/*
4672
 * unimplemented functions
4673
 */
4674
4675
static uint8_t
4676
btrfs_jentry_walk([[maybe_unused]] TSK_FS_INFO * a_fs, [[maybe_unused]] int a_entry,
4677
    [[maybe_unused]] TSK_FS_JENTRY_WALK_CB a_cb, [[maybe_unused]] void *a_fn)
4678
0
{
4679
0
    btrfs_error(TSK_ERR_FS_UNSUPFUNC, "Journal support for Btrfs is not implemented");
4680
0
    return 1;
4681
0
}
4682
4683
static uint8_t
4684
btrfs_jblk_walk([[maybe_unused]] TSK_FS_INFO * a_fs, [[maybe_unused]] TSK_DADDR_T a_daddr,
4685
                [[maybe_unused]] TSK_DADDR_T a_daddrt, [[maybe_unused]] int a_entry, [[maybe_unused]] TSK_FS_JBLK_WALK_CB a_cb,
4686
                [[maybe_unused]] void *a_fn)
4687
0
{
4688
0
    btrfs_error(TSK_ERR_FS_UNSUPFUNC, "Journal support for Btrfs is not implemented");
4689
0
    return 1;
4690
0
}
4691
4692
static uint8_t
4693
btrfs_jopen([[maybe_unused]] TSK_FS_INFO * a_fs, [[maybe_unused]] TSK_INUM_T a_inum)
4694
0
{
4695
0
    btrfs_error(TSK_ERR_FS_UNSUPFUNC, "Journal support for Btrfs is not implemented");
4696
0
    return 1;
4697
0
}
4698
4699
static uint8_t
4700
btrfs_fscheck([[maybe_unused]] TSK_FS_INFO * a_fs, [[maybe_unused]] FILE * a_file)
4701
0
{
4702
0
    btrfs_error(TSK_ERR_FS_UNSUPFUNC, "fscheck not implemented yet for Btrfs");
4703
0
    return 1;
4704
0
}
4705
4706
4707
4708
/*
4709
 * tree printing
4710
 */
4711
4712
4713
#ifdef BTRFS_DEBUG
4714
static void
4715
btrfs_tree_dump(BTRFS_INFO * a_btrfs, const TSK_DADDR_T a_address,
4716
    const char *a_description)
4717
{
4718
    BTRFS_TREENODE *node = NULL;
4719
4720
    btrfs_debug("############## dumping tree '%s' at address 0x%" PRIxDADDR " ##############\n", a_description, a_address);
4721
    if (!btrfs_treenode_push(a_btrfs, &node, a_address, BTRFS_FIRST)) {
4722
        tsk_error_reset();
4723
        btrfs_debug("could not dump treelevel at address 0x%" PRIxDADDR "\n", a_address);
4724
        return;
4725
    }
4726
4727
    btrfs_tree_header_debugprint(&node->header);
4728
4729
    for (unsigned int i = 0; i < node->header.number_of_items; i++) {
4730
        btrfs_debug("tree: ####### node %d #######\n", node->index);
4731
        btrfs_key_debugprint(&node->key);
4732
4733
        if (node->header.level) {
4734
            btrfs_key_pointer_rest_debugprint(&node->kp);
4735
        } else {
4736
            btrfs_item_rest_debugprint(&node->item);
4737
4738
            uint8_t *data = btrfs_treenode_itemdata(node);
4739
            uint32_t len = btrfs_treenode_itemsize(node);
4740
4741
            switch (node->key.item_type) {
4742
            case BTRFS_ITEM_TYPE_INODE_ITEM: {
4743
                BTRFS_INODE_ITEM ii;
4744
                btrfs_inode_rawparse(data, &ii);
4745
                btrfs_inode_debugprint(&ii);
4746
                break;
4747
            }
4748
            case BTRFS_ITEM_TYPE_INODE_REF: {
4749
                BTRFS_INODE_REF *testref = btrfs_inode_ref_fromraw(data, len);
4750
                btrfs_inode_ref_debugprint(testref);
4751
                btrfs_inode_ref_free(testref);
4752
                break;
4753
            }
4754
            case BTRFS_ITEM_TYPE_XATTR_ITEM: {
4755
                BTRFS_DIR_ENTRY *de = btrfs_dir_entry_fromraw(data, len);
4756
                btrfs_dir_entry_debugprint(de);
4757
                btrfs_dir_entry_free(de);
4758
                break;
4759
            }
4760
            case BTRFS_ITEM_TYPE_DIR_ITEM:
4761
            case BTRFS_ITEM_TYPE_DIR_INDEX: {
4762
                BTRFS_DIR_ENTRY *de = btrfs_dir_entry_fromraw(data, len);
4763
                btrfs_dir_entry_debugprint(de);
4764
                btrfs_dir_entry_free(de);
4765
                break;
4766
            }
4767
            case BTRFS_ITEM_TYPE_EXTENT_DATA: {
4768
                BTRFS_EXTENT_DATA *ed = btrfs_extent_data_fromraw(data, len);
4769
                if (ed) {
4770
                    btrfs_extent_data_debugprint(ed);
4771
                    btrfs_extent_data_free(ed);
4772
                } else {
4773
                    btrfs_debug("error while deriving EXTENT_DATA item\n");
4774
                }
4775
                break;
4776
            }
4777
            case BTRFS_ITEM_TYPE_ROOT_ITEM: {
4778
                BTRFS_ROOT_ITEM ri;
4779
                btrfs_root_item_rawparse(data, &ri);
4780
                btrfs_root_item_debugprint(&ri);
4781
                break;
4782
            }
4783
            case BTRFS_ITEM_TYPE_EXTENT_ITEM:
4784
            case BTRFS_ITEM_TYPE_METADATA_ITEM: {
4785
                BTRFS_EXTENT_ITEM ei;
4786
                btrfs_extent_item_rawparse(data, &ei);
4787
                btrfs_extent_item_debugprint(&ei);
4788
                break;
4789
            }
4790
            case BTRFS_ITEM_TYPE_DEV_ITEM: {
4791
                BTRFS_DEV_ITEM di;
4792
                btrfs_dev_item_rawparse(data, &di);
4793
                btrfs_dev_item_debugprint(&di);
4794
                break;
4795
            }
4796
            case BTRFS_ITEM_TYPE_CHUNK_ITEM:
4797
                BTRFS_CHUNK_ITEM *ci = btrfs_chunk_item_fromraw(data);
4798
                btrfs_chunk_item_debugprint(ci);
4799
                btrfs_chunk_item_free(ci);
4800
                break;
4801
            }
4802
        }
4803
4804
        btrfs_treenode_set_index(node, false, 1);
4805
    }
4806
4807
    // if not leaf, recursively print subtrees
4808
    if (node->header.level) {
4809
        btrfs_treenode_set_index(node, true, 0);
4810
        for (unsigned int i = 0; i < node->header.number_of_items; i++) {
4811
            char text[128];
4812
            snprintf(text, sizeof(text), "%s - subtree %d", a_description, node->index);
4813
            btrfs_tree_dump(a_btrfs, node->kp.block_number, text);
4814
            btrfs_treenode_set_index(node, false, 1);
4815
        }
4816
    }
4817
4818
    btrfs_treenode_free(node);
4819
}
4820
#endif
4821
4822
4823
4824
/*
4825
 * open/close filesystem
4826
 */
4827
4828
4829
/**
4830
 * Closes the Btrfs filesystem
4831
 * @param a_fs FS info
4832
 */
4833
static void
4834
btrfs_close(TSK_FS_INFO * a_fs)
4835
0
{
4836
0
    if (!a_fs)
4837
0
        return;
4838
4839
0
    BTRFS_INFO *btrfs = (BTRFS_INFO*) a_fs;
4840
4841
0
    a_fs->tag = 0;
4842
4843
    // treenode cache
4844
0
    tsk_deinit_lock(&btrfs->treenode_cache_lock);
4845
0
    if (btrfs->treenode_cache_map) {
4846
0
        for (btrfs_treenode_cache_map_t::iterator map_it = btrfs->treenode_cache_map->begin();
4847
0
                map_it != btrfs->treenode_cache_map->end(); map_it++) {
4848
0
            delete[] map_it->second;
4849
0
        }
4850
0
        delete btrfs->treenode_cache_map;
4851
0
    }
4852
0
    delete btrfs->treenode_cache_lru;
4853
4854
0
    delete btrfs->sb;
4855
0
    delete btrfs->chunks;
4856
0
    delete btrfs->subvolumes;
4857
0
    delete btrfs->virt2real_inums;
4858
4859
0
    tsk_fs_free(a_fs);
4860
0
}
4861
4862
4863
#ifdef BTRFS_DEBUG
4864
static TSK_WALK_RET_ENUM
4865
btrfs_blockwalk_test_cb(const TSK_FS_BLOCK * a_block, void *a_ptr)
4866
{
4867
    // only print blocks which are not: raw and unalloced
4868
    if (a_block->flags != (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_AONLY | TSK_FS_BLOCK_FLAG_RAW | TSK_FS_BLOCK_FLAG_UNALLOC))
4869
        btrfs_debug("block 0x%016" PRIxDADDR ": 0x%03x\n", a_block->addr, a_block->flags);
4870
    return TSK_WALK_CONT;
4871
}
4872
4873
4874
#if 0
4875
static TSK_WALK_RET_ENUM
4876
btrfs_inodewalk_test_cb(TSK_FS_FILE * a_file, void *a_ptr)
4877
{
4878
    btrfs_debug("inode %" PRIuINUM ": 0x%x\n", a_file->meta->addr, a_file->meta->flags);
4879
    return TSK_WALK_CONT;
4880
}
4881
#endif
4882
#endif
4883
4884
4885
/**
4886
 * Tries to open a Btrfs filesystem
4887
 * @param img_info image info
4888
 * @param offset byte offset within image
4889
 * @param ftype FS type
4890
 * @param pass - ignored
4891
 * @return pointer to a Btrfs filesystem if no error occured, otherwise NULL
4892
 */
4893
TSK_FS_INFO *
4894
btrfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
4895
           TSK_FS_TYPE_ENUM ftype, [[maybe_unused]] const char *pass, uint8_t test)
4896
0
{
4897
    // clean up any error messages that are lying around
4898
0
    tsk_error_reset();
4899
4900
    // check FS type
4901
0
    if (!TSK_FS_TYPE_ISBTRFS(ftype)) {
4902
0
        btrfs_error(TSK_ERR_FS_ARG, "Invalid FS Type in btrfs_open");
4903
0
        return NULL;
4904
0
    }
4905
4906
0
    const auto deleter = [](BTRFS_INFO* btrfs) {
4907
0
      tsk_fs_close(&btrfs->fs_info);
4908
0
    };
4909
4910
0
    std::unique_ptr<BTRFS_INFO, decltype(deleter)> btrfs{
4911
0
      (BTRFS_INFO*) tsk_fs_malloc(sizeof(BTRFS_INFO)),
4912
0
      deleter
4913
0
    };
4914
4915
    // create struct (mem is zeroed!)
4916
0
    if (!btrfs) {
4917
0
        return NULL;
4918
0
    }
4919
4920
    // init struct
4921
0
    TSK_FS_INFO *fs = &btrfs->fs_info;
4922
4923
0
    btrfs->test = test;
4924
#ifdef BTRFS_DEBUG
4925
    btrfs->test = 1;
4926
#endif
4927
0
    fs->img_info = img_info;
4928
0
    fs->offset = offset;
4929
0
    fs->ftype = ftype;
4930
0
    fs->dev_bsize = fs->img_info->sector_size;
4931
4932
0
    fs->tag = TSK_FS_INFO_TAG;
4933
0
    fs->endian = BTRFS_ENDIAN;
4934
0
    fs->flags = TSK_FS_INFO_FLAG_HAVE_NANOSEC;
4935
0
    fs->duname = "Block";
4936
4937
0
    fs->block_getflags = btrfs_block_getflags;
4938
0
    fs->block_walk = btrfs_block_walk;
4939
4940
0
    fs->file_add_meta = btrfs_file_add_meta;
4941
0
    fs->inode_walk = btrfs_inode_walk;
4942
4943
0
    fs->dir_open_meta = btrfs_dir_open_meta;
4944
0
    fs->name_cmp = btrfs_name_cmp;
4945
4946
0
    fs->get_default_attr_type = btrfs_get_default_attr_type;
4947
0
    fs->load_attrs = btrfs_load_attrs;
4948
4949
0
    fs->fsstat = btrfs_fsstat;
4950
0
    fs->istat = btrfs_istat;
4951
4952
0
    fs->close = btrfs_close;
4953
4954
    // unimplemented functions
4955
0
    fs->jblk_walk = btrfs_jblk_walk;
4956
0
    fs->jentry_walk = btrfs_jentry_walk;
4957
0
    fs->jopen = btrfs_jopen;
4958
0
    fs->fscheck = btrfs_fscheck;
4959
4960
    // derive superblock
4961
0
    if (!btrfs_superblock_search(btrfs.get())) {
4962
0
        btrfs_error(TSK_ERR_FS_MAGIC, "No valid superblock found in btrfs_open");
4963
0
        if (tsk_verbose)
4964
0
            tsk_fprintf(stderr, "btrfs_open: No valid superblock found\n");
4965
0
        return NULL;
4966
0
    }
4967
#ifdef BTRFS_DEBUG
4968
    btrfs_superblock_debugprint(btrfs->sb);
4969
#endif
4970
0
    if (tsk_verbose)
4971
0
        tsk_fprintf(stderr, "btrfs_open: Found valid superblock having generation: %" PRId64 "\n",
4972
0
                btrfs->sb->generation);
4973
4974
4975
    // ensure we support all features
4976
0
    uint64_t incompat_flags_unsupported =
4977
0
            btrfs->sb->incompat_flags & ~BTRFS_SUPERBLOCK_INCOMPAT_FLAGS_SUPPORTED;
4978
0
    if (incompat_flags_unsupported) {
4979
0
        btrfs_debug("Unsupported superblock incompat_flags:\n");
4980
#ifdef BTRFS_DEBUG
4981
        btrfs_fsstat_print_incompat_flags(stdout, incompat_flags_unsupported);
4982
#endif
4983
0
        btrfs_error(TSK_ERR_FS_MAGIC, "Unsupported superblock incompat_flags: 0x%" PRIx64,
4984
0
                incompat_flags_unsupported);
4985
0
        if (tsk_verbose) {
4986
0
            tsk_fprintf(stderr, "btrfs_open: Unsupported superblock incompat_flags:\n");
4987
0
            btrfs_fsstat_print_incompat_flags(stderr, incompat_flags_unsupported);
4988
0
        }
4989
0
        return NULL;
4990
0
    }
4991
4992
0
    fs->block_size = btrfs->sb->sectorsize;
4993
0
    fs->block_count = btrfs->sb->dev_item.total_bytes / fs->block_size;
4994
0
    fs->first_block = 0;
4995
0
    fs->last_block = fs->block_count - 1;
4996
4997
    // prevent reading after image end in case of incomplete image
4998
    // was: fs->last_block_act = MIN(fs->last_block, (fs->img_info->size - fs->offset) / fs->block_size - 1);
4999
0
    fs->last_block_act = (fs->img_info->size - fs->offset) / fs->block_size - 1;
5000
0
    if (fs->last_block_act > fs->last_block) {
5001
0
        fs->last_block_act = fs->last_block;
5002
0
    }
5003
5004
0
    fs->fs_id_used = sizeof(btrfs->sb->uuid);
5005
0
    memcpy(fs->fs_id, btrfs->sb->uuid, fs->fs_id_used);
5006
5007
5008
    // init treenode cache
5009
0
    tsk_init_lock(&btrfs->treenode_cache_lock);
5010
0
    btrfs->treenode_cache_map = new btrfs_treenode_cache_map_t;
5011
0
    btrfs->treenode_cache_lru = new btrfs_treenode_cache_lru_t;
5012
5013
5014
    // init physical <-> logical address mapping
5015
    // step 1 - parse superblock system chunks for initial mapping
5016
0
    btrfs->chunks = btrfs_chunks_from_superblock(btrfs.get());
5017
5018
    // step 2 - based on this, replace it with chunk tree mapping
5019
0
    BTRFS_CACHED_CHUNK_MAPPING *old_chunks = btrfs->chunks;
5020
0
    btrfs->chunks = btrfs_chunks_from_chunktree(btrfs.get());
5021
0
    if (!btrfs->chunks) {
5022
0
        tsk_error_errstr2_concat("- btrfs_open: parsing chunk tree");
5023
0
        return NULL;
5024
0
    }
5025
0
    delete old_chunks;
5026
5027
5028
    // init virtual <-> real inum mapping
5029
0
    btrfs->subvolumes = new btrfs_subvolumes_t;
5030
0
    btrfs->virt2real_inums = new btrfs_virt2real_inums_t;
5031
0
    if (!btrfs_parse_subvolumes(btrfs.get())) {
5032
0
        tsk_error_errstr2_concat("- btrfs_open: parsing all subvolumes");
5033
0
        return NULL;
5034
0
    }
5035
5036
    // set root inum (using FS_TREE instead of possible custom default subvol)
5037
0
    if (!btrfs_inum_real2virt_map(btrfs.get(), BTRFS_OBJID_FS_TREE, btrfs_subvol_root_inum(btrfs.get(), BTRFS_OBJID_FS_TREE), &fs->root_inum)) {
5038
0
        tsk_error_set_errstr2("btrfs_open: mapping root inum");
5039
0
        return NULL;
5040
0
    }
5041
5042
0
    fs->inum_count = btrfs->virt2real_inums->size() + BTRFS_VINUM_COUNT_SPECIAL;
5043
0
    fs->first_inum = 0;
5044
0
    fs->last_inum = fs->inum_count - 1;
5045
5046
    // derive extent tree root node address
5047
0
    if (!btrfs_root_tree_derive_subtree_address(btrfs.get(), BTRFS_OBJID_EXTENT_TREE, &btrfs->extent_tree_root_node_address)) {
5048
0
        return NULL;
5049
0
    }
5050
5051
0
    if (tsk_verbose)
5052
0
        tsk_fprintf(stderr, "btrfs_open: SB mirror: %d, node size: %ld block size: %d, blocks: %p virtual inodes: %lud subvols: %zd, label: '%s'\n",
5053
0
                btrfs->sb_mirror_index, btrfs->sb->nodesize, fs->block_size, fs->block_count, fs->inum_count, btrfs->subvolumes->size(), btrfs->sb->label);
5054
5055
#ifdef BTRFS_DEBUG
5056
    // debug parsing some trees
5057
    btrfs_tree_dump(btrfs, btrfs->sb->root_tree_root, "root tree");
5058
    btrfs_tree_dump(btrfs, btrfs->extent_tree_root_node_address, "extent tree");
5059
    btrfs_tree_dump(btrfs, btrfs->sb->chunk_tree_root, "chunk tree");
5060
    if (btrfs->sb->log_tree_root)
5061
        btrfs_tree_dump(btrfs, btrfs->sb->log_tree_root, "log tree");
5062
5063
    uint64_t tmp_tree_root;
5064
5065
    btrfs_root_tree_derive_subtree_address(btrfs, 0x04, &tmp_tree_root);
5066
    btrfs_tree_dump(btrfs, tmp_tree_root, "device tree");
5067
5068
    btrfs_root_tree_derive_subtree_address(btrfs, 0x07, &tmp_tree_root);
5069
    btrfs_tree_dump(btrfs, tmp_tree_root, "checksum tree");
5070
5071
    // output subvolumes
5072
    for (btrfs_subvolumes_t::iterator it = btrfs->subvolumes->begin(); it != btrfs->subvolumes->end(); it++) {
5073
        char subtree_text[30];
5074
        snprintf(subtree_text, sizeof(subtree_text), "subvolume 0x%" PRIx64, it->first);
5075
        btrfs_tree_dump(btrfs, it->second.ri.root_node_block_number, it->first == BTRFS_OBJID_FS_TREE ? "FS tree" : subtree_text);
5076
    }
5077
5078
    // output allocation flags of all blocks which are not: raw and unalloced
5079
    btrfs_debug("##### blocks which are not: raw and unalloced #####\n");
5080
    TSK_FS_BLOCK_WALK_FLAG_ENUM block_walk_test_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) (TSK_FS_BLOCK_WALK_FLAG_ALLOC | TSK_FS_BLOCK_WALK_FLAG_UNALLOC | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META | TSK_FS_BLOCK_WALK_FLAG_AONLY);
5081
    tsk_fs_block_walk(fs, fs->first_block, fs->last_block, block_walk_test_flags, btrfs_blockwalk_test_cb, NULL);
5082
5083
#if 0
5084
    // output meta flags of all inodes
5085
    TSK_FS_META_FLAG_ENUM inode_walk_test_flags = (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
5086
    tsk_fs_meta_walk(fs, fs->first_inum, fs->last_inum, inode_walk_test_flags, btrfs_inodewalk_test_cb, NULL);
5087
#endif
5088
5089
    // inum mapping virt->real
5090
    btrfs_debug("##### inum mapping virt->real #####\n");
5091
    for (uint64_t vinum = 0; vinum < btrfs->virt2real_inums->size(); vinum++)
5092
        btrfs_debug("%4" PRId64 " -> 0x%4" PRIx64 " 0x%4" PRIx64 "\n", vinum, (*btrfs->virt2real_inums)[vinum].first, (*btrfs->virt2real_inums)[vinum].second);
5093
5094
    // inum mapping real->virt
5095
    btrfs_debug("##### inum mapping real->virt #####\n");
5096
    for (btrfs_subvolumes_t::iterator it = btrfs->subvolumes->begin(); it != btrfs->subvolumes->end(); it++)
5097
        for (btrfs_real2virt_inums_t::iterator ii = it->second.real2virt_inums.begin(); ii != it->second.real2virt_inums.end(); ii++)
5098
            btrfs_debug("0x%4" PRIx64 " 0x%4" PRIx64 " -> %4" PRId64 "\n", it->first, ii->first, ii->second);
5099
#endif
5100
5101
0
    return (TSK_FS_INFO*) btrfs.release();
5102
0
}