[Ocfs2-tools-devel] [PATCH 2/2] Break a chain loop in group desc

Goldwyn Rodrigues rgoldwyn at gmail.com
Fri Sep 21 08:39:26 PDT 2012


This patch detects and a loop by checking hops against the theoretical
limit of number of chains in a chain_rec. If a loop is found, it breaks
it by storing the block numbers and comparing with exiting block
numbers.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn at suse.de>

---
 fsck.ocfs2/fsck.ocfs2.checks.8.in |    9 ++++++
 fsck.ocfs2/pass0.c                |   61 ++++++++++++++++++++++++++++++++++---
 include/ocfs2-kernel/ocfs2_fs.h   |    2 +-
 3 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/fsck.ocfs2/fsck.ocfs2.checks.8.in
b/fsck.ocfs2/fsck.ocfs2.checks.8.in
index 788c89b..3986b76 100644
--- a/fsck.ocfs2/fsck.ocfs2.checks.8.in
+++ b/fsck.ocfs2/fsck.ocfs2.checks.8.in
@@ -202,6 +202,15 @@ valid in its bitmap.
 Answering yes decreases the number of recorded free bits so that it equals
 the total number of bits in the group descriptor's bitmap.

+.SS "GROUP_CHAIN_LOOP"
+A chain may loop if the next field of the group descriptor points to one of
+the previous group descriptors in the chain. This causes the ocfs2 code, both
+user space and kernel module to loop forever.
+
+Answering yes breaks the loop at an optimum location so that all the existing
+group descriptors are in the chain. However, it cannot re-connect stray group
+descriptors and must rely on the rest of the fsck code to fix it.
+
 .SS "CHAIN_COUNT"
 The chain list embedded in an inode is limited by the block size and the
 number of bytes consumed by the rest of the inode.  A chain list header was
diff --git a/fsck.ocfs2/pass0.c b/fsck.ocfs2/pass0.c
index c698893..a6b6a2a 100644
--- a/fsck.ocfs2/pass0.c
+++ b/fsck.ocfs2/pass0.c
@@ -666,6 +666,46 @@ out:
 	return ret;
 }

+static errcode_t break_loop(o2fsck_state *ost, struct ocfs2_chain_rec *chain,
+		unsigned int max_depth)
+{
+	uint64_t *list;
+	int i;
+	unsigned int depth = 0;
+	uint64_t blkno = chain->c_blkno;
+	char *buf;
+	struct ocfs2_group_desc *gd;
+	errcode_t ret = ocfs2_malloc0(sizeof(uint64_t) * max_depth, &list);
+	if (ret)
+		goto out;
+	ret =  ocfs2_malloc_block(ost->ost_fs->fs_io, &buf);
+	if (ret)
+		goto out;
+	gd = (struct ocfs2_group_desc *)buf;
+
+	while (blkno && (depth<=max_depth)) {
+		list[depth++] = blkno;
+		ret = ocfs2_read_group_desc(ost->ost_fs, blkno, buf);
+		if (ret)
+			goto out;
+		blkno = gd->bg_next_group;
+		for (i=0; i<depth; i++)
+			if (list[i]==blkno) {
+				gd->bg_next_group = 0;
+				verbosef("Breaking gd loop %"PRIu64"\n", blkno);
+				ret = ocfs2_write_group_desc(ost->ost_fs,
+						blkno, buf);
+				goto out;
+			}
+	}
+out:
+	if (list)
+		ocfs2_free(&list);
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
 /* this takes a slightly ridiculous number of arguments :/ */
 static errcode_t check_chain(o2fsck_state *ost,
 			     struct ocfs2_dinode *di,
@@ -675,7 +715,8 @@ static errcode_t check_chain(o2fsck_state *ost,
 			     char *buf2,
 			     int *chain_changed,
 			     ocfs2_bitmap *allowed,
-			     ocfs2_bitmap *forbidden)
+			     ocfs2_bitmap *forbidden,
+			     unsigned int max_depth)
 {
 	struct ocfs2_group_desc *bg1 = (struct ocfs2_group_desc *)buf1;
 	struct ocfs2_group_desc *bg2 = (struct ocfs2_group_desc *)buf2;
@@ -792,6 +833,14 @@ static errcode_t check_chain(o2fsck_state *ost,
 		/* the loop will now start by reading bg1->next_group */
 		memcpy(buf1, buf2, ost->ost_fs->fs_blocksize);
 		depth++;
+		if (depth > max_depth) {
+			 if (prompt(ost, PY, PR_GROUP_CHAIN_LOOP,
+			    "Loop detected in chain %d at block %"PRIu64
+			    ". Break the loop?",cs->cs_chain_no,
+			    (uint64_t) chain->c_blkno))
+				ret = break_loop(ost, chain, max_depth);
+			break;
+		}
 	}

 	/* we hit the premature end of a chain.. clear the last
@@ -854,6 +903,7 @@ static errcode_t verify_chain_alloc(o2fsck_state *ost,
 	int changed = 0, trust_next_free = 1;
 	errcode_t ret = 0;
 	uint64_t chain_bytes;
+	unsigned int num_gds, max_chain_len;

 	if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE,
 		   strlen(OCFS2_INODE_SIGNATURE))) {
@@ -883,9 +933,12 @@ static errcode_t verify_chain_alloc(o2fsck_state *ost,
 	/* XXX should we check suballoc_node? */

 	cl = &di->id2.i_chain;
+	num_gds = (di->i_clusters + cl->cl_cpg)/cl->cl_cpg;
+	max_chain_len = (num_gds + cl->cl_count)/cl->cl_count;

-	verbosef("cl cpg %u bpc %u count %u next %u\n",
-		 cl->cl_cpg, cl->cl_bpc, cl->cl_count, cl->cl_next_free_rec);
+	verbosef("cl cpg %u bpc %u count %u next %u gds %u max_ch_len %u\n",
+		 cl->cl_cpg, cl->cl_bpc, cl->cl_count, cl->cl_next_free_rec,
+		 num_gds, max_chain_len);

 	max_count = ocfs2_chain_recs_per_inode(ost->ost_fs->fs_blocksize);

@@ -948,7 +1001,7 @@ static errcode_t verify_chain_alloc(o2fsck_state *ost,
 			.cs_cpg = cl->cl_cpg,
 		};
 		ret = check_chain(ost, di, &cs, cr, buf1, buf2, &changed,
-				  allowed, forbidden);
+				  allowed, forbidden, max_chain_len);
 		/* XXX what?  not checking ret? */

 		if (cr->c_blkno != 0) {
diff --git a/include/ocfs2-kernel/ocfs2_fs.h b/include/ocfs2-kernel/ocfs2_fs.h
index 3eafb07..79e4f2f 100644
--- a/include/ocfs2-kernel/ocfs2_fs.h
+++ b/include/ocfs2-kernel/ocfs2_fs.h
@@ -1682,7 +1682,7 @@ static inline int
ocfs2_sprintf_system_inode_name(char *buf, int len,
 }

 static inline void ocfs2_set_de_type(struct ocfs2_dir_entry *de,
-				    umode_t mode)
+				    unsigned short mode)
 {
 	de->file_type = ocfs2_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
 }
-- 
1.7.10.4



More information about the Ocfs2-tools-devel mailing list