#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 lba* lba; u8* lba_buf; u32 n_alloc; u32 n; u64 pblk; lba = calloc(1, offsetof(struct lba, pblk[lblk_per_pblk(params)])); lba_buf = zm->lbat + lblk * lba_len(params); lba_get(params, lba_buf, lba); if (lba->len) { if (lba->len == CBD_UNCOMPRESSED) { printf(" lblk[%u]: UNCOMPRESSED\n", lblk); } else { printf(" lblk[%u]: len=%u\n", lblk, lba->len); } } else { printf(" lblk[%u]: EMPTY\n", lblk); } if (lba->len > PBLK_SIZE * lblk_per_pblk(params)) { printf(" :E: Length out of bounds\n"); return; } n_alloc = (lba->len == CBD_UNCOMPRESSED) ? lblk_per_pblk(params) : DIV_ROUND_UP(lba->len, PBLK_SIZE); for (n = 0; n < n_alloc; ++n) { u32 pblk_zone; u32 rel_pblk; pblk = lba->pblk[n]; if (pblk < CBD_HEADER_BLOCKS) { printf(" [%u] :E: Alloc in header: %lu\n", n, pblk); continue; } pblk_zone = (pblk - CBD_HEADER_BLOCKS) / zone_len(params); if (pblk_zone >= params->nr_zones) { printf(" [%u] :E: Alloc beyond end: %lu\n", n, pblk); continue; } if (pblk < zone_data_off(params, pblk_zone)) { printf(" [%u] :E: Alloc in metadata: %lu\n", n, pblk); continue; } rel_pblk = pblk - zone_data_off(params, pblk_zone); /* XXX: Cannot happen? */ if (rel_pblk >= pbat_len(params) * PBLK_SIZE_BITS) { printf(" [%u] :E: Alloc out of zone: %lu\n", n, pblk); continue; } printf(" [%u] pblk=%lu pblk_zone=%u rel_pblk=%u\n", n, (unsigned long)pblk, pblk_zone, rel_pblk); if (pblk_used[pblk_zone][rel_pblk/8] & (1 << (rel_pblk % 8))) { printf(" [%u] :E: Duplicate allocation for zone %u block %u\n", n, pblk_zone, rel_pblk); continue; } pblk_used[pblk_zone][rel_pblk/8] |= (1 << (rel_pblk % 8)); } free(lba); } static void check_one_zone(const struct cbd_params* params, u32 zone, const struct zone_metadata* zm, u8** pblk_used) { u32 lblk_alloc_len; u32 n; bool zone_empty; u32 lblk; printf("Zone %u: lbat=[%lu..%lu] alloc=[%lu .. %lu]\n", (unsigned int)zone, (unsigned long)zone_off(params, zone), (unsigned long)(zone_data_off(params, zone) - 1), (unsigned long)zone_data_off(params, zone), (unsigned long)zone_off(params, zone + 1)); zone_empty = true; lblk_alloc_len = params->lblk_per_zone * lba_len(params); for (n = 0; n < lblk_alloc_len; ++n) { if (zm->lbat[n]) { zone_empty = false; break; } } if (zone_empty) { printf(" [empty]\n"); return; } 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(pbat_len(params), PBLK_SIZE); } 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(pbat_len(&header.params), 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; }