Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 9383e745e23602bc45f9c92184feea59 > files > 122

gfs2-utils-0.1.62-28.el5.src.rpm

commit 6f1e7d39673a0c8d145d923204f7daa4dd56a0aa
Author: Bob Peterson <rpeterso@redhat.com>
Date:   Fri Sep 17 11:25:09 2010 -0500

    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#629010

diff --git a/gfs2/fsck/link.c b/gfs2/fsck/link.c
index 525836d..0503f42 100644
--- a/gfs2/fsck/link.c
+++ b/gfs2/fsck/link.c
@@ -81,6 +81,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 7d65fe0..b89b25a 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -121,10 +121,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 330dace..3a4e462 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -539,6 +539,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)
 {
@@ -595,76 +657,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. */
@@ -697,10 +700,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
@@ -735,22 +737,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);
 
@@ -772,21 +769,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 f584aab..3bdcf60 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -280,13 +280,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);
 
@@ -338,13 +341,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 4beb3c4..2b1fa46 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -787,6 +787,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--;
@@ -905,9 +912,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;
 
@@ -1719,8 +1723,8 @@ 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 disk inode, */
-        /* so adjust it in the superblock struct */
+	/* 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: */
 	rgd = gfs2_blk2rgrpd(sdp, diblock);