[Ocfs2-tools-devel] [PATCH 33/44] fsck.ocfs2: Add check for refcount record list.

Tao Ma tao.ma at ORACLE.COM
Mon Dec 28 01:01:18 PST 2009


Add check for refcount list.
They are REFCOUNT_LIST_COUNT, REFCOUNT_LIST_USED,
REFCOUNT_CLUSTER_RANGE and REFCOUNT_CLUSTER_COLLISION.

Please note that to check whether the refcounted cluster
has some collision, we need a new parameter "c_end" which
indicates the end of the refcount record we have checked
before.

Signed-off-by: Tao Ma <tao.ma at oracle.com>
---
 fsck.ocfs2/fsck.ocfs2.checks.8.in |   30 ++++++++++
 fsck.ocfs2/refcount.c             |  116 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 145 insertions(+), 1 deletions(-)

diff --git a/fsck.ocfs2/fsck.ocfs2.checks.8.in b/fsck.ocfs2/fsck.ocfs2.checks.8.in
index 4537cf2..1f5ba62 100644
--- a/fsck.ocfs2/fsck.ocfs2.checks.8.in
+++ b/fsck.ocfs2/fsck.ocfs2.checks.8.in
@@ -592,6 +592,36 @@ Answering yes implies that the generation number in the refcount block is
 incorrect and that the extent block is valid.  The generation number in the
 block is updated to match the generation number in the volume.
 
+.SS "REFCOUNT_LIST_COUNT"
+The number of entries in a refcount list is bounded by the size of the block
+which contains it.  An refcount list was found which claims to have more
+entries than would fit in its container.
+
+Answering yes updates the count field in the refcount list to match the
+container. Answering no to this question may stop further fixes from being
+done because the count value can not be trusted.
+
+.SS "REFCOUNT_LIST_USED"
+The number of free entries in a refcount list must be less than the total
+number of entries in the list.   A list was found which claims to have more
+free entries than possible entries.
+
+Answering yes sets the number of free entries in the list equal to the total
+possible entries.
+
+.SS "REFCOUNT_CLUSTER_RANGE"
+A refcount record was found which references a cluster which can not be
+referenced by a refcount.  The referenced cluster is either very early in the
+volume, and thus reserved, or beyond the end of the volume.
+
+Answering yes removes this refcount record from the tree.
+
+.SS "REFCOUNT_CLUSTER_COLLISION"
+A refcount record was found which references a cluster which has a collision
+with the previous valid refcount record.
+
+Answering yes removes this refcount record from the tree.
+
 \" pass1b.c
 
 .SS "DUP_CLUSTERS_SYSFILE_CLONE"
diff --git a/fsck.ocfs2/refcount.c b/fsck.ocfs2/refcount.c
index 6e21a45..25d7029 100644
--- a/fsck.ocfs2/refcount.c
+++ b/fsck.ocfs2/refcount.c
@@ -16,13 +16,114 @@
  */
 
 #include <inttypes.h>
+#include <assert.h>
+
 #include "ocfs2/ocfs2.h"
 #include "problem.h"
+#include "fsck.h"
+#include "extent.h"
 
 static const char *whoami = "refcount.c";
 
