[Ocfs2-tools-devel] [PATCH] extras/check_metaecc: A tool to check metaecc of a metadata block, v3

Coly Li coly.li at suse.de
Thu May 20 13:19:22 PDT 2010


check_metaecc can be used to check whether a specific block is ecc-broken or not. When kernel reports an metaecc validation failure, this tool can help us to check whether it's ecc broken block on disk, or some error happens in memory.

Signed-off-by: Coly Li <coly.li at suse.de>
Cc: Mark Fasheh <mfasheh at suse.com>
Cc: Sunil Mushran <sunil.mushran at oracle.com>
Cc: Joel Becker <Joel.Becker at oracle.com>
---
 extras/Makefile        |    8 +-
 extras/check_metaecc.c |  292 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/ocfs2/ocfs2.h  |    8 ++
 libocfs2/blockcheck.c  |    1 -
 libocfs2/blockcheck.h  |   31 -----
 5 files changed, 306 insertions(+), 34 deletions(-)

diff --git a/extras/Makefile b/extras/Makefile
index a7a1946..1203688 100644
--- a/extras/Makefile
+++ b/extras/Makefile
@@ -2,7 +2,7 @@ TOPDIR = ..
 
 include $(TOPDIR)/Preamble.make
 
-UNINST_PROGRAMS = find_hardlinks find_dup_extents find_inode_paths set_random_bits decode_lockres encode_lockres mark_journal_dirty find_allocation_fragments compute_groups
+UNINST_PROGRAMS = find_hardlinks find_dup_extents find_inode_paths set_random_bits decode_lockres encode_lockres mark_journal_dirty find_allocation_fragments compute_groups check_metaecc
 
 INCLUDES = -I$(TOPDIR)/include
 
@@ -15,8 +15,9 @@ ENCODE_LOCKRES_CFILES = encode_lockres.c
 MARK_JOURNAL_DIRTY_CFILES = mark_journal_dirty.c
 FIND_ALLOC_FRAG_CFILES = find_allocation_fragments.c
 COMPUTE_GROUPS_CFILES = compute_groups.c
+CHECK_METAECC_CFILES = check_metaecc.c
 
-DIST_FILES = $(FIND_HARDLINKS_CFILES) $(FIND_DUP_EXTENTS_CFILES) $(FIND_INODE_PATHS_CFILES) $(SET_RANDOM_BITS_CFILES) $(DECODE_LOCKRES_CFILES) $(ENCODE_LOCKRES_CFILES) $(MARK_JOURNAL_DIRTY_CFILES) $(FIND_ALLOC_FRAG_CFILES) $(COMPUTE_GROUPS_CFILES)
+DIST_FILES = $(FIND_HARDLINKS_CFILES) $(FIND_DUP_EXTENTS_CFILES) $(FIND_INODE_PATHS_CFILES) $(SET_RANDOM_BITS_CFILES) $(DECODE_LOCKRES_CFILES) $(ENCODE_LOCKRES_CFILES) $(MARK_JOURNAL_DIRTY_CFILES) $(FIND_ALLOC_FRAG_CFILES) $(COMPUTE_GROUPS_CFILES) $(CHECK_METAECC_CFILES)
 
 FIND_HARDLINKS_OBJS = $(subst .c,.o,$(FIND_HARDLINKS_CFILES))
 FIND_DUP_EXTENTS_OBJS = $(subst .c,.o,$(FIND_DUP_EXTENTS_CFILES))
@@ -27,6 +28,7 @@ ENCODE_LOCKRES_OBJS  = $(subst .c,.o,$(ENCODE_LOCKRES_CFILES))
 MARK_JOURNAL_DIRTY_OBJS = $(subst .c,.o,$(MARK_JOURNAL_DIRTY_CFILES))
 FIND_ALLOC_FRAG_OBJS = $(subst .c,.o,$(FIND_ALLOC_FRAG_CFILES))
 COMPUTE_GROUPS_OBJS = $(subst .c,.o,$(COMPUTE_GROUPS_CFILES))
+CHECK_METAECC_OBJS = $(subst .c,.o,$(CHECK_METAECC_CFILES))
 
 LIBOCFS2 = ../libocfs2/libocfs2.a
 EXTRAS_LIBS = $(LIBOCFS2) $(COM_ERR_LIBS)
