[Ocfs2-tools-devel] [PATCH 7/9] ocfs2-tools: add xattr support in fsck.ocfs2

Tiger Yang tiger.yang at oracle.com
Sat Jan 24 00:03:58 PST 2009


This patch will check all extended attributes related with each inode,
It will check and try to fix xattr entries in inode, block and bucket,
extent tree in index block and xattr's value tree, mark all xattr
block/cluster as used.

Signed-off-by: Tiger Yang <tiger.yang at oracle.com>
---
 fsck.ocfs2/Makefile               |    3 +-
 fsck.ocfs2/extent.c               |   17 +--
 fsck.ocfs2/fsck.ocfs2.checks.8.in |   24 +++
 fsck.ocfs2/include/extent.h       |   13 ++
 fsck.ocfs2/include/xattr.h        |   29 +++
 fsck.ocfs2/pass1.c                |    4 +
 fsck.ocfs2/xattr.c                |  367 +++++++++++++++++++++++++++++++++++++
 include/ocfs2/ocfs2.h             |    1 +
 libocfs2/xattr.c                  |   18 ++
 9 files changed, 460 insertions(+), 16 deletions(-)
 create mode 100644 fsck.ocfs2/include/xattr.h
 create mode 100644 fsck.ocfs2/xattr.c

diff --git a/fsck.ocfs2/Makefile b/fsck.ocfs2/Makefile
index d9d0862..9249cb5 100644
--- a/fsck.ocfs2/Makefile
+++ b/fsck.ocfs2/Makefile
@@ -31,7 +31,8 @@ CFILES =	fsck.c		\
 		problem.c 	\
 		slot_recovery.c \
 		strings.c 	\
-		util.c
+		util.c		\
+		xattr.c
 
 HFILES = 	include/fsck.h		\
 		include/dirblocks.h	\
diff --git a/fsck.ocfs2/extent.c b/fsck.ocfs2/extent.c
index 665704d..5f6639c 100644
--- a/fsck.ocfs2/extent.c
+++ b/fsck.ocfs2/extent.c
@@ -46,19 +46,6 @@
 
 static const char *whoami = "extent.c";
 
-struct extent_info {
-	uint64_t	ei_max_size;
-	uint64_t	ei_clusters;
-	uint64_t	ei_last_eb_blk;
-	uint16_t	ei_expected_depth;
-	unsigned	ei_expect_depth:1;
-};
-
-static errcode_t check_el(o2fsck_state *ost, struct extent_info *ei,
-			  struct ocfs2_dinode *di,
-			  struct ocfs2_extent_list *el,
-			  uint16_t max_recs, int *changed);
-
 static errcode_t check_eb(o2fsck_state *ost, struct extent_info *ei,
 			  struct ocfs2_dinode *di, uint64_t blkno,
 			  int *is_valid)
@@ -233,7 +220,7 @@ out:
 	return ret;
 }
 
-static errcode_t check_el(o2fsck_state *ost, struct extent_info *ei,
+errcode_t check_el(o2fsck_state *ost, struct extent_info *ei,
 			  struct ocfs2_dinode *di,
 			  struct ocfs2_extent_list *el,
 			  uint16_t max_recs, int *changed)
@@ -373,4 +360,4 @@ errcode_t o2fsck_check_extents(o2fsck_state *ost,
 		o2fsck_write_inode(ost, di->i_blkno, di);
 
 	return ret;
-}	
+}
diff --git a/fsck.ocfs2/fsck.ocfs2.checks.8.in b/fsck.ocfs2/fsck.ocfs2.checks.8.in
index 22982c0..fbd4b5f 100644
--- a/fsck.ocfs2/fsck.ocfs2.checks.8.in
+++ b/fsck.ocfs2/fsck.ocfs2.checks.8.in
@@ -739,6 +739,30 @@ that this value isn't right.
 
 Answering yes change this value to the right number.
 
