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

File gfs2_fsck_do_not_delete_directories_if_they_get_too_big.patch of Package cluster

commit 6a73e92beebf62fe1a1da1284dcd56a028e4662f
Author: Bob Peterson <rpeterso@redhat.com>
Date:   Tue Aug 17 12:26:47 2010 -0500

    fsck.gfs2 deletes directories if they get too big
    
    This patch fixes a problem whereby too many levels of
    indirection caused fsck.gfs2 to get confused and delete
    directories.  The problem was that gfs2.fsck only understood
    three levels of data for directories: (1) dinode, (2) block
    pointers to leaf blocks, (3) leaf blocks.  If a directory
    gets sufficiently big, it goes into four or more levels of
    indirection, with level 2 (and possibly more) being indirect
    blocks leading to the leaf blocks.
    
    rhbz#624691

diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 0ab8d7e..ef1b6b5 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1125,7 +1125,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 	uint32_t height = ip->i_di.di_height;
 	struct gfs2_buffer_head *bh, *nbh, *metabh = ip->i_bh;
 	osi_list_t *prev_list, *cur_list, *tmp;
-	int i, head_size, iblk_type;
+	int h, head_size, iblk_type;
 	uint64_t *ptr, block;
 	int error = 0, err;
 
@@ -1136,35 +1136,34 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 	   because it checks everything through the hash table using
 	   "depth" field calculations. However, we still have to check the
 	   indirect blocks, even if the height == 1.  */
-	if (S_ISDIR(ip->i_di.di_mode)) {
+	if (S_ISDIR(ip->i_di.di_mode))
 		height++;
-		iblk_type = GFS2_METATYPE_JD;
-	} else {
-		iblk_type = GFS2_METATYPE_IN;
-	}
 
 	/* if(<there are no indirect blocks to check>) */
 	if (height < 2)
 		return 0;
-	for (i = 1; i < height; i++) {
-		prev_list = &mlp[i - 1];
-		cur_list = &mlp[i];
+	for (h = 1; h < height; h++) {
+		if (h > 1) {
+			if (S_ISDIR(ip->i_di.di_mode) &&
+			    h == ip->i_di.di_height + 1)
+				iblk_type = GFS2_METATYPE_JD;
+			else
+				iblk_type = GFS2_METATYPE_IN;
+			head_size = sizeof(struct gfs2_meta_header);
+		} else {
+			iblk_type = GFS2_METATYPE_DI;
+			head_size = sizeof(struct gfs2_dinode);
+		}
+		prev_list = &mlp[h - 1];
+		cur_list = &mlp[h];
 
 		for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
 			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
 					    b_altlist);
 
-			if (i > 1) {
-				/* if this isn't really a block list skip it */
-				if (gfs2_check_meta(bh, iblk_type))
-					continue;
-				head_size = sizeof(struct gfs2_meta_header);
-			} else {
-				/* if this isn't really a dinode, skip it */
-				if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
-					continue;
-				head_size = sizeof(struct gfs2_dinode);
-			}
+			if (gfs2_check_meta(bh, iblk_type))
+				continue;
+
 			/* Now check the metadata itself */
 			for (ptr = (uint64_t *)(bh->b_data + head_size);
 			     (char *)ptr < (bh->b_data + ip->i_sbd->bsize);
@@ -1177,7 +1176,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 					continue;
 
 				block = be64_to_cpu(*ptr);
-				err = pass->check_metalist(ip, block, &nbh,
+				err = pass->check_metalist(ip, block, &nbh, h,
 							   pass->private);
 				/* check_metalist should hold any buffers
 				   it gets with "bread". */
@@ -1459,7 +1458,7 @@ int remove_dentry_from_dir(struct gfs2_sbd *sbp, uint64_t dir,
 }
 
 int delete_metadata(struct gfs2_inode *ip, uint64_t block,
-		    struct gfs2_buffer_head **bh, void *private)
+		    struct gfs2_buffer_head **bh, int h, void *private)
 {
 	return delete_block_if_notdup(ip, block, bh, _("metadata"), private);
 }
@@ -1491,7 +1490,7 @@ int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 }
 
 static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, void *private)
+			  struct gfs2_buffer_head **bh, int h, void *private)
 {
 	uint8_t q;
 	const char *desc = (const char *)private;
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index c602492..7aae9f2 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -19,7 +19,7 @@ extern int delete_block(struct gfs2_inode *ip, uint64_t block,
 		 struct gfs2_buffer_head **bh, const char *btype,
 		 void *private);
 extern int delete_metadata(struct gfs2_inode *ip, uint64_t block,
-		    struct gfs2_buffer_head **bh, void *private);
+			   struct gfs2_buffer_head **bh, int h, void *private);
 extern int delete_leaf(struct gfs2_inode *ip, uint64_t block,
 		struct gfs2_buffer_head *bh, void *private);
 extern int delete_data(struct gfs2_inode *ip, uint64_t block, void *private);
@@ -62,7 +62,8 @@ struct metawalk_fxns {
 	int (*check_leaf) (struct gfs2_inode *ip, uint64_t block,
 			   struct gfs2_buffer_head *bh, void *private);
 	int (*check_metalist) (struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, void *private);
+			       struct gfs2_buffer_head **bh, int h,
+			       void *private);
 	int (*check_data) (struct gfs2_inode *ip, uint64_t block,
 			   void *private);
 	int (*check_eattr_indir) (struct gfs2_inode *ip, uint64_t block,
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 6c4762d..4fb7abf 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -35,9 +35,10 @@ struct block_count {
 static int leaf(struct gfs2_inode *ip, uint64_t block,
 		struct gfs2_buffer_head *bh, void *private);
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, void *private);
+			  struct gfs2_buffer_head **bh, int h, void *private);
 static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, void *private);
