cbd/libcbd/check.c

236 lines
6.4 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 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,
bool force,
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);
if (!force && !(header.params.flags & CBD_FLAG_DIRTY)) {
printf("%s: clean\n", dev);
close(devfd);
return 0;
}
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);
close(devfd);
return 0;
}