Coverage Report

Created: 2025-07-23 06:45

/src/sleuthkit/tsk/fs/ext2fs_journal.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
   @@@ UNALLOC only if seq is less - alloc can be less than block if it wrapped around ...
3
** ext2fs_journal
4
** The Sleuth Kit
5
**
6
** Journaling code for TSK_FS_INFO_TYPE_EXT_3 image
7
**
8
** Brian Carrier [carrier <at> sleuthkit [dot] org]
9
** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
10
** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved
11
**
12
**
13
** This software is distributed under the Common Public License 1.0
14
**
15
*/
16
17
/** \file ext2fs_journal.c
18
 * Contains the internal TSK Ext3 journal walking code.
19
 */
20
21
#include "tsk_fs_i.h"
22
#include "tsk_ext2fs.h"
23
24
25
26
/* Everything in the journal is in big endian */
27
#define big_tsk_getu32(x) \
28
0
  (uint32_t)((((uint8_t *)x)[3] <<  0) + \
29
0
  (((uint8_t *)x)[2] <<  8) + \
30
0
  (((uint8_t *)x)[1] << 16) + \
31
0
  (((uint8_t *)x)[0] << 24) )
32
33
34
/*
35
 *
36
 */
37
38
static TSK_WALK_RET_ENUM
39
load_sb_action(
40
  TSK_FS_FILE * fs_file,
41
  [[maybe_unused]] TSK_OFF_T a_off,
42
  [[maybe_unused]] TSK_DADDR_T addr,
43
  char *buf,
44
  size_t size,
45
  [[maybe_unused]] TSK_FS_BLOCK_FLAG_ENUM flags,
46
  [[maybe_unused]] void *ptr)
47
0
{
48
0
    TSK_FS_INFO *fs = fs_file->fs_info;
49
0
    ext2fs_journ_sb *sb;
50
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
51
0
    EXT2FS_JINFO *jinfo = ext2fs->jinfo;
52
53
0
    if (size < 1024) {
54
0
        tsk_error_reset();
55
0
        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
56
0
        tsk_error_set_errstr
57
0
            ("FS block size is less than 1024, not supported in journal yet");
58
0
        return TSK_WALK_ERROR;
59
0
    }
60
61
0
    sb = (ext2fs_journ_sb *) buf;
62
63
0
    if (big_tsk_getu32(sb->magic) != EXT2_JMAGIC) {
64
0
        tsk_error_reset();
65
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
66
0
        tsk_error_set_errstr("Journal inode %" PRIuINUM
67
0
            " does not have a valid magic value: %" PRIx32,
68
0
            jinfo->j_inum, big_tsk_getu32(sb->magic));
69
0
        return TSK_WALK_ERROR;
70
0
    }
71
72
0
    jinfo->bsize = big_tsk_getu32(sb->bsize);
73
0
    jinfo->first_block = big_tsk_getu32(sb->first_blk);
74
0
    jinfo->last_block = big_tsk_getu32(sb->num_blk) - 1;
75
0
    jinfo->start_blk = big_tsk_getu32(sb->start_blk);
76
0
    jinfo->start_seq = big_tsk_getu32(sb->start_seq);
77
78
0
    return TSK_WALK_STOP;
79
0
}
80
81
/* Place journal data in *fs
82
 *
83
 * Return 0 on success and 1 on error
84
 * */
85
uint8_t
86
ext2fs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
87
0
{
88
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
89
0
    EXT2FS_JINFO *jinfo;
90
91
    // clean up any error messages that are lying around
92
0
    tsk_error_reset();
93
94
0
    if (!fs) {
95
0
        tsk_error_reset();
96
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
97
0
        tsk_error_set_errstr("ext2fs_jopen: fs is null");
98
0
        return 1;
99
0
    }
100
101
0
    ext2fs->jinfo = jinfo =
102
0
        (EXT2FS_JINFO *) tsk_malloc(sizeof(EXT2FS_JINFO));
103
0
    if (jinfo == NULL) {
104
0
        return 1;
105
0
    }
106
0
    jinfo->j_inum = inum;
107
108
0
    jinfo->fs_file = tsk_fs_file_open_meta(fs, NULL, inum);
109
0
    if (!jinfo->fs_file) {
110
0
        free(jinfo);
111
0
        return 1;
112
//      error("error finding journal inode %" PRIu32, inum);
113
0
    }
114
115
0
    if (tsk_fs_file_walk(jinfo->fs_file, TSK_FS_FILE_WALK_FLAG_NONE, load_sb_action, NULL)) {
116
0
        tsk_error_reset();
117
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
118
0
        tsk_error_set_errstr("Error loading ext3 journal");
119
0
        tsk_fs_file_close(jinfo->fs_file);
120
0
        free(jinfo);
121
0
        return 1;
122
0
    }
123
124
0
    if (tsk_verbose)
125
0
        tsk_fprintf(stderr,
126
0
            "journal opened at inode %" PRIuINUM " bsize: %" PRIu32
127
0
            " First JBlk: %" PRIuDADDR " Last JBlk: %" PRIuDADDR "\n",
128
0
            inum, jinfo->bsize, jinfo->first_block, jinfo->last_block);
129
130
0
    return 0;
131
0
}
132
133
134
/* Limitations: does not use the action or any flags
135
 *
136
 * return 0 on success and 1 on error
137
 * */
