Viewing: check_iam.c
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* 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 <asm/byteorder.h>
#include <linux/lustre/lustre_user.h>
#include <linux/lustre/lustre_fid.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,
INDEX_NODE,
LEAF_NODE,
IDLE_NODE
};
struct node_info {
int referenced;
int recycled;
int node_type;
};
static void usage(char *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 {
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;
};
static 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 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, 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 (block_type == INDEX_NODE) {
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) {
params->rc = -1;
rc = -1;
}
if (verbose)
printf("\tptr: %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;
} else if (block_type == LEAF_NODE) {
if (print_records)
print_record(i, entries, params);
entries += params->keysize + params->recsize;
}
}
return 0;
}
static int check_leaf(char *buf, struct iam_params *params)
{
struct iam_leaf_head *leaf;
int counted_limit;
int leaf_count;
leaf = (struct iam_leaf_head *)buf;
params->node_info[params->current_block].node_type = LEAF_NODE;
counted_limit = node_limit(sizeof(struct iam_leaf_head),
params->blocksize,
params->keysize + params->recsize);
leaf_count = __le16_to_cpu(leaf->ill_count);
if (verbose)
printf("Leaf block, count %i, limit %i\n", leaf_count,
counted_limit);
if (leaf_count > counted_limit) {
printf("More elements (%i) then limit (%i)\n", leaf_count,
counted_limit);
return -1;
}
if (check_entries((unsigned char *)(buf + sizeof(struct iam_leaf_head)),
params->blocksize - sizeof(struct iam_leaf_head),
counted_limit < leaf_count ?
counted_limit : leaf_count, params, LEAF_NODE)) {
printf("Broken entries\n");
return -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;
int limit_count;
index = (struct iam_index_head *)buf;
limit = &index->limit;
params->node_info[params->current_block].node_type = INDEX_NODE;
limit_count = __le16_to_cpu(limit->count);
if (verbose)
printf("Index block, count %i, limit %i\n", 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 (limit_count > __le16_to_cpu(limit->limit)) {
printf("More elements (%i) then limit (%i)\n", 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),
limit_count - 1, params, INDEX_NODE)) {
printf("Broken entries\n");
return -1;
}
return 0;
}
static int check_root(void *buf, size_t size, struct iam_params *params)
{
__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: ");
switch (__le64_to_cpu(*magic)) {
case IAM_LFIX_ROOT_MAGIC: {
struct iam_lfix_root *root = buf;
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)
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)
puts("LVAR");
break;
}
default:
fprintf(stderr, "Bad magic %llu\n", __le64_to_cpu(*magic));
return -1;
}
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;
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("\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->idle_blocks);
}
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;
}
root_entry_size = params->keysize + params->ptrsize;
counted_limit = root_limit(params->root_gap, params->node_gap,
params->blocksize, root_entry_size);
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(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("Root entries: count %i, limit %i\n",
__le16_to_cpu(limit->count),
__le16_to_cpu(limit->limit));
/* 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;
}
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, ");
if (check_leaf(buf, params)) {
printf("Broken leaf block\n");
params->rc = -1;
}
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;
}
/*
* 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, type 0x%x }",
libcfs_nid2str(__le64_to_cpu(nr->nrr.nrr_start_nid)),
libcfs_nid2str(__le64_to_cpu(nr->nrr.nrr_end_nid)),
nm_idx_get_type(__le32_to_cpu(nk->nk_range_id)));
break;
case NODEMAP_NID_MASK_IDX:
printf(" }\t{ subnet: %s/%hhd, type: 0x%x }",
libcfs_nidstr(&nr->nrr2.nrr_nid_prefix),
nr->nrr2.nrr_netmask,
nm_idx_get_type(__le32_to_cpu(nk->nk_range_id)));
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;
int rc = 0;
int opt;
void *buf;
int fd;
struct stat sb;
struct record_type *rec_type = NULL;
params.rc = 0;
print_records = false;
do {
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:
break;
default:
fprintf(stderr, "Unable to parse options.");
case 'h':
usage(argv[0]);
return 0;
}
} while (opt != -1);
if (optind >= argc) {
fprintf(stderr, "Expected filename after options\n");
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.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");
goto err;
}
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;
}