#include <errno.h>
#include <sys/types.h>
+#include <asm/byteorder.h>
#include <linux/lustre/lustre_user.h>
#include <linux/lustre/lustre_fid.h>
-#include <asm/byteorder.h>
+#include <linux/lustre/lustre_disk.h>
+#include <linux/lnet/nidstr.h>
#include <lustre/libiam.h>
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof((a)[0])))
+#endif /* !ARRAY_SIZE */
+
+struct record_cb {
+ int (*key)(const void *key, size_t size);
+ int (*rec)(const void *rec, size_t size);
+ int (*key_rec)(const void *key, size_t keys,
+ const void *rec, size_t recs);
+};
+
+struct record_type {
+ char *type;
+ char *filename;
+ struct record_cb cb;
+};
+
static int verbose;
static bool print_records;
+static struct record_cb *print_cb;
+
+static int hexdump(const void *buf, size_t size);
+static int print_fid(const void *buf, size_t size);
+static int print_oid(const void *buf, size_t size);
+static int print_lfsck_namespace(const void *buf, size_t size);
+static int print_dangling_rec_key(const void *buf, size_t size);
+static int print_dangling_rec(const void *buf, size_t size);
+static int print_nodemap_key(const void *buf, size_t size);
+static int print_nodemap(const void *key, size_t keys,
+ const void *rec, size_t recs);
+
+#define HEXDUMP_IDX 0
+#define GUESS_START_IDX (HEXDUMP_IDX + 1)
+static struct record_type record_type_array[] = {
+ [HEXDUMP_IDX] {
+ .type = "hexdump",
+ .filename = NULL,
+ .cb = {hexdump, hexdump},
+ },
+ {
+ .type = "oi_map",
+ .filename = "oi.",
+ .cb = {print_fid, print_oid},
+ },
+ {
+ .type = "lfsck_namespace",
+ .filename = "lfsck_namespace_",
+ .cb = {print_fid, print_lfsck_namespace},
+ },
+ {
+ .type = "lfsck_dangling_rec",
+ .filename = "lfsck_layout_",
+ .cb = {print_dangling_rec_key, print_dangling_rec},
+ },
+ {
+ .type = "nodemap",
+ .filename = "nodemap",
+ .cb = {print_nodemap_key, NULL, print_nodemap},
+ },
+ { 0 }
+};
+
+static struct record_type *get_rec_type(const char *type)
+{
+ struct record_type *curr;
+
+ for (curr = &record_type_array[0]; curr->type; curr++) {
+ if (strcmp(type, curr->type) == 0)
+ return curr;
+ }
+
+ fprintf(stderr, "Record type '%s' not found\n", type);
+
+ return NULL;
+}
+
+static struct record_type *guess_rec_type(const char *filename)
+{
+ struct record_type *curr;
+ struct record_type *start = &record_type_array[GUESS_START_IDX];
+
+ for (curr = start; curr->type; curr++) {
+ if (curr->filename && strstr(filename, curr->filename))
+ return curr;
+ }
+
+ fprintf(stderr, "Failed to guess record type for '%s', fallback to hexdump\n",
+ filename);
+
+ return &record_type_array[HEXDUMP_IDX];
+}
+
+static void print_rec_types(void)
+{
+ struct record_type *curr;
+
+ for (curr = &record_type_array[0]; curr->type; curr++)
+ printf("%s%s", curr->type, curr[1].type ? ", " : "");
+}
enum {
ROOT_NODE,
static void usage(char *str)
{
- printf("Usage: %s [-hrv] iam_file\n", str);
+ printf("Usage: %s [-hrv] [-t rec_type] iam_file\n", basename(str));
+ fputs("\t-h: this help.\n"
+ "\t-r: print IAM keys and records.\n"
+ "\t-v: verbose mode to debug the file.\n"
+ "\t-t: type of record to print (", stdout);
+ print_rec_types();
+ puts(").\n"
+ "\t If not specified, this will be guess with file name.");
}
struct iam_params {
return 0;
}
+static void print_record(int idx, void *entry, struct iam_params *params)
+{
+ void *key = entry;
+ void *rec = entry + params->keysize;
+
+ if (verbose)
+ printf("%03d: ", idx);
+
+ if (print_cb->key_rec) {
+ if (print_cb->key_rec(key, params->keysize,
+ rec, params->recsize)) {
+ fprintf(stderr, "Bad key or rec for idx %d\n", idx);
+ params->rc = -1;
+ }
+ putchar('\n');
+
+ return;
+ }
+
+ if (print_cb->key && print_cb->key(key, params->keysize)) {
+ fprintf(stderr, "Bad key for idx %d\n", idx);
+ params->rc = -1;
+ }
+ putchar('\t');
+ if (print_cb->rec && print_cb->rec(rec, params->recsize)) {
+ fprintf(stderr, "Bad record for idx %d\n", idx);
+ params->rc = -1;
+ }
+ putchar('\n');
+}
+
static int check_entries(unsigned char *entries, size_t size, int count,
struct iam_params *params, int block_type)
{
unsigned int ptr;
- int i, j, rc;
+ int i, rc;
for (i = 0; i < count; i++) {
rc = 0;
if (block_type == INDEX_NODE) {
- if (verbose)
- printf("key:");
-
- for (j = 0; j < params->keysize; j++, entries++)
- if (verbose)
- printf("%02x", *entries);
+ if (verbose && print_cb->key) {
+ printf("%03d: ", i);
+ print_cb->key(entries, params->keysize);
+ }
+ entries += params->keysize;
ptr = __le32_to_cpu(*((__le32 *)entries));
if (ptr >= params->blocks_count) {
rc = -1;
}
if (verbose)
- printf(", ptr: %u%s\n", ptr,
+ printf("\tptr: %u%s\n", ptr,
rc ? " wrong" : "");
entries += params->ptrsize;
}
params->node_info[ptr].referenced = 1;
} else if (block_type == LEAF_NODE) {
- struct lu_fid fid;
- struct osd_inode_id *inode;
+ if (print_records)
+ print_record(i, entries, params);
- fid_be_to_cpu(&fid, (struct lu_fid *)entries);
- inode = (struct osd_inode_id *)(entries + sizeof(fid));
entries += params->keysize + params->recsize;
-
- if (print_records)
- printf(DFID" %u/%u\n", PFID(&fid),
- __be32_to_cpu(inode->oii_ino),
- __be32_to_cpu(inode->oii_gen));
}
}
return 0;
}
-static int check_root(char *buf, size_t size, struct iam_params *params)
+static int check_root(void *buf, size_t size, struct iam_params *params)
{
- struct iam_lfix_root *root;
+ __le64 *magic = buf;
unsigned int counted_limit;
int min;
struct dx_countlimit *limit;
+ __u32 *idle_blocks;
+ int root_entry_size;
+ int entries_off;
if (verbose)
- printf("Root format ");
+ printf("Root format: ");
+
+ switch (__le64_to_cpu(*magic)) {
+ case IAM_LFIX_ROOT_MAGIC: {
+ struct iam_lfix_root *root = buf;
- root = (struct iam_lfix_root *)buf;
- if (root->ilr_magic == __cpu_to_le64(IAM_LFIX_ROOT_MAGIC)) {
params->fmt = FMT_LFIX;
+ 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->root_gap = sizeof(*root);
if (verbose)
- printf("LFIX,");
- } else if (root->ilr_magic == __cpu_to_le64(IAM_LVAR_ROOT_MAGIC)) {
+ puts("LFIX");
+ break;
+ }
+ case IAM_LVAR_ROOT_MAGIC: {
+ struct lvar_root *root = buf;
+
params->fmt = FMT_LVAR;
+ params->keysize = sizeof(lvar_hash_t);
+ params->recsize = __le16_to_cpu(root->vr_recsize);
+ params->ptrsize = __le16_to_cpu(root->vr_ptrsize);
+ params->indirect_levels = root->vr_indirect_levels;
+ params->root_gap = sizeof(*root);
if (verbose)
- printf("LVAR,");
- } else {
- printf("Bad magic %llu\n", __le64_to_cpu(root->ilr_magic));
- params->rc = -1;
+ puts("LVAR");
+ break;
+ }
+ default:
+ fprintf(stderr, "Bad magic %llu\n", __le64_to_cpu(*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;
+ limit = buf + params->root_gap;
+ idle_blocks = buf + params->root_gap + sizeof(*limit);
+ params->idle_blocks = __le32_to_cpu(*idle_blocks);
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);
}
if (verbose) {
- printf("Idle blocks block number %lu\n", params->idle_blocks);
- printf("keysize %i, recsize %i, ptrsize %i, indirect_levels %i\n",
+ printf("\tkeysize: %i\n"
+ "\trecsize: %i\n"
+ "\tptrsize: %i\n"
+ "\tindirect_levels: %i\n"
+ "\tidle_blocks: %lu\n",
params->keysize, params->recsize, params->ptrsize,
- params->indirect_levels);
+ params->indirect_levels, params->idle_blocks);
}
if (params->ptrsize != 4 && params->ptrsize != 8) {
return -1;
}
+ root_entry_size = params->keysize + params->ptrsize;
counted_limit = root_limit(params->root_gap, params->node_gap,
- params->blocksize,
- params->keysize + params->ptrsize);
+ params->blocksize, root_entry_size);
if (__le16_to_cpu(limit->limit) != counted_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));
+ __le16_to_cpu(limit->count),
+ __le16_to_cpu(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));
+ printf("Root entries: count %i, limit %i\n",
+ __le16_to_cpu(limit->count),
+ __le16_to_cpu(limit->limit));
- /* cound - 1, because limit is entry itself */
- if (check_entries(root->entries,
- size - offsetof(struct iam_lfix_root, entries),
+ /* count - 1, because limit is entry itself */
+ entries_off = params->root_gap + root_entry_size;
+ if (check_entries(buf + entries_off, size - entries_off,
min - 1, params, INDEX_NODE)) {
printf("Broken entries\n");
return -1;
switch (head->ill_magic) {
case __cpu_to_le16(IAM_LEAF_HEADER_MAGIC):
if (verbose)
- printf("FIX leaf,");
+ printf("FIX leaf, ");
if (check_leaf(buf, params)) {
printf("Broken leaf block\n");
params->rc = -1;
break;
}
}
+
static int check_unconnected(struct iam_params *params)
{
unsigned long i;
}
return rc;
}
+
+
+/*
+ * print callbacks
+ */
+
+static int hexdump(const void *buf, size_t size)
+{
+ const __u8 *ptr = buf;
+ int i;
+
+ printf("0x");
+ for (i = 0; i < size; i++)
+ printf("%02x", ptr[i]);
+
+ return 0;
+}
+
+static int print_fid(const void *buf, size_t size)
+{
+ struct lu_fid fid;
+
+ if (size < sizeof(fid)) {
+ putchar('-');
+ return -1;
+ }
+
+ fid_be_to_cpu(&fid, buf);
+ printf(DFID, PFID(&fid));
+
+ return 0;
+}
+
+static int print_oid(const void *buf, size_t size)
+{
+ const struct osd_inode_id *oid = buf;
+
+ if (size < sizeof(*oid)) {
+ putchar('-');
+ return -1;
+ }
+
+ printf("%u/%u", __be32_to_cpu(oid->oii_ino),
+ __be32_to_cpu(oid->oii_gen));
+
+ return 0;
+}
+
+static int print_lfsck_namespace(const void *buf, size_t size)
+{
+ static const char * const fl2str[] = {
+ "CHECK_LINKEA", /* LNTF_CHECK_LINKEA */
+ "CHECK_ORPHAN", /* LNTF_CHECK_PARENT */
+ "CHECK_ORPHAN", /* LNTF_CHECK_ORPHAN */
+ "UNCERTAIN_LMV", /* LNTF_UNCERTAIN_LMV */
+ "RECHECK_NAME_HASH", /* LNTF_RECHECK_NAME_HASH */
+ "CHECK_AGENT_ENTRY", /* LNTF_CHECK_AGENT_ENTRY */
+ };
+ const __u8 *flags = buf;
+ bool first = true;
+ int i;
+
+ if (size < sizeof(*flags)) {
+ putchar('-');
+ return -1;
+ }
+
+ printf("0x%x (", *flags);
+ if (!*flags) {
+ putchar(')');
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fl2str); i++) {
+ if (*flags & (1<<i)) {
+ printf("%s%s", first ? "" : "|", fl2str[i]);
+ first = false;
+ }
+ }
+
+ putchar(')');
+
+ return 0;
+}
+
+struct lfsck_layout_dangling_key {
+ struct lu_fid lldk_fid;
+ __u32 lldk_comp_id;
+ __u32 lldk_ea_off;
+};
+
+static inline void lldk_be_to_cpu(struct lfsck_layout_dangling_key *des,
+ const struct lfsck_layout_dangling_key *src)
+{
+ fid_be_to_cpu(&des->lldk_fid, &src->lldk_fid);
+ des->lldk_comp_id = __be32_to_cpu(src->lldk_comp_id);
+ des->lldk_ea_off = __be32_to_cpu(src->lldk_ea_off);
+}
+
+static int print_dangling_rec_key(const void *buf, size_t size)
+{
+ const struct lfsck_layout_dangling_key *src = buf;
+ struct lfsck_layout_dangling_key key;
+
+ if (size < sizeof(key)) {
+ putchar('-');
+ return -1;
+ }
+
+ lldk_be_to_cpu(&key, src);
+ printf("{ parent: "DFID", comp_id: %u, ea_off: %u }",
+ PFID(&key.lldk_fid), key.lldk_comp_id, key.lldk_ea_off);
+
+ return 0;
+}
+
+static int print_dangling_rec(const void *buf, size_t size)
+{
+ struct lu_fid fid;
+ __u32 idx;
+
+ if (size < sizeof(fid)) {
+ putchar('-');
+ return -1;
+ }
+
+ fid_be_to_cpu(&fid, buf);
+ idx = fid.f_ver;
+ fid.f_ver = 0x0;
+ printf("{ cfid: "DFID", ost_idx: %d }", PFID(&fid), idx);
+
+ return 0;
+}
+
+static inline enum nodemap_idx_type nm_idx_get_type(unsigned int id)
+{
+ return id >> NM_TYPE_SHIFT;
+}
+
+static enum nodemap_idx_type nodemap_get_key_type(const struct nodemap_key *key)
+{
+ __u32 nodemap_id;
+
+ nodemap_id = __le32_to_cpu(key->nk_nodemap_id);
+ return nm_idx_get_type(nodemap_id);
+}
+
+static int nodemap_get_key_subtype(const struct nodemap_key *key)
+{
+ enum nodemap_idx_type type = nodemap_get_key_type(key);
+
+ return type == NODEMAP_CLUSTER_IDX ? key->nk_cluster_subid : -1;
+}
+
+static const char *nodemap_type2str(int type)
+{
+ static const char * const type2str[] = {
+ [NODEMAP_EMPTY_IDX] = "empty",
+ [NODEMAP_CLUSTER_IDX] = "cluster",
+ [NODEMAP_RANGE_IDX] = "range",
+ [NODEMAP_UIDMAP_IDX] = "uidmap",
+ [NODEMAP_GIDMAP_IDX] = "gidmap",
+ [NODEMAP_PROJIDMAP_IDX] = "projidmap",
+ [NODEMAP_NID_MASK_IDX] = "nid_mask",
+ [NODEMAP_GLOBAL_IDX] = "global",
+ };
+
+ if (type >= ARRAY_SIZE(type2str) || !type2str[type])
+ return "unknown";
+
+ return type2str[type];
+}
+
+static int print_nodemap_key(const void *buf, size_t size)
+{
+ const struct nodemap_key *nk = buf;
+ int type;
+
+ if (size < sizeof(*nk))
+ return -1;
+
+ type = nodemap_get_key_type(nk);
+ printf("{ id: 0x%x, type: %s(%d) }",
+ __le32_to_cpu(nk->nk_nodemap_id) & NM_TYPE_MASK,
+ nodemap_type2str(type), type);
+
+ return 0;
+}
+
+static int print_nodemap(const void *key, size_t keys,
+ const void *rec, size_t recs)
+{
+ const struct nodemap_key *nk = key;
+ const union nodemap_rec *nr = rec;
+ int type;
+
+ if (keys < sizeof(*nk) || recs < sizeof(*nr))
+ return -1;
+
+ type = nodemap_get_key_type(nk);
+ printf("{ id: 0x%x, type: %s(%d)",
+ __le32_to_cpu(nk->nk_nodemap_id) & NM_TYPE_MASK,
+ nodemap_type2str(type), type);
+
+ switch (type) {
+ case NODEMAP_EMPTY_IDX:
+ fputs(" }\t{}", stdout);
+ if (nk->nk_nodemap_id)
+ return -1;
+ break;
+ case NODEMAP_CLUSTER_IDX:
+ fputs(", subtype: ", stdout);
+
+ switch (nodemap_get_key_subtype(nk)) {
+ case NODEMAP_CLUSTER_REC:
+ printf("cluster }\t{ name: %s, flag: 0x%hhx, flag2: 0x%hhx, squash_uid: %u, squash_gid: %u, squash_projid: %u}",
+ nr->ncr.ncr_name,
+ nr->ncr.ncr_flags, nr->ncr.ncr_flags2,
+ __le32_to_cpu(nr->ncr.ncr_squash_uid),
+ __le32_to_cpu(nr->ncr.ncr_squash_gid),
+ __le32_to_cpu(nr->ncr.ncr_squash_projid));
+ break;
+ case NODEMAP_CLUSTER_ROLES:
+ printf("roles }\t{ roles: 0x%llx}",
+ __le64_to_cpu(nr->ncrr.ncrr_roles));
+ break;
+ default:
+ printf("unknown(%d) }\t{}",
+ nodemap_get_key_subtype(nk));
+ break;
+ }
+ break;
+ case NODEMAP_RANGE_IDX:
+ printf(" }\t{ start_nid: %s, end_nid: %s }",
+ libcfs_nid2str(__le64_to_cpu(nr->nrr.nrr_start_nid)),
+ libcfs_nid2str(__le64_to_cpu(nr->nrr.nrr_end_nid)));
+ break;
+ case NODEMAP_NID_MASK_IDX:
+ printf(" }\t{ subnet: %s/%hhd }",
+ libcfs_nidstr(&nr->nrr2.nrr_nid_prefix),
+ nr->nrr2.nrr_netmask);
+ break;
+ case NODEMAP_UIDMAP_IDX:
+ case NODEMAP_GIDMAP_IDX:
+ case NODEMAP_PROJIDMAP_IDX:
+ printf(", id_client: %u }\t{ id_fs: %u }",
+ __le32_to_cpu(nk->nk_id_client),
+ __le32_to_cpu(nr->nir.nir_id_fs));
+ break;
+ case NODEMAP_GLOBAL_IDX:
+ printf(" }\t{ is_active: %hhu }",
+ nr->ngr.ngr_is_active);
+
+ if (nk->nk_unused)
+ return -1;
+ break;
+ default:
+ fputs(" }\t{}", stdout);
+ break;
+ }
+
+ return 0;
+}
+
int main(int argc, char **argv)
{
struct iam_params params;
void *buf;
int fd;
struct stat sb;
+ struct record_type *rec_type = NULL;
params.rc = 0;
print_records = false;
do {
- opt = getopt(argc, argv, "hvr");
+ opt = getopt(argc, argv, "hvrt:");
switch (opt) {
case 'v':
verbose++;
break;
+ case 't':
+ rec_type = get_rec_type(optarg);
case 'r':
print_records = true;
case -1:
return -1;
}
+ if (!rec_type)
+ rec_type = guess_rec_type(argv[optind]);
+
+ print_cb = &rec_type->cb;
+ if (verbose && print_records)
+ printf("Record type to print: %s\n", rec_type->type);
+
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);