138
uint8_t
139
ext2fs_jentry_walk(
140
  TSK_FS_INFO * fs,
141
  [[maybe_unused]] int flags,
142
  [[maybe_unused]] TSK_FS_JENTRY_WALK_CB action,
143
  [[maybe_unused]] void *ptr)
144
0
{
145
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
146
0
    EXT2FS_JINFO *jinfo = ext2fs->jinfo;
147
0
    char *journ;
148
0
    TSK_FS_LOAD_FILE buf1;
149
0
    TSK_DADDR_T i;
150
//    int b_desc_seen = 0;
151
0
    ext2fs_journ_sb *journ_sb = NULL;
152
0
    ext4fs_journ_commit_head *commit_head;
153
154
    // clean up any error messages that are lying around
155
0
    tsk_error_reset();
156
157
158
0
    if ((jinfo == NULL) || (jinfo->fs_file == NULL)
159
0
        || (jinfo->fs_file->meta == NULL)) {
160
0
        tsk_error_reset();
161
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
162
0
        tsk_error_set_errstr("ext2fs_jentry_walk: journal is not open");
163
0
        return 1;
164
0
    }
165
166
0
    if ((TSK_DADDR_T)jinfo->fs_file->meta->size !=
167
0
        (jinfo->last_block + 1) * jinfo->bsize) {
168
0
        tsk_error_reset();
169
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
170
0
        tsk_error_set_errstr
171
0
            ("ext2fs_jentry_walk: journal file size is different from \nsize reported in journal super block");
172
0
        return 1;
173
0
    }
174
175
    /* Load the journal into a buffer */
176
0
    buf1.left = buf1.total = (size_t) jinfo->fs_file->meta->size;
177
0
    journ = buf1.cur = buf1.base = (char*) tsk_malloc(buf1.left);
178
0
    if (journ == NULL) {
179
0
        return 1;
180
0
    }
181
182
0
    if (tsk_fs_file_walk(jinfo->fs_file, TSK_FS_FILE_WALK_FLAG_NONE,
183
0
      tsk_fs_load_file_action, (void *) &buf1)) {
184
0
        free(journ);
185
0
        return 1;
186
0
    }
187
188
0
    if (buf1.left > 0) {
189
0
        tsk_error_reset();
190
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
191
0
        tsk_error_set_errstr
192
0
            ("ext2fs_jentry_walk: Buffer not fully copied");
193
0
        free(journ);
194
0
        return 1;
195
0
    }
196
197
198
    /* Process the journal
199
     * Cycle through each block
200
     */
201
0
    tsk_printf("JBlk\tDescription\n");
202
203
    /* Note that 'i' is incremented when we find a descriptor block and
204
     * process its contents. */
205
0
    for (i = 0; i < jinfo->last_block; i++) {
206
0
        ext2fs_journ_head *head;
207
208
209
        /* if there is no magic, then it is a normal block
210
         * These should be accounted for when we see its corresponding
211
         * descriptor.  We get the 'unknown' when its desc has
212
         * been reused, it is in the next batch to be overwritten,
213
         * or if it has not been used before
214
         */
215
0
        head = (ext2fs_journ_head *) & journ[i * jinfo->bsize];
216
0
        if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) {
217
0
            if (i < jinfo->first_block) {
218
0
                tsk_printf("%" PRIuDADDR ":\tUnused\n", i);
219
0
            }
220
221
#if 0
222
            /* For now, we ignore the case of the iitial entries before a descriptor, it is too hard ... */
223
224
            else if (b_desc_seen == 0) {
225
                ext2fs_journ_head *head2 = NULL;
226
                TSK_DADDR_T a;
227
                int next_head = 0, next_seq = 0;
228
                ext2fs_journ_dentry *dentry;
229
230
                /* This occurs when the log cycled around
231
                 * We need to find out where the descriptor is
232
                 * and where we need to end */
233
                b_desc_seen = 1;
234
235
                for (a = i; a < jinfo->last_block; a++) {
236
                    head2 =
237
                        (ext2fs_journ_head *) & journ[a * jinfo->bsize];
238
                    if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC)) {
239
                        next_head = a;
240
                        next_seq = big_tsk_getu32(head2->entry_seq);
241
                        break;
242
                    }
243
244
                }
245
                if (next_head == 0) {
246
                    tsk_printf("%" PRIuDADDR ":\tFS Block Unknown\n", i);
247
                }
248
249
                /* Find the last descr in the journ */
250
                for (a = jinfo->last_block; a > i; a--) {
251
                    head2 =
252
                        (ext2fs_journ_head *) & journ[a * jinfo->bsize];
253
                    if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC)
254
                        && (big_tsk_getu32(head2->entry_type) ==
255
                            EXT2_J_ETYPE_DESC)
256
                        && (next_seq == big_tsk_getu32(head2->entry_seq))) {
257
                        break;
258
259
// @@@@ We should abort if we reach a commit before  descriptor
260
261
                    }
