gfs2-utils: master - fsck.gfs2: invalidate invalid mode inodes

Bob Peterson rpeterso at fedoraproject.org
Tue Jan 26 21:10:51 UTC 2010


Gitweb:        http://git.fedorahosted.org/git/gfs2-utils.git?p=gfs2-utils.git;a=commitdiff;h=00f523c4235deb2c4a2d3a602d04fd4127e91e9b
Commit:        00f523c4235deb2c4a2d3a602d04fd4127e91e9b
Parent:        833077443d4eb716fba13ef90f0333a714c1d2dc
Author:        Bob Peterson <bob at ganesha.peterson>
AuthorDate:    Mon Jan 25 13:52:20 2010 -0600
Committer:     Bob Peterson <rpeterso at redhat.com>
CommitterDate: Tue Jan 26 15:09:51 2010 -0600

fsck.gfs2: invalidate invalid mode inodes

When fsck.gfs2 encountered a dinode that had an invalid type
(file, directory, socket, etc), it marked that block in the blockmap
as an invalid block.  That caused pass2 to delete it AND its metadata.
Since the metadata had not been previously processed, it would be
deleted wholesale.  That's okay for most cases, but if there was
a duplicate reference to any of those metadata blocks, pass2 would
not realize it and free the block anyway, out from under the valid
reference.  In other words, the valid reference to the block would
remain intact but the block was freed in the bitmap because it was
associated with the dinode that had an unknown type.  That means big
time corruption because future block allocations could then reuse the
freed metadata, once again causing another duplicate block reference.

This patch invalidates the metadata associated with dinodes that
do not have a valid inode type.  That causes all of its metadata to
be processed and duplicate references are put on the new invalid
references list.  Later, in pass1b, the duplicate block references
are resolved and the last remaining reference is left in a valid
state.  And pass2 had no more cause to free the block.

rhbz#455300
---
 gfs2/fsck/pass1.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 100 insertions(+), 0 deletions(-)

diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 23894b6..ee6a1fc 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -61,6 +61,19 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 				     void *private);
 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);
+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,
+			   void *private);
+static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
+				  uint64_t parent,
+				  struct gfs2_buffer_head **bh,
+				  void *private);
+static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
+				 uint64_t parent, struct gfs2_buffer_head **bh,
+				 void *private);
 
 struct metawalk_fxns pass1_fxns = {
 	.private = NULL,
@@ -81,6 +94,15 @@ struct metawalk_fxns undo_fxns = {
 	.check_data = undo_check_data,
 };
 
+struct metawalk_fxns invalidate_fxns = {
+	.private = NULL,
+	.check_metalist = invalidate_metadata,
+	.check_data = invalidate_data,
+	.check_leaf = invalidate_leaf,
+	.check_eattr_indir = invalidate_eattr_indir,
+	.check_eattr_leaf = invalidate_eattr_leaf,
+};
+
 static int leaf(struct gfs2_inode *ip, uint64_t block,
 		struct gfs2_buffer_head *bh, void *private)
 {
@@ -671,6 +693,72 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 }
 
 /**
+ * mark_block_invalid - mark blocks associated with an inode as invalid
+ *                      unless the block is a duplicate.
+ *
+ * An "invalid" block is now considered free in the bitmap, and pass2 will
+ * delete any invalid blocks.  This is nearly identical to function
+ * delete_block_if_notdup.
+ */
+static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
+			      enum dup_ref_type reftype, const char *btype)
+{
+	uint8_t q;
+
+	if (gfs2_check_range(ip->i_sbd, block) != 0)
+		return -EFAULT;
+
+	q = block_type(block);
+	if (q != gfs2_block_free) {
+		add_duplicate_ref(ip, block, reftype, 0, INODE_INVALID);
+		log_info( _("%s block %lld (0x%llx), part of inode "
+			    "%lld (0x%llx), was free so the invalid "
+			    "reference is ignored.\n"),
+			  btype, (unsigned long long)block,
+			  (unsigned long long)block,
+			  (unsigned long long)ip->i_di.di_num.no_addr,
+			  (unsigned long long)ip->i_di.di_num.no_addr);
+		return 0;
+	}
+	fsck_blockmap_set(ip, block, btype, gfs2_meta_inval);
+	return 0;
+}
+
+static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
+			       struct gfs2_buffer_head **bh, void *private)
+{
+	return mark_block_invalid(ip, block, ref_as_meta, _("metadata"));
+}
+
+static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block,
+			   struct gfs2_buffer_head *bh, void *private)
+{
+	return mark_block_invalid(ip, block, ref_as_meta, _("leaf"));
+}
+
+static int invalidate_data(struct gfs2_inode *ip, uint64_t block,
+			   void *private)
+{
+	return mark_block_invalid(ip, block, ref_as_data, _("data"));
+}
+
+static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
+				  uint64_t parent,
+				  struct gfs2_buffer_head **bh, void *private)
+{
+	return mark_block_invalid(ip, block, ref_as_ea,
+				  _("indirect extended attribute"));
+}
+
+static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
+				 uint64_t parent, struct gfs2_buffer_head **bh,
+				 void *private)
+{
+	return mark_block_invalid(ip, block, ref_as_ea,
+				  _("extended attribute"));
+}
+
+/**
  * Check for massive amounts of pointer corruption.  If the block has
  * lots of out-of-range pointers, we can't trust any of the pointers.
  * For example, a stray pointer with a value of 0x1d might be
@@ -880,6 +968,18 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 		}
 		break;
 	default:
+		/* We found a dinode that has an invalid mode, so we can't
+		   tell if it's a data file, directory or a socket.
+		   Regardless, we have to invalidate its metadata in case there
+		   are duplicate blocks referenced.  If we don't call
+		   check_metatree, the blocks it references will be deleted
+		   wholesale by pass2, and if any of those blocks are
+		   duplicates--referenced by another dinode for some reason--
+		   we will mark it free, even though it's in use.  In other
+		   words, we would introduce file system corruption. So we
+		   need to keep track of the fact that it's invalid and
+		   skip parts that we can't be sure of based on dinode type. */
+		check_metatree(ip, &invalidate_fxns);
 		if (fsck_blockmap_set(ip, block, _("invalid mode"),
 				      gfs2_inode_invalid)) {
 			stack;


More information about the cluster-commits mailing list