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

Tiger Yang tiger.yang at oracle.com
Thu Feb 26 23:54:49 PST 2009


This patch will check all extended attributes related with each inode,
It will check and try to fix xattr structures 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 |   51 +++
 fsck.ocfs2/include/extent.h       |   13 +
 fsck.ocfs2/include/xattr.h        |   26 ++
 fsck.ocfs2/pass1.c                |    4 +
 fsck.ocfs2/xattr.c                |  610 +++++++++++++++++++++++++++++++++++++
 7 files changed, 708 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..3094c31 100644
--- a/fsck.ocfs2/fsck.ocfs2.checks.8.in
+++ b/fsck.ocfs2/fsck.ocfs2.checks.8.in
@@ -739,6 +739,57 @@ that this value isn't right.
 
 Answering yes change this value to the right number.
 
+.SS "XATTR_BLOCK_INVALID"
+Extended attributes are stored off an extended attribute block
+referenced by the inode.  This inode references an invalid extended
+attribute block.
+
+Answering yes will remove this block.
+
+.SS "XATTR_COUNT_INVALID"
+The count of extended attributes in an inode, block, or bucket
+does not match the number of entries found by fsck.
+
+Answering yes will change this to the correct count.
+
+.SS "XATTR_OFFSET_INVALID"
+The name_offset field of an extended attribute entry is not correct.
+Without a correct name_offset field, the entry cannot be used.
+
+Answering yes will remove this entry.
+
+.SS "XATTR_LOCATION_INVALID"
+The xe_local field and xe_value_size field of an extended attribute
+entry does not match. So the entry cannot be used.
+
+Answering yes will remove this entry.
+
+.SS "XATTR_HASH_INVALID"
+Extended attributes use a hash of their name for lookup purposes.
+The name_hash of this extended attribute entry is not correct.
+
+Answering yes will change this to the correct hash.
+
+.SS "XATTR_FREE_START_INVALID"
+Extended attributes use free_start to indicate the offset of the
+free space in inode, block, or bucket. The free_start field
+of this object is not correct.
+
+Answering yes will change this to the correct offset.
+
+.SS "XATTR_VALUE_LEN_INVALID"
+Extended attributes use name_value_len to store the total length
+of all entry's name and value in inode, block or bucket.
+the name_value_len filed of this object is not correct.
+
+Answering yes will change this to the correct value.
+
+.SS "XATTR_BUCKET_COUNT_INVALID"
+The count of extended attributes bucket pointed by one extent record
+does not match the number of buckets found by fsck.
+
+Answering yes will change this to the correct count.
+
 .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..382e3b6
