aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--e2fsck/e2fsck.c4
-rw-r--r--e2fsck/e2fsck.h1
-rw-r--r--e2fsck/pass1.c86
-rw-r--r--e2fsck/pass1b.c2
-rw-r--r--e2fsck/pass5.c2
-rw-r--r--e2fsck/problem.c8
-rw-r--r--e2fsck/problem.h3
-rw-r--r--tests/f_itable_collision/expect.1101
-rw-r--r--tests/f_itable_collision/expect.27
-rw-r--r--tests/f_itable_collision/image.gzbin0 -> 2817 bytes
-rw-r--r--tests/f_itable_collision/name1
-rwxr-xr-xtests/f_itable_collision/script38
12 files changed, 242 insertions, 11 deletions
diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index 0ec15404..fcda7d75 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -108,6 +108,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
ext2fs_free_block_bitmap(ctx->block_ea_map);
ctx->block_ea_map = 0;
}
+ if (ctx->block_metadata_map) {
+ ext2fs_free_block_bitmap(ctx->block_metadata_map);
+ ctx->block_metadata_map = 0;
+ }
if (ctx->inode_bb_map) {
ext2fs_free_inode_bitmap(ctx->inode_bb_map);
ctx->inode_bb_map = 0;
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 80ab18aa..53f10b1a 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -374,6 +374,7 @@ struct e2fsck_struct {
* e2fsck functions themselves.
*/
void *priv_data;
+ ext2fs_block_bitmap block_metadata_map; /* Metadata blocks */
};
/* Used by the region allocation code */
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index fa0f89e2..3f287b09 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -93,6 +93,7 @@ struct process_block_struct {
struct problem_context *pctx;
ext2fs_block_bitmap fs_meta_blocks;
e2fsck_t ctx;
+ blk64_t bad_ref;
};
struct process_inode_block {
@@ -696,6 +697,15 @@ void e2fsck_pass1(e2fsck_t ctx)
ctx->flags |= E2F_FLAG_ABORT;
return;
}
+ pctx.errcode = e2fsck_allocate_block_bitmap(fs,
+ _("metadata block map"), EXT2FS_BMAP64_RBTREE,
+ "block_metadata_map", &ctx->block_metadata_map);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
e2fsck_setup_tdb_icount(ctx, 0, &ctx->inode_link_info);
if (!ctx->inode_link_info) {
e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE,
@@ -1904,7 +1914,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
struct process_block_struct *pb,
blk64_t start_block, blk64_t end_block,
blk64_t eof_block,
- ext2_extent_handle_t ehandle)
+ ext2_extent_handle_t ehandle,
+ int try_repairs)
{
struct ext2fs_extent extent;
blk64_t blk, last_lblk;
@@ -1931,7 +1942,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
problem = 0;
/* Ask to clear a corrupt extent block */
- if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) {
+ if (try_repairs &&
+ pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) {
pctx->blk = extent.e_pblk;
pctx->blk2 = extent.e_lblk;
pctx->num = extent.e_len;
@@ -1963,7 +1975,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
problem = PR_1_TOOBIG_DIR;
/* Corrupt but passes checks? Ask to fix checksum. */
- if (failed_csum) {
+ if (try_repairs && failed_csum) {
pctx->blk = extent.e_pblk;
pctx->blk2 = extent.e_lblk;
pctx->num = extent.e_len;
@@ -1975,7 +1987,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
}
}
- if (problem) {
+ if (try_repairs && problem) {
report_problem:
pctx->blk = extent.e_pblk;
pctx->blk2 = extent.e_lblk;
@@ -2031,13 +2043,32 @@ fix_problem_now:
if (!is_leaf) {
blk64_t lblk = extent.e_lblk;
+ int next_try_repairs = 1;
blk = extent.e_pblk;
+
+ /*
+ * If this lower extent block collides with critical
+ * metadata, don't try to repair the damage. Pass 1b
+ * will reallocate the block; then we can try again.
+ */
+ if (pb->ino != EXT2_RESIZE_INO &&
+ ext2fs_test_block_bitmap2(ctx->block_metadata_map,
+ extent.e_pblk)) {
+ next_try_repairs = 0;
+ pctx->blk = blk;
+ fix_problem(ctx,
+ PR_1_CRITICAL_METADATA_COLLISION,
+ pctx);
+ ctx->flags |= E2F_FLAG_RESTART_LATER;
+ }
pctx->errcode = ext2fs_extent_get(ehandle,
EXT2_EXTENT_DOWN, &extent);
if (pctx->errcode) {
pctx->str = "EXT2_EXTENT_DOWN";
problem = PR_1_EXTENT_HEADER_INVALID;
+ if (!next_try_repairs)
+ return;
if (pctx->errcode ==
EXT2_ET_EXTENT_HEADER_BAD ||
pctx->errcode ==
@@ -2065,7 +2096,8 @@ fix_problem_now:
}
}
scan_extent_node(ctx, pctx, pb, extent.e_lblk,
- last_lblk, eof_block, ehandle);
+ last_lblk, eof_block, ehandle,
+ next_try_repairs);
if (pctx->errcode)
return;
pctx->errcode = ext2fs_extent_get(ehandle,
@@ -2188,7 +2220,7 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx,
eof_lblk = ((EXT2_I_SIZE(inode) + fs->blocksize - 1) >>
EXT2_BLOCK_SIZE_BITS(fs->super)) - 1;
- scan_extent_node(ctx, pctx, pb, 0, 0, eof_lblk, ehandle);
+ scan_extent_node(ctx, pctx, pb, 0, 0, eof_lblk, ehandle, 1);
if (pctx->errcode &&
fix_problem(ctx, PR_1_EXTENT_ITERATE_FAILURE, pctx)) {
pb->num_blocks = 0;
@@ -2256,6 +2288,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
pb.pctx = pctx;
pb.ctx = ctx;
pb.inode_modified = 0;
+ pb.bad_ref = 0;
pctx->ino = ino;
pctx->errcode = 0;
@@ -2596,8 +2629,35 @@ static int process_block(ext2_filsys fs,
blk >= ext2fs_blocks_count(fs->super))
problem = PR_1_ILLEGAL_BLOCK_NUM;
+ /*
+ * If this IND/DIND/TIND block is squatting atop some critical metadata
+ * (group descriptors, superblock, bitmap, inode table), any write to
+ * "fix" mapping problems will destroy the metadata. We'll let pass 1b
+ * fix that and restart fsck.
+ */
+ if (blockcnt < 0 &&
+ p->ino != EXT2_RESIZE_INO &&
+ ext2fs_test_block_bitmap2(ctx->block_metadata_map, blk)) {
+ p->bad_ref = blk;
+ pctx->blk = blk;
+ fix_problem(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx);
+ ctx->flags |= E2F_FLAG_RESTART_LATER;
+ }
+
if (problem) {
p->num_illegal_blocks++;
+ /*
+ * A bit of subterfuge here -- we're trying to fix a block
+ * mapping, but know that the IND/DIND/TIND block has collided
+ * with some critical metadata. So, fix the in-core mapping so
+ * iterate won't go insane, but return 0 instead of
+ * BLOCK_CHANGED so that it won't write the remapping out to
+ * our multiply linked block.
+ */
+ if (p->bad_ref && ref_block == p->bad_ref) {
+ *block_nr = 0;
+ return 0;
+ }
if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
p->clear = 1;
@@ -2978,6 +3038,7 @@ static void mark_table_blocks(e2fsck_t ctx)
pctx.group = i;
ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
+ ext2fs_reserve_super_and_bgd(fs, i, ctx->block_metadata_map);
/*
* Mark the blocks used for the inode table
@@ -2996,8 +3057,10 @@ static void mark_table_blocks(e2fsck_t ctx)
ctx->invalid_bitmaps++;
}
} else {
- ext2fs_mark_block_bitmap2(ctx->block_found_map,
- b);
+ ext2fs_mark_block_bitmap2(
+ ctx->block_found_map, b);
+ ext2fs_mark_block_bitmap2(
+ ctx->block_metadata_map, b);
}
}
}
@@ -3016,8 +3079,9 @@ static void mark_table_blocks(e2fsck_t ctx)
} else {
ext2fs_mark_block_bitmap2(ctx->block_found_map,
ext2fs_block_bitmap_loc(fs, i));
- }
-
+ ext2fs_mark_block_bitmap2(ctx->block_metadata_map,
+ ext2fs_block_bitmap_loc(fs, i));
+ }
}
/*
* Mark block used for the inode bitmap
@@ -3031,6 +3095,8 @@ static void mark_table_blocks(e2fsck_t ctx)
ctx->invalid_bitmaps++;
}
} else {
+ ext2fs_mark_block_bitmap2(ctx->block_metadata_map,
+ ext2fs_inode_bitmap_loc(fs, i));
ext2fs_mark_block_bitmap2(ctx->block_found_map,
ext2fs_inode_bitmap_loc(fs, i));
}
diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
index b4041d18..c2a3cf30 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -389,7 +389,7 @@ static int process_pass1b_block(ext2_filsys fs EXT2FS_ATTR((unused)),
p->dup_blocks++;
ext2fs_mark_inode_bitmap2(inode_dup_map, p->ino);
- if (lc != p->cur_cluster)
+ if (blockcnt < 0 || lc != p->cur_cluster)
add_dupe(ctx, p->ino, EXT2FS_B2C(fs, *block_nr), p->inode);
finish:
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index e23680a7..eeb16c38 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -75,6 +75,8 @@ void e2fsck_pass5(e2fsck_t ctx)
ctx->inode_dir_map = 0;
ext2fs_free_block_bitmap(ctx->block_found_map);
ctx->block_found_map = 0;
+ ext2fs_free_block_bitmap(ctx->block_metadata_map);
+ ctx->block_metadata_map = 0;
print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
}
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index cb7c7004..18d80259 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1030,6 +1030,14 @@ static struct e2fsck_problem problem_table[] = {
N_("@i %i has INLINE_DATA_FL flag on @f without inline data support.\n"),
PROMPT_CLEAR, 0 },
+ /*
+ * Inode block conflicts with critical metadata, skipping
+ * block checks
+ */
+ { PR_1_CRITICAL_METADATA_COLLISION,
+ N_("@i %i block %b conflicts with critical metadata, skipping block checks.\n"),
+ PROMPT_NONE, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index bc9fa9c8..9001ef49 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -600,6 +600,9 @@ struct problem_context {
/* INLINE_DATA feature is set in a non-inline-data filesystem */
#define PR_1_INLINE_DATA_SET 0x010070
+/* file metadata collides with critical metadata */
+#define PR_1_CRITICAL_METADATA_COLLISION 0x010071
+
/*
* Pass 1b errors
*/
diff --git a/tests/f_itable_collision/expect.1 b/tests/f_itable_collision/expect.1
new file mode 100644
index 00000000..00cdced8
--- /dev/null
+++ b/tests/f_itable_collision/expect.1
@@ -0,0 +1,101 @@
+Pass 1: Checking inodes, blocks, and sizes
+Inode 12 block 37 conflicts with critical metadata, skipping block checks.
+Illegal block number passed to ext2fs_test_block_bitmap #268435455 for in-use block map
+Illegal block number passed to ext2fs_mark_block_bitmap #268435455 for in-use block map
+Inode 12, i_blocks is 48, should be 56. Fix? yes
+
+Inode 13 has a bad extended attribute block 34. Clear? yes
+
+Deleted inode 33 has zero dtime. Fix? yes
+
+Inodes that were part of a corrupted orphan linked list found. Fix? yes
+
+Inode 49 was part of the orphaned inode list. FIXED.
+Inode 14 block 36 conflicts with critical metadata, skipping block checks.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Inode 14 has illegal block(s). Clear? yes
+
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Illegal indirect block (4294967295) in inode 14. CLEARED.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967295 for metadata block map
+Too many illegal blocks in inode 14.
+Clear inode? yes
+
+Restarting e2fsck from the beginning...
+Pass 1: Checking inodes, blocks, and sizes
+Inode 12 block 37 conflicts with critical metadata, skipping block checks.
+Illegal block number passed to ext2fs_test_block_bitmap #4294967294 for in-use block map
+Illegal block number passed to ext2fs_mark_block_bitmap #4294967294 for in-use block map
+Illegal block number passed to ext2fs_test_block_bitmap #268435455 for in-use block map
+Illegal block number passed to ext2fs_mark_block_bitmap #268435455 for in-use block map
+
+Running additional passes to resolve blocks claimed by more than one inode...
+Pass 1B: Rescanning for multiply-claimed blocks
+Illegal block number passed to ext2fs_test_block_bitmap #4294967294 for multiply claimed block map
+Illegal block number passed to ext2fs_test_block_bitmap #268435455 for multiply claimed block map
+Multiply-claimed block(s) in inode 12: 37
+Pass 1C: Scanning directories for inodes with multiply-claimed blocks
+Pass 1D: Reconciling multiply-claimed blocks
+(There are 1 inodes containing multiply-claimed blocks.)
+
+File /a (inode #12, mod time Fri Jun 27 18:34:44 2014)
+ has 1 multiply-claimed block(s), shared with 1 file(s):
+ <filesystem metadata>
+Clone multiply-claimed blocks? yes
+
+Illegal block number passed to ext2fs_test_block_bitmap #4294967294 for multiply claimed block map
+Illegal block number passed to ext2fs_test_block_bitmap #268435455 for multiply claimed block map
+Pass 2: Checking directory structure
+Setting filetype for entry 'bad1' in / (2) to 1.
+Setting filetype for entry 'bad2' in / (2) to 1.
+Restarting e2fsck from the beginning...
+Pass 1: Checking inodes, blocks, and sizes
+Inode 12 has an invalid extent
+ (logical block 0, invalid physical block 4294967294, len 1)
+Clear? yes
+
+Inode 12 has an invalid extent
+ (logical block 5, invalid physical block 268435455, len 1)
+Clear? yes
+
+Inode 12, i_blocks is 56, should be 40. Fix? yes
+
+Pass 2: Checking directory structure
+Entry 'b' in / (2) has deleted/unused inode 14. Clear? yes
+
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Block bitmap differences: -9 -13 -42
+Fix? yes
+
+Free blocks count wrong for group #0 (485, counted=488).
+Fix? yes
+
+Free blocks count wrong (485, counted=488).
+Fix? yes
+
+Inode bitmap differences: -14 +34 +50
+Fix? yes
+
+Free inodes count wrong for group #0 (114, counted=113).
+Fix? yes
+
+Free inodes count wrong (114, counted=113).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 15/128 files (6.7% non-contiguous), 24/512 blocks
+Exit status is 0
diff --git a/tests/f_itable_collision/expect.2 b/tests/f_itable_collision/expect.2
new file mode 100644
index 00000000..4abc6006
--- /dev/null
+++ b/tests/f_itable_collision/expect.2
@@ -0,0 +1,7 @@
+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: 15/128 files (6.7% non-contiguous), 24/512 blocks
+Exit status is 0
diff --git a/tests/f_itable_collision/image.gz b/tests/f_itable_collision/image.gz
new file mode 100644
index 00000000..6218b146
--- /dev/null
+++ b/tests/f_itable_collision/image.gz
Binary files differ
diff --git a/tests/f_itable_collision/name b/tests/f_itable_collision/name
new file mode 100644
index 00000000..dab3d457
--- /dev/null
+++ b/tests/f_itable_collision/name
@@ -0,0 +1 @@
+collision between IND/extent tree blocks and inode table
diff --git a/tests/f_itable_collision/script b/tests/f_itable_collision/script
new file mode 100755
index 00000000..7eedfd37
--- /dev/null
+++ b/tests/f_itable_collision/script
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Run this test with a specific time, because we're crosslinking an extent tree
+# block with the inode table. When fsck sets dtime to now, we want "now" to be
+# our preprogrammed value.
+
+FSCK_OPT=-fy
+IMAGE=$test_dir/image.gz
+E2FSCK_TIME=4294967294
+export E2FSCK_TIME
+
+gzip -d < $IMAGE > $TMPFILE
+e2label $TMPFILE test_filesys
+
+# Run fsck to fix things?
+EXP1=$test_dir/expect.1
+OUT1=$test_name.1.log
+rm -rf $test_name.failed $test_name.ok
+
+$FSCK $FSCK_OPT $TMPFILE 2>&1 | tail -n +2 > $OUT1
+echo "Exit status is $?" >> $OUT1
+
+# Run a second time
+EXP2=$test_dir/expect.2
+OUT2=$test_name.2.log
+
+$FSCK $FSCK_OPT $TMPFILE 2>&1 | tail -n +2 > $OUT2
+echo "Exit status is $?" >> $OUT2
+
+# Figure out what happened
+if cmp -s $EXP1 $OUT1 && cmp -s $EXP2 $OUT2; then
+ echo "$test_name: $test_description: ok"
+ touch $test_name.ok
+else
+ echo "$test_name: $test_description: failed"
+ diff -u $EXP1 $OUT1 >> $test_name.failed
+ diff -u $EXP2 $OUT2 >> $test_name.failed
+fi