From: Bob Peterson <rpete...@redhat.com> This patch fixes a couple of problems fsck.gfs2 when it discovered corrupt extended attributes and tried to delete them. First, in check_eattr_entries it wasn't reporting the corruption. Second, when the EA block was freed, it wasn't returning a proper return code which caused fsck to not update the inode accordingly. Third, it wasn't zeroing out the EA block address properly. Fourth, I did a small amount of reformatting.
rhbz#675723 --- gfs2/fsck/metawalk.c | 56 ++++++++++++++++++++++++++++++++++++++++--------- gfs2/fsck/pass1.c | 4 +-- gfs2/fsck/pass1c.c | 7 +++-- gfs2/fsck/pass2.c | 9 +++++-- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c index 94b0148..db7168b 100644 --- a/gfs2/fsck/metawalk.c +++ b/gfs2/fsck/metawalk.c @@ -755,6 +755,13 @@ static int check_eattr_entries(struct gfs2_inode *ip, bh, ea_hdr, ea_hdr_prev, pass->private)) { + log_err(_("Bad extended attribute " + "found at block %lld " + "(0x%llx)"), + (unsigned long long) + be64_to_cpu(*ea_data_ptr), + (unsigned long long) + be64_to_cpu(*ea_data_ptr)); if (query( _("Repair the bad Extended " "Attribute? (y/n) "))) { ea_hdr->ea_num_ptrs = i; @@ -807,12 +814,14 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct metawalk_fxns *pass) { struct gfs2_buffer_head *bh = NULL; - int error = 0; - - log_debug( _("Checking EA leaf block #%"PRIu64" (0x%" PRIx64 ").\n"), - block, block); if (pass->check_eattr_leaf) { + int error = 0; + + log_debug( _("Checking EA leaf block #%llu (0x%llx).\n"), + (unsigned long long)block, + (unsigned long long)block); + error = pass->check_eattr_leaf(ip, block, parent, &bh, pass->private); if (error < 0) { @@ -886,13 +895,18 @@ int find_remove_dup(struct gfs2_inode *ip, uint64_t block, const char *btype) /** * free_block_if_notdup - free blocks associated with an inode, but if it's a * duplicate, just remove that designation instead. - * Returns: 0 if the block was freed, 1 if a duplicate reference was removed + * Returns: 1 if the block was freed, 0 if a duplicate reference was removed + * Note: The return code is handled this way because there are places in + * metawalk.c that assume "1" means "change was made" and "0" means + * change was not made. */ int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block, const char *btype) { - if (!find_remove_dup(ip, block, btype)) + if (!find_remove_dup(ip, block, btype)) { /* not a dup */ fsck_blockmap_set(ip, block, btype, gfs2_block_free); + return 1; + } return 0; } @@ -1446,16 +1460,36 @@ int delete_data(struct gfs2_inode *ip, uint64_t block, void *private) int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, void *private) { - return delete_block_if_notdup(ip, block, NULL, - _("indirect extended attribute"), - private); + int ret; + + ret = delete_block_if_notdup(ip, block, NULL, + _("indirect extended attribute"), + private); + /* Even if it's a duplicate reference, we want to eliminate the + reference itself, and adjust di_blocks accordingly. */ + if (ip->i_di.di_eattr) { + ip->i_di.di_blocks--; + if (block == ip->i_di.di_eattr) + ip->i_di.di_eattr = 0; + bmodified(ip->i_bh); + } + return ret; } int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent, struct gfs2_buffer_head **bh, void *private) { - return delete_block_if_notdup(ip, block, NULL, _("extended attribute"), - private); + int ret; + + ret = delete_block_if_notdup(ip, block, NULL, _("extended attribute"), + private); + if (ip->i_di.di_eattr) { + ip->i_di.di_blocks--; + if (block == ip->i_di.di_eattr) + ip->i_di.di_eattr = 0; + bmodified(ip->i_bh); + } + return ret; } static int alloc_metalist(struct gfs2_inode *ip, uint64_t block, diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c index 32ebfba..3e6b816 100644 --- a/gfs2/fsck/pass1.c +++ b/gfs2/fsck/pass1.c @@ -1181,9 +1181,7 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip) (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); check_metatree(ip, &invalidate_fxns); - if (fsck_blockmap_set(ip, block, _("invalid mode"), - gfs2_inode_invalid)) - goto bad_dinode; + check_inode_eattr(ip, &invalidate_fxns); return 0; } else if (error) goto bad_dinode; diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c index 150dc2f..f7a7842 100644 --- a/gfs2/fsck/pass1c.c +++ b/gfs2/fsck/pass1c.c @@ -62,9 +62,10 @@ static int ask_remove_eattr(struct gfs2_inode *ip) ip->i_di.di_eattr = 0; bmodified(ip->i_bh); log_err( _("Bad Extended Attribute removed.\n")); - } else - log_err( _("Bad Extended Attribute not removed.\n")); - return 1; + return 1; + } + log_err( _("Bad Extended Attribute not removed.\n")); + return 0; } static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block, diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c index 7e20315..e02fdd0 100644 --- a/gfs2/fsck/pass2.c +++ b/gfs2/fsck/pass2.c @@ -203,9 +203,9 @@ static int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr, struct gfs2_ea_header *ea_hdr_prev, void *private) { - uint64_t block = be64_to_cpu(*ea_data_ptr); + uint64_t block = be64_to_cpu(*ea_data_ptr); - return delete_metadata(ip, block, NULL, 0, private); + return delete_metadata(ip, block, NULL, 0, private); } struct metawalk_fxns pass2_fxns_delete = { @@ -343,7 +343,10 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent, entry_ip = ip; else entry_ip = fsck_load_inode(sdp, entryblock); - check_inode_eattr(entry_ip, &pass2_fxns_delete); + if (ip->i_di.di_eattr) { + check_inode_eattr(entry_ip, + &pass2_fxns_delete); + } check_metatree(entry_ip, &pass2_fxns_delete); if (entry_ip != ip) fsck_inode_put(&entry_ip); -- 1.7.7.5