262
                }
263
264
                /* We did not find a descriptor in the journ!
265
                 * print unknown for the rest of the journ
266
                 */
267
                if (a == i) {
268
                    tsk_printf("%" PRIuDADDR ":\tFS Block Unknown\n", i);
269
                    continue;
270
                }
271
272
273
                dentry =
274
                    (ext2fs_journ_dentry *) ((uintptr_t) head2 +
275
                    sizeof(ext2fs_journ_head));;
276
277
278
                /* Cycle through the descriptor entries */
279
                while ((uintptr_t) dentry <=
280
                    ((uintptr_t) head2 + jinfo->bsize -
281
                        sizeof(ext2fs_journ_head))) {
282
283
284
                    /* Only start to look after the index in the desc has looped */
285
                    if (++a <= jinfo->last_block) {
286
                        ext2fs_journ_head *head3;
287
288
                        /* Look at the block that this entry refers to */
289
                        head3 =
290
                            (ext2fs_journ_head *) & journ[i *
291
                            jinfo->bsize];
292
                        if ((big_tsk_getu32(head3->magic) == EXT2_JMAGIC)) {
293
                            i--;
294
                            break;
295
                        }
296
297
                        /* If it doesn't have the magic, then it is a
298
                         * journal entry and we print the FS info */
299
                        tsk_printf("%" PRIuDADDR ":\tFS Block %" PRIu32
300
                            "\n", i, big_tsk_getu32(dentry->fs_blk));
301
302
                        /* Our counter is over the end of the journ */
303
                        if (++i > jinfo->last_block)
304
                            break;
305
306
                    }
307
308
                    /* Increment to the next */
309
                    if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_LAST)
310
                        break;
311
312
                    /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */
313
                    else if (big_tsk_getu32(dentry->flag) &
314
                        EXT2_J_DENTRY_SAMEID)
315
                        dentry =
316
                            (ext2fs_journ_dentry *) ((uintptr_t) dentry +
317
                            sizeof(ext2fs_journ_dentry));
318
319
                    else
320
                        dentry =
321
                            (ext2fs_journ_dentry *) ((uintptr_t) dentry +
322
                            sizeof(ext2fs_journ_dentry)
323
                            + 16);
324
325
                }
326
            }
327
#endif
328
0
            else {
329
0
                tsk_printf("%" PRIuDADDR
330
0
                    ":\tUnallocated FS Block Unknown\n", i);
331
0
            }
332
0
        }
333
334
        /* The super block */
