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

Tiger Yang tiger.yang at oracle.com
Tue Mar 24 19:50:08 PDT 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                |  760 +++++++++++++++++++++++++++++++++++++
 7 files changed, 858 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 d80e13f..15880bc 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..d22f82e
--- /dev/null
+++ b/fsck.ocfs2/xattr.c
@@ -0,0 +1,760 @@
+/* -*- 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))
+#define MIN_VALUE	4
+#define XE_OFFSET(xh, xe)	((char *)(xe) - (char *)(xh))
+
+enum xattr_location {
+	IN_INODE = 0,
+	IN_BLOCK,
+	IN_BUCKET
+};
+
+static const char *xattr_object[] = {
+	"inode",
+	"block",
+	"bucket",
+};
+
+struct xattr_info {
+	enum xattr_location location;
+	uint32_t max_offset;
+	uint64_t blkno;
+};
+
+/*
+ * This use to indicate the used area of xattr in inode, block and bucket.
+ * The used area include all xattr structs, such as header, entry, name+value.
+ */
+struct used_area {
+	uint16_t offset;	/* the offset of the area */
+	uint16_t length;	/* the length of the area */
+	uint16_t entry_num;	/* if this area is xattr entry, indicate the
+				   number of it (start from 1),
+				   otherwise it should be 0. */
+	struct used_area *next;	/* indicate the next used area */
+};
+
+static int check_xattr_count(o2fsck_state *ost,
+			     struct ocfs2_dinode *di,
+			     struct ocfs2_xattr_header *xh,
+			     int *changed,
+			     struct xattr_info *xi)
+{
+	struct ocfs2_xattr_entry *entry = xh->xh_entries;
+	struct ocfs2_xattr_entry *pre_xe = entry;
+	uint16_t det_count = 0;
+	uint16_t max_count = (xi->max_offset - HEADER_SIZE) /
+			     (ENTRY_SIZE + MIN_VALUE);
+
+	while (!IS_LAST_ENTRY(entry)) {
+		/*
+		 * xattr entries in bucket had sorted by name_hash,
+		 * so this can help us to detect count.
+		 */
+		if (xi->location == IN_BUCKET &&
+		    entry->xe_name_hash < pre_xe->xe_name_hash)
+				break;
+		if (det_count >= max_count)
+			break;
+		det_count++;
+		pre_xe = entry;
+		entry++;
+	}
+
+	if (xh->xh_count > det_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, det_count)) {
+			xh->xh_count = det_count;
+			if (!det_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 struct used_area *new_used_area(uint16_t off, uint16_t len, uint16_t en)
+{
+	struct used_area *ua = NULL;
+
+	ua = malloc(sizeof(struct used_area));
+	if (!ua)
+		return NULL;
+
+	ua->offset = off;
+	ua->length = len;
+	ua->entry_num = en;
+	ua->next = NULL;
+
+	return ua;
+}
+
+static struct used_area *new_used_map(uint16_t max_off)
+{
+	struct used_area *header = NULL;
+
+	/* create a linked list header for the who used map*/
+	header = new_used_area(0, max_off, 0);
+	if (!header) {
+		com_err(whoami, OCFS2_ET_NO_MEMORY, "Unable to allocate"
+			" buffer for extended attribute ");
+		return NULL;
+	}
+
+	return header;
+}
+
+static void free_used_map(struct used_area *header)
+{
+	struct used_area *area = NULL;
+	struct used_area *temp = NULL;
+
+	area = header;
+	while (area) {
+		temp = area->next;
+		free(area);
+		area = temp;
+	}
+
+	return;
+}
+
+static errcode_t set_used_area(struct used_area *header,
+			       uint16_t off, uint16_t len,
+			       uint16_t en)
+{
+	struct used_area *area = NULL;
+	struct used_area *new_area = NULL;
+
+	if (!header)
+		return OCFS2_ET_INVALID_ARGUMENT;
+
+	new_area = new_used_area(off, len, en);
+	if (!new_area) {
+		com_err(whoami, OCFS2_ET_NO_MEMORY, "Unable to allocate"
+			" buffer for extended attribute ");
+		return OCFS2_ET_NO_MEMORY;
+	}
+
+	area = header;
+	while (area->next)
+		area = area->next;
+
+	new_area->next = area->next;
+	area->next = new_area;
+
+	return 0;
+}
+
+static void clear_used_area(struct used_area *header, uint16_t off,
+			    uint16_t len)
+{
+	struct used_area *area = NULL;
+	struct used_area *temp = NULL;
+
+	area = header;
+	while (area->next) {
+		if (off == area->next->offset && len == area->next->length) {
+			temp = area->next;
+			break;
+		}
+		area = area->next;
+	}
+
+	if (temp) {
+		area->next = temp->next;
+		free(temp);
+	}
+	return;
+}
+
+static int check_fits(struct used_area *header, uint16_t off, uint16_t len)
+{
+	struct used_area *area = NULL;
+
+	if (!header)
+		return -1;
+
+	if (off >= header->length)
+		return -1;
+
+	area = header->next;
+	while (area) {
+		if (off >= area->offset &&
+		    off < (area->offset + area->length))
+			return -1;
+		area = area->next;
+	}
+
+	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, ret = 0;
+	struct used_area *entry = NULL;
+	uint16_t count;
+	struct used_area *used_map;
+
+rescan:
+	count = xh->xh_count;
+	used_map = new_used_map(xi->max_offset);
+	/* set xattr header as used area */
+	set_used_area(used_map, 0, sizeof(struct ocfs2_xattr_header), 0);
+
+	for (i = 0 ; i < xh->xh_count; i++) {
+		struct ocfs2_xattr_entry *xe = &xh->xh_entries[i];
+		uint16_t value_len;
+		uint32_t hash;
+
+		if (check_fits(used_map, XE_OFFSET(xh, xe), ENTRY_SIZE)) {
+			if (!prompt(ost, PY, PR_XATTR_OFFSET_INVALID,
+				    "Extended attribute entry in %s #%"
+				    PRIu64" refers to a used area at %u,"
+				    " clear this entry?",
+				    xattr_object[xi->location], xi->blkno,
+				    XE_OFFSET(xh, xe))) {
+				ret = -1;
+				break;
+			} else
+				goto wipe_entry;
+		}
+
+		/* 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)) {
+				ret = -1;
+				break;
+			} else
+				goto wipe_entry;
+		}
+
+		/* 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)) {
+				ret = -1;
+				break;
+			} else
+				goto wipe_entry;
+		}
+
+		/* mark the entry area as used*/
+		set_used_area(used_map, XE_OFFSET(xh, xe), ENTRY_SIZE, i + 1);
+		/* get the value's real size in inode, block or bucket */
+		value_len = ocfs2_xattr_value_real_size(xe->xe_name_len,
+							xe->xe_value_size);
+		if (check_fits(used_map, xe->xe_name_offset, value_len)) {
+			if (!prompt(ost, PY, PR_XATTR_OFFSET_INVALID,
+				    "Extended attribute entry in %s #%"PRIu64
+				    " refers to a used area at %u,"
+				    " clear this entry?",
+				    xattr_object[xi->location], xi->blkno,
+				    xe->xe_name_offset)) {
+				ret = -1;
+				break;
+			} else {
+				clear_used_area(used_map, XE_OFFSET(xh, xe),
+						ENTRY_SIZE);
+				goto wipe_entry;
+			}
+		}
+		/* mark the value area as used */
+		set_used_area(used_map, xe->xe_name_offset, value_len, 0);
+
+		/* check and fix name hash */
+		hash = ocfs2_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;
+		}
+
+		continue;
+wipe_entry:
+		/*
+		 * we don't wipe entry at here, just reduce the count,
+		 * we will wipe them when we finish the check.
+		 */
+		count -= 1;
+		*changed = 1;
+	}
+
+	i = 1;
+	if (*changed && xh->xh_count != count) {
+		/*
+		 * according to used map, remove bad entries from entry area,
+		 * and left the name+value in the object.
+		 */
+		entry = used_map;
+		while (entry) {
+			if (entry->entry_num) {
+				if (entry->entry_num != i)
+					memcpy(&xh->xh_entries[i - 1],
+					 &xh->xh_entries[entry->entry_num - 1],
+					 ENTRY_SIZE);
+				i++;
+			}
+			entry = entry->next;
+		}
+		xh->xh_count = i - 1;
+		/*
+		 * we need scan the entries again, because we only add valid
+		 * entries into used map, some invalid entries's offset happen
+		 * to be the position of entries we discard so them could pass
+		 * the first check.
+		 */
+		free_used_map(used_map);
+		goto rescan;
+	}
+
+	free_used_map(used_map);
+	return ret;
+}
+
+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;
+	/* 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 + MIN_VALUE);
+
+	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) {
+			max_buckets = i;
+			break;
+		}
+		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)
+{
+	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);
+
+	return check_xattr(ost, di, xh, i_changed, &xi);
+}
+
+/*
+ * 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)
+			return ret;
+		if (i_changed) {
+			o2fsck_write_inode(ost, di->i_blkno, di);
+			i_changed = 0;
+		}
+	}
+
+	if (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