+.SS "XATTR_COUNT_INVALID"
+For entended attributes in inode, block or bucket,  Fsck has found the
+counts of them isn't right.
+
+Answering yes change this value to the right number.
+
+.SS "XATTR_ENTRY_INVALID"
+For entended attributes entry, Fsck has found that it's name_offset
+isn't right.
+
+Answering yes to remove this xattr entry.
+
+.SS "XATTR_HASH_INVALID"
+For entended attributes entry, Fsck has found that it's name_hash
+isn't right.
+
+Answering yes change this value to the right hash value.
+
+.SS "XATTR_BLOCK_INVALID"
+For entended attributes block, Fsck has found that it's signature
+isn't right.
+
+Answering yes to remove this xattr block.
+
 .SH "SEE ALSO"
 .BR fsck.ocfs2(8)
 
diff --git a/fsck.ocfs2/include/extent.h b/fsck.ocfs2/include/extent.h
index 6fff28b..1266b7f 100644
--- a/fsck.ocfs2/include/extent.h
+++ b/fsck.ocfs2/include/extent.h
@@ -22,8 +22,21 @@
 
 #include "fsck.h"
 
+struct extent_info {
+	uint64_t	ei_max_size;
+	uint64_t	ei_clusters;
+	uint64_t	ei_last_eb_blk;
+	uint16_t	ei_expected_depth;
+	unsigned	ei_expect_depth:1;
+};
+
 errcode_t o2fsck_check_extents(o2fsck_state *ost,
                                struct ocfs2_dinode *di);
 
+errcode_t check_el(o2fsck_state *ost, struct extent_info *ei,
+			  struct ocfs2_dinode *di,
+			  struct ocfs2_extent_list *el,
+			  uint16_t max_recs, int *changed);
+
 #endif /* __O2FSCK_EXTENT_H__ */
 
diff --git a/fsck.ocfs2/include/xattr.h b/fsck.ocfs2/include/xattr.h
new file mode 100644
index 0000000..3895482
--- /dev/null
+++ b/fsck.ocfs2/include/xattr.h
@@ -0,0 +1,29 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * xattr.h
+ *
+ * Copyright (C) 2004, 2008 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef __O2FSCK_XATTR_H__
+#define __O2FSCK_XATTR_H__
+
+#include "fsck.h"
+
+#define OCFS2_IS_VALID_XATTR_BLOCK(ptr)                                 \
+	(!strcmp((ptr)->xb_signature, OCFS2_XATTR_BLOCK_SIGNATURE))
+
+errcode_t o2fsck_check_xattr(o2fsck_state *ost,
+			     struct ocfs2_dinode *di);
+
+#endif /* __O2FSCK_XATTR_H__ */
+
diff --git a/fsck.ocfs2/pass1.c b/fsck.ocfs2/pass1.c
index 209f8ff..6f385f6 100644
--- a/fsck.ocfs2/pass1.c
+++ b/fsck.ocfs2/pass1.c
@@ -65,6 +65,7 @@
 #include "pass1.h"
 #include "problem.h"
 #include "util.h"
+#include "xattr.h"
 
 static const char *whoami = "pass1";
 
@@ -1385,6 +1386,9 @@ errcode_t o2fsck_pass1(o2fsck_state *ost)
 								  blkno, di);
 					if (ret)
 						goto out;
+					ret = o2fsck_check_xattr(ost, di);
+					if (ret)
+						goto out;
 				}
 
 				valid = di->i_flags & OCFS2_VALID_FL;
