[Ocfs2-tools-devel] [PATCH] fsck.ocfs2: fix corruption when truncate reflink file

Junxiao Bi junxiao.bi at oracle.com
Mon Aug 31 18:41:10 PDT 2015


The following steps will cause a corruption:
1. reflink test test.ref
2. open test.ref
3. remove test.ref
4. crash the system
5. fsck.ocfs2 -a $ocfs2_volume

fsck to check, you will find the following error:

Pass 1: Checking inodes and blocks
[REFCOUNT_COUNT] Refcount tree at 6901 claims to have 2 files associated with it, but we only found 1.Update the count number? y
[REFCOUNT_COUNT_INVALID] clusters 9612289 with len 768 have 2 refcount while there are 1 files point to them. Correct the refcount value? y
[CLUSTER_ALLOC_BIT] Cluster 9612289 is in use but isn't set in the global cluster bitmap. Set its bit in the bitmap? y

The root cause of this issue is that refcount tree is not updated right when
truncating files in orphan dir. The refcount flag in extents have been memset
to zero when try to decrease refcount, so share cluster freed but not update
refcount tree. This causes corruption [REFCOUNT_COUNT_INVALID] and [CLUSTER_ALLOC_BIT].

[REFCOUNT_COUNT] error is because refcount tree's rf_count is decreased by 1
when file size is truncated to zero.

Signed-off-by: Junxiao Bi <junxiao.bi at oracle.com>
Reviewed-by: Ryan Ding<ryan.ding at oracle.com>
---
 include/ocfs2/ocfs2.h |    2 ++
 libocfs2/refcount.c   |   51 +++++++++++++++++++++++++++++++++++++++++++++++++
 libocfs2/truncate.c   |   11 +++++++++--
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index 3b54880..ee838f1 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -518,6 +518,8 @@ int ocfs2_get_refcount_rec(ocfs2_filesys *fs,
 errcode_t ocfs2_create_refcount_tree(ocfs2_filesys *fs, uint64_t *refcount_loc);
 errcode_t ocfs2_attach_refcount_tree(ocfs2_filesys *fs,
 				     uint64_t ino, uint64_t refcount_loc);
+errcode_t ocfs2_detach_refcount_tree(ocfs2_filesys *fs,
+				     uint64_t ino, uint64_t refcount_loc);
 errcode_t ocfs2_swap_dir_entries_from_cpu(void *buf, uint64_t bytes);
 errcode_t ocfs2_swap_dir_entries_to_cpu(void *buf, uint64_t bytes);
 void ocfs2_swap_dir_trailer(struct ocfs2_dir_block_trailer *trailer);
diff --git a/libocfs2/refcount.c b/libocfs2/refcount.c
index 6afe032..ee95a88 100644
--- a/libocfs2/refcount.c
+++ b/libocfs2/refcount.c
@@ -2205,6 +2205,57 @@ out:
 	return ret;
 }
 
+errcode_t ocfs2_detach_refcount_tree(ocfs2_filesys *fs,
+				     uint64_t ino, uint64_t refcount_loc)
+{
+	errcode_t ret;
+	char *buf = NULL;
+	struct ocfs2_dinode *di;
+	struct ocfs2_refcount_block *rb;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		return ret;
+
+	ret = ocfs2_read_refcount_block(fs, refcount_loc, buf);
+	if (ret)
+		goto out;
+
+	rb = (struct ocfs2_refcount_block *)buf;
+	rb->rf_count += -1;
+
+	if (!rb->rf_count) {
+		ret = ocfs2_delete_refcount_block(fs, rb->rf_blkno);
+		if (ret) {
+			com_err("refcount", ret, "remove refcount tree <%lu> failed.\n", rb->rf_blkno);
+			goto out;
+		}
+	} else {
+		ret = ocfs2_write_refcount_block(fs, refcount_loc, buf);
+		if (ret) {
+			com_err("refcount", ret, "update refcount tree <%lu> failed.\n", rb->rf_blkno);
+			goto out;
+		}
+	}
+
+	ret = ocfs2_read_inode(fs, ino, buf);
+	if (ret) {
+		com_err("refcount", ret, "read inode %lu fail, stop setting refcount tree <%lu>.\n",
+			ino, rb->rf_blkno);
+		goto out;
+	}
+
+	di = (struct ocfs2_dinode *)buf;
+
+	di->i_refcount_loc = 0;
+	di->i_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL;
+
+	ret = ocfs2_write_inode(fs, ino, buf);
+out:
+	ocfs2_free(&buf);
+	return ret;
+}
+
 struct xattr_value_cow_object {
 	struct ocfs2_xattr_value_root *xv;
 	uint64_t xe_blkno;
diff --git a/libocfs2/truncate.c b/libocfs2/truncate.c
index 7327253..0b993ca 100644
--- a/libocfs2/truncate.c
+++ b/libocfs2/truncate.c
@@ -78,6 +78,7 @@ static int truncate_iterate(ocfs2_filesys *fs,
 	int func_ret = OCFS2_EXTENT_ERROR;
 	char *buf = NULL;
 	struct ocfs2_extent_list *el = NULL;
+	int cleanup_rec = 0;
 
 	if ((rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec)) <=
 							new_size_in_clusters)
@@ -98,7 +99,7 @@ static int truncate_iterate(ocfs2_filesys *fs,
 				goto bail;
 		}
 
-		memset(rec, 0, sizeof(struct ocfs2_extent_rec));
+		cleanup_rec = 1;
 	} else {
 		/* we're truncating into the middle of the rec */
 		len = rec->e_cpos +
@@ -139,7 +140,7 @@ static int truncate_iterate(ocfs2_filesys *fs,
 				ret = ocfs2_delete_extent_block(fs, rec->e_blkno);
 				if (ret)
 					goto bail;
-				memset(rec, 0, sizeof(struct ocfs2_extent_rec));
+					cleanup_rec = 1;
 			}
 		}
 	}
@@ -158,6 +159,8 @@ static int truncate_iterate(ocfs2_filesys *fs,
 
 	func_ret =  OCFS2_EXTENT_CHANGED;
 bail:
+	if (cleanup_rec)
+		memset(rec, 0, sizeof(struct ocfs2_extent_rec));
 	if (buf)
 		ocfs2_free(&buf);
 	return func_ret;
@@ -387,6 +390,10 @@ errcode_t ocfs2_truncate_full(ocfs2_filesys *fs, uint64_t ino,
 		ci->ci_inode->i_size = new_i_size;
 		ret = ocfs2_write_cached_inode(fs, ci);
 	}
+
+	if (!ret && !new_i_size && ci->ci_inode->i_refcount_loc &&
+		(ci->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL))
+		ret = ocfs2_detach_refcount_tree(fs, ino, ci->ci_inode->i_refcount_loc);
 out:
 	if (ci)
 		ocfs2_free_cached_inode(fs, ci);
-- 
1.7.9.5




More information about the Ocfs2-tools-devel mailing list