--- /dev/null
+++ b/fsck.ocfs2/include/xattr.h
@@ -0,0 +1,26 @@
+/* -*- 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"
+
+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 1831ede..ec4594b 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";
 
@@ -1391,6 +1392,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..f59bc95
--- /dev/null
+++ b/fsck.ocfs2/xattr.c
@@ -0,0 +1,610 @@
+/* -*- 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"
+
+static const char *whoami = "xattr.c";
+
+#define IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0)
+#define HEADER_SIZE	(sizeof(struct ocfs2_xattr_header))
+#define ENTRY_SIZE	(sizeof(struct ocfs2_xattr_entry))
+
+enum xattr_location {
+	IN_INODE = 0,
+	IN_BLOCK,
+	IN_BUCKET
+};
+
+static const char *xattr_object[] = {
+	"inode",
+	"block",
+	"bucket",
+};
+
+struct xattr_info {
+	uint16_t location;
+	uint32_t max_offset;
+	uint64_t blkno;
+};
+
+static uint16_t detect_xattr_count_in_bucket(struct ocfs2_xattr_header *xh,
+					     struct xattr_info *xi)
+{
+	uint16_t count = 0;
+	struct ocfs2_xattr_entry *entry = xh->xh_entries;
+	struct ocfs2_xattr_entry *pre_xe = entry;
+	uint16_t max_count = (xi->max_offset - HEADER_SIZE) / ENTRY_SIZE;
+
+	if (xh->xh_count == 0 && xh->xh_free_start == OCFS2_XATTR_BUCKET_SIZE)
+		return 0;
+
+	while (!IS_LAST_ENTRY(entry)) {
+		/*
+		 * If we could not get the reasonable count,
+		 * we will remove them by setting count to zero
+		 */
+		if (count >= max_count)
+			return 0;
+		/* xattr entries in bucket had sorted by name_hash */
+		if (entry->xe_name_hash < pre_xe->xe_name_hash)
+			return count;
+		count++;
+		pre_xe = entry;
+		entry++;
+	}
+
+	return count;
+}
+
+static int check_xattr_count(o2fsck_state *ost,
+			     struct ocfs2_dinode *di,
+			     struct ocfs2_xattr_header *xh,
+			     int *changed,
+			     struct xattr_info *xi)
+{
+	uint16_t count = 0;
+	uint16_t max_count = (xi->max_offset - HEADER_SIZE) / ENTRY_SIZE;
+	struct ocfs2_xattr_entry *entry = xh->xh_entries;
+
+	/*
+	 * The last entry's name_offset is not always the
+	 * minimum offset of all entry in inode, block or bucket.
+	 * And xattr entries in bucket may not have GAP between
+	 * entry and name/value, we have to use different way
+	 * to detect the count of the entries.
+	 */
+	if (xi->location == IN_BUCKET)
+		count = detect_xattr_count_in_bucket(xh, xi);
+	else {
+		/*
+		 * The gap between entry and name/value is the only way
+		 * for us to know the total count of the entries when
+		 * them in inode, block.
+		 */
+		while (!IS_LAST_ENTRY(entry)) {
+			if (count >= max_count) {
+				/*
+				 * If we could not get the reasonable count,
+				 * we will remove them by setting count to zero
+				 */
+				count = 0;
+				break;
+			}
+			count++;
+			entry++;
+		}
+	}
+
+	if (xh->xh_count != count) {
+		if (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?",
+			   xattr_object[xi->location], xi->blkno,
+			   xh->xh_count, count)) {
+			xh->xh_count = count;
+			if (!count && xi->location == IN_BUCKET) {
+				xh->xh_free_start = OCFS2_XATTR_BUCKET_SIZE;
+				xh->xh_name_value_len = 0;
+			}
+			*changed = 1;
+		} else
+			return -1;
+	}
+
+	return 0;
+}
+
+static errcode_t 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];
+		uint32_t hash;
+
+		/* check and fix name_offset */
+		if (xe->xe_name_offset >= xi->max_offset) {
+			if (prompt(ost, PY, PR_XATTR_OFFSET_INVALID,
+				   "Extended attribute entry in %s #%"PRIu64
+				   " refers to an invalid name offset %u,"
+				   " clear this entry?",
+				   xattr_object[xi->location], xi->blkno,
+				   xe->xe_name_offset)) {
+				/*
+				 * remove the entry and left the name/value,
+				 * because we don't know the correct offset
+				 * of them.
+				 */
+				memmove((void *)xe, (void *)(xe + 1),
+					(xh->xh_count - i - 1) * ENTRY_SIZE);
+				memset(&xh->xh_entries[xh->xh_count - 1], 0,
+					ENTRY_SIZE);
+				xh->xh_count -= 1;
+				i -= 1;
+				*changed = 1;
+				continue;
+			} else
+				return -1;
+		}
+
+		/* check type and value size */
+		if ((ocfs2_xattr_is_local(xe) &&
+		     xe->xe_value_size > OCFS2_XATTR_INLINE_SIZE) ||
+		    (!ocfs2_xattr_is_local(xe) &&
+		     xe->xe_value_size <= OCFS2_XATTR_INLINE_SIZE)) {
+			char *local;
+
+			if (ocfs2_xattr_is_local(xe))
+				local = "";
+			else
+				local = "not ";
+			if (prompt(ost, PY, PR_XATTR_LOCATION_INVALID,
+				   "Extended attribute entry in %s #%"PRIu64
+				   " claims to have value %sin local, but the"
+				   " value size is %"PRIu64
+				   ", clear this entry?",
+				   xattr_object[xi->location], xi->blkno,
+				   local, xe->xe_value_size)) {
+				/*
+				 * remove the entry and left the name/value,
+				 * because we don't know the correct value
+				 * size of it.
+				 */
+				memmove((void *)xe, (void *)(xe + 1),
+					(xh->xh_count - i - 1) * ENTRY_SIZE);
+				memset(&xh->xh_entries[xh->xh_count - 1], 0,
+					ENTRY_SIZE);
+				xh->xh_count -= 1;
+				i -= 1;
+				*changed = 1;
+				continue;
+			} else
+				return -1;
+		}
+
+		/* check and fix name hash */
+		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 attribute entry in %s #%"PRIu64
+			   " refers to an invalid name hash %u,"
+			   " Fix the name hash?",
+			   xattr_object[xi->location], xi->blkno,
+			   xe->xe_name_hash)) {
+			xe->xe_name_hash = hash;
+			*changed = 1;
+		}
+	}
+
+	return 0;
+}
+
+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)
+{
+	errcode_t ret;
+	uint16_t min_offs, total_len;
+
+	/* At first we check and fix the total xattr entry count */
+	if (check_xattr_count(ost, di, xh, changed, xi))
+		return 0;
+	/* if count is correct, then check and fix the xattr entry */
+	if (check_xattr_entry(ost, di, xh, changed, xi))
+		return 0;
+
+	ret = check_xattr_value(ost, di, xh, changed);
+	if (ret)
+		return ret;
+
+	if (xi->location == IN_BUCKET) {
+		/* check and fix xh_free_start */
+		min_offs = ocfs2_xattr_min_offset(xh, xi->max_offset);
+		if (xh->xh_free_start != min_offs &&
+		    prompt(ost, PY, PR_XATTR_FREE_START_INVALID,
+			   "Extended attribute in %s #%"PRIu64" claims to"
+			   " have free space start at %u , but fsck believes"
+			   " it is %u, Fix the value of free start?",
+			   xattr_object[xi->location], xi->blkno,
+			   xh->xh_free_start, min_offs)) {
+			xh->xh_free_start = min_offs;
+			*changed = 1;
+		}
+		/* check and fix xh_name_value_len */
+		total_len = ocfs2_xattr_name_value_len(xh);
+		if (xh->xh_name_value_len != total_len &&
+		    prompt(ost, PY, PR_XATTR_VALUE_LEN_INVALID,
+			   "Extended attribute in %s #%"PRIu64" claims to have"
+			   " the total length %u of all EAs name and value"
+			   " in this object, but fsck believes it is %u,"
+			   " Fix the value of the total length?",
+			   xattr_object[xi->location], xi->blkno,
+			   xh->xh_name_value_len, total_len)) {
+			xh->xh_name_value_len = total_len;
+			*changed = 1;
+		}
+	}
+
+	return 0;
+}
+
+static uint16_t detect_xattr_bucket_count(char *bucket,
+					  uint32_t max_buckets)
+{
+	int i;
+	char *bucket_buf = NULL;
+	struct ocfs2_xattr_header *xh;
+	uint16_t max_count, max_offset;
+
+	max_offset = OCFS2_XATTR_BUCKET_SIZE;
+	max_count = (OCFS2_XATTR_BUCKET_SIZE - HEADER_SIZE) / ENTRY_SIZE;
+
+	bucket_buf = bucket;
+	for (i = 0; i < max_buckets; i++) {
+		xh = (struct ocfs2_xattr_header *)bucket_buf;
+		if (xh->xh_count < max_count &&
+		    xh->xh_free_start >
+		    (xh->xh_count * ENTRY_SIZE &&
+		    xh->xh_free_start <= max_offset &&
+		    xh->xh_name_value_len <= max_offset - xh->xh_free_start)) {
+			bucket_buf += OCFS2_XATTR_BUCKET_SIZE;
+			continue;
+		} else
+			return i;
+	}
+
+	return i;
+}
+
+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;
+	char *bucket_buf = 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 max_buckets = clusters * bpc;
+	uint32_t max_blocks = max_buckets * blk_per_bucket;
+	uint32_t num_buckets = 0;
+	uint64_t blk = 0;
+
+	/* malloc space for all buckets */
+	ret = ocfs2_malloc_blocks(ost->ost_fs->fs_io, max_blocks, &bucket);
+	if (ret) {
+		com_err(whoami, ret, "while allocating room to read"
+			" extended attributes bucket");
+		goto out;
+	}
+
+	/* read all buckets for detect (some of them may not be used) */
+	bucket_buf = bucket;
+	blk = blkno;
+	for (i = 0; i < max_buckets; i++) {
+		ret = ocfs2_read_xattr_bucket(ost->ost_fs, blk, bucket_buf);
+		if (ret) {
+			com_err(whoami, ret, "while reading bucket of"
+				" extended attributes ");
+			goto out;
+		}
+		blk += blk_per_bucket;
+		bucket_buf += OCFS2_XATTR_BUCKET_SIZE;
+	}
+
+	/*
+	 * The real bucket num in this series of blocks is stored
+	 * in the 1st bucket.
+	 */
+	xh = (struct ocfs2_xattr_header *)bucket;
+	if (xh->xh_num_buckets == 0 || xh->xh_num_buckets > max_buckets) {
+		num_buckets = detect_xattr_bucket_count(bucket, max_buckets);
+		if (prompt(ost, PY, PR_XATTR_BUCKET_COUNT_INVALID,
+			   "Extended attribute buckets start at %"PRIu64
+			   " claims to have %u buckets, but fsck believes"
+			   " it is %u, Fix the bucket count?",
+			   blkno, xh->xh_num_buckets,
+			   num_buckets ? num_buckets : 1)) {
+			if (num_buckets == 0) {
+				/*
+				 * If buckets count is 0, we need clean
+				 * xh_count and set xh_num_buckets to 1.
+				 */
+				xh->xh_count = 0;
+				xh->xh_free_start = OCFS2_XATTR_BUCKET_SIZE;
+				xh->xh_num_buckets = 1;
+			} else
+				xh->xh_num_buckets = num_buckets;
+			/* only update first bucket */
+			ret = ocfs2_write_xattr_bucket(ost->ost_fs, blkno,
+							bucket);
+			if (ret) {
+				com_err(whoami, ret, "while writing bucket of"
+					" extended attributes ");
+				goto out;
+			}
+			if (num_buckets == 0)
+				goto out;
+		} else
+			goto out;
+
+	} else
+		num_buckets = xh->xh_num_buckets;
+
+	bucket_buf = bucket;
+	for (i = 0; i < num_buckets; i++) {
+		int changed = 0;
+		struct xattr_info xi = {
+			.location = IN_BUCKET,
+			.max_offset = OCFS2_XATTR_BUCKET_SIZE,
+			.blkno = blkno,
+		};
+
+		xh = (struct ocfs2_xattr_header *)bucket_buf;
+		ret = check_xattr(ost, di, xh, &changed, &xi);
+		if (ret)
+			break;
+		if (changed) {
+			ret = ocfs2_write_xattr_bucket(ost->ost_fs, blkno,
+							bucket_buf);
+			if (ret) {
+				com_err(whoami, ret, "while writing bucket of"
+					" extended attributes ");
+				goto out;
+			}
+		}
+		blkno += blk_per_bucket;
+		bucket_buf += OCFS2_XATTR_BUCKET_SIZE;
+	}
+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 extended attributes ");
+			goto out;
+		}
+
+		ret = ocfs2_check_xattr_buckets(ost, di, p_blkno,
+						num_clusters);
+		if (ret) {
+			com_err(whoami, ret, "while iterating bucket"
+				" of extended attributes ");
+			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 extended attribute ");
+		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"
+			" extended attributes ");
+		goto out;
+	}
+
+	xb = (struct ocfs2_xattr_block *)blk;
+
+	if (strcmp((char *)xb->xb_signature, OCFS2_XATTR_BLOCK_SIGNATURE)) {
+		if (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 = IN_BLOCK,
+			.max_offset = ost->ost_fs->fs_blocksize -
+				      offsetof(struct ocfs2_xattr_block,
+						xb_attrs.xb_header),
+			.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 (!ret && b_changed) {
+		ret = ocfs2_write_xattr_block(ost->ost_fs,
+					      di->i_xattr_loc, blk);
+		if (ret)
+			com_err(whoami, ret, "while writing externel block of"
+				" extended attributes ");
+	}
+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 = IN_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 (!ret && 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 (!ret && i_changed)
+		o2fsck_write_inode(ost, di->i_blkno, di);
+
+	return ret;
+}
-- 
1.5.4.1




More information about the Ocfs2-tools-devel mailing list