@@ -58,4 +60,6 @@ find_allocation_fragments: $(FIND_ALLOC_FRAG_OBJS) $(LIBOCFS2)
 compute_groups: $(COMPUTE_GROUPS_OBJS) $(LIBOCFS2)
 	$(LINK) $(EXTRAS_LIBS)
 
+check_metaecc: $(CHECK_METAECC_OBJS) $(LIBOCFS2)
+	$(LINK) $(EXTRAS_LIBS)
 include $(TOPDIR)/Postamble.make
diff --git a/extras/check_metaecc.c b/extras/check_metaecc.c
new file mode 100644
index 0000000..45e6386
--- /dev/null
+++ b/extras/check_metaecc.c
@@ -0,0 +1,292 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * check_metaecc.c
+ *
+ * Simple tool to check ecc of a metadata block.
+ *
+ * Copyright (C) 2010 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
+ * 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.
+ *
+ * Authors: Coly Li <coly.li at suse.de>
+ *
+ */
+
+#define _XOPEN_SOURCE 600 /* Triggers magic in features.h */
+#define _LARGEFILE64_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "ocfs2/ocfs2.h"
+#include "ocfs2/byteorder.h"
+
+static void print_usage(void)
+{
+	fprintf(stderr,
+		"Usage: check_metaecc <device> <block #>\n");
+	exit(1);
+}
+
+/* copied from find_inode_paths.c */
+static uint64_t read_number(const char *num)
+{
+	uint64_t val;
+	char *ptr;
+
+	val = strtoull(num, &ptr, 0);
+	if (!ptr || *ptr)
+		return 0;
+
+	return val;
+}
+
+static errcode_t check_metaecc(ocfs2_filesys *fs,
+				uint64_t blk,
+				char *dev,
+				char *block)
+{
+	char signature[8];
+	char name[256] = {0, };
+	struct ocfs2_block_check check;
+	int do_check = 1;
+	errcode_t err = 0;
+
+	memcpy(signature, block, sizeof(signature));
+
+	if (!strncmp(signature, OCFS2_SUPER_BLOCK_SIGNATURE,
+			sizeof(OCFS2_SUPER_BLOCK_SIGNATURE))) {
+		struct ocfs2_dinode *di = (struct ocfs2_dinode *)block;
+		check = di->i_check;
+		snprintf(name, sizeof(name), OCFS2_SUPER_BLOCK_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_INODE_SIGNATURE,
+			sizeof(OCFS2_INODE_SIGNATURE))) {
+		struct ocfs2_dinode *di;
+		di = (struct ocfs2_dinode *)block;
+		check = di->i_check;
+		snprintf(name, sizeof(name), OCFS2_INODE_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_EXTENT_BLOCK_SIGNATURE,
+			sizeof(OCFS2_EXTENT_BLOCK_SIGNATURE))) {
+		struct ocfs2_extent_block *eb;
+		eb = (struct ocfs2_extent_block *)block;
+		check = eb->h_check;
+		snprintf(name, sizeof(name), OCFS2_EXTENT_BLOCK_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_GROUP_DESC_SIGNATURE,
+			sizeof(OCFS2_GROUP_DESC_SIGNATURE))) {
+		struct ocfs2_group_desc *gd;
+		gd = (struct ocfs2_group_desc *)block;
+		check = gd->bg_check;
+		snprintf(name, sizeof(name), OCFS2_GROUP_DESC_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_XATTR_BLOCK_SIGNATURE,
+			sizeof(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+		struct ocfs2_xattr_block *xb;
+		xb = (struct ocfs2_xattr_block *)block;
+		check = xb->xb_check;
+		snprintf(name, sizeof(name), OCFS2_XATTR_BLOCK_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_REFCOUNT_BLOCK_SIGNATURE,
+			sizeof(OCFS2_REFCOUNT_BLOCK_SIGNATURE))) {
+		struct ocfs2_refcount_block *rb;
+		rb = (struct ocfs2_refcount_block *)block;
+		check = rb->rf_check;
+		snprintf(name, sizeof(name), OCFS2_REFCOUNT_BLOCK_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_DX_ROOT_SIGNATURE,
+			sizeof(OCFS2_DX_ROOT_SIGNATURE))) {
+		struct ocfs2_dx_root_block *dx_root;
+		dx_root = (struct ocfs2_dx_root_block *)block;
+		check = dx_root->dr_check;
+		snprintf(name, sizeof(name), OCFS2_DX_ROOT_SIGNATURE);
+	} else if (!strncmp(signature, OCFS2_DX_LEAF_SIGNATURE,
+			sizeof(OCFS2_DX_LEAF_SIGNATURE))) {
+		struct ocfs2_dx_leaf *dx_leaf;
+		dx_leaf = (struct ocfs2_dx_leaf *)block;
+		check = dx_leaf->dl_check;
+		snprintf(name, sizeof(name), OCFS2_DX_LEAF_SIGNATURE);
+	} else {
+		if (ocfs2_supports_dir_trailer(fs)) {
+			struct ocfs2_dir_block_trailer *trailer;
+			trailer = ocfs2_dir_trailer_from_block(fs, block);
+			if (!strncmp((char *)trailer->db_signature,
+					OCFS2_DIR_TRAILER_SIGNATURE,
+					sizeof(OCFS2_DIR_TRAILER_SIGNATURE))) {
+				check = trailer->db_check;
+				snprintf(name, sizeof(name),
+					OCFS2_DIR_TRAILER_SIGNATURE);
+			}
+		} else {
+			snprintf(name, sizeof(name),
+				"Unknow: 0x%x%x%x%x%x%x%x%x\n",
+				signature[0], signature[1],
+				signature[2], signature[3],
+				signature[4], signature[5],
+				signature[6], signature[7]);
+			do_check = 0;
+		}
+	}
+
+	fprintf(stderr, "Signature of block #%"PRIu64" on "
+		"device %s : \"%s\"\n", blk, dev, name);
+
+	/* modified from ocfs2_block_check_validate(),
+	 * rested code is only display format related  */
+	if (do_check) {
+		struct ocfs2_block_check new_check;
+		uint32_t crc, ecc;
+		int crc_offset, result_offset, offset;
+		char outbuf[256] = {0,};
+
+		new_check.bc_crc32e = le32_to_cpu(check.bc_crc32e);
+		new_check.bc_ecc = le16_to_cpu(check.bc_ecc);
+		memset(&check, 0, sizeof(struct ocfs2_block_check));
+
+		crc_offset = snprintf(outbuf, sizeof(outbuf),
+				"Block %4"PRIu64"    ", blk);
+		result_offset = snprintf(outbuf + crc_offset,
+					sizeof(outbuf) - crc_offset,
+					"CRC32: %.8"PRIx32"    "
+					"ECC: %.4"PRIx16"    ",
+					new_check.bc_crc32e, new_check.bc_ecc);
+		result_offset += crc_offset;
+
+		/* Fast path - if the crc32 validates, we're good to go */
+		crc = crc32_le(~0, (void *)block, fs->fs_blocksize);
+		if (crc == new_check.bc_crc32e) {
+			snprintf(outbuf + result_offset,
+				sizeof(outbuf) - result_offset, "PASS\n");
+			fprintf(stderr, outbuf);
+			goto do_check_end;
+		}
+
+		/* OK, try ECC fixups */
+		ecc = ocfs2_hamming_encode_block(block, fs->fs_blocksize);
+		ocfs2_hamming_fix_block(block, fs->fs_blocksize,
+					ecc ^ new_check.bc_ecc);
+
+		crc = crc32_le(~0, (void *)block, fs->fs_blocksize);
+		if (crc == new_check.bc_crc32e) {
+			snprintf(outbuf + result_offset,
+				sizeof(outbuf) - result_offset, "ECC Fixup\n");
+			fprintf(stderr, outbuf);
+			goto do_check_end;
+		}
+
+		snprintf(outbuf + result_offset,
+			sizeof(outbuf) - result_offset, "FAIL\n");
+		fprintf(stderr, outbuf);
+
+		offset = snprintf(outbuf, sizeof(outbuf), "Calculated");
+		while (offset < crc_offset)
+			outbuf[offset++] = ' ';
+		snprintf(outbuf + crc_offset, sizeof(outbuf) - crc_offset,
+			"CRC32: %.8"PRIx32"    ECC: %.4"PRIx16"\n",
+			crc, ecc);
+		fprintf(stderr, outbuf);
+		err = -1;
+do_check_end:
+		check.bc_crc32e = cpu_to_le32(new_check.bc_crc32e);
+		check.bc_ecc = cpu_to_le16(new_check.bc_ecc);
+	}
+
+	return err;
+}
+
+int main(int argc, char *argv[])
+{
+	errcode_t err;
+	int ret = 1;
+	int force = 0;
+	ocfs2_filesys *fs;
+	char *dev, *block;
+	uint64_t blkno;
+	char c;
+
+	static struct option long_options[] = {
+		{"force", 0, 0, 'F'},
+		{0, 0, 0, 0}
+	};
+
+	while (1) {
+		c = getopt_long(argc, argv, "F", long_options, NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'F':
+			force = 1;
+			break;
+		default:
+			print_usage();
+			break;
+		}
+	}
+	if (optind != (argc - 2))
+		print_usage();
+
+	initialize_ocfs_error_table();
+
+	dev = argv[optind];
+	blkno = read_number(argv[optind + 1]);
+	if (blkno == 0) {
+		fprintf(stderr, "invalid block number\n");
+		print_usage();
+	}
+
+	err = ocfs2_open(dev, OCFS2_FLAG_RO, 0, 0, &fs);
+	if (err) {
+		com_err(argv[0], err,
+			"while opening device \"%s\"", dev);
+		goto out;
+	}
+
+	if (!ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super))) {
+		fprintf(stderr,
+			"metaecc feature is not enabled on volume %s, "
+			"validation might be invalid.\n", dev);
+		if (!force) {
+			fprintf(stderr,
+				"To skip this check, use --force or -F\n");
+			goto out;
+		}
+	}
+
+	err = ocfs2_malloc_block(fs->fs_io, &block);
+	if (err) {
+		com_err(argv[0], err,
+			"while reading block #%"PRIu64" on \"%s\"\n",
+			blkno, dev);
+		goto out_close;
+	}
+
+	err = ocfs2_read_blocks(fs, blkno, 1, block);
+	if (err) {
+		com_err(argv[0], err,
+			"while reading block #%"PRIu64" on \"%s\"\n",
+			blkno, dev);
+		goto out_free;
+	}
+
+	err = check_metaecc(fs, blkno, dev, block);
+
+out_free:
+	ocfs2_free(&block);
+out_close:
+	err = ocfs2_close(fs);
+	if (err) {
+		com_err(argv[0], err,
+			"while closing device \"%s\"", dev);
+		ret = 1;
+	}
+out:
+	return ret;
+}
+
diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index 2931207..e452960 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -1539,6 +1539,14 @@ void release_lookup_res(struct ocfs2_dir_lookup_result *res);
 int ocfs2_find_max_rec_len(ocfs2_filesys *fs, char *buf);
 void ocfs2_dx_list_remove_entry(struct ocfs2_dx_entry_list *entry_list, int index);
 int ocfs2_is_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, unsigned long de_off);
