A new user interface for you! Read more...

File fsck_gfs2_truncates_directories_with_more_than_100,000_entries.patch of Package cluster

From f230a400c7b99d9cc95e19ab6fdca9fcd64811ee Mon Sep 17 00:00:00 2001
From: Bob Peterson <rpeterso@redhat.com>
Date: Fri, 17 Sep 2010 13:42:50 -0500
Subject: [PATCH] fsck.gfs2 truncates directories with more than 100,000 entries

This patch allows fsck.gfs2 to properly process "continuation
leaf blocks" in directories.  It also fixes a memory leak.

rhbz#628013
---
 gfs2/fsck/link.c         |    8 ++
 gfs2/fsck/lost_n_found.c |   18 ++++-
 gfs2/fsck/metawalk.c     |  172 ++++++++++++++++++++++------------------------
 gfs2/fsck/pass1.c        |   15 +++-
 gfs2/libgfs2/fs_ops.c    |   12 ++-
 5 files changed, 123 insertions(+), 102 deletions(-)

diff --git a/gfs2/fsck/link.c b/gfs2/fsck/link.c
index 5bb3ae2..ce8eb52 100644
--- a/gfs2/fsck/link.c
+++ b/gfs2/fsck/link.c
@@ -68,6 +68,14 @@ int decrement_link(uint64_t inode_no, uint64_t referenced_from,
 	/* If the list has entries, look for one that matches
 	 * inode_no */
 	if(ii) {
+		if (!ii->counted_links) {
+			log_debug( _("Directory %lld (0x%llx)'s link to "
+			     " %"PRIu64" (0x%" PRIx64 ") via %s is zero!\n"),
+			   (unsigned long long)referenced_from,
+			   (unsigned long long)referenced_from,
+			   inode_no, inode_no, why);
+			return 0;
+		}
 		ii->counted_links--;
 		log_debug( _("Directory %lld (0x%llx) decremented counted "
 			     "links to %u for %"PRIu64" (0x%" PRIx64 ") "
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 4a3a926..3256bdc 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -108,10 +108,20 @@ int add_inode_to_lf(struct gfs2_inode *ip){
 				       ip->i_di.di_num.no_addr,
 				       _(".. unlinked, moving to lost+found"));
 			dip = fsck_load_inode(sdp, di->dotdot_parent);
-			dip->i_di.di_nlink--;
-			log_debug(_("Decrementing its links to %d\n"),
-				  dip->i_di.di_nlink);
-			bmodified(dip->i_bh);
+			if (dip->i_di.di_nlink > 0) {
+				dip->i_di.di_nlink--;
+				log_debug(_("Decrementing its links to %d\n"),
+					  dip->i_di.di_nlink);
+				bmodified(dip->i_bh);
+			} else if (!dip->i_di.di_nlink) {
+				log_debug(_("Its link count is zero.\n"));
+			} else {
+				log_debug(_("Its link count is %d!  "
+					    "Changing it to 0.\n"),
+					  dip->i_di.di_nlink);
+				dip->i_di.di_nlink = 0;
+				bmodified(dip->i_bh);
+			}
 			fsck_inode_put(&dip);
 			di = NULL;
 		} else
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index ef1b6b5..2151ced 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -377,7 +377,6 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 				log_debug( _("First dirent is a sentinel (place holder).\n"));
 				first = 0;
 			} else {
-				/* FIXME: Do something about this */
 				log_err( _("Directory entry with inode number of "
 					"zero in leaf %llu (0x%llx) of "
 					"directory %llu (0x%llx)!\n"),
@@ -526,6 +525,68 @@ static int fix_leaf_pointers(struct gfs2_inode *dip, int *lindex,
 	return 0;
 }
 
+/* check_num_ptrs - check a previously processed leaf's pointer count */
+static int check_num_ptrs(struct gfs2_inode *ip, uint64_t old_leaf,
+			  int *ref_count, int *exp_count, int *lindex,
+			  struct gfs2_leaf *oldleaf)
+{
+	int factor = 0, divisor = *ref_count, multiple = 1, error = 0;
+	struct gfs2_buffer_head *lbh;
+
+	/* Check to see if the number of pointers we found is a power of 2.
+	   It needs to be and if it's not we need to fix it.*/
+	while (divisor > 1) {
+		factor++;
+		divisor /= 2;
+		multiple = multiple << 1;
+	}
+	if (*ref_count != multiple) {
+		log_err( _("Directory #%llu (0x%llx) has an "
+			   "invalid number of pointers to "
+			   "leaf #%llu (0x%llx)\n\tFound: %u, "
+			   "which is not a factor of 2.\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)old_leaf,
+			 (unsigned long long)old_leaf, *ref_count);
+		if (!query( _("Attempt to fix it? (y/n) "))) {
+			log_err( _("Directory inode was not fixed.\n"));
+			return 1;
+		}
+		error = fix_leaf_pointers(ip, lindex, *ref_count, multiple);
+		if (error)
+			return error;
+		*ref_count = multiple;
+		log_err( _("Directory inode was fixed.\n"));
+	}
+	/* Check to see if the counted number of leaf pointers is what we
+	   expect. */
+	if (*ref_count != *exp_count) {
+		log_err( _("Directory #%llu (0x%llx) has an "
+			   "incorrect number of pointers to "
+			   "leaf #%llu (0x%llx)\n\tFound: "
+			   "%u,  Expected: %u\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)old_leaf,
+			 (unsigned long long)old_leaf, *ref_count, *exp_count);
+		if (!query( _("Attempt to fix it? (y/n) "))) {
+			log_err( _("Directory leaf was not fixed.\n"));
+			return 1;
+		}
+		lbh = bread(ip->i_sbd, old_leaf);
+		gfs2_leaf_in(oldleaf, lbh);
+		log_err( _("Leaf depth was %d, changed to %d\n"),
+			 oldleaf->lf_depth, ip->i_di.di_depth - factor);
+		oldleaf->lf_depth = ip->i_di.di_depth - factor;
+		gfs2_leaf_out(oldleaf, lbh);
+		brelse(lbh);
+		*exp_count = *ref_count;
+		log_err( _("Directory leaf was fixed.\n"));
+	}
+	return 0;
+}
+
 /* Checks exhash directory entries */
 static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 {
@@ -582,76 +643,17 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 			ref_count++;
 			continue;
 		}
-		if (gfs2_check_range(ip->i_sbd, old_leaf) == 0) {
-			int factor = 0, divisor = ref_count, multiple = 1;
-
-			/* Check to see if the number of pointers we found is
-			   a power of 2.  It needs to be and if it's not we
-			   need to fix it.*/
-			while (divisor > 1) {
-				factor++;
-				divisor /= 2;
-				multiple = multiple << 1;
-			}
-			if (ref_count != multiple) {
-				log_err( _("Directory #%llu (0x%llx) has an "
-					   "invalid number of pointers to "
-					   "leaf #%llu (0x%llx)\n\tFound: %u, "
-					   "which is not a factor of 2.\n"),
-					 (unsigned long long)
-					 ip->i_di.di_num.no_addr,
-					 (unsigned long long)
-					 ip->i_di.di_num.no_addr,
-					 (unsigned long long)old_leaf,
-					 (unsigned long long)old_leaf,
-					 ref_count);
-				if (!query( _("Attempt to fix it? (y/n) "))) {
-					log_err( _("Directory inode was not "
-						   "fixed.\n"));
-					return 1;
-				}
-				error = fix_leaf_pointers(ip, &lindex,
-							  ref_count, multiple);
+
+		do {
+			if (gfs2_check_range(ip->i_sbd, old_leaf) == 0) {
+				error = check_num_ptrs(ip, old_leaf,
+						       &ref_count, &exp_count,
+						       &lindex, &oldleaf);
 				if (error)
 					return error;
-				ref_count = multiple;
-				log_err( _("Directory inode was fixed.\n"));
-			}
-			/* Check to see if the counted number of leaf pointers
-			   is what we expect. */
-			if (ref_count != exp_count) {
-				log_err( _("Directory #%llu (0x%llx) has an "
-					   "incorrect number of pointers to "
-					   "leaf #%llu (0x%llx)\n\tFound: "
-					   "%u,  Expected: %u\n"),
-					 (unsigned long long)
-					 ip->i_di.di_num.no_addr,
-					 (unsigned long long)
-					 ip->i_di.di_num.no_addr,
-					 (unsigned long long)old_leaf,
-					 (unsigned long long)old_leaf,
-					 ref_count, exp_count);
-				if (!query( _("Attempt to fix it? (y/n) "))) {
-					log_err( _("Directory leaf was not "
-						   "fixed.\n"));
-					return 1;
-				}
-				lbh = bread(sbp, old_leaf);
-				gfs2_leaf_in(&oldleaf, lbh);
-				log_err( _("Leaf depth was %d, changed to "
-					   "%d\n"), oldleaf.lf_depth,
-					 ip->i_di.di_depth - factor);
-				oldleaf.lf_depth = ip->i_di.di_depth - factor;
-				gfs2_leaf_out(&oldleaf, lbh);
-				brelse(lbh);
-				exp_count = ref_count;
-				log_err( _("Directory leaf was fixed.\n"));
 			}
-		}
-		ref_count = 1;
-
-		count = 0;
-		do {
+			ref_count = 1;
+			count = 0;
 			if (fsck_abort)
 				break;
 			/* Make sure the block number is in range. */
@@ -684,10 +686,9 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 				break;
 			}
 			gfs2_leaf_in(&leaf, lbh);
-			if(pass->check_leaf) {
+			if(pass->check_leaf)
 				error = pass->check_leaf(ip, leaf_no, lbh,
 							 pass->private);
-			}
 
 			/*
 			 * Early versions of GFS2 had an endianess bug in the
@@ -722,22 +723,17 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 			  leaf depth %u\n"),
 			  exp_count, ip->i_di.di_depth, leaf.lf_depth);*/
 
-			if(pass->check_dentry && S_ISDIR(ip->i_di.di_mode)) {
+			if (pass->check_dentry && S_ISDIR(ip->i_di.di_mode)) {
 				error = check_entries(ip, lbh, DIR_EXHASH,
 						      &count, pass);
-
-				/* Since the buffer possibly got
-				 * updated directly, release it now,
-				 * and grab it again later if we need it. */
-
-				brelse(lbh);
-
 				if(error < 0) {
 					stack;
+					brelse(lbh);
 					return -1;
 				}
 
 				if(count != leaf.lf_entries) {
+					brelse(lbh);
 					lbh = bread(sbp, leaf_no);
 					gfs2_leaf_in(&leaf, lbh);
 
@@ -759,21 +755,17 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 						log_warn( _("Leaf entry count updated\n"));
 					} else
 						log_err( _("Leaf entry count left in inconsistant state\n"));
-					brelse(lbh);
 				}
-				/* FIXME: Need to get entry count and
-				 * compare it against leaf->lf_entries */
-				break; /* not a chain; go back to outer loop */
-			} else {
-				brelse(lbh);
-				if(!leaf.lf_next)
-					break;
-				leaf_no = leaf.lf_next;
-				log_debug( _("Leaf chain detected.\n"));
 			}
+			brelse(lbh);
+			old_leaf = leaf_no;
+			memcpy(&oldleaf, &leaf, sizeof(oldleaf));
+			if (!leaf.lf_next)
+				break;
+			leaf_no = leaf.lf_next;
+			log_debug( _("Leaf chain 0x%llx detected.\n"),
+				   (unsigned long long)leaf_no);
 		} while(1); /* while we have chained leaf blocks */
-		old_leaf = leaf_no;
-		memcpy(&oldleaf, &leaf, sizeof(oldleaf));
 	} /* for every leaf block */
 	return 0;
 }
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 17b23df..bc500fd 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -265,13 +265,16 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 			return 1;
 		}
 		brelse(nbh);
+		nbh = NULL;
 	} else /* blk check ok */
 		*bh = nbh;
 
 	bc->indir_count++;
-	if (found_dup)
+	if (found_dup) {
+		if (nbh)
+			brelse(nbh);
 		return 1; /* don't process the metadata again */
-	else
+	} else
 		fsck_blockmap_set(ip, block, _("indirect"),
 				  gfs2_indir_blk);
 
@@ -323,13 +326,17 @@ static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
 			return 1;
 		}
 		brelse(nbh);
+		nbh = NULL;
 	} else /* blk check ok */
 		*bh = nbh;
 
 	bc->indir_count--;
-	if (found_dup)
+	if (found_dup) {
+		if (nbh)
+			brelse(nbh);
+		*bh = NULL;
 		return 1; /* don't process the metadata again */
-	else
+	} else
 		fsck_blockmap_set(ip, block, _("bad indirect"),
 				  gfs2_block_free);
 	return 0;
diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index e0a1681..4019894 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -790,6 +790,13 @@ void dirent2_del(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
 	uint16_t cur_rec_len, prev_rec_len;
 
 	bmodified(bh);
+	if (gfs2_check_meta(bh, GFS2_METATYPE_LF) == 0) {
+		struct gfs2_leaf *lf = (struct gfs2_leaf *)bh->b_data;
+
+		lf->lf_entries = be16_to_cpu(lf->lf_entries) - 1;
+		lf->lf_entries = cpu_to_be16(lf->lf_entries);
+	}
+
 	if (dip->i_di.di_entries) {
 		bmodified(dip->i_bh);
 		dip->i_di.di_entries--;
@@ -908,9 +915,6 @@ static void dir_split_leaf(struct gfs2_inode *dip, uint32_t lindex,
 
 			dirent2_del(dip, obh, prev, dent);
 
-			oleaf->lf_entries = be16_to_cpu(oleaf->lf_entries) - 1;
-			oleaf->lf_entries = cpu_to_be16(oleaf->lf_entries);
-
 			if (!prev)
 				prev = dent;
 
@@ -1726,7 +1730,7 @@ int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t diblock)
 	/* Set the bitmap type for inode to free space: */
 	gfs2_set_bitmap(sdp, ip->i_di.di_num.no_addr, GFS2_BLKST_FREE);
 	inode_put(&ip);
-	/* inode_put deallocated the extra block used by the dist inode, */
+	/* inode_put deallocated the extra block used by the disk inode, */
 	/* so adjust it in the superblock struct */
 	sdp->blks_alloced--;
 	/* Now we have to adjust the rg freespace count and inode count: */
-- 
1.7.2.3