233 lines
6.5 KiB
C
233 lines
6.5 KiB
C
#include <libcbd.h>
|
|
|
|
#include <cbdutil.h>
|
|
|
|
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 c_len;
|
|
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;
|
|
}
|
|
c_len = (lba->len == CBD_UNCOMPRESSED) ? PBLK_SIZE * lblk_per_pblk(params) : lba->len;
|
|
for (n = 0; n < lblk_per_pblk(params); ++n) {
|
|
pblk = lba->pblk[n];
|
|
if (c_len > PBLK_SIZE * n) {
|
|
u32 pblk_zone;
|
|
u32 rel_pblk;
|
|
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));
|
|
}
|
|
else {
|
|
if (pblk) {
|
|
printf(" [%u] :E: Unexpected pblk alloc: %lu\n", n, pblk);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|