diff --git a/fsck.ocfs2/xattr.c b/fsck.ocfs2/xattr.c
new file mode 100644
index 0000000..bf5376b
--- /dev/null
+++ b/fsck.ocfs2/xattr.c
@@ -0,0 +1,367 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * xattr.c
+ *
+ * Copyright (C) 2004, 2008 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <string.h>
+#include <inttypes.h>
+
+#include "ocfs2/byteorder.h"
+#include "ocfs2/ocfs2.h"
+
+#include "xattr.h"
+#include "extent.h"
+#include "fsck.h"
+#include "problem.h"
+#include "util.h"
+
+#define IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0)
+
+static const char *whoami = "xattr.c";
+
+struct xattr_info {
+	char *location;
+	uint32_t max_offset;
+	uint64_t blkno;
+};
+
+static void check_xattr_header(o2fsck_state *ost,
+			       struct ocfs2_dinode *di,
+			       struct ocfs2_xattr_header *xh,
+			       int *changed,
+			       struct xattr_info *xi)
+{
+	uint16_t count = 0;
+	struct ocfs2_xattr_entry *entry = xh->xh_entries;
+
+	while (!IS_LAST_ENTRY(entry)) {
+		count++;
+		entry++;
+	}
+
+	if (xh->xh_count != count &&
+	    prompt(ost, PY, PR_XATTR_COUNT_INVALID,
+		   "Extended attributes in %s #%"PRIu64" claims to have %u "
+		   "entries, but fsck believes it is %u. "
+		   "Fix the entries count?",
+		   xi->location, xi->blkno, xh->xh_count, count)) {
+
+		xh->xh_count = count;
+		*changed = 1;
+	}
+
+	return;
+}
+
+static void check_xattr_entry(o2fsck_state *ost,
+			      struct ocfs2_dinode *di,
+			      struct ocfs2_xattr_header *xh,
+			      int *changed,
+			      struct xattr_info *xi)
+{
+	int i;
+
+	for (i = 0 ; i < xh->xh_count; i++) {
+		struct ocfs2_xattr_entry *xe = &xh->xh_entries[i];
+
+		if (xe->xe_name_offset >= xi->max_offset &&
+		    prompt(ost, PY, PR_XATTR_ENTRY_INVALID,
+		    "Extended attributes entry in %s #%"PRIu64"refers to"
+		    "an invalid name offset %u, clear this entry?",
+		    xi->location, xi->blkno, xe->xe_name_offset)) {
+			/*
+			 * only remove the entry but left the name+value here,
+			 * because we don't know the correct offset of them.
+			 */
+			memmove((void *)(xe + 1), (void *)xe,
+				(xh->xh_count - i - 1) *
+				sizeof(struct ocfs2_xattr_entry));
+			xh->xh_count -= 1;
+			i -= 1;
+			*changed = 1;
+		} else if (xe->xe_name_offset < xi->max_offset) {
+			/*check and fix name hash*/
+			uint32_t hash = xattr_name_hash(
+				ost->ost_fs->fs_super->id2.i_super.s_uuid_hash,
+				(void *)xh + xe->xe_name_offset,
+				xe->xe_name_len);
+			if (xe->xe_name_hash != hash &&
+			    prompt(ost, PY, PR_XATTR_HASH_INVALID,
+			    "Extended attributes entry in %s #%"PRIu64"refers to"
+			    "an invalid name hash %u, Fix the name hash?",
+			    xi->location, xi->blkno, xe->xe_name_hash)) {
+				xe->xe_name_hash = hash;
+				*changed = 1;
+			}
+		}
+	}
+
+	return;
+}
+
+static errcode_t check_xattr_value(o2fsck_state *ost,
+				   struct ocfs2_dinode *di,
+				   struct ocfs2_xattr_header *xh,
+				   int *changed)
+{
+	int i;
+	struct extent_info ei = {0, };
+	errcode_t ret = 0;
+
+	for (i = 0 ; i < xh->xh_count; i++) {
+		int change = 0;
+		struct ocfs2_xattr_entry *xe = &xh->xh_entries[i];
+
+		if (!ocfs2_xattr_is_local(xe)) {
+			struct ocfs2_xattr_value_root *xv =
+				(struct ocfs2_xattr_value_root *)
+				((void *)xh + xe->xe_name_offset +
+				OCFS2_XATTR_SIZE(xe->xe_name_len));
+			struct ocfs2_extent_list *el = &xv->xr_list;
+			ret = check_el(ost, &ei, di, el, 1, &change);
+			if (ret)
+				return ret;
+			if (change)
+				*changed = 1;
+		}
+	}
+
+	return ret;
+}
+
+static errcode_t check_xattr(o2fsck_state *ost,
+			     struct ocfs2_dinode *di,
+			     struct ocfs2_xattr_header *xh,
+			     int *changed,
+			     struct xattr_info *xi)
+{
+	check_xattr_header(ost, di, xh, changed, xi);
+	check_xattr_entry(ost, di, xh, changed, xi);
+	return check_xattr_value(ost, di, xh, changed);
+}
+
+static errcode_t ocfs2_check_xattr_buckets(o2fsck_state *ost,
+					   struct ocfs2_dinode *di,
+					   uint64_t blkno,
+					   uint32_t clusters)
+{
+	int i;
+	errcode_t ret = 0;
+	char *bucket = NULL;
+	struct ocfs2_xattr_header *xh;
+	int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(ost->ost_fs);
+	uint32_t bpc = ocfs2_xattr_buckets_per_cluster(ost->ost_fs);
+	uint32_t num_buckets = clusters * bpc;
+
+	ret = ocfs2_malloc_blocks(ost->ost_fs->fs_io, blk_per_bucket, &bucket);
+	if (ret) {
+		com_err(whoami, ret, "while allocating room to read bucket "
+			"of xattr data");
+		goto out;
+	}
+
+	for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) {
+		int changed = 0;
+		struct xattr_info xi= {
+			.location = "bucket",
+			.max_offset = blk_per_bucket * ost->ost_fs->fs_blocksize,
+			.blkno = blkno,
+		};
+
+		ret = io_read_block(ost->ost_fs->fs_io, blkno,
+				    blk_per_bucket, bucket);
+		if (ret) {
+			com_err(whoami, ret, "while reading blocks of xattr "
+				"bucket");
+			goto out;
+		}
+
+		xh = (struct ocfs2_xattr_header *)bucket;
+		ocfs2_swap_xattrs_to_cpu(xh);
+		/*
+		 * The real bucket num in this series of blocks is stored
+		 * in the 1st bucket.
+		 */
+		if (i == 0)
+			num_buckets = xh->xh_num_buckets;
+
+		ret = check_xattr(ost, di, xh, &changed, &xi);
+		if (ret)
+			break;
+		if (changed) {
+			ocfs2_swap_xattrs_from_cpu(xh);
+			io_write_block(ost->ost_fs->fs_io, blkno,
+				       blk_per_bucket, bucket);
+		}
+	}
+out:
+	if (bucket)
+		ocfs2_free(&bucket);
+
+	return ret;
+}
+
+static errcode_t o2fsck_check_xattr_index_block(o2fsck_state *ost,
+						struct ocfs2_dinode *di,
+						struct ocfs2_xattr_block *xb,
+						int *changed)
+{
+	struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list;
+	errcode_t ret = 0;
+	uint32_t name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0;
+	uint64_t p_blkno = 0;
+	struct extent_info ei = {0, };
+
+	if (!el->l_next_free_rec)
+		return 0;
+
+	ret = check_el(ost, &ei, di, el,
+		ocfs2_xattr_recs_per_xb(ost->ost_fs->fs_blocksize),
+		changed);
+	if (ret)
+		return ret;
+
+	while (name_hash > 0) {
+		ret = ocfs2_xattr_get_rec(ost->ost_fs, xb, name_hash, &p_blkno,
+					  &e_cpos, &num_clusters);
+		if (ret) {
+			com_err(whoami, ret, "while getting bucket record "
+				"of xattr data.");
+			goto out;
+		}
+
+		ret = ocfs2_check_xattr_buckets(ost, di, p_blkno,
+						num_clusters);
+		if (ret) {
+			com_err(whoami, ret, "while iterating buckets "
+				"of xattr data.");
+			goto out;
+		}
+
+		if (e_cpos == 0)
+			break;
+
+		name_hash = e_cpos - 1;
+	}
+
+out:
+	return ret;
+}
+
+static errcode_t o2fsck_check_xattr_block(o2fsck_state *ost,
+					  struct ocfs2_dinode *di,
+					  int *i_changed)
+{
+	errcode_t ret;
+	char *blk = NULL;
+	struct ocfs2_xattr_block *xb = NULL;
+	int b_changed = 0;
+
+	ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &blk);
+	if (ret) {
+		com_err(whoami, ret, "while allocating room to read block "
+			"of xattr.");
+		return ret;
+	}
+
+	ret = ocfs2_read_xattr_block(ost->ost_fs, di->i_xattr_loc, blk);
+	if (ret) {
+		com_err(whoami, ret, "while reading externel block of xattr.");
+		goto out;
+	}
+
+	xb = (struct ocfs2_xattr_block *)blk;
+
+	if (strcmp((const char *)xb->xb_signature, OCFS2_XATTR_BLOCK_SIGNATURE) &&
+	    prompt(ost, PY, PR_XATTR_BLOCK_INVALID,
+		   "Extended attributes block %"PRIu64" has bad signature %.*s,"
+		   "remove this block ?",
+		   (uint64_t)di->i_xattr_loc, 7, xb->xb_signature)) {
+		di->i_xattr_loc = 0;
+		*i_changed = 1;
+		goto out;
+	}
+
+	if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) {
+		struct ocfs2_xattr_header *xh = &xb->xb_attrs.xb_header;
+		struct xattr_info xi = {
+			.location = "block",
+			.max_offset = ost->ost_fs->fs_blocksize,
+			.blkno = di->i_xattr_loc,
+		};
+
+		ret = check_xattr(ost, di, xh, &b_changed, &xi);
+	} else
+		ret = o2fsck_check_xattr_index_block(ost, di, xb, &b_changed);
+
+	if (b_changed)
+		ocfs2_write_xattr_block(ost->ost_fs, di->i_xattr_loc, blk);
+out:
+	if (blk)
+		ocfs2_free(&blk);
+
+	return ret;
+}
+
+static errcode_t o2fsck_check_xattr_ibody(o2fsck_state *ost,
+					  struct ocfs2_dinode *di,
+					  int *i_changed)
+{
+	errcode_t ret;
+	struct ocfs2_xattr_header *xh = NULL;
+	struct xattr_info xi = {
+		.location = "inode",
+		.max_offset = di->i_xattr_inline_size,
+		.blkno = di->i_blkno,
+	};
+
+	xh = (struct ocfs2_xattr_header *)
+		 ((void *)di + ost->ost_fs->fs_blocksize -
+		  di->i_xattr_inline_size);
+
+	ocfs2_swap_xattrs_to_cpu(xh);
+
+	ret = check_xattr(ost, di, xh, i_changed, &xi);
+
+	if (i_changed)
+		ocfs2_swap_xattrs_from_cpu(xh);
+
+	return ret;
+}
+
+/*
+ * o2fsck_check_xattr
+ *
+ * Check extended attribute in inode block or external block.
+ */
+errcode_t o2fsck_check_xattr(o2fsck_state *ost,
+			     struct ocfs2_dinode *di)
+{
+	errcode_t ret = 0;
+	int i_changed = 0;
+
+	if (!(di->i_dyn_features & OCFS2_HAS_XATTR_FL))
+		return 0;
+
+	if (di->i_dyn_features & OCFS2_INLINE_XATTR_FL)
+		ret = o2fsck_check_xattr_ibody(ost, di, &i_changed);
+
+	if (!ret && di->i_xattr_loc)
+		ret = o2fsck_check_xattr_block(ost, di, &i_changed);
+
+	if (i_changed)
+		o2fsck_write_inode(ost, di->i_blkno, di);
+
+	return ret;
+}
diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index fdeec5e..4c8acab 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -1086,6 +1086,7 @@ errcode_t ocfs2_block_iterate_inode(ocfs2_filesys *fs,
 				    void *priv_data);
 
 uint32_t xattr_uuid_hash(unsigned char *uuid);