+/* routines for block check */
+uint32_t ocfs2_hamming_encode(uint32_t parity, void *data,
+				unsigned int d, unsigned int nr);
+uint32_t ocfs2_hamming_encode_block(void *data, unsigned int d);
+void ocfs2_hamming_fix(void *data, unsigned int d,
+			unsigned int nr, unsigned int fix);
+void ocfs2_hamming_fix_block(void *data, unsigned int d, unsigned int fix);
+uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len);
 
 
 
diff --git a/libocfs2/blockcheck.c b/libocfs2/blockcheck.c
index 8263af7..adc2d8b 100644
--- a/libocfs2/blockcheck.c
+++ b/libocfs2/blockcheck.c
@@ -34,7 +34,6 @@
 #include "ocfs2/bitops.h"
 #include "ocfs2/byteorder.h"
 
-#include "blockcheck.h"
 #include "crc32table.h"
 
 
diff --git a/libocfs2/blockcheck.h b/libocfs2/blockcheck.h
deleted file mode 100644
index 253d936..0000000
--- a/libocfs2/blockcheck.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8; -*-
- * vim: noexpandtab sw=8 ts=8 sts=0:
- *
- * blockcheck.h
- *
- * Checksum and ECC codes for the OCFS2 userspace library.
- *
- * 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 _BLOCKCHECK_H
-#define _BLOCKCHECK_H
-
-extern uint32_t ocfs2_hamming_encode(uint32_t parity, void *data,
-				     unsigned int d, unsigned int nr);
-extern uint32_t ocfs2_hamming_encode_block(void *data, unsigned int d);
-extern void ocfs2_hamming_fix(void *data, unsigned int d, unsigned int nr,
-			      unsigned int fix);
-extern void ocfs2_hamming_fix_block(void *data, unsigned int d,
-				    unsigned int fix);
-extern uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len);
-#endif



More information about the Ocfs2-tools-devel mailing list