Sophie

Sophie

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

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

commit a04bba916c90b702c59773894861c061ffe71d03
Author: Bob Peterson <bob@ganesha.peterson>
Date:   Fri Jan 22 15:29:19 2010 -0600

    fsck.gfs2: reprocess lost+found and other inode metadata when blocks are added
    
    On rare occasions, fsck.gfs2 needs to allocate blocks in the file system.
    For example, this can happen when new directory leafs are added to
    lost+found and when lost+found's hash table needs to be doubled.
    While libgfs2 did a stellar job of updating the rgrp bitmap information,
    those newly allocated blocks were unfortunately not being properly
    accounted for in the fsck.gfs2 blockmap.  Therefore, pass5 would
    mistakenly mark them as free blocks.  Further use of the file
    system would often allocate those blocks a second time, creating
    a duplicate block reference.  That means very bad file system
    corruption.  This patch fixes the problem by checking for block
    count changes in inodes at critical points, and subsequently fixing
    up the blockmap using new "alloc_*" helper functions.
    
    rhbz#455300

diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 7a4d4b3..37c0477 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -40,6 +40,7 @@
 int add_inode_to_lf(struct gfs2_inode *ip){
 	char tmp_name[256];
 	__be32 inode_type;
+	uint64_t lf_blocks;
 	struct dir_info *di;
 
 	if(!lf_dip) {
@@ -94,6 +95,8 @@ int add_inode_to_lf(struct gfs2_inode *ip){
 		log_err( _("Trying to add lost+found to itself...skipping"));
 		return 0;
 	}
+	lf_blocks = lf_dip->i_di.di_blocks;
+
 	switch(ip->i_di.di_mode & S_IFMT){
 	case S_IFDIR:
 		log_info( _("Adding .. entry pointing to lost+found for "
@@ -169,6 +172,11 @@ int add_inode_to_lf(struct gfs2_inode *ip){
 
 	dir_add(lf_dip, tmp_name, strlen(tmp_name), &(ip->i_di.di_num),
 		inode_type);
+	/* If the lf directory had new blocks added we have to mark them
+	   properly in the bitmap so they're not freed. */
+	if (lf_dip->i_di.di_blocks != lf_blocks)
+		reprocess_inode(lf_dip, "lost+found");
+
 	/* This inode is linked from lost+found */
   	increment_link(ip->i_di.di_num.no_addr, lf_dip->i_di.di_num.no_addr,
 		       _("from lost+found"));
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index f9fd733..461cabe 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1442,3 +1442,101 @@ int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 	return delete_blocks(ip, block, NULL, _("extended attribute"),
 			     private);
 }
+
+static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
+			  struct gfs2_buffer_head **bh, void *private)
+{
+	uint8_t q;
+	const char *desc = (const char *)private;
+
+	/* No need to range_check here--if it was added, it's in range. */
+	/* We can't check the bitmap here because this function is called
+	   after the bitmap has been set but before the blockmap has. */
+	*bh = bread(ip->i_sbd, block);
+	q = block_type(block);
+	if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) { /* If not marked yet */
+		log_debug(_("%s reference to new metadata block "
+			    "%lld (0x%llx) is now marked as indirect.\n"),
+			  desc, (unsigned long long)block,
+			  (unsigned long long)block);
+		gfs2_blockmap_set(bl, block, gfs2_indir_blk);
+	}
+	return 0;
+}
+
+static int alloc_data(struct gfs2_inode *ip, uint64_t block, void *private)
+{
+	uint8_t q;
+	const char *desc = (const char *)private;
+
+	/* No need to range_check here--if it was added, it's in range. */
+	/* We can't check the bitmap here because this function is called
+	   after the bitmap has been set but before the blockmap has. */
+	q = block_type(block);
+	if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) { /* If not marked yet */
+		log_debug(_("%s reference to new data block "
+			    "%lld (0x%llx) is now marked as data.\n"),
+			  desc, (unsigned long long)block,
+			  (unsigned long long)block);
+		gfs2_blockmap_set(bl, block, gfs2_block_used);
+	}
+	return 0;
+}
+
+static int alloc_leaf(struct gfs2_inode *ip, uint64_t block,
+		      struct gfs2_buffer_head *bh, void *private)
+{
+	uint8_t q;
+
+	/* No need to range_check here--if it was added, it's in range. */
+	/* We can't check the bitmap here because this function is called
+	   after the bitmap has been set but before the blockmap has. */
+	q = block_type(block);
+	if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) /* If not marked yet */
+		fsck_blockmap_set(ip, block, _("newly allocated leaf"),
+				  gfs2_leaf_blk);
+	return 0;
+}
+
+struct metawalk_fxns alloc_fxns = {
+	.private = NULL,
+	.check_leaf = alloc_leaf,
+	.check_metalist = alloc_metalist,
+	.check_data = alloc_data,
+	.check_eattr_indir = NULL,
+	.check_eattr_leaf = NULL,
+	.check_dentry = NULL,
+	.check_eattr_entry = NULL,
+	.check_eattr_extentry = NULL,
+	.finish_eattr_indir = NULL,
+};
+
+/*
+ * reprocess_inode - fixes the blockmap to match the bitmap due to an
+ *                   unexpected block allocation via libgfs2.
+ *
+ * The problem we're trying to overcome here is when a new block must be
+ * added to a dinode because of a write.  This will happen when lost+found
+ * needs a new indirect block for its hash table.  In that case, the write
+ * causes a new block to be assigned in the bitmap but that block is not yet
+ * accurately reflected in the fsck blockmap.  We need to compensate here.
+ *
+ * We can't really use fsck_blockmap_set here because the new block
+ * was already allocated by libgfs2 and therefore it took care of
+ * the rgrp free space variable.  fsck_blockmap_set adjusts the free space
+ * in the rgrp according to the change, which has already been done.
+ * So it's only our blockmap that now disagrees with the rgrp bitmap, so we
+ * need to fix only that.
+ */
+void reprocess_inode(struct gfs2_inode *ip, const char *desc)
+{
+	int error;
+
+	alloc_fxns.private = (void *)desc;
+	log_info( _("%s had blocks added; reprocessing its metadata tree "
+		    "at height=%d.\n"), desc, ip->i_di.di_height);
+	error = check_metatree(ip, &alloc_fxns);
+	if (error)
+		log_err( _("Error %d reprocessing the %s metadata tree.\n"),
+			 error, desc);
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index f8feb2d..c4c6d84 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -44,6 +44,7 @@ extern int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
 		       const char *caller, int line);
 extern int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
 		       enum gfs2_mark_block new_blockmap_state);
