Before this patch, pass1b would find a duplicate reference to
extended attributes, which may be an entire tree structure. After
deciding which inode gets to keep the extended attributes, it
removes the extended attributes from other dinodes with another
pointer to it. However, if the EAs are big, and there's a whole
list of them as indirect blocks, we only want to traverse the
indirect list if that list is owned by the second (bad) reference.

If we do traverse the tree, we're basically freeing EA blocks
belonging to the only remaining valid reference, and that's wrong.

This patch adds a check to function resolve_dup_references that
avoids a whole EA tree traversal if the duplicate EA reference is
the root of the EAs (not an indirect list of blocks). Instead,
the reference to the block is just removed from the inode.

rhbz#1257625
---
 gfs2/fsck/pass1b.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 382d5e9..4c41fef 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -198,8 +198,27 @@ static void resolve_dup_references(struct gfs2_sbd *sdp, 
struct duptree *dt,
                   it again. That could free blocks that used to be duplicate
                   references that are now resolved (and gone). */
                if (q != GFS2_BLKST_FREE) {
-                       /* Clear the EAs for the inode first */
-                       check_inode_eattr(ip, &pass1b_fxns_delete);
+                       /* If the inode's eattr pointer is to the duplicate
+                          ref block, we don't want to call check_inode_eattr
+                          because that would traverse the structure, and it's
+                          not ours to do anymore; it rightly belongs to a
+                          different dinode. On the other hand, if the dup
+                          block is buried deep within the eattr structure
+                          of this dinode, we need to traverse the structure
+                          because it IS ours, and we need to remove all the
+                          eattr leaf blocks: they do belong to us (except for
+                          the duplicate referenced one, which is handled). */
+                       if (ip->i_di.di_eattr == dt->block) {
+                               ip->i_di.di_eattr = 0;
+                               if (ip->i_di.di_blocks > 0)
+                                       ip->i_di.di_blocks--;
+                               ip->i_di.di_flags &= ~GFS2_DIF_EA_INDIRECT;
+                               bmodified(ip->i_bh);
+                               dup_listent_delete(dt, id);
+                       } else {
+                               /* Clear the EAs for the inode first */
+                               check_inode_eattr(ip, &pass1b_fxns_delete);
+                       }
                        /* If the reference was as metadata or data, we've got
                           a corrupt dinode that will be deleted. */
                        if ((this_ref != ref_as_ea) &&
-- 
2.4.3

Reply via email to