From: Darrick J. Wong Date: Mon, 8 Sep 2014 23:12:15 +0000 (-0700) Subject: e2fsck: detect and repair external journal superblock checksum errors X-Git-Tag: v1.43-WIP-2015-05-18~203 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6aabb75493a77aaee301fb5e9d39202439c4a325;p=thirdparty%2Fe2fsprogs.git e2fsck: detect and repair external journal superblock checksum errors Verify the (ext4) superblock checksum of an external journal device and prompt to correct the checksum if nothing else is wrong with the superblock. Signed-off-by: Darrick J. Wong Cc: TR Reardon Signed-off-by: Theodore Ts'o --- diff --git a/e2fsck/journal.c b/e2fsck/journal.c index a19d40b6a..16bd757ea 100644 --- a/e2fsck/journal.c +++ b/e2fsck/journal.c @@ -456,7 +456,6 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) } memcpy(&jsuper, start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET, sizeof(jsuper)); - brelse(bh); #ifdef WORDS_BIGENDIAN if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); @@ -465,6 +464,7 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; + brelse(bh); goto errout; } /* Make sure the journal UUID is correct */ @@ -472,9 +472,32 @@ static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) sizeof(jsuper.s_uuid))) { fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; + brelse(bh); goto errout; } + /* Check the superblock checksum */ + if (jsuper.s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { + struct struct_ext2_filsys fsx; + struct ext2_super_block superx; + void *p; + + p = start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET; + memcpy(&fsx, ctx->fs, sizeof(fsx)); + memcpy(&superx, ctx->fs->super, sizeof(superx)); + fsx.super = &superx; + fsx.super->s_feature_ro_compat |= + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM; + if (!ext2fs_superblock_csum_verify(&fsx, p) && + fix_problem(ctx, PR_0_EXT_JOURNAL_SUPER_CSUM_INVALID, + &pctx)) { + ext2fs_superblock_csum_set(&fsx, p); + mark_buffer_dirty(bh); + } + } + brelse(bh); + maxlen = ext2fs_blocks_count(&jsuper); journal->j_maxlen = (maxlen < 1ULL << 32) ? maxlen : (1ULL << 32) - 1; start++; diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 99ca7cb8c..4b41a2179 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -459,6 +459,11 @@ static struct e2fsck_problem problem_table[] = { N_("First_meta_bg is too big. (%N, max value %g). "), PROMPT_CLEAR, 0 }, + /* External journal has corrupt superblock */ + { PR_0_EXT_JOURNAL_SUPER_CSUM_INVALID, + N_("External @j @S checksum does not match @S. "), + PROMPT_FIX, PR_PREEN_OK }, + /* Pass 1 errors */ /* Pass 1: Checking inodes, blocks, and sizes */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 5c92d0a24..f86c531de 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -264,6 +264,9 @@ struct problem_context { /* The first_meta_bg is too big */ #define PR_0_FIRST_META_BG_TOO_BIG 0x000049 +/* External journal has corrupt superblock */ +#define PR_0_EXT_JOURNAL_SUPER_CSUM_INVALID 0x00004A + /* * Pass 1 errors */ diff --git a/tests/j_corrupt_ext_jnl_sb_block/expect b/tests/j_corrupt_ext_jnl_sb_block/expect new file mode 100644 index 000000000..e638e1178 --- /dev/null +++ b/tests/j_corrupt_ext_jnl_sb_block/expect @@ -0,0 +1,5 @@ +External journal does not support this filesystem + +test_filesys: ********** WARNING: Filesystem still has errors ********** + +Exit status is 12 diff --git a/tests/j_corrupt_ext_jnl_sb_block/image.tar.bz2 b/tests/j_corrupt_ext_jnl_sb_block/image.tar.bz2 new file mode 100644 index 000000000..efb382faf Binary files /dev/null and b/tests/j_corrupt_ext_jnl_sb_block/image.tar.bz2 differ diff --git a/tests/j_corrupt_ext_jnl_sb_block/name b/tests/j_corrupt_ext_jnl_sb_block/name new file mode 100644 index 000000000..a5188be8d --- /dev/null +++ b/tests/j_corrupt_ext_jnl_sb_block/name @@ -0,0 +1 @@ +corrupt external journal fs superblock block (metadata_csum) diff --git a/tests/j_corrupt_ext_jnl_sb_block/script b/tests/j_corrupt_ext_jnl_sb_block/script new file mode 100644 index 000000000..02b8e6507 --- /dev/null +++ b/tests/j_corrupt_ext_jnl_sb_block/script @@ -0,0 +1,36 @@ +FSCK_OPT=-fy +OUT=$test_name.log +if [ -f $test_dir/expect.gz ]; then + EXP=$test_name.tmp + gunzip < $test_dir/expect.gz > $EXP1 +else + EXP=$test_dir/expect +fi + +cp /dev/null $OUT + +bzip2 -dc < $test_dir/image.tar.bz2 | tar x +test -d "$JOURNAL_DUMP_DIR" -a -w "$JOURNAL_DUMP_DIR" && cp $test_name.img "$JOURNAL_DUMP_DIR/$test_name.img" +test -d "$JOURNAL_DUMP_DIR" -a -w "$JOURNAL_DUMP_DIR" && cp $test_name.img.jnl "$JOURNAL_DUMP_DIR/$test_name.img.jnl" + +$FSCK $FSCK_OPT -N test_filesys -j $test_name.img.jnl $test_name.img > $OUT.new 2>&1 +status=$? +echo Exit status is $status >> $OUT.new +sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new >> $OUT +rm -f $OUT.new + +rm -f $TMPFILE $test_name.img $test_name.img.jnl + +cmp -s $OUT $EXP +status=$? + +if [ "$status" = 0 ] ; then + echo "$test_name: $test_description: ok" + touch $test_name.ok +else + echo "$test_name: $test_description: failed" + diff $DIFF_OPTS $EXP $OUT > $test_name.failed + rm -f $test_name.tmp +fi + +unset IMAGE FSCK_OPT OUT EXP diff --git a/tests/j_corrupt_ext_jnl_sb_csum/expect b/tests/j_corrupt_ext_jnl_sb_csum/expect new file mode 100644 index 000000000..70a4fe721 --- /dev/null +++ b/tests/j_corrupt_ext_jnl_sb_csum/expect @@ -0,0 +1,25 @@ +External journal superblock checksum does not match superblock. Fix? yes + +test_filesys: recovering journal +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +Block bitmap differences: +(1--31) +34 +(50--82) +Fix? yes + +Inode bitmap differences: +(1--11) +Fix? yes + + +test_filesys: ***** FILE SYSTEM WAS MODIFIED ***** +test_filesys: 11/128 files (0.0% non-contiguous), 66/2048 blocks +Exit status is 1 +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +test_filesys: 11/128 files (0.0% non-contiguous), 66/2048 blocks +Exit status is 0 diff --git a/tests/j_corrupt_ext_jnl_sb_csum/image.tar.bz2 b/tests/j_corrupt_ext_jnl_sb_csum/image.tar.bz2 new file mode 100644 index 000000000..d04d584c0 Binary files /dev/null and b/tests/j_corrupt_ext_jnl_sb_csum/image.tar.bz2 differ diff --git a/tests/j_corrupt_ext_jnl_sb_csum/name b/tests/j_corrupt_ext_jnl_sb_csum/name new file mode 100644 index 000000000..c182f8101 --- /dev/null +++ b/tests/j_corrupt_ext_jnl_sb_csum/name @@ -0,0 +1 @@ +corrupt external journal fs superblock csum (metadata_csum) diff --git a/tests/j_corrupt_ext_jnl_sb_csum/script b/tests/j_corrupt_ext_jnl_sb_csum/script new file mode 100644 index 000000000..7a110bfac --- /dev/null +++ b/tests/j_corrupt_ext_jnl_sb_csum/script @@ -0,0 +1,42 @@ +FSCK_OPT=-fy +OUT=$test_name.log +if [ -f $test_dir/expect.gz ]; then + EXP=$test_name.tmp + gunzip < $test_dir/expect.gz > $EXP1 +else + EXP=$test_dir/expect +fi + +cp /dev/null $OUT + +bzip2 -dc < $test_dir/image.tar.bz2 | tar x +test -d "$JOURNAL_DUMP_DIR" -a -w "$JOURNAL_DUMP_DIR" && cp $test_name.img "$JOURNAL_DUMP_DIR/$test_name.img" +test -d "$JOURNAL_DUMP_DIR" -a -w "$JOURNAL_DUMP_DIR" && cp $test_name.img.jnl "$JOURNAL_DUMP_DIR/$test_name.img.jnl" + +$FSCK $FSCK_OPT -N test_filesys -j $test_name.img.jnl $test_name.img > $OUT.new 2>&1 +status=$? +echo Exit status is $status >> $OUT.new +sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new >> $OUT +rm -f $OUT.new + +$FSCK $FSCK_OPT -N test_filesys -j $test_name.img.jnl $test_name.img > $OUT.new 2>&1 +status=$? +echo Exit status is $status >> $OUT.new +sed -f $cmd_dir/filter.sed -e "s;$TMPFILE;test.img;" $OUT.new >> $OUT +rm -f $OUT.new + +rm -f $TMPFILE $test_name.img $test_name.img.jnl + +cmp -s $OUT $EXP +status=$? + +if [ "$status" = 0 ] ; then + echo "$test_name: $test_description: ok" + touch $test_name.ok +else + echo "$test_name: $test_description: failed" + diff $DIFF_OPTS $EXP $OUT > $test_name.failed + rm -f $test_name.tmp +fi + +unset IMAGE FSCK_OPT OUT EXP