+extern void reprocess_inode(struct gfs2_inode *ip, const char *desc);
 extern struct duptree *dupfind(uint64_t block);
 
 #define is_duplicate(dblock) ((dupfind(dblock)) ? 1 : 0)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 290edbb..3be17c3 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -562,6 +562,8 @@ int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 	if(!ds.dotdir) {
 		log_err( _("No '.' entry found for %s directory.\n"), dirname);
 		if (query( _("Is it okay to add '.' entry? (y/n) "))) {
+			uint64_t cur_blks = sysinode->i_di.di_blocks;
+
 			sprintf(tmp_name, ".");
 			filename_len = strlen(tmp_name); /* no trailing NULL */
 			if(!(filename = malloc(sizeof(char) * filename_len))) {
@@ -579,6 +581,8 @@ int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 			log_warn( _("Adding '.' entry\n"));
 			dir_add(sysinode, filename, filename_len,
 				&(sysinode->i_di.di_num), DT_DIR);
+			if (cur_blks != sysinode->i_di.di_blocks)
+				reprocess_inode(sysinode, dirname);
 			/* This system inode is linked to itself via '.' */
 			increment_link(sysinode->i_di.di_num.no_addr,
 				       sysinode->i_di.di_num.no_addr,
@@ -744,6 +748,8 @@ int pass2(struct gfs2_sbd *sbp)
 				dirblk, dirblk);
 
 			if (query( _("Is it okay to add '.' entry? (y/n) "))) {
+				uint64_t cur_blks;
+
 				sprintf(tmp_name, ".");
 				filename_len = strlen(tmp_name); /* no trailing
 								    NULL */
@@ -761,8 +767,18 @@ int pass2(struct gfs2_sbd *sbp)
 				}
 				memcpy(filename, tmp_name, filename_len);
 
+				cur_blks = ip->i_di.di_blocks;
 				dir_add(ip, filename, filename_len,
 					&(ip->i_di.di_num), DT_DIR);
+				if (cur_blks != ip->i_di.di_blocks) {
+					char dirname[80];
+
+					sprintf(dirname, _("Directory at %lld "
+							   "(0x%llx)"),
+						(unsigned long long)dirblk,
+						(unsigned long long)dirblk);
+					reprocess_inode(ip, dirname);
+				}
 				/* directory links to itself via '.' */
 				increment_link(ip->i_di.di_num.no_addr,
 					       ip->i_di.di_num.no_addr,
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 0e3cfbf..11cae81 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -33,6 +33,7 @@ static int attach_dotdot_to(struct gfs2_sbd *sbp, uint64_t newdotdot,
 	char *filename;
 	int filename_len;
 	struct gfs2_inode *ip, *pip;
+	uint64_t cur_blks;
 
 	ip = fsck_load_inode(sbp, block);
 	pip = fsck_load_inode(sbp, newdotdot);
@@ -63,7 +64,16 @@ static int attach_dotdot_to(struct gfs2_sbd *sbp, uint64_t newdotdot,
 		log_warn( _("Unable to remove \"..\" directory entry.\n"));
 	else
 		decrement_link(olddotdot, block, _("old \"..\""));
+	cur_blks = ip->i_di.di_blocks;
 	dir_add(ip, filename, filename_len, &pip->i_di.di_num, DT_DIR);
+	if (cur_blks != ip->i_di.di_blocks) {
+		char dirname[80];
+
+		sprintf(dirname, _("Directory at %lld (0x%llx)"),
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		reprocess_inode(ip, dirname);
+	}
 	increment_link(newdotdot, block, _("new \"..\""));
 	bmodified(ip->i_bh);
 	fsck_inode_put(&ip);