+uint32_t xattr_name_hash(uint32_t uuid_hash, const char *name, int name_len);
 int ocfs2_xattr_find_leaf(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb,
 			  uint32_t cpos, char **leaf_buf);
 uint16_t ocfs2_xattr_buckets_per_cluster(ocfs2_filesys *fs);
diff --git a/libocfs2/xattr.c b/libocfs2/xattr.c
index 921a093..fb3eb5b 100644
--- a/libocfs2/xattr.c
+++ b/libocfs2/xattr.c
@@ -32,6 +32,24 @@ uint32_t xattr_uuid_hash(unsigned char *uuid)
 	return hash;
 }
 
+uint32_t xattr_name_hash(uint32_t uuid_hash,
+			 const char *name,
+			 int name_len)
+{
+	/* Get hash value of uuid from super block */
+	uint32_t hash = uuid_hash;
+	int i;
+
+	/* hash extended attribute name */
+	for (i = 0; i < name_len; i++) {
+		hash = (hash << OCFS2_HASH_SHIFT) ^
+		       (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^
+		       *name++;
+	}
+
+	return hash;
+}
+
 uint16_t ocfs2_xattr_buckets_per_cluster(ocfs2_filesys *fs)
 {
 	return fs->fs_clustersize / OCFS2_XATTR_BUCKET_SIZE;
-- 
1.5.4.4




More information about the Ocfs2-tools-devel mailing list