+static void check_rl(o2fsck_state *ost,
+		     uint64_t rb_blkno, uint64_t root_blkno,
+		     struct ocfs2_refcount_list *rl,
+		     uint64_t *c_end, int *changed)
+{
+	struct ocfs2_refcount_rec *rec;
+	uint16_t i;
+	size_t cpy;
+	int trust_used = 1;
+	int max_recs = ocfs2_refcount_recs_per_rb(ost->ost_fs->fs_blocksize);
+
+	verbosef("count %u next_free %u\n", rl->rl_count, rl->rl_used);
+
+	if (rl->rl_count > max_recs &&
+	    prompt(ost, PY, PR_REFCOUNT_LIST_COUNT,
+		   "Refcount list in refcount tree %"PRIu64" claims to have %u "
+		   "records, but the maximum is %u. Fix the list's count?",
+		   root_blkno, rl->rl_count, max_recs)) {
+
+		rl->rl_count = max_recs;
+		*changed = 1;
+	}
+
+	if (max_recs > rl->rl_count)
+		max_recs = rl->rl_count;
+
+	if (rl->rl_used > max_recs) {
+		if (prompt(ost, PY, PR_REFCOUNT_LIST_USED,
+			   "Refcount list in refcount tree %"PRIu64" claims %u "
+			   "as the used record, but fsck believes "
+			   "the largest valid value is %u.  Clamp the used "
+			   "record value?", root_blkno,
+			   rl->rl_used, max_recs)) {
+
+			rl->rl_used = rl->rl_count;
+			*changed = 1;
+		} else
+			trust_used = 0;
+	}
+
+	if (trust_used)
+		max_recs = rl->rl_used;
+
+	for (i = 0; i < max_recs; i++) {
+		rec = &rl->rl_recs[i];
+
+		/* offer to remove records that point to nowhere */
+		if (ocfs2_block_out_of_range(ost->ost_fs,
+			ocfs2_clusters_to_blocks(ost->ost_fs,
+					rec->r_cpos + rec->r_clusters - 1)) &&
+		    prompt(ost, PY, PR_REFCOUNT_CLUSTER_RANGE,
+			   "Refcount record %u in refcount block %"PRIu64" "
+			   "of refcount tree %"PRIu64" refers to a cluster "
+			   "that is out of range.  Remove "
+			   "this record from the refcount list?",
+			   i, rb_blkno, root_blkno)) {
+			if (!trust_used) {
+				printf("Can't remove the record becuase "
+				       "rl_used hasn't been fixed\n");
+				continue;
+			}
+			goto remove_rec;
+		}
+
+		if (rec->r_cpos < *c_end &&
+		    prompt(ost, PY, PR_REFCOUNT_CLUSTER_COLLISION,
+			   "Refcount record %u in refcount block %"PRIu64" "
+			   "of refcount tree %"PRIu64" refers to a cluster "
+			   "that is collided with the previous record.  Remove "
+			   "this record from the refcount list?",
+			   i, rb_blkno, root_blkno)) {
+			if (!trust_used) {
+				printf("Can't remove the record becuase "
+				       "next_free_rec hasn't been fixed\n");
+				continue;
+			}
+			goto remove_rec;
+		}
+
+		*c_end = rec->r_cpos + rec->r_clusters;
+		continue;
+remove_rec:
+		cpy = (max_recs - i - 1) * sizeof(*rec);
+		/* shift the remaining recs into this ones place */
+		if (cpy != 0) {
+			memcpy(rec, rec + 1, cpy);
+			memset(&rl->rl_recs[max_recs - 1], 0,
+			       sizeof(*rec));
+			i--;
+		}
+		rl->rl_used--;
+		max_recs--;
+		*changed = 1;
+		continue;
+	}
+}
+
 static errcode_t check_rb(o2fsck_state *ost, uint64_t blkno,
-			  uint64_t root_blkno, int *is_valid)
+			  uint64_t root_blkno, uint64_t *c_end, int *is_valid)
 {
 	int changed = 0;
 	char *buf = NULL;
@@ -86,6 +187,19 @@ static errcode_t check_rb(o2fsck_state *ost, uint64_t blkno,
 
 	/* XXX worry about suballoc node/bit */
 
+	if (rb->rf_flags & OCFS2_REFCOUNT_TREE_FL) {
+		struct extent_info ei = {0, };
+		uint16_t max_recs =
+			ocfs2_extent_recs_per_rb(ost->ost_fs->fs_blocksize);
+
+		check_el(ost, &ei, rb->rf_blkno, &rb->rf_list,
+			 max_recs, &changed);
+	} else {
+		assert(c_end);
+		check_rl(ost, root_blkno, blkno,
+			 &rb->rf_records, c_end, &changed);
+	}
+
 	if (changed) {
 		ret = ocfs2_write_refcount_block(ost->ost_fs, blkno, buf);
 		if (ret) {
-- 
1.5.5




More information about the Ocfs2-tools-devel mailing list