[Debian-ha-commits] [ocfs2-tools] 38/58: fsck.ocfs2: fix corruption when truncate reflink file

Valentin Vidic vvidic-guest at moszumanska.debian.org
Tue Jun 20 13:40:43 UTC 2017


This is an automated email from the git hooks/post-receive script.

vvidic-guest pushed a commit to branch upstream
in repository ocfs2-tools.

commit 5423218e46e7df6e6947cbc4ada37e431a6fc9e8
Author: Junxiao Bi <junxiao.bi at oracle.com>
Date:   Fri Aug 7 17:04:40 2015 +0800

    fsck.ocfs2: fix corruption when truncate reflink file
    
    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>
    Acked-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 1ea5cf8..f399c67 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -520,6 +520,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 bb23530..de1a07c 100644
--- a/libocfs2/refcount.c
+++ b/libocfs2/refcount.c
@@ -2207,6 +2207,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 451db28..2a14210 100644
--- a/libocfs2/truncate.c
+++ b/libocfs2/truncate.c
@@ -79,6 +79,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)
@@ -99,7 +100,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 +
@@ -140,7 +141,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;
 			}
 		}
 	}
@@ -159,6 +160,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;
@@ -399,6 +402,10 @@ truncate:
 		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);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-ha/ocfs2-tools.git



More information about the Debian-HA-Commits mailing list