From c3ffaf833b7ddb3c1d422c71f59e1623029ede0c Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 24 Dec 2004 01:42:22 -0500 Subject: [PATCH] Add support to detect corrupted resize_inode's to e2fsck. --- e2fsck/ChangeLog | 15 ++++++ e2fsck/e2fsck.h | 1 + e2fsck/pass1.c | 34 ++++++++++--- e2fsck/problem.c | 21 ++++++-- e2fsck/problem.h | 6 +++ e2fsck/super.c | 147 ++++++++++++++++++++++++++++++++++++++++++------------- 6 files changed, 179 insertions(+), 45 deletions(-) diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index d6df0ec..b843459 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,3 +1,18 @@ +2004-12-24 Theodore Ts'o + + * pass1.c (e2fsck_pass1): At the end of the pass 1 processing, if + we have been signalled to do so, recreate the resize inode. + + * super.c (check_resize_inode): New function which checks to make + sure the resize inode is valid. It is called by + check_super_block(). If it is invalid, it will signal to + pass1.c that the resize inode needs to recreate. + + * e2fsck.h (E2F_FLAG_RESIZE_INODE): New flag + + * problem.c, problem.h (PR_0_RESIZE_INODE_INVALID, + PR_1_RESIZE_INODE_CREATE): Add new problem codes. + 2004-12-23 Theodore Ts'o * swapfs.c (swap_inodes): Since swap_inodes bypasses the inode diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 99ee20a..6dec935 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -163,6 +163,7 @@ struct resource_track { #define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly * specified by the user */ #define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */ +#define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */ /* * Defines for indicating the e2fsck pass number diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 9632214..1e76ef9 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -721,6 +721,24 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->block_ea_map = 0; } + if (ctx->flags & E2F_FLAG_RESIZE_INODE) { + ext2fs_block_bitmap save_bmap; + errcode_t retval; + + save_bmap = fs->block_map; + fs->block_map = ctx->block_found_map; + clear_problem_context(&pctx); + pctx.errcode = ext2fs_create_resize_inode(fs); + if (pctx.errcode) { + fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } + fs->block_map = save_bmap; + ctx->flags &= ~E2F_FLAG_RESIZE_INODE; + } + if (ctx->flags & E2F_FLAG_RESTART) { /* * Only the master copy of the superblock and block @@ -1526,14 +1544,14 @@ static int process_block(ext2_filsys fs, } if (p->ino == EXT2_RESIZE_INO) { - if (blockcnt >= 0) { - /* Check that the block is in the correct place - * in the appropriate backup reserved gdt area */ - } else if (blockcnt == BLOCK_COUNT_IND) { - /* Check that the block is in the correct place - * in the primary reserved gdt area */ - } else /* The resize inode's DIND block should be - * allocated as a normal block. */ + /* + * The resize inode has already be sanity checked + * during pass #0 (the superblock checks). All we + * have to do is mark the double indirect block as + * being in use; all of the other blocks are handled + * by mark_table_blocks()). + */ + if (blockcnt == BLOCK_COUNT_DIND) mark_block_used(ctx, blk); } else mark_block_used(ctx, blk); diff --git a/e2fsck/problem.c b/e2fsck/problem.c index bb81d52..8951082 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -39,7 +39,8 @@ #define PROMPT_SUPPRESS 16 #define PROMPT_UNLINK 17 #define PROMPT_CLEAR_HTREE 18 -#define PROMPT_NULL 19 +#define PROMPT_RECREATE 19 +#define PROMPT_NULL 20 /* * These are the prompts which are used to ask the user if they want @@ -65,7 +66,8 @@ static const char *prompt[] = { N_("Suppress messages"),/* 16 */ N_("Unlink"), /* 17 */ N_("Clear HTree index"),/* 18 */ - "", /* 19 */ + N_("Recreate"), /* 19 */ + "", /* 29 */ }; /* @@ -92,7 +94,8 @@ static const char *preen_msg[] = { N_("SUPPRESSED"), /* 16 */ N_("UNLINKED"), /* 17 */ N_("HTREE INDEX CLEARED"),/* 18 */ - "", /* 19 */ + N_("WILL RECREATE"), /* 19 */ + "", /* 20 */ }; static const struct e2fsck_problem problem_table[] = { @@ -325,6 +328,11 @@ static const struct e2fsck_problem problem_table[] = { N_("Resize_@i not enabled, but the resize inode is non-zero. "), PROMPT_CLEAR, 0 }, + /* Resize inode invalid */ + { PR_0_RESIZE_INODE_INVALID, + N_("Resize @i not valid. "), + PROMPT_RECREATE, 0 }, + /* Pass 1 errors */ /* Pass 1: Checking inodes, blocks, and sizes */ @@ -718,7 +726,12 @@ static const struct e2fsck_problem problem_table[] = { N_("Bad @b @i has an indirect @b (%b) that conflicts with\n" "@f metadata. "), PROMPT_CLEAR, PR_LATCH_BBLOCK }, - + + /* Resize inode failed */ + { PR_1_RESIZE_INODE_CREATE, + N_("Resize @i (re)creation failed: %m."), + PROMPT_ABORT, 0 }, + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 1eb4ab6..aa0d30e 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -181,6 +181,9 @@ struct problem_context { /* Resize_inode not enabled, but resize inode is non-zero */ #define PR_0_CLEAR_RESIZE_INODE 0x00002F +/* Resize inode invalid */ +#define PR_0_RESIZE_INODE_INVALID 0x000030 + /* * Pass 1 errors */ @@ -416,6 +419,9 @@ struct problem_context { /* Bad block has indirect block that conflicts with filesystem block */ #define PR_1_BB_FS_BLOCK 0x01004D +/* Resize inode failed */ +#define PR_1_RESIZE_INODE_CREATE 0x01004E + /* * Pass 1b errors */ diff --git a/e2fsck/super.c b/e2fsck/super.c index 11ab7c9..71968ea 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -312,6 +312,116 @@ return_abort: return 1; } +/* + * Check the resize inode to make sure it is sane. We check both for + * the case where on-line resizing is not enabled (in which case the + * resize inode should be cleared) as well as the case where on-line + * resizing is enabled. + */ +void check_resize_inode(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + struct problem_context pctx; + int i, j, gdt_off, ind_off; + blk_t blk, pblk, expect; + __u32 *dind_buf = 0, *ind_buf; + errcode_t retval; + + clear_problem_context(&pctx); + pctx.ino = EXT2_RESIZE_INO; + retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); + if (retval) { + ctx->flags |= E2F_FLAG_RESIZE_INODE; + return; + } + + /* + * If the resize inode feature isn't set, then + * s_reserved_gdt_blocks must be zero, and the resize inode + * must be cleared. + */ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_RESIZE_INODE)) { + if (fs->super->s_reserved_gdt_blocks) { + pctx.num = fs->super->s_reserved_gdt_blocks; + if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS, + &pctx)) { + fs->super->s_reserved_gdt_blocks = 0; + ext2fs_mark_super_dirty(fs); + } + } + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) && + fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, + "clear_resize"); + } + return; + } + /* + * The resize inode feature is enabled; check to make sure the + * only block in use is the double indirect block + */ + blk = inode.i_block[EXT2_DIND_BLOCK]; + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (i != EXT2_DIND_BLOCK && inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) || !blk || + (blk < fs->super->s_first_data_block || + blk >= fs->super->s_blocks_count)) { + resize_inode_invalid: + if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, + "clear_resize"); + ctx->flags |= E2F_FLAG_RESIZE_INODE; + } + if (!(ctx->options & E2F_OPT_READONLY)) { + fs->super->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); + } + goto cleanup; + } + dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2, + "resize dind buffer"); + ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize); + + retval = io_channel_read_blk(fs->io, blk, 1, dind_buf); + if (retval) + goto resize_inode_invalid; + + gdt_off = fs->desc_blocks; + pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks; + for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4; + i++, gdt_off++, pblk++) { + gdt_off %= fs->blocksize/4; + if (dind_buf[gdt_off] != pblk) + goto resize_inode_invalid; + retval = io_channel_read_blk(fs->io, pblk, 1, ind_buf); + if (retval) + goto resize_inode_invalid; + ind_off = 0; + for (j = 1; j < fs->group_desc_count; j++) { + if (!ext2fs_bg_has_super(fs, j)) + continue; + expect = pblk + (j * fs->super->s_blocks_per_group); + if (ind_buf[ind_off] != expect) + goto resize_inode_invalid; + ind_off++; + } + } + +cleanup: + if (dind_buf) + ext2fs_free_mem(&dind_buf); + + } void check_super_block(e2fsck_t ctx) { @@ -325,7 +435,6 @@ void check_super_block(e2fsck_t ctx) dgrp_t i; blk_t should_be; struct problem_context pctx; - struct ext2_inode inode; __u32 free_blocks = 0, free_inodes = 0; inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super); @@ -368,6 +477,9 @@ void check_super_block(e2fsck_t ctx) MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max); check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count, MAX_CHECK, 0, sb->s_blocks_count / 4); + check_super_value(ctx, "reserved_gdt_blocks", + sb->s_reserved_gdt_blocks, MAX_CHECK, 0, + fs->blocksize/4); if (!ctx->num_blocks) { pctx.errcode = e2fsck_get_device_size(ctx); @@ -542,38 +654,7 @@ void check_super_block(e2fsck_t ctx) ext2fs_mark_super_dirty(fs); } - /* - * If the resize inode feature isn't set, then - * s_reserved_gdt_blocks must be zero, and the resize inode - * must be cleared. - */ - if (!(fs->super->s_feature_compat & - EXT2_FEATURE_COMPAT_RESIZE_INODE)) { - if (fs->super->s_reserved_gdt_blocks) { - pctx.num = fs->super->s_reserved_gdt_blocks; - if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS, - &pctx)) { - fs->super->s_reserved_gdt_blocks = 0; - ext2fs_mark_super_dirty(fs); - } - } - e2fsck_read_inode(ctx, EXT2_RESIZE_INO, &inode, - "check_resize"); - for (i=0; i < EXT2_N_BLOCKS; i++) { - if (inode.i_block[i]) - break; - } - pctx.ino = EXT2_RESIZE_INO; - if ((i < EXT2_N_BLOCKS) && - fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) { - for (i=0; i < EXT2_N_BLOCKS; i++) { - inode.i_block[i] = 0; - } - inode.i_blocks = 0; - e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, - "clear_resize"); - } - } + check_resize_inode(ctx); /* * Clean up any orphan inodes, if present. -- 1.8.3.1