commit b09dae840a4e91e59cf73089da8e18bc0f299e80 Author: Abhi Das Date: Fri Dec 5 10:16:49 2014 -0600 fsck.gfs2: fix broken i_goal values in inodes This patch allows fsck.gfs2 to fix bad values for the i_goal field in inodes that could arise from a gfs1->gfs2 conversion or through other corruption Resolves: rhbz#1149516 Signed-off-by: Abhi Das diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c index 329fc3b..9aa9fb8 100644 --- a/gfs2/fsck/metawalk.c +++ b/gfs2/fsck/metawalk.c @@ -1395,7 +1395,8 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp, */ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, struct gfs2_buffer_head *bh, int head_size, - uint64_t *blks_checked, uint64_t *error_blk) + uint64_t *last_block, uint64_t *blks_checked, + uint64_t *error_blk) { int error = 0, rc = 0; uint64_t block, *ptr; @@ -1410,7 +1411,7 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass, if (skip_this_pass || fsck_abort) return error; - block = be64_to_cpu(*ptr); + *last_block = block = be64_to_cpu(*ptr); /* It's important that we don't call valid_block() and bypass calling check_data on invalid blocks because that would defeat the rangecheck_block related functions in @@ -1490,7 +1491,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass) struct gfs2_buffer_head *bh; uint32_t height = ip->i_di.di_height; int i, head_size; - uint64_t blks_checked = 0; + uint64_t blks_checked = 0, last_block = 0; int error, rc; int metadata_clean = 0; uint64_t error_blk = 0; @@ -1514,6 +1515,9 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass) * comprise the directory hash table, so we perform the directory * checks and exit. */ if (is_dir(&ip->i_di, ip->i_sbd->gfs1)) { + last_block = ip->i_di.di_num.no_addr; + if (pass->check_i_goal) + pass->check_i_goal(ip, last_block, pass->private); if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH)) goto out; /* check validity of leaf blocks and leaf chains */ @@ -1540,7 +1544,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass) if (pass->check_data) error = check_data(ip, pass, bh, head_size, - &blks_checked, &error_blk); + &last_block, &blks_checked, &error_blk); if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS) pass->big_file_msg(ip, blks_checked); } @@ -1552,6 +1556,8 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass) (unsigned long long)ip->i_di.di_num.no_addr); fflush(stdout); } + if (!error && pass->check_i_goal) + pass->check_i_goal(ip, last_block, pass->private); undo_metalist: if (!error) goto out; diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h index a4e0676..b3c1299 100644 --- a/gfs2/fsck/metawalk.h +++ b/gfs2/fsck/metawalk.h @@ -90,6 +90,7 @@ enum meta_check_rc { * check_dentry: * check_eattr_entry: * check_eattr_extentry: + * check_i_goal: */ struct metawalk_fxns { void *private; @@ -141,6 +142,8 @@ struct metawalk_fxns { struct gfs2_ea_header *ea_hdr, struct gfs2_ea_header *ea_hdr_prev, void *private); + int (*check_i_goal) (struct gfs2_inode *ip, uint64_t goal_blk, + void *private); int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers, int leaf_pointer_errors, void *private); void (*big_file_msg) (struct gfs2_inode *ip, uint64_t blks_checked); diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c index 90ca357..60f0356 100644 --- a/gfs2/fsck/pass1.c +++ b/gfs2/fsck/pass1.c @@ -62,6 +62,7 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, struct gfs2_ea_header *ea_hdr, struct gfs2_ea_header *ea_hdr_prev, void *private); +static int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk, void *private); static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers, int leaf_pointer_errors, void *private); static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block, @@ -99,6 +100,7 @@ struct metawalk_fxns pass1_fxns = { .check_dentry = NULL, .check_eattr_entry = check_eattr_entries, .check_eattr_extentry = check_extended_leaf_eattr, + .check_i_goal = NULL, .finish_eattr_indir = finish_eattr_indir, .big_file_msg = big_file_comfort, .repair_leaf = pass1_repair_leaf, @@ -792,6 +794,48 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr, return error; } +/** + * check_i_goal + * @ip + * @goal_blk: What the goal block should be for this inode + * + * The goal block for a regular file is typically the last + * data block of the file. If we can't get the right value, + * the inode metadata block is the next best thing. + * + * Returns: 0 if corrected, 1 if not corrected + */ +static int check_i_goal(struct gfs2_inode *ip, uint64_t goal_blk, + void *private) +{ + struct gfs2_sbd *sdp = ip->i_sbd; + + if (fsck_system_inode(sdp, ip->i_di.di_num.no_addr)) + return 0; + if (!goal_blk) + goal_blk = ip->i_di.di_num.no_addr; + if (ip->i_di.di_goal_meta != goal_blk || + ip->i_di.di_goal_data != goal_blk) { + log_err( _("Error: inode %llu (0x%llx) has invalid " + "allocation goal block %llu (0x%llx). Should" + " be %llu (0x%llx)\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_goal_meta, + (unsigned long long)ip->i_di.di_goal_meta, + (unsigned long long)goal_blk, + (unsigned long long)goal_blk); + if (query( _("Fix the invalid goal block? (y/n) "))) { + ip->i_di.di_goal_meta = ip->i_di.di_goal_data = goal_blk; + bmodified(ip->i_bh); + } else { + log_err(_("Invalid goal block not fixed.\n")); + return 1; + } + } + return 0; +} + static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, void *private) @@ -1624,6 +1668,9 @@ int pass1(struct gfs2_sbd *sdp) /* Make sure the system inodes are okay & represented in the bitmap. */ check_system_inodes(sdp); + /* Turn the check_i_goal function ON for the non-system inodes */ + pass1_fxns.check_i_goal = check_i_goal; + /* So, do we do a depth first search starting at the root * inode, or use the rg bitmaps, or just read every fs block * to find the inodes? If we use the depth first search, why