Gang He
2015-Aug-21  07:43 UTC
[Ocfs2-devel] [PATCH 0/4] ocfs2: add file extent block online check
Besides inode block online check, add file extent block online check this time, we will add more kinds of meta block online check/repair in the future. Gang He (4): ocfs2: update filecheck copyright ocfs2: add errno and macro definitions ocfs2: filecheck validate_extent_block function ocfs2: add file extent block check fs/ocfs2/alloc.c | 116 +++++++++++++++++++++++ fs/ocfs2/alloc.h | 4 + fs/ocfs2/filecheck.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/ocfs2/filecheck.h | 3 +- fs/ocfs2/journal.h | 3 + fs/ocfs2/ocfs2_trace.h | 24 +++++ 6 files changed, 393 insertions(+), 10 deletions(-) -- 2.1.2
Update filecheck copyright to the right time/contributor Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/filecheck.c | 2 +- fs/ocfs2/filecheck.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c index a492e55..4b5a673 100644 --- a/fs/ocfs2/filecheck.c +++ b/fs/ocfs2/filecheck.c @@ -5,7 +5,7 @@ * * Code which implements online file check. * - * Copyright (C) 2007, 2009 Oracle. All rights reserved. + * Copyright (C) 2015 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h index c65fee9..5ec331b 100644 --- a/fs/ocfs2/filecheck.h +++ b/fs/ocfs2/filecheck.h @@ -5,7 +5,7 @@ * * Online file check. * - * Copyright (C) 2007 Oracle. All rights reserved. + * Copyright (C) 2015 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public -- 2.1.2
Gang He
2015-Aug-21  07:43 UTC
[Ocfs2-devel] [PATCH 2/4] ocfs2: add errno and macro definitions
Add new errno, macro definitions and header file inclusion,
which will be used for file extent block online check.
Signed-off-by: Gang He <ghe at suse.com>
---
 fs/ocfs2/filecheck.c | 4 ++++
 fs/ocfs2/filecheck.h | 1 +
 fs/ocfs2/journal.h   | 3 +++
 3 files changed, 8 insertions(+)
diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c
index 4b5a673..e8bc0cf 100644
--- a/fs/ocfs2/filecheck.c
+++ b/fs/ocfs2/filecheck.c
@@ -31,7 +31,10 @@
 #include "ocfs2.h"
 #include "ocfs2_fs.h"
 #include "stackglue.h"
+#include "dlmglue.h"
 #include "inode.h"
+#include "alloc.h"
+#include "journal.h"
 
 #include "filecheck.h"
 
