Blame SOURCES/bz1178604-4-fsck_gfs2_Reprocess_nodes_if_anything_changed_addendum_1_of_2.patch

7ddfb4
commit 5eccbb33c28604436d38f4148fbb651151af1aef
7ddfb4
Author: Bob Peterson <rpeterso@redhat.com>
7ddfb4
Date:   Wed Jan 14 10:54:28 2015 -0600
7ddfb4
7ddfb4
    fsck.gfs2: Reprocess nodes if anything changed - addendum 1 of 2
7ddfb4
    
7ddfb4
    Before this patch, fsck.gfs2 called "reprocess_inode" in several
7ddfb4
    places, especially when the number of blocks had changed from the
7ddfb4
    original. That works in almost all cases. However, there's a corner
7ddfb4
    case where a block may be deleted, due to errors (for example, a
7ddfb4
    bad directory leaf), and another block takes its place. In that
7ddfb4
    case the count of the number of blocks is still the same, but the
7ddfb4
    inode should be reprocessed anyway, because the deleted blocks
7ddfb4
    and replacement blocks may be at different locations or maybe of
7ddfb4
    different types. A hash table block may be "data" when it's freed,
7ddfb4
    but if the block is reused, it may be repurposed as a "leaf" block.
7ddfb4
    That may confuse subsequent processing. Another reason to call
7ddfb4
    reprocess_inode is when the goal block changes to a value outside
7ddfb4
    the resource group, due to block allocations for repairs.
7ddfb4
    
7ddfb4
    This patch is part 1 of 2 addendum fixes to make fsck fix i_goal
7ddfb4
    values correctly. The original three patches to fix this problem were
7ddfb4
    checked in against a wrong bz 1149516 (which is the rhel6 version
7ddfb4
    of the bug). The complete fix contains those three patches (b09dae8,
7ddfb4
    d79246d, c5b09c4) and this two-patch addendum for a total of 5
7ddfb4
    patches.
7ddfb4
    
7ddfb4
    Resolves: rhbz#1178604
7ddfb4
    Signed-off-by: Bob Peterson <rpeterso@redhat.com>
7ddfb4
7ddfb4
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
7ddfb4
index 9672c7a..b2e35e2 100644
7ddfb4
--- a/gfs2/fsck/lost_n_found.c
7ddfb4
+++ b/gfs2/fsck/lost_n_found.c
7ddfb4
@@ -174,7 +174,6 @@ void make_sure_lf_exists(struct gfs2_inode *ip)
7ddfb4
 int add_inode_to_lf(struct gfs2_inode *ip){
7ddfb4
 	char tmp_name[256];
7ddfb4
 	__be32 inode_type;
7ddfb4
-	uint64_t lf_blocks;
7ddfb4
 	struct gfs2_sbd *sdp = ip->i_sbd;
7ddfb4
 	int err = 0;
7ddfb4
 	uint32_t mode;
7ddfb4
@@ -184,7 +183,6 @@ int add_inode_to_lf(struct gfs2_inode *ip){
7ddfb4
 		log_err( _("Trying to add lost+found to itself...skipping"));
7ddfb4
 		return 0;
7ddfb4
 	}
7ddfb4
-	lf_blocks = lf_dip->i_di.di_blocks;
7ddfb4
 
7ddfb4
 	if (sdp->gfs1)
7ddfb4
 		mode = gfs_to_gfs2_mode(ip);
7ddfb4
@@ -242,10 +240,6 @@ int add_inode_to_lf(struct gfs2_inode *ip){
7ddfb4
 			 tmp_name, strerror(errno));
7ddfb4
 		exit(FSCK_ERROR);
7ddfb4
 	}
7ddfb4
-	/* If the lf directory had new blocks added we have to mark them
7ddfb4
-	   properly in the bitmap so they're not freed. */
7ddfb4
-	if (lf_dip->i_di.di_blocks != lf_blocks)
7ddfb4
-		reprocess_inode(lf_dip, "lost+found");
7ddfb4
 
7ddfb4
 	/* This inode is linked from lost+found */
7ddfb4
 	incr_link_count(ip->i_di.di_num, lf_dip, _("from lost+found"));