+			       struct gfs2_buffer_head **bh, int h,
+			       void *private);
 static int check_data(struct gfs2_inode *ip, uint64_t block, void *private);
 static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
 			   void *private);
@@ -60,7 +61,8 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
 			      int leaf_pointer_errors, void *private);
 static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, void *private);
+			       struct gfs2_buffer_head **bh, int h,
+			       void *private);
 static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block,
 			   struct gfs2_buffer_head *bh, void *private);
 static int invalidate_data(struct gfs2_inode *ip, uint64_t block,
@@ -111,7 +113,8 @@ struct metawalk_fxns invalidate_fxns = {
  * deleted, do you? Or worse, reused for lost+found.
  */
 static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
-				struct gfs2_buffer_head **bh, void *private)
+				struct gfs2_buffer_head **bh, int h,
+				void *private)
 {
 	struct block_count *bc = (struct block_count *)private;
 
@@ -205,7 +208,7 @@ static int leaf(struct gfs2_inode *ip, uint64_t block,
 }
 
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, void *private)
+			  struct gfs2_buffer_head **bh, int h, void *private)
 {
 	uint8_t q;
 	int found_dup = 0, iblk_type;
@@ -225,7 +228,7 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 
 		return 1;
 	}
-	if (S_ISDIR(ip->i_di.di_mode)) {
+	if (S_ISDIR(ip->i_di.di_mode) && h == ip->i_di.di_height) {
 		iblk_type = GFS2_METATYPE_JD;
 		blktypedesc = _("a directory hash table block");
 	} else {
@@ -248,13 +251,13 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	nbh = bread(ip->i_sbd, block);
 
 	if (gfs2_check_meta(nbh, iblk_type)){
-		log_debug( _("Inode %lld (0x%llx) has a bad indirect block "
-			     "pointer %lld (0x%llx) (points to something "
-			     "that is not %s).\n"),
-			   (unsigned long long)ip->i_di.di_num.no_addr,
-			   (unsigned long long)ip->i_di.di_num.no_addr,
-			   (unsigned long long)block,
-			   (unsigned long long)block, blktypedesc);
+		log_err( _("Inode %lld (0x%llx) has a bad indirect block "
+			   "pointer %lld (0x%llx) (points to something "
+			   "that is not %s).\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)block,
+			 (unsigned long long)block, blktypedesc);
 		if(!found_dup) {
 			fsck_blockmap_set(ip, block, _("bad indirect"),
 					  gfs2_meta_inval);
@@ -276,7 +279,8 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 }
 
 static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, void *private)
+			       struct gfs2_buffer_head **bh, int h,
+			       void *private)
 {
 	struct duptree *d;
 	int found_dup = 0, iblk_type;
@@ -290,7 +294,7 @@ static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
 				  _("itself"), gfs2_block_free);
 		return 1;
 	}
-	if (S_ISDIR(ip->i_di.di_mode))
+	if (S_ISDIR(ip->i_di.di_mode) && h == ip->i_di.di_height)
 		iblk_type = GFS2_METATYPE_JD;
 	else
 		iblk_type = GFS2_METATYPE_IN;
@@ -834,7 +838,8 @@ static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
 }
 
 static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, void *private)
+			       struct gfs2_buffer_head **bh, int h,
+			       void *private)
 {
 	return mark_block_invalid(ip, block, ref_as_meta, _("metadata"));
 }
@@ -915,7 +920,8 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
 }
 
 static int rangecheck_metadata(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, void *private)
+			       struct gfs2_buffer_head **bh, int h,
+			       void *private)
 {
 	return rangecheck_block(ip, block, bh, _("metadata"), private);
 }
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 680eb38..4d97879 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -27,7 +27,7 @@ struct dup_handler {
 };
 
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, void *private);
+			  struct gfs2_buffer_head **bh, int h, void *private);
 static int check_data(struct gfs2_inode *ip, uint64_t block, void *private);
 static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 			     uint64_t parent, struct gfs2_buffer_head **bh,
@@ -74,7 +74,7 @@ struct metawalk_fxns find_dirents = {
 };
 
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, void *private)
+			  struct gfs2_buffer_head **bh, int h, void *private)
 {
 	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
 }
@@ -197,7 +197,8 @@ static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
 }
 
 static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
-			      struct gfs2_buffer_head **bh, void *private)
+			      struct gfs2_buffer_head **bh, int h,
+			      void *private)
 {
 	struct dup_handler *dh = (struct dup_handler *) private;
 	struct duptree *d;
@@ -246,21 +247,21 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
 
 static int clear_dup_data(struct gfs2_inode *ip, uint64_t block, void *private)
 {
-	return clear_dup_metalist(ip, block, NULL, private);
+	return clear_dup_metalist(ip, block, NULL, 0, private);
 }
 
 static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 				 uint64_t parent, struct gfs2_buffer_head **bh,
 				 void *private)
 {
-	return clear_dup_metalist(ip, block, NULL, private);
+	return clear_dup_metalist(ip, block, NULL, 0, private);
 }
 
 static int clear_dup_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 				uint64_t parent, struct gfs2_buffer_head **bh,
 				void *private)
 {
-	return clear_dup_metalist(ip, block, NULL, private);
+	return clear_dup_metalist(ip, block, NULL, 0, private);
 }
 
 static int clear_eattr_entry (struct gfs2_inode *ip,
@@ -313,7 +314,7 @@ static int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
 {
 	uint64_t block = be64_to_cpu(*ea_data_ptr);
 
-	return clear_dup_metalist(ip, block, NULL, private);
+	return clear_dup_metalist(ip, block, NULL, 0, private);
 }
 
 /* Finds all references to duplicate blocks in the metadata */