#include #include struct zone_metadata { u8* pbat; u8* lbat; }; static void pblk_read(int fd, u64 pblk, u32 count, u8* data) { off_t pos; size_t remain; ssize_t ret; pos = lseek(fd, pblk * PBLK_SIZE, SEEK_SET); if (pos == (off_t)-1) { error("Failed to seek\n"); } remain = count * PBLK_SIZE; while (remain) { ret = read(fd, data, remain); if (ret <= 0) { error("Failed to read\n"); } remain -= ret; data += ret; } } static void check_header(const struct cbd_header* header) { if (memcmp(header->magic, CBD_MAGIC, sizeof(header->magic))) { error("Bad magic\n"); } if (header->version_major != CBD_VERSION_MAJOR) { error("Bad major version\n"); } if (header->version_minor != CBD_VERSION_MINOR) { error("Bad major version\n"); } if (header->params.algorithm == CBD_ALG_NONE || header->params.algorithm >= CBD_ALG_MAX) { error("Bad algorithm\n"); } if (header->params.compression > 9) { error("Bad compression\n"); } if (header->params.lblk_shift < LBLK_SHIFT_MIN || header->params.lblk_shift >= LBLK_SHIFT_MAX) { error("Bad logical block shift\n"); } } static void check_one_lblk(const struct cbd_params* params, u32 zone, u32 lblk, const struct zone_metadata* zm, u8** pblk_used) { struct lbat_elem* elem; u8* elem_buf; u32 n; u64 pblk; u32 rel_pblk; elem = calloc(1, offsetof(struct lbat_elem, pblk[lblk_per_pblk(params)])); elem_buf = zm->lbat + lblk * lbat_elem_len(params); lbat_elem_get(params, elem_buf, elem); printf(" lblk[%u]: len=%u\n", lblk, elem->len); for (n = 0; n < lblk_per_pblk(params); ++n) { pblk = elem->pblk[n]; if (elem->len > PBLK_SIZE * n) { /* XXX: allow out-of-zone allocs for v1.1 */ if (pblk < zone_data_off(params, zone) || pblk >= zone_off(params, zone + 1)) { printf("Alloc out of bounds for zone %u block %u index %u: %lu\n", (unsigned int)zone, lblk, n, (unsigned long)pblk); continue; } rel_pblk = pblk - zone_data_off(params, zone); printf(" [%u] pblk=%lu rel_pblk=%u\n", n, (unsigned long)pblk, rel_pblk); if (pblk_used[zone][rel_pblk/8] & (1 << (rel_pblk % 8))) { printf("Duplicate allocation for zone %u block %u\n", (unsigned int)zone, (unsigned int)rel_pblk); continue; } pblk_used[zone][rel_pblk/8] |= (1 << (rel_pblk % 8)); } else { if (pblk) { printf("Unexpected pblk alloc for zone %u block %u index %u: %lu\n", (unsigned int)zone, lblk, n, (unsigned long)pblk); } } } free(elem); } static void check_one_zone(const struct cbd_params* params, u32 zone, const struct zone_metadata* zm, u8** pblk_used) { u32 lblk; printf("Zone %u: alloc [%lu .. %lu]\n", (unsigned int)zone, (unsigned long)zone_data_off(params, zone), (unsigned long)zone_off(params, zone + 1)); for (lblk = 0; lblk < params->lblk_per_zone; ++lblk) { check_one_lblk(params, zone, lblk, zm, pblk_used); } } static void check_zone_metadata(const struct cbd_params* params, const struct zone_metadata* zmvec) { u8** pblk_used; u32 zone; pblk_used = calloc(params->nr_zones, sizeof(void*)); for (zone = 0; zone < params->nr_zones; ++zone) { pblk_used[zone] = calloc(1, pbat_len(params)); } for (zone = 0; zone < params->nr_zones; ++zone) { check_one_zone(params, zone, &zmvec[zone], pblk_used); } for (zone = 0; zone < params->nr_zones; ++zone) { free(pblk_used[zone]); } free(pblk_used); } int cbd_check(const char* dev, tristate_t auto_response) { int devfd; struct cbd_header header; uint8_t pblkbuf[PBLK_SIZE]; struct zone_metadata* zmvec; u32 zone; devfd = open(dev, O_RDONLY); if (devfd < 0) { error("Cannot open device\n"); } pblk_read(devfd, 0, 1, pblkbuf); cbd_header_get(pblkbuf, &header); check_header(&header); zmvec = calloc(header.params.nr_zones, sizeof(struct zone_metadata)); for (zone = 0; zone < header.params.nr_zones; ++zone) { zmvec[zone].pbat = calloc(1, PBLK_SIZE); pblk_read(devfd, pbat_off(&header.params, zone), pbat_len(&header.params), zmvec[zone].pbat); zmvec[zone].lbat = calloc(lbat_len(&header.params), PBLK_SIZE); pblk_read(devfd, lbat_off(&header.params, zone), lbat_len(&header.params), zmvec[zone].lbat); } check_zone_metadata(&header.params, zmvec); for (zone = 0; zone < header.params.nr_zones; ++zone) { free(zmvec[zone].lbat); free(zmvec[zone].pbat); } free(zmvec); return 0; }