@@ -45,6 +48,7 @@ static const char * const ocfs2_filecheck_errs[] = {
 	"INPROGRESS",
 	"READONLY",
 	"INVALIDINO",
+	"INVALIDEXT",
 	"BLOCKECC",
 	"BLOCKNO",
 	"VALIDFLAG",
diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h
index 5ec331b..e9c3fe6 100644
--- a/fs/ocfs2/filecheck.h
+++ b/fs/ocfs2/filecheck.h
@@ -32,6 +32,7 @@ enum {
 	OCFS2_FILECHECK_ERR_INPROGRESS,		/* In progress */
 	OCFS2_FILECHECK_ERR_READONLY,		/* Read only */
 	OCFS2_FILECHECK_ERR_INVALIDINO,		/* Invalid ino */
+	OCFS2_FILECHECK_ERR_INVALIDEXT,		/* Invalid extent block */
 	OCFS2_FILECHECK_ERR_BLOCKECC,		/* Block ecc */
 	OCFS2_FILECHECK_ERR_BLOCKNO,		/* Block number */
 	OCFS2_FILECHECK_ERR_VALIDFLAG,		/* Inode valid flag */
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index f4cd3c3..8ab70cd 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -350,6 +350,9 @@ void ocfs2_journal_dirty(handle_t *handle, struct
buffer_head *bh);
 /* simple file updates like chmod, etc. */
 #define OCFS2_INODE_UPDATE_CREDITS 1
 
+/* extent block update */
+#define OCFS2_EXTENT_BLOCK_UPDATE_CREDITS 1
+
 /* extended attribute block update */
 #define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1
 
-- 
2.1.2
Gang He
2015-Aug-21  07:43 UTC
[Ocfs2-devel] [PATCH 3/4] ocfs2: filecheck validate_extent_block function
Add validate_extent_block/repair_extent_block functions
for online filecheck.
Signed-off-by: Gang He <ghe at suse.com>
---
 fs/ocfs2/alloc.c       | 116 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/alloc.h       |   4 ++
 fs/ocfs2/ocfs2_trace.h |  24 ++++++++++
 3 files changed, 144 insertions(+)
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 0afb4cb..35fbbf1 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -51,6 +51,7 @@
 #include "xattr.h"
 #include "refcounttree.h"
 #include "ocfs2_trace.h"
+#include "filecheck.h"
 
 #include "buffer_head_io.h"
 
@@ -934,6 +935,121 @@ bail:
 	return rc;
 }
 
+static int ocfs2_filecheck_validate_extent_block(struct super_block *sb,
+					struct buffer_head *bh)
+{
+	int rc;
+	struct ocfs2_extent_block *eb +		(struct ocfs2_extent_block *)bh->b_data;
+
+	trace_ocfs2_filecheck_validate_extent_block(
+					(unsigned long long)bh->b_blocknr);
+
+	BUG_ON(!buffer_uptodate(bh));
+
+	if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
+		mlog(ML_ERROR,
+		"Filecheck: extent block #%llu has bad signature %.*s\n",
+		(unsigned long long)bh->b_blocknr,
+		7, eb->h_signature);
+		return -OCFS2_FILECHECK_ERR_INVALIDEXT;
+	}
+
+	rc = ocfs2_validate_meta_ecc(sb, bh->b_data, &eb->h_check);
+	if (rc) {
+		mlog(ML_ERROR, "Filecheck: checksum failed for extent "
+		"block %llu\n",
+		(unsigned long long)bh->b_blocknr);
+		return -OCFS2_FILECHECK_ERR_BLOCKECC;
+	}
+
+	if (le64_to_cpu(eb->h_blkno) != bh->b_blocknr) {
+		mlog(ML_ERROR,
+		"Filecheck: extent block #%llu has an invalid h_blkno "
+		"of %llu\n",
+		(unsigned long long)bh->b_blocknr,
+		(unsigned long long)le64_to_cpu(eb->h_blkno));
+		return -OCFS2_FILECHECK_ERR_BLOCKNO;
+	}
+
+	if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) {
+		mlog(ML_ERROR,
+		"Filecheck: extent block #%llu has an invalid "
+		"h_fs_generation of #%u\n",
+		(unsigned long long)bh->b_blocknr,
+		le32_to_cpu(eb->h_fs_generation));
+		return -OCFS2_FILECHECK_ERR_GENERATION;
+	}
+
+	return 0;
+}
+
+static int ocfs2_filecheck_repair_extent_block(struct super_block *sb,
+					struct buffer_head *bh)
+{
+	int rc;
+	int changed = 0;
+	struct ocfs2_extent_block *eb +		(struct ocfs2_extent_block *)bh->b_data;
+
+	rc = ocfs2_filecheck_validate_extent_block(sb, bh);
+	/* Can't fix invalid extent block */
+	if (!rc || rc == -OCFS2_FILECHECK_ERR_INVALIDEXT)
+		return rc;
+
+	trace_ocfs2_filecheck_repair_extent_block(
+					(unsigned long long)bh->b_blocknr);
+
+	if (le64_to_cpu(eb->h_blkno) != bh->b_blocknr) {
+		eb->h_blkno = cpu_to_le64(bh->b_blocknr);
+		changed = 1;
+		mlog(ML_ERROR,
+		"Filecheck: reset extent block #%llu: h_blkno to %llu\n",
+		(unsigned long long)bh->b_blocknr,
+		(unsigned long long)le64_to_cpu(eb->h_blkno));
+	}
+
+	if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) {
+		eb->h_fs_generation = cpu_to_le32(OCFS2_SB(sb)->fs_generation);
+		changed = 1;
+		mlog(ML_ERROR,
+		"Filecheck: reset extent block #%llu: "
+		"h_fs_generation to %u\n",
+		(unsigned long long)bh->b_blocknr,
+		le32_to_cpu(eb->h_fs_generation));
+	}
+
+	if (changed || ocfs2_validate_meta_ecc(sb, bh->b_data,
&eb->h_check)) {
+		ocfs2_compute_meta_ecc(sb, bh->b_data, &eb->h_check);
+		mark_buffer_dirty(bh);
+		mlog(ML_ERROR,
+		"Filecheck: reset extent block #%llu: compute meta ecc\n",
+		(unsigned long long)bh->b_blocknr);
+	}
+
+	return 0;
+}
+
+int
+ocfs2_filecheck_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno,
+				struct buffer_head **bh, unsigned int flags)
+{
+	int rc;
+	struct buffer_head *tmp = *bh;
+
+	if (!flags) /* check extent block */
+		rc = ocfs2_read_block(ci, eb_blkno, &tmp,
+				ocfs2_filecheck_validate_extent_block);
+	else /* repair extent block */
+		rc = ocfs2_read_block(ci, eb_blkno, &tmp,
+				ocfs2_filecheck_repair_extent_block);
+
+	if (!rc && !*bh)
+		*bh = tmp;
+
+	return rc;
+}
+
 int ocfs2_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno,
 			    struct buffer_head **bh)
 {
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index fb09b97..c882d52 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -92,6 +92,10 @@ void ocfs2_init_refcount_extent_tree(struct ocfs2_extent_tree
*et,
 int ocfs2_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno,
 			    struct buffer_head **bh);
 
+int
+ocfs2_filecheck_read_extent_block(struct ocfs2_caching_info *ci, u64 eb_blkno,
+				struct buffer_head **bh, unsigned int flags);
+
 struct ocfs2_alloc_context;
 int ocfs2_insert_extent(handle_t *handle,
 			struct ocfs2_extent_tree *et,
diff --git a/fs/ocfs2/ocfs2_trace.h b/fs/ocfs2/ocfs2_trace.h
index d9205e0..4788748 100644
--- a/fs/ocfs2/ocfs2_trace.h
+++ b/fs/ocfs2/ocfs2_trace.h
@@ -557,6 +557,30 @@ TRACE_EVENT(ocfs2_validate_extent_block,
 	TP_printk("%llu ", __entry->blkno)
 );
 
+TRACE_EVENT(ocfs2_filecheck_validate_extent_block,
+	TP_PROTO(unsigned long long blkno),
+	TP_ARGS(blkno),
+	TP_STRUCT__entry(
+		__field(unsigned long long, blkno)
+	),
+	TP_fast_assign(
+		__entry->blkno = blkno;
+	),
+	TP_printk("%llu ", __entry->blkno)
+);
+
+TRACE_EVENT(ocfs2_filecheck_repair_extent_block,
+	TP_PROTO(unsigned long long blkno),
+	TP_ARGS(blkno),
+	TP_STRUCT__entry(
+		__field(unsigned long long, blkno)
+	),
+	TP_fast_assign(
+		__entry->blkno = blkno;
+	),
+	TP_printk("%llu ", __entry->blkno)
+);
+
 TRACE_EVENT(ocfs2_rotate_leaf,
 	TP_PROTO(unsigned int insert_cpos, int insert_index,
 		 int has_empty, int next_free,
-- 
2.1.2
Gang He
2015-Aug-21  07:43 UTC
[Ocfs2-devel] [PATCH 4/4] ocfs2: add file extent block check
Add file extent block online check, besides inode block online check,
we will add more kinds of meta block online check/repair.
Signed-off-by: Gang He <ghe at suse.com>
---
 fs/ocfs2/filecheck.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 239 insertions(+), 8 deletions(-)
diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c
index e8bc0cf..6aedb9b 100644
--- a/fs/ocfs2/filecheck.c
+++ b/fs/ocfs2/filecheck.c
@@ -462,38 +462,269 @@ ocfs2_filecheck_done_entry(struct
ocfs2_filecheck_sysfs_entry *ent,
 	spin_unlock(&ent->fs_fcheck->fc_lock);
 }
 
+static int
+ocfs2_filecheck_inode_block(struct super_block *sb, unsigned long ino,
+				unsigned int flags, struct inode **ret)
+{
+	int rc = 0;
+	struct inode *inode;
+
+	if (flags == OCFS2_FILECHECK_TYPE_CHK)
+		flags = OCFS2_FI_FLAG_FILECHECK_CHK;
+	else
+		flags = OCFS2_FI_FLAG_FILECHECK_FIX;
+
+	inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
+	if (IS_ERR(inode))
+		rc = (int)(long)inode;
+	else
+		*ret = inode;
+
+	return rc;
+}
+
+static int
+ocfs2_filecheck_extent_list(handle_t *handle, struct inode *inode,
+			unsigned int flags, struct ocfs2_extent_list *el)
+{
+	int i, rc = 0;
+	u64 blkno;
+	struct buffer_head *bh = NULL;
+	struct ocfs2_extent_block *eb;
+	struct ocfs2_extent_list *ell;
+
+	if (!el || !el->l_tree_depth)
+		return 0;
+
+	mlog(ML_NOTICE, "ocfs2_filecheck_extent_list: inode %llu tree_depth
"
+		"%u count %u next_free_rec %u\n",
+		(unsigned long long)OCFS2_I(inode)->ip_blkno,
+		le16_to_cpu(el->l_tree_depth),
+		le16_to_cpu(el->l_count),
+		le16_to_cpu(el->l_next_free_rec));
+
+	if (le16_to_cpu(el->l_next_free_rec) == 0) {
+		mlog(ML_ERROR,
+		"Inode %llu has empty extent list at depth %u\n",
+		(unsigned long long)OCFS2_I(inode)->ip_blkno,
+		le16_to_cpu(el->l_tree_depth));
+		rc = -EROFS;
+		goto out;
+	}
+
+	if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) {
+		mlog(ML_ERROR,
+		"Inode %llu has bad count in extent list "
+		"at depth %u (next free=%u, count=%u)\n",
+		(unsigned long long)OCFS2_I(inode)->ip_blkno,
+		le16_to_cpu(el->l_tree_depth),
+		le16_to_cpu(el->l_next_free_rec),
+		le16_to_cpu(el->l_count));
+		rc = -EROFS;
+		goto out;
+	}
+
+	for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
+		blkno = le64_to_cpu(el->l_recs[i].e_blkno);
+		if (blkno == 0) {
+			mlog(ML_ERROR,
+			"Inode %llu has bad blkno in extent list "
+			"at depth %u (index %d)\n",
+			(unsigned long long)OCFS2_I(inode)->ip_blkno,
+			le16_to_cpu(el->l_tree_depth), i);
+			rc = -EROFS;
+			goto out;
+		}
+
+		mlog(ML_NOTICE, "ocfs2_filecheck_extent_list: inode %llu "
+		"rec[%d]: cpos %u clusters %u blkno %llu\n ",
+		(unsigned long long)OCFS2_I(inode)->ip_blkno,
+		i,
+		le32_to_cpu(el->l_recs[i].e_cpos),
+		le32_to_cpu(el->l_recs[i].e_int_clusters),
+		le64_to_cpu(el->l_recs[i].e_blkno));
+
+		brelse(bh);
+		bh = NULL;
+
+		rc = ocfs2_filecheck_read_extent_block(INODE_CACHE(inode),
+						blkno, &bh, flags);
+		if (rc) {
+			mlog_errno(rc);
+			goto out;
+		}
+
+		if (flags && buffer_dirty(bh)) {
+			/* Dirty buffer means that filecheck just repaired
+			 * this buffer during reading it from disk.
+			 */
+			clear_buffer_dirty(bh);
+
+			rc = ocfs2_extend_trans(handle,
+					OCFS2_EXTENT_BLOCK_UPDATE_CREDITS);
+			if (rc) {
+				mlog_errno(rc);
+				goto out;
+			}
+
+			rc = ocfs2_journal_access_eb(handle,
+				INODE_CACHE(inode), bh,
+				OCFS2_JOURNAL_ACCESS_WRITE);
+			if (rc < 0) {
+				mlog_errno(rc);
+				goto out;
+			}
+
+			ocfs2_journal_dirty(handle, bh);
+		}
+
+		eb = (struct ocfs2_extent_block *)bh->b_data;
+		ell = &eb->h_list;
+		rc = ocfs2_filecheck_extent_list(handle, inode, flags, ell);
+		if (rc)
+			goto out;
+	}
+
+out:
+	brelse(bh);
+	return rc;
+}
+
+static int
+ocfs2_filecheck_extent_block(struct inode *inode, struct buffer_head *di_bh,
+				unsigned int flags)
+{
+	int rc = 0;
+	handle_t *handle = NULL;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_dinode *di;
+	struct ocfs2_extent_list *el;
+
+	if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)
+		return 0;
+
+	if (flags) {
+		handle = ocfs2_start_trans(osb,
+					OCFS2_EXTENT_BLOCK_UPDATE_CREDITS);
+		if (IS_ERR(handle)) {
+			rc = PTR_ERR(handle);
+			mlog_errno(rc);
+			goto out;
+		}
+	}
+
+	di = (struct ocfs2_dinode *)di_bh->b_data;
+	el = &di->id2.i_list;
+	rc = ocfs2_filecheck_extent_list(handle, inode, flags, el);
+
+	if (flags)
+		ocfs2_commit_trans(osb, handle);
+
+out:
+	return rc;
+}
+
+static int
+ocfs2_filecheck_file_block(struct inode *inode, unsigned int flags)
+{
+	int rc;
+	struct buffer_head *di_bh = NULL;
+
+	mutex_lock(&inode->i_mutex);
+
+	rc = ocfs2_rw_lock(inode, 1);
+	if (rc) {
+		mlog_errno(rc);
+		goto out;
+	}
+
+	rc = ocfs2_inode_lock(inode, &di_bh, 1);
+	if (rc) {
+		mlog_errno(rc);
+		goto out_rw_unlock;
+	}
+
+	do {
+		down_write(&OCFS2_I(inode)->ip_alloc_sem);
+		rc = ocfs2_filecheck_extent_block(inode, di_bh, flags);
+		up_write(&OCFS2_I(inode)->ip_alloc_sem);
+		if (rc)
+			break;
+
+		/* TODO: Add check/fix for xattr/refcount blocks */
+
+	} while (0);
+
+	brelse(di_bh);
+	ocfs2_inode_unlock(inode, 1);
+out_rw_unlock:
+	ocfs2_rw_unlock(inode, 1);
+out:
+	mutex_unlock(&inode->i_mutex);
+
+	return rc;
+}
+
+static int
+ocfs2_filecheck_other_block(struct inode *inode, unsigned int flags)
+{
+	int rc = 0;
+
+	switch (inode->i_mode & S_IFMT) {
+	case S_IFREG:
+		rc = ocfs2_filecheck_file_block(inode, flags);
+		break;
+	case S_IFDIR:
+		/* TODO: Add check/fix for directory blocks */
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
 static unsigned short
 ocfs2_filecheck_handle(struct super_block *sb,
 				unsigned long ino, unsigned int flags)
 {
+	int rc;
 	unsigned short ret = OCFS2_FILECHECK_ERR_SUCCESS;
 	struct inode *inode = NULL;
-	int rc;
 
-	inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
-	if (IS_ERR(inode)) {
-		rc = (int)(-(long)inode);
+	do {
+		rc = ocfs2_filecheck_inode_block(sb, ino, flags, &inode);
+		if (rc)
+			break;
+
+		rc = ocfs2_filecheck_other_block(inode, flags);
+	} while (0);
+
+	if (rc) {
+		rc = (-rc);
 		if (rc >= OCFS2_FILECHECK_ERR_START &&
 			rc < OCFS2_FILECHECK_ERR_END)
 			ret = rc;
 		else
 			ret = OCFS2_FILECHECK_ERR_FAILED;
-	} else
+	}
+
+	if (inode)
 		iput(inode);
 
 	return ret;
 }
 
-static void
+static inline void
 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
 				struct ocfs2_filecheck_entry *entry)
 {
 	if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
 		entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
-				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
+				entry->fe_ino, OCFS2_FILECHECK_TYPE_CHK);
 	else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
 		entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
-				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
+				entry->fe_ino, OCFS2_FILECHECK_TYPE_FIX);
 	else
 		entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
 
-- 
2.1.2