335
0
        else if ((big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_SB1) ||
336
0
            (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_SB2)) {
337
0
            tsk_printf("%" PRIuDADDR ":\tSuperblock (seq: %" PRIu32 ")\n",
338
0
                i, big_tsk_getu32(head->entry_seq));
339
0
            journ_sb = (ext2fs_journ_sb *)head;
340
0
            tsk_printf("sb version: %d\n",
341
0
                big_tsk_getu32(head->entry_type));
342
0
            tsk_printf("sb version: %d\n",
343
0
                big_tsk_getu32(journ_sb->entrytype));
344
0
            tsk_printf("sb feature_compat flags 0x%08X\n",
345
0
                big_tsk_getu32(journ_sb->feature_compat));
346
0
            if (big_tsk_getu32(journ_sb->
347
0
                    feature_compat) & JBD2_FEATURE_COMPAT_CHECKSUM)
348
0
                tsk_printf("\tJOURNAL_CHECKSUMS\n");
349
0
            tsk_printf("sb feature_incompat flags 0x%08X\n",
350
0
                big_tsk_getu32(journ_sb->feature_incompat));
351
0
            if (big_tsk_getu32(journ_sb->
352
0
                    feature_incompat) & JBD2_FEATURE_INCOMPAT_REVOKE)
353
0
                tsk_printf("\tJOURNAL_REVOKE\n");
354
0
            if (big_tsk_getu32(journ_sb->
355
0
                    feature_incompat) & JBD2_FEATURE_INCOMPAT_64BIT)
356
0
                tsk_printf("\tJOURNAL_64BIT\n");
357
0
            if (big_tsk_getu32(journ_sb->
358
0
                    feature_incompat) & JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)
359
0
                tsk_printf("\tJOURNAL_ASYNC_COMMIT\n");
360
0
            tsk_printf("sb feature_ro_incompat flags 0x%08X\n",
361
0
                big_tsk_getu32(journ_sb->feature_ro_incompat));
362
0
        }
363
364
        /* Revoke Block */
365
0
        else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_REV) {
366
0
            tsk_printf("%" PRIuDADDR ":\t%sRevoke Block (seq: %" PRIu32
367
0
                ")\n", i, ((i < jinfo->start_blk)
368
0
                    || (big_tsk_getu32(head->entry_seq) <
369
0
                        jinfo->start_seq)) ? "Unallocated " : "Allocated ",
370
0
                big_tsk_getu32(head->entry_seq));
371
0
        }
372
373
        /* The commit is the end of the entries */
374
0
        else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_COM) {
375
0
            tsk_printf("%" PRIuDADDR ":\t%sCommit Block (seq: %" PRIu32, i,
376
0
                ((i < jinfo->start_blk)
377
0
                    || (big_tsk_getu32(head->entry_seq) <
378
0
                        jinfo->start_seq)) ? "Unallocated " : "Allocated ",
379
0
                big_tsk_getu32(head->entry_seq));
380
0
            commit_head = (ext4fs_journ_commit_head *)head;
381
            //tsk_printf("commit seq %" PRIu32 "\n", big_tsk_getu32(commit_head->c_header.entry_seq));
382
0
            if (big_tsk_getu32(journ_sb->
383
0
                    feature_compat) & JBD2_FEATURE_COMPAT_CHECKSUM) {
384
0
                int chksum_type = commit_head->chksum_type;
385
0
                if (chksum_type) {
386
0
                    tsk_printf(", checksum_type: %d",
387
0
                        commit_head->chksum_type);
388
0
                    switch (commit_head->chksum_type) {
389
0
                    case JBD2_CRC32_CHKSUM:
390
0
                        tsk_printf("-CRC32");
391
0
                        break;
392
0
                    case JBD2_MD5_CHKSUM:
393
0
                        tsk_printf("-MD5");
394
0
                        break;
395
0
                    case JBD2_SHA1_CHKSUM:
396
0
                        tsk_printf("-SHA1");
397
0
                        break;
398
0
                    default:
399
0
                        tsk_printf("-UNKOWN");
400
0
                        break;
401
0
                    }
402
0
                    tsk_printf(", checksum_size: %d",
403
0
                        commit_head->chksum_size);
404
0
                    tsk_printf(", chksum: 0x%08X",
405
0
                        big_tsk_getu32(commit_head->chksum));
406
0
                }
407
0
            }
408
0
            tsk_printf(", sec: %llu.%u", tsk_getu64(TSK_BIG_ENDIAN,
409
0
                    commit_head->commit_sec),
410
0
                NSEC_PER_SEC / 10 * tsk_getu32(TSK_BIG_ENDIAN,
411
0
                    commit_head->commit_nsec));
412
0
            tsk_printf(")\n");
413
0
        }
414
415
        /* The descriptor describes the FS blocks that follow it */
