/* -*- 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 * */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/byteorder.h" static void print_usage(void) { fprintf(stderr, "Usage: check_metaecc \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, "%s", 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, "%s", outbuf); goto do_check_end; } snprintf(outbuf + result_offset, sizeof(outbuf) - result_offset, "FAIL\n"); fprintf(stderr, "%s", 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, "%s", 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; }