7ddfb4
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
7ddfb4
index 9aa9fb8..82abb40 100644
7ddfb4
--- a/gfs2/fsck/metawalk.c
7ddfb4
+++ b/gfs2/fsck/metawalk.c
7ddfb4
@@ -1639,11 +1639,11 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
7ddfb4
 {
7ddfb4
 	struct gfs2_inode *ip;
7ddfb4
 	int error = 0;
7ddfb4
-	uint64_t cur_blks;
7ddfb4
+	struct alloc_state as;
7ddfb4
 
7ddfb4
 	ip = fsck_load_inode(sdp, block);
7ddfb4
 
7ddfb4
-	cur_blks = ip->i_di.di_blocks;
7ddfb4
+	astate_save(ip, &as);
7ddfb4
 
7ddfb4
 	if (ip->i_di.di_flags & GFS2_DIF_EXHASH)
7ddfb4
 		error = check_leaf_blks(ip, pass);
7ddfb4
@@ -1653,7 +1653,7 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
7ddfb4
 	if (error < 0)
7ddfb4
 		stack;
7ddfb4
 
7ddfb4
-	if (ip->i_di.di_blocks != cur_blks)
7ddfb4
+	if (astate_changed(ip, &as))
7ddfb4
 		reprocess_inode(ip, _("Current"));
7ddfb4
 
7ddfb4
 	fsck_inode_put(&ip); /* does a brelse */
7ddfb4
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
7ddfb4
index 2971b8c..40c80b9 100644
7ddfb4
--- a/gfs2/fsck/pass2.c
7ddfb4
+++ b/gfs2/fsck/pass2.c
7ddfb4
@@ -1712,7 +1712,8 @@ build_it:
7ddfb4
 static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
7ddfb4
 		     int builder(struct gfs2_sbd *sdp))
