--- /dev/null
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ *
+ * User-level tool to check iam files sanity.
+ *
+ * Author: Artem Blagodarenko <artem.blagodarenko@hpe.com>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <endian.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <linux/lustre/lustre_user.h>
+#include <asm/byteorder.h>
+#include <lustre/libiam.h>
+
+static int verbose;
+
+enum {
+ ROOT_NODE,
+ INDEX_NODE,
+ LEAF_NODE,
+ IDLE_NODE
+};
+
+struct node_info {
+ int referenced;
+ int recycled;
+ int node_type;
+};
+
+void usage(char *str)
+{
+ printf("usage: %s [-h] [-v] iam_file\n", str);
+}
+
+struct iam_params {
+ char *filename;
+ int blocksize;
+ int fmt;
+ int keysize;
+ int recsize;
+ int ptrsize;
+ int indirect_levels;
+ int root_gap;
+ int node_gap;
+ unsigned long idle_blocks;
+ unsigned long current_block;
+ unsigned long long file_size;
+ unsigned long blocks_count;
+ struct node_info *node_info;
+ int rc;
+};
+
+int check_idle_blocks(char *buf, struct iam_params *params)
+{
+ struct iam_idle_head *idle;
+ int i;
+
+ idle = (struct iam_idle_head *)buf;
+
+ if (idle->iih_magic != __cpu_to_le16(IAM_IDLE_HEADER_MAGIC)) {
+ printf("Wrong magic 0x%x\n", idle->iih_magic);
+ return -1;
+ }
+
+ if (verbose) {
+ printf(", %i blocks, next table in block %i, idle blocks: ",
+ __le16_to_cpu(idle->iih_count),
+ __le32_to_cpu(idle->iih_next));
+ }
+
+ for (i = 0; i < __le32_to_cpu(idle->iih_count); i++) {
+ unsigned int blk = __le32_to_cpu(idle->iih_blks[i]);
+
+ if (verbose)
+ printf("%i, ", blk);
+ if (blk >= params->blocks_count) {
+ printf("Pointer to the idle block (%i) outside the file\n",
+ blk);
+ params->rc = -1;
+ } else {
+ if (params->node_info[blk].referenced && verbose)
+ printf("Reference to recycled node (%i)\n",
+ blk);
+ params->node_info[blk].recycled = 1;
+ }
+ }
+
+ if (verbose)
+ printf("\n");
+
+ return 0;
+}
+
+static int check_entries(unsigned char *entries, size_t size, int count,
+ struct iam_params *params)
+{
+ unsigned int ptr;
+ int i, j, rc;
+
+ for (i = 0; i < count; i++) {
+ rc = 0;
+ size -= (params->keysize + params->ptrsize);
+
+ if (size < 0) {
+ if (verbose)
+ printf("index outside of buffer\n");
+
+ return -1;
+ }
+
+ if (verbose)
+ printf("key:");
+
+ for (j = 0; j < params->keysize; j++, entries++)
+ if (verbose)
+ printf("%02x", *entries);
+
+ ptr = __le32_to_cpu(*((__le32 *)entries));
+
+ if (ptr >= params->blocks_count) {
+ params->rc = -1;
+ rc = -1;
+ }
+ if (verbose)
+ printf(", ptr: %u%s\n", ptr, rc ? " wrong" : "");
+
+ entries += params->ptrsize;
+
+ if (rc)
+ continue;
+
+ if (params->node_info[ptr].recycled && verbose) {
+ printf("Reference to recycled node (%u) from node %lu\n",
+ ptr, params->current_block);
+ }
+ params->node_info[ptr].referenced = 1;
+ }
+
+ return 0;
+}
+
+static int check_index(char *buf, struct iam_params *params)
+{
+ struct iam_index_head *index;
+ int counted_limit;
+ struct dx_countlimit *limit;
+
+ index = (struct iam_index_head *)buf;
+ limit = &index->limit;
+
+ params->node_info[params->current_block].node_type = INDEX_NODE;
+
+ if (verbose)
+ printf("Index block, count %i, limit %i\n",
+ __le16_to_cpu(limit->count),
+ __le16_to_cpu(limit->limit));
+
+ counted_limit = node_limit(params->node_gap, params->blocksize,
+ params->keysize + params->ptrsize);
+
+ if (__le16_to_cpu(limit->limit) != counted_limit) {
+ fprintf(stderr, "Wrong limit %i, counted limit %i\n",
+ __le16_to_cpu(limit->limit), counted_limit);
+ return -1;
+ }
+
+
+ if (__le16_to_cpu(limit->count) > __le16_to_cpu(limit->limit)) {
+ printf("More elements (%i) then limit (%i)\n",
+ __le16_to_cpu(limit->count),
+ __le16_to_cpu(limit->limit));
+ return -1;
+ }
+
+ /* count - 1, because limit is entry itself */
+ if (check_entries(index->entries,
+ params->blocksize - offsetof(struct iam_index_head,
+ entries),
+ __le16_to_cpu(limit->count) - 1, params)) {
+ printf("Broken entries\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_root(char *buf, size_t size, struct iam_params *params)
+{
+ struct iam_lfix_root *root;
+ unsigned int counted_limit;
+ int min;
+ struct dx_countlimit *limit;
+
+ if (verbose)
+ printf("Root format ");
+
+ root = (struct iam_lfix_root *)buf;
+ if (root->ilr_magic == __cpu_to_le64(IAM_LFIX_ROOT_MAGIC)) {
+ params->fmt = FMT_LFIX;
+ if (verbose)
+ printf("LFIX,");
+ } else if (root->ilr_magic == __cpu_to_le64(IAM_LVAR_ROOT_MAGIC)) {
+ params->fmt = FMT_LVAR;
+ if (verbose)
+ printf("LVAR,");
+ } else {
+ printf("Bad magic %llu\n", __le64_to_cpu(root->ilr_magic));
+ return -1;
+ }
+
+ limit = &root->limit;
+
+ params->keysize = __le16_to_cpu(root->ilr_keysize);
+ params->recsize = __le16_to_cpu(root->ilr_recsize);
+ params->ptrsize = __le16_to_cpu(root->ilr_ptrsize);
+ params->indirect_levels = root->ilr_indirect_levels;
+
+ params->node_info[0].referenced = 1; //self referance
+ params->node_info[0].node_type = ROOT_NODE;
+
+ params->idle_blocks = __le32_to_cpu(root->idle_blocks);
+ if (params->idle_blocks >= params->blocks_count) {
+ printf("Idle blocks number (%lu) is out of blocks range (%lu)\n",
+ params->idle_blocks, params->blocks_count);
+ params->rc = -1;
+ } else {
+ params->node_info[params->idle_blocks].referenced = 1;
+ params->node_info[params->idle_blocks].node_type = IDLE_NODE;
+ }
+
+ if (verbose) {
+ printf("Idle blocks block number %lu\n", params->idle_blocks);
+ printf("keysize %i, recsize %i, ptrsize %i, indirect_levels %i\n",
+ params->keysize, params->recsize, params->ptrsize,
+ params->indirect_levels);
+ }
+
+ if (params->ptrsize != 4 && params->ptrsize != 8) {
+ printf("Invalid ptrsize (%i). Only 4 and 8 are supported\n",
+ params->ptrsize);
+ return -1;
+ }
+
+ if (params->keysize < 1 || params->recsize < 0) {
+ printf("Too small key(%i) or recorod(%i)\n",
+ params->keysize, params->recsize);
+ return -1;
+ }
+
+ if ((params->keysize + params->recsize +
+ (int)sizeof(struct iam_leaf_head)) > (params->blocksize / 3)) {
+ printf("Too large record + key or too small block, %i, %i\n",
+ (params->keysize + params->recsize +
+ (int)sizeof(struct iam_leaf_head)),
+ params->blocksize);
+ return -1;
+ }
+
+ counted_limit = root_limit(params->root_gap, params->node_gap,
+ params->blocksize,
+ params->keysize + params->ptrsize);
+
+
+ if (__le16_to_cpu(limit->limit) != counted_limit) {
+ fprintf(stderr, "Wrong limit %i, counted limit %i\n",
+ __le16_to_cpu(limit->limit), counted_limit);
+ params->rc = -1;
+ }
+
+ min = (counted_limit < __le16_to_cpu(limit->limit)) ?
+ counted_limit : __le16_to_cpu(limit->limit);
+
+ if (__le16_to_cpu(limit->count) > __le16_to_cpu(limit->limit)) {
+ printf("More elements (%i) then limit (%i)\n",
+ __le16_to_cpu(root->limit.count),
+ __le16_to_cpu(root->limit.limit));
+ params->rc = -1;
+ }
+
+ min = (__le16_to_cpu(limit->count) < min) ?
+ __le16_to_cpu(limit->count) : min;
+
+
+ if (verbose)
+ printf("count %i, limit %i\n",
+ __le16_to_cpu(root->limit.count),
+ __le16_to_cpu(root->limit.limit));
+
+ /* cound - 1, because limit is entry itself */
+ if (check_entries(root->entries,
+ size - offsetof(struct iam_lfix_root, entries),
+ min - 1, params)) {
+ printf("Broken entries\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_block(char *buf, struct iam_params *params)
+{
+ struct iam_leaf_head *head;
+
+ head = (struct iam_leaf_head *)buf;
+
+ if (verbose)
+ printf("Block %lu,", params->current_block);
+
+ switch (head->ill_magic) {
+ case __cpu_to_le16(IAM_LEAF_HEADER_MAGIC):
+ if (verbose)
+ printf("FIX leaf,");
+ params->node_info[params->current_block].node_type =
+ LEAF_NODE;
+ break;
+ case __cpu_to_le16(IAM_LVAR_ROOT_MAGIC):
+ if (verbose)
+ printf("LVAR leaf,");
+ break;
+ case __cpu_to_le16(IAM_IDLE_HEADER_MAGIC):
+ if (verbose)
+ printf("IDLE block");
+
+ params->node_info[params->current_block].referenced = 1;
+
+ if (check_idle_blocks(buf, params)) {
+ printf("Broken idle blocks\n");
+ params->rc = -1;
+ }
+ break;
+ default:
+ if (check_index(buf, params)) {
+ printf("Broken index node\n");
+ params->rc = -1;
+ }
+ break;
+ }
+ if (verbose)
+ printf("count %i\n", head->ill_count);
+
+ return 0;
+}
+
+static void print_node_type(int type)
+{
+ switch (type) {
+ case ROOT_NODE:
+ printf("ROOT\n");
+ break;
+ case INDEX_NODE:
+ printf("INDEX\n");
+ break;
+ case LEAF_NODE:
+ printf("LEAF\n");
+ break;
+ case IDLE_NODE:
+ printf("IDLE\n");
+ break;
+ default:
+ printf("UNKNOWN %i\n", type);
+ break;
+ }
+}
+static int check_unconnected(struct iam_params *params)
+{
+ unsigned long i;
+ int rc = 0;
+
+ for (i = 0; i < params->blocks_count; i++) {
+ if (params->node_info[i].referenced &&
+ params->node_info[i].recycled) {
+ printf("Node %lu referenced and recycled. FAIL, ", i);
+ print_node_type(params->node_info[i].node_type);
+ }
+
+ if (!params->node_info[i].referenced &&
+ !params->node_info[i].recycled) {
+ printf("Unconnected node %lu. FAIL, ", i);
+ print_node_type(params->node_info[i].node_type);
+ rc = -1;
+ }
+ }
+ return rc;
+}
+int main(int argc, char **argv)
+{
+ struct iam_params params;
+ int rc = 0;
+ int opt;
+ void *buf;
+ int fd;
+ struct stat sb;
+
+ params.rc = 0;
+ do {
+ opt = getopt(argc, argv, "hv");
+ switch (opt) {
+ case 'v':
+ verbose++;
+ case -1:
+ break;
+ default:
+ fprintf(stderr, "Unable to parse options.");
+ case 'h':
+ printf("HERE\n");
+ usage(argv[0]);
+ return 0;
+ }
+ } while (opt != -1);
+
+ if (optind >= argc) {
+ fprintf(stderr, "Expected filename after options\n");
+ return -1;
+ }
+
+ params.filename = argv[optind];
+ params.blocksize = 4096;
+ params.current_block = 0;
+ params.root_gap = sizeof(struct iam_lfix_root);
+ params.node_gap = 0;
+
+ fd = open(params.filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Can not open file %s, %s\n",
+ params.filename, strerror(errno));
+ return -1;
+ }
+
+ if (fstat(fd, &sb) == -1) {
+ fprintf(stderr, "Error stat file.\n");
+ close(fd);
+ return -1;
+ }
+ params.file_size = (unsigned long long)sb.st_size;
+ params.blocks_count = params.file_size / params.blocksize +
+ ((params.file_size % params.blocksize) ? 1 : 0);
+
+ if (verbose)
+ printf("Filesize %llu, blocks count %lu\n", params.file_size,
+ params.blocks_count);
+ buf = malloc(params.blocksize);
+ if (buf == NULL) {
+ fprintf(stderr, "Can't allocate buffer\n");
+ close(fd);
+ return -1;
+ }
+ params.node_info = malloc(params.blocks_count *
+ sizeof(struct node_info));
+ memset(params.node_info, 0,
+ params.blocks_count * sizeof(struct node_info));
+
+ /* Read root block */
+ if (read(fd, buf, params.blocksize) < params.blocksize) {
+ fprintf(stderr, "Can't read root block\n");
+ params.rc = -1;
+ goto err;
+ }
+
+ rc = check_root(buf, params.blocksize, ¶ms);
+ if (rc) {
+ printf("Root node is insane\n");
+ params.rc = rc;
+ }
+
+ params.current_block++;
+
+ /* Read all another blocks */
+ while (read(fd, buf, params.blocksize)) {
+ rc = check_block(buf, ¶ms);
+ if (rc) {
+ printf("Node with offset 0x%lx in %s is broken\n",
+ params.current_block * params.blocksize,
+ params.filename);
+ params.rc = rc;
+ }
+ params.current_block++;
+ }
+
+ rc = check_unconnected(¶ms);
+ if (rc)
+ printf("There are unconnected nodes\n");
+err:
+ if (!(rc ? rc : params.rc))
+ printf("NO ERRORS\n");
+ else
+ printf("FINISHED WITH ERRORS\n");
+
+ free(params.node_info);
+ free(buf);
+ close(fd);
+
+ return rc ?: params.rc;
+}