416
0
        else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_DESC) {
417
0
            ext2fs_journ_dentry *dentry;
418
0
            int unalloc = 0;
419
420
//            b_desc_seen = 1;
421
422
423
            /* Is this an unallocated journ block or sequence */
424
0
            if ((i < jinfo->start_blk) ||
425
0
                (big_tsk_getu32(head->entry_seq) < jinfo->start_seq))
426
0
                unalloc = 1;
427
428
0
            tsk_printf("%" PRIuDADDR ":\t%sDescriptor Block (seq: %" PRIu32
429
0
                ")\n", i, (unalloc) ? "Unallocated " : "Allocated ",
430
0
                big_tsk_getu32(head->entry_seq));
431
432
0
            dentry =
433
0
                (ext2fs_journ_dentry *) ((uintptr_t) head +
434
0
                sizeof(ext2fs_journ_head));;
435
436
            /* Cycle through the descriptor entries to account for the journal blocks */
437
0
            while ((uintptr_t) dentry <=
438
0
                ((uintptr_t) head + jinfo->bsize -
439
0
                    sizeof(ext2fs_journ_head))) {
440
0
                ext2fs_journ_head *head2;
441
442
443
                /* Our counter is over the end of the journ */
444
0
                if (++i > jinfo->last_block)
445
0
                    break;
446
447
448
                /* Look at the block that this entry refers to */
449
0
                head2 = (ext2fs_journ_head *) & journ[i * jinfo->bsize];
450
0
                if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC) &&
451
0
                    (big_tsk_getu32(head2->entry_seq) >=
452
0
                        big_tsk_getu32(head->entry_seq))) {
453
0
                    i--;
454
0
                    break;
455
0
                }
456
457
                /* If it doesn't have the magic, then it is a
458
                 * journal entry and we print the FS info */
459
0
                tsk_printf("%" PRIuDADDR ":\t%sFS Block %" PRIu32 "\n", i,
460
0
                    (unalloc) ? "Unallocated " : "Allocated ",
461
0
                    big_tsk_getu32(dentry->fs_blk));
462
463
                /* Increment to the next */
464
0
                if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_LAST)
465
0
                    break;
466
467
                /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */
468
0
                else if (big_tsk_getu32(dentry->flag) &
469
0
                    EXT2_J_DENTRY_SAMEID)
470
0
                    dentry =
471
0
                        (ext2fs_journ_dentry *) ((uintptr_t) dentry +
472
0
                        sizeof(ext2fs_journ_dentry));
473
474
0
                else
475
0
                    dentry =
476
0
                        (ext2fs_journ_dentry *) ((uintptr_t) dentry +
477
0
                        sizeof(ext2fs_journ_dentry) + 16);
478
0
            }
479
0
        }
480
0
    }
481
482
0
    free(journ);
483
0
    return 0;
484
0
}
485
486
487
488
489
490
/*
491
 * Limitations for 1st version: start must equal end and action is ignored
492
 *
493
 * Return 0 on success and 1 on error
494
 */
495
uint8_t
496
ext2fs_jblk_walk(
497
  TSK_FS_INFO * fs,
498
  TSK_DADDR_T start,
499
  TSK_DADDR_T end,
500
  [[maybe_unused]] int flags,
501
  [[maybe_unused]] TSK_FS_JBLK_WALK_CB action,
502
  [[maybe_unused]] void *ptr)
503
0
{
504
0
    EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
505
0
    EXT2FS_JINFO *jinfo = ext2fs->jinfo;
506
0
    uint8_t *journ;
507
0
    TSK_FS_LOAD_FILE buf1;
508
0
    ext2fs_journ_head *head;
509
510
    // clean up any error messages that are lying around
511
0
    tsk_error_reset();
512
513
0
    if ((jinfo == NULL) || (jinfo->fs_file == NULL)
514
0
        || (jinfo->fs_file->meta == NULL)) {
515
0
        tsk_error_reset();
516
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
517
0
        tsk_error_set_errstr("ext2fs_jblk_walk: journal is not open");
518
0
        return 1;
519
0
    }
520
521
0
    if (jinfo->last_block < end) {
522
0
        tsk_error_reset();
523
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
524
0
        tsk_error_set_errstr("ext2fs_jblk_walk: end is too large ");
525
0
        return 1;
526
0
    }
527
528
0
    if (start != end) {
529
0
        tsk_error_reset();
530
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
531
0
        tsk_error_set_errstr
532
0
            ("ext2fs_blk_walk: only start == end is currently supported");
533
0
        return 1;
534
0
    }
535
536
0
    if ((TSK_DADDR_T)jinfo->fs_file->meta->size !=
537
0
        (jinfo->last_block + 1) * jinfo->bsize) {
538
0
        tsk_error_reset();
539
0
        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
540
0
        tsk_error_set_errstr
541
0
            ("ext2fs_jblk_walk: journal file size is different from size reported in journal super block");
542
0
        return 1;
543
0
    }
544
545
546
    /* Load into buffer and then process it
547
     * Only get the minimum needed
548
     */
549
0
    buf1.left = buf1.total = (size_t) ((end + 1) * jinfo->bsize);
550
0
    buf1.cur = buf1.base = (char*) tsk_malloc(buf1.left);
551
0
    journ = (uint8_t*) buf1.cur;
552
0
    if (journ == NULL) {
553
0
        return 1;
554
0
    }
555
556
0
    if (tsk_fs_file_walk(jinfo->fs_file, TSK_FS_FILE_WALK_FLAG_NONE,
557
0
            tsk_fs_load_file_action, (void *) &buf1)) {
558
0
        free(journ);
559
0
        return 1;
560
0
    }
561
562
0
    if (buf1.left > 0) {
563
0
        tsk_error_reset();
564
0
        tsk_error_set_errno(TSK_ERR_FS_FWALK);
565
0
        tsk_error_set_errstr("ext2fs_jblk_walk: Buffer not fully copied");
566
0
        free(journ);
567
0
        return 1;
568
0
    }
569
570
0
    head = (ext2fs_journ_head *) & journ[end * jinfo->bsize];
571
572
573
    /* Check if our target block is a journal data structure.
574
     *
575
     * If not,
576
     * we need to look for its descriptor to see if it has been
577
     * escaped
578
     */
579
0
    if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) {
580
0
        TSK_DADDR_T i;
581
582
        /* cycle backwards until we find a desc block */
583
0
        for (i = end - 1; i > 0; i--) {
584
0
            ext2fs_journ_dentry *dentry;
585
0
            TSK_DADDR_T diff;
586
587
0
            head = (ext2fs_journ_head *) & journ[i * jinfo->bsize];
588
589
0
            if (big_tsk_getu32(head->magic) != EXT2_JMAGIC)
590
0
                continue;
591
592
            /* If we get a commit, then any desc we find will not
593
             * be for our block, so forget about it */
594
0
            if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_COM)
595
0
                break;
596
597
            /* Skip any other data structure types */
598
0
            if (big_tsk_getu32(head->entry_type) != EXT2_J_ETYPE_DESC)
599
0
                continue;
600
601
            /* We now have the previous descriptor
602
             *
603
             * NOTE: We have no clue if this is the correct
604
             * descriptor if it is not the current 'run' of
605
             * transactions, but this is the best we can do
606
             */
607
0
            diff = end - i;
608
609
0
            dentry =
610
0
                (ext2fs_journ_dentry *) (&journ[i * jinfo->bsize] +
611
0
                sizeof(ext2fs_journ_head));
612
613
0
            while ((uintptr_t) dentry <=
614
0
                ((uintptr_t) & journ[(i + 1) * jinfo->bsize] -
615
0
                    sizeof(ext2fs_journ_head))) {
616
617
0
                if (--diff == 0) {
618
0
                    if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_ESC) {
619
0
                        journ[end * jinfo->bsize] = 0xC0;
620
0
                        journ[end * jinfo->bsize + 1] = 0x3B;
621
0
                        journ[end * jinfo->bsize + 2] = 0x39;
622
0
                        journ[end * jinfo->bsize + 3] = 0x98;
623
0
                    }
624
0
                    break;
625
0
                }
626
627
                /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */
628
0
                if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_SAMEID)
629
0
                    dentry =
630
0
                        (ext2fs_journ_dentry *) ((uintptr_t) dentry +
631
0
                        sizeof(ext2fs_journ_dentry));
632
0
                else
633
0
                    dentry =
634
0
                        (ext2fs_journ_dentry *) ((uintptr_t) dentry +
635
0
                        sizeof(ext2fs_journ_dentry) + 16);
636
637
0
            }
638
0
            break;
639
0
        }
640
0
    }
641
642
0
    if (fwrite(&journ[end * jinfo->bsize], jinfo->bsize, 1, stdout) != 1) {
643
0
        tsk_error_reset();
644
0
        tsk_error_set_errno(TSK_ERR_FS_WRITE);
645
0
        tsk_error_set_errstr
646
0
            ("ext2fs_jblk_walk: error writing buffer block");
647
0
        free(journ);
648
0
        return 1;
649
0
    }
650
651
0
    free(journ);
652
0
    return 0;
653
0
}