7ddfb4
 {
7ddfb4
-	uint64_t iblock = 0, cur_blks;
7ddfb4
+	uint64_t iblock = 0;
7ddfb4
+	struct alloc_state as;
7ddfb4
 	struct dir_status ds = {0};
7ddfb4
 	int error = 0;
7ddfb4
 
7ddfb4
@@ -1729,14 +1730,14 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
7ddfb4
 
7ddfb4
 	pass2_fxns.private = (void *) &ds;
7ddfb4
 	if (ds.q == gfs2_bad_block) {
7ddfb4
-		cur_blks = sysinode->i_di.di_blocks;
7ddfb4
+		astate_save(sysinode, &as);
7ddfb4
 		/* First check that the directory's metatree is valid */
7ddfb4
 		error = check_metatree(sysinode, &pass2_fxns);
7ddfb4
 		if (error < 0) {
7ddfb4
 			stack;
7ddfb4
 			return error;
7ddfb4
 		}
7ddfb4
-		if (sysinode->i_di.di_blocks != cur_blks)
7ddfb4
+		if (astate_changed(sysinode, &as))
7ddfb4
 			reprocess_inode(sysinode, _("System inode"));
7ddfb4
 	}
7ddfb4
 	error = check_dir(sysinode->i_sbd, iblock, &pass2_fxns);
7ddfb4
@@ -1757,7 +1758,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
7ddfb4
 	if (!ds.dotdir) {
7ddfb4
 		log_err( _("No '.' entry found for %s directory.\n"), dirname);
7ddfb4
 		if (query( _("Is it okay to add '.' entry? (y/n) "))) {
7ddfb4
-			cur_blks = sysinode->i_di.di_blocks;
7ddfb4
+			astate_save(sysinode, &as);
7ddfb4
 			log_warn( _("Adding '.' entry\n"));
7ddfb4
 			error = dir_add(sysinode, ".", 1, &(sysinode->i_di.di_num),
7ddfb4
 			                (sysinode->i_sbd->gfs1 ? GFS_FILE_DIR : DT_DIR));
7ddfb4
@@ -1766,7 +1767,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
7ddfb4
 				         strerror(errno));
7ddfb4
 				return -errno;
7ddfb4
 			}
7ddfb4
-			if (cur_blks != sysinode->i_di.di_blocks)
7ddfb4
+			if (astate_changed(sysinode, &as))
7ddfb4
 				reprocess_inode(sysinode, dirname);
7ddfb4
 			/* This system inode is linked to itself via '.' */
7ddfb4
 			incr_link_count(sysinode->i_di.di_num, sysinode,
7ddfb4
@@ -1848,7 +1849,8 @@ static inline int is_system_dir(struct gfs2_sbd *sdp, uint64_t block)
7ddfb4
  */
7ddfb4
 int pass2(struct gfs2_sbd *sdp)
7ddfb4
 {
7ddfb4
-	uint64_t dirblk, cur_blks;
7ddfb4
+	uint64_t dirblk;
7ddfb4
+	struct alloc_state as;
7ddfb4
 	uint8_t q;
7ddfb4
 	struct dir_status ds = {0};
7ddfb4
 	struct gfs2_inode *ip;
7ddfb4
@@ -1915,14 +1917,14 @@ int pass2(struct gfs2_sbd *sdp)
7ddfb4
 			/* First check that the directory's metatree
7ddfb4
 			 * is valid */
7ddfb4
 			ip = fsck_load_inode(sdp, dirblk);
7ddfb4
-			cur_blks = ip->i_di.di_blocks;
7ddfb4
+			astate_save(ip, &as);
7ddfb4
 			error = check_metatree(ip, &pass2_fxns);
7ddfb4
 			fsck_inode_put(&ip);
7ddfb4
 			if (error < 0) {
7ddfb4
 				stack;
7ddfb4
 				return error;
7ddfb4
 			}
7ddfb4
-			if (ip->i_di.di_blocks != cur_blks)
7ddfb4
+			if (astate_changed(ip, &as))
7ddfb4
 				reprocess_inode(ip, "current");
7ddfb4
 		}
7ddfb4
 		error = check_dir(sdp, dirblk, &pass2_fxns);
7ddfb4
@@ -1982,7 +1984,7 @@ int pass2(struct gfs2_sbd *sdp)
7ddfb4
 				(unsigned long long)dirblk);
7ddfb4
 
7ddfb4
 			if (query( _("Is it okay to add '.' entry? (y/n) "))) {
7ddfb4
-				cur_blks = ip->i_di.di_blocks;
7ddfb4
+				astate_save(ip, &as);
7ddfb4
 				error = dir_add(ip, ".", 1, &(ip->i_di.di_num),
7ddfb4
 						(sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
7ddfb4
 				if (error) {
7ddfb4
@@ -1990,7 +1992,7 @@ int pass2(struct gfs2_sbd *sdp)
7ddfb4
 					        strerror(errno));
7ddfb4
 					return -errno;
7ddfb4
 				}
7ddfb4
-				if (cur_blks != ip->i_di.di_blocks) {
7ddfb4
+				if (astate_changed(ip, &as)) {
7ddfb4
 					char dirname[80];
7ddfb4
 
7ddfb4
 					sprintf(dirname, _("Directory at %lld "
7ddfb4
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
7ddfb4
index 9582b5b..167dac0 100644
7ddfb4
--- a/gfs2/fsck/pass3.c
7ddfb4
+++ b/gfs2/fsck/pass3.c
7ddfb4
@@ -24,7 +24,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
7ddfb4
 	int filename_len = 2;
7ddfb4
 	int err;
7ddfb4
 	struct gfs2_inode *ip, *pip;
7ddfb4
-	uint64_t cur_blks;
7ddfb4
+	struct alloc_state as;
7ddfb4
 
7ddfb4
 	ip = fsck_load_inode(sdp, block);
7ddfb4
 	pip = fsck_load_inode(sdp, newdotdot);
7ddfb4
@@ -39,7 +39,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
7ddfb4
 		log_warn( _("Unable to remove \"..\" directory entry.\n"));
7ddfb4
 	else
7ddfb4
 		decr_link_count(olddotdot, block, _("old \"..\""));
7ddfb4
-	cur_blks = ip->i_di.di_blocks;
7ddfb4
+	astate_save(ip, &as);
7ddfb4
 	err = dir_add(ip, filename, filename_len, &pip->i_di.di_num,
7ddfb4
 		      (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
7ddfb4
 	if (err) {
7ddfb4
@@ -47,7 +47,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
7ddfb4
 		        filename, strerror(errno));
7ddfb4
 		exit(FSCK_ERROR);
7ddfb4
 	}
7ddfb4
-	if (cur_blks != ip->i_di.di_blocks) {
7ddfb4
+	if (astate_changed(ip, &as)) {
7ddfb4
 		char dirname[80];
7ddfb4
 
7ddfb4
 		sprintf(dirname, _("Directory at %lld (0x%llx)"),
7ddfb4
@@ -179,6 +179,7 @@ int pass3(struct gfs2_sbd *sdp)
7ddfb4
 	struct dir_info *di, *tdi;
7ddfb4
 	struct gfs2_inode *ip;
7ddfb4
 	uint8_t q;
7ddfb4
+	struct alloc_state lf_as = {.as_blocks = 0, .as_meta_goal = 0};
7ddfb4
 
7ddfb4
 	di = dirtree_find(sdp->md.rooti->i_di.di_num.no_addr);
7ddfb4
 	if (di) {
7ddfb4
@@ -225,6 +226,8 @@ int pass3(struct gfs2_sbd *sdp)
7ddfb4
 	 */
7ddfb4
 	log_info( _("Checking directory linkage.\n"));
7ddfb4
 	for (tmp = osi_first(&dirtree); tmp; tmp = next) {
7ddfb4
+		if (lf_dip && lf_as.as_blocks == 0)
7ddfb4
+			astate_save(lf_dip, &lf_as);
7ddfb4
 		next = osi_next(tmp);
7ddfb4
 		di = (struct dir_info *)tmp;
7ddfb4
 		while (!di->checked) {
7ddfb4
@@ -327,8 +330,14 @@ int pass3(struct gfs2_sbd *sdp)
7ddfb4
 			break;
7ddfb4
 		}
7ddfb4
 	}
7ddfb4
-	if (lf_dip)
7ddfb4
+	if (lf_dip) {
7ddfb4
+		/* If the lf directory had new blocks added we have to mark
7ddfb4
+		   them properly in the blockmap so they're not freed. */
7ddfb4
+		if (astate_changed(lf_dip, &lf_as))
7ddfb4
+			reprocess_inode(lf_dip, "lost+found");
7ddfb4
+		
7ddfb4
 		log_debug( _("At end of pass3, lost+found entries is %u\n"),
7ddfb4
 				  lf_dip->i_di.di_entries);
7ddfb4
+	}
7ddfb4
 	return FSCK_OK;
7ddfb4
 }
7ddfb4
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
7ddfb4
index 7b3cb87..f0c13fc 100644
7ddfb4
--- a/gfs2/fsck/pass4.c
7ddfb4
+++ b/gfs2/fsck/pass4.c
7ddfb4
@@ -48,12 +48,15 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
7ddfb4
 	struct gfs2_inode *ip;
7ddfb4
 	int lf_addition = 0;
7ddfb4
 	uint8_t q;
7ddfb4
+	struct alloc_state lf_as = {.as_blocks = 0, .as_meta_goal = 0};
7ddfb4
 
7ddfb4
 	/* FIXME: should probably factor this out into a generic
7ddfb4
 	 * scanning fxn */
7ddfb4
 	for (tmp = osi_first(&inodetree); tmp; tmp = next) {
7ddfb4
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
7ddfb4
 			return 0;
7ddfb4
+		if (lf_dip && lf_as.as_blocks == 0)
7ddfb4
+			astate_save(lf_dip, &lf_as);
7ddfb4
 		next = osi_next(tmp);
7ddfb4
 		if (!(ii = (struct inode_info *)tmp)) {
7ddfb4
 			log_crit( _("osi_tree broken in scan_info_list!!\n"));
7ddfb4
@@ -182,6 +185,9 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
7ddfb4
 			 (unsigned long long)ii->di_num.no_addr, ii->di_nlink);
7ddfb4
 	} /* osi_list_foreach(tmp, list) */
7ddfb4
 
7ddfb4
+	if (lf_dip && astate_changed(lf_dip, &lf_as))
7ddfb4
+		reprocess_inode(lf_dip, "lost+found");
7ddfb4
+
7ddfb4
 	if (lf_addition) {
7ddfb4
 		if (!(ii = inodetree_find(lf_dip->i_di.di_num.no_addr))) {
7ddfb4
 			log_crit( _("Unable to find lost+found inode in inode_hash!!\n"));
7ddfb4
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
7ddfb4
index 276c726..66b9c7a 100644
7ddfb4
--- a/gfs2/fsck/util.h
7ddfb4
+++ b/gfs2/fsck/util.h
7ddfb4
@@ -12,6 +12,11 @@
7ddfb4
 #define INODE_VALID 1
7ddfb4
 #define INODE_INVALID 0
7ddfb4
 
7ddfb4
+struct alloc_state {
7ddfb4
+	uint64_t as_blocks;
7ddfb4
+	uint64_t as_meta_goal;
7ddfb4
+};
7ddfb4
+
7ddfb4
 struct di_info *search_list(osi_list_t *list, uint64_t addr);
7ddfb4
 void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked);
7ddfb4
 void warm_fuzzy_stuff(uint64_t block);
7ddfb4
@@ -27,6 +32,21 @@ extern const char *reftypes[ref_types + 1];
7ddfb4
 #define BLOCKMAP_BYTE_OFFSET4(x) (((x) & 0x0000000000000001) << 2)
7ddfb4
 #define BLOCKMAP_MASK4 (0xf)
7ddfb4
 
7ddfb4
+static inline void astate_save(struct gfs2_inode *ip, struct alloc_state *as)
7ddfb4
+{
7ddfb4
+	as->as_blocks = ip->i_di.di_blocks;
7ddfb4
+	as->as_meta_goal = ip->i_di.di_goal_meta;
7ddfb4
+}
7ddfb4
+
7ddfb4
+static inline int astate_changed(struct gfs2_inode *ip, struct alloc_state *as)
7ddfb4
+{
7ddfb4
+	if (as->as_blocks != ip->i_di.di_blocks)
7ddfb4
+		return 1;
7ddfb4
+	if (as->as_meta_goal != ip->i_di.di_goal_meta)
7ddfb4
+		return 1;
7ddfb4
+	return 0;
7ddfb4
+}
7ddfb4
+
7ddfb4
 static inline uint8_t block_type(uint64_t bblock)
7ddfb4
 {
7ddfb4
 	static unsigned char *byte;