📄 vhd-util-check.c
字号:
err = 0;
out:
free(buf);
return err;
}
static int
vhd_util_check_differencing_header(vhd_context_t *vhd)
{
char *msg;
msg = vhd_util_check_validate_differencing_header(vhd);
if (msg) {
printf("differencing header is invalid: %s\n", msg);
return -EINVAL;
}
return 0;
}
static int
vhd_util_check_bat(vhd_context_t *vhd)
{
off64_t eof, eoh;
int i, j, err, block_size;
err = vhd_seek(vhd, 0, SEEK_END);
if (err) {
printf("error calculating eof: %d\n", err);
return err;
}
eof = vhd_position(vhd);
if (eof == (off64_t)-1) {
printf("error calculating eof: %d\n", -errno);
return -errno;
}
/* adjust eof for vhds with short footers */
if (eof % 512) {
if (eof % 512 != 511) {
printf("invalid file size: 0x%"PRIx64"\n", eof);
return -EINVAL;
}
eof++;
}
err = vhd_get_bat(vhd);
if (err) {
printf("error reading bat: %d\n", err);
return err;
}
err = vhd_end_of_headers(vhd, &eoh);
if (err) {
printf("error calculating end of metadata: %d\n", err);
return err;
}
eof -= sizeof(vhd_footer_t);
eof >>= VHD_SECTOR_SHIFT;
eoh >>= VHD_SECTOR_SHIFT;
block_size = vhd->spb + vhd->bm_secs;
for (i = 0; i < vhd->header.max_bat_size; i++) {
uint32_t off = vhd->bat.bat[i];
if (off == DD_BLK_UNUSED)
continue;
if (off < eoh) {
printf("block %d (offset 0x%x) clobbers headers\n",
i, off);
return -EINVAL;
}
if (off + block_size > eof) {
printf("block %d (offset 0x%x) clobbers footer\n",
i, off);
return -EINVAL;
}
for (j = 0; j < vhd->header.max_bat_size; j++) {
uint32_t joff = vhd->bat.bat[j];
if (i == j)
continue;
if (joff == DD_BLK_UNUSED)
continue;
if (off == joff)
err = -EINVAL;
if (off > joff && off < joff + block_size)
err = -EINVAL;
if (off + block_size > joff &&
off + block_size < joff + block_size)
err = -EINVAL;
if (err) {
printf("block %d (offset 0x%x) clobbers "
"block %d (offset 0x%x)\n",
i, off, j, joff);
return err;
}
}
}
return 0;
}
static int
vhd_util_check_batmap(vhd_context_t *vhd)
{
char *msg;
int i, err;
err = vhd_get_bat(vhd);
if (err) {
printf("error reading bat: %d\n", err);
return err;
}
err = vhd_get_batmap(vhd);
if (err) {
printf("error reading batmap: %d\n", err);
return err;
}
msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap);
if (msg) {
printf("batmap is invalid: %s\n", msg);
return -EINVAL;
}
for (i = 0; i < vhd->header.max_bat_size; i++) {
if (!vhd_batmap_test(vhd, &vhd->batmap, i))
continue;
if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
printf("batmap shows unallocated block %d full\n", i);
return -EINVAL;
}
}
return 0;
}
static int
vhd_util_check_parent_locators(vhd_context_t *vhd)
{
int i, n, err;
vhd_parent_locator_t *loc;
char *msg, *file, *ppath, *location, *pname;
int mac, macx, w2ku, w2ru, wi2r, wi2k, found;
mac = 0;
macx = 0;
w2ku = 0;
w2ru = 0;
wi2r = 0;
wi2k = 0;
found = 0;
pname = NULL;
ppath = NULL;
location = NULL;
err = vhd_header_decode_parent(vhd, &vhd->header, &pname);
if (err) {
printf("error decoding parent name: %d\n", err);
return err;
}
n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]);
for (i = 0; i < n; i++) {
ppath = NULL;
location = NULL;
loc = vhd->header.loc + i;
msg = vhd_util_check_validate_parent_locator(vhd, loc);
if (msg) {
err = -EINVAL;
printf("invalid parent locator %d: %s\n", i, msg);
goto out;
}
if (loc->code == PLAT_CODE_NONE)
continue;
switch (loc->code) {
case PLAT_CODE_MACX:
if (macx++)
goto dup;
break;
case PLAT_CODE_MAC:
if (mac++)
goto dup;
break;
case PLAT_CODE_W2KU:
if (w2ku++)
goto dup;
break;
case PLAT_CODE_W2RU:
if (w2ru++)
goto dup;
break;
case PLAT_CODE_WI2R:
if (wi2r++)
goto dup;
break;
case PLAT_CODE_WI2K:
if (wi2k++)
goto dup;
break;
default:
err = -EINVAL;
printf("invalid platform code for locator %d\n", i);
goto out;
}
if (loc->code != PLAT_CODE_MACX &&
loc->code != PLAT_CODE_W2RU &&
loc->code != PLAT_CODE_W2KU)
continue;
err = vhd_parent_locator_read(vhd, loc, &ppath);
if (err) {
printf("error reading parent locator %d: %d\n", i, err);
goto out;
}
file = basename(ppath);
if (strcmp(pname, file)) {
err = -EINVAL;
printf("parent locator %d name (%s) does not match "
"header name (%s)\n", i, file, pname);
goto out;
}
err = vhd_find_parent(vhd, ppath, &location);
if (err) {
printf("error resolving %s: %d\n", ppath, err);
goto out;
}
err = access(location, R_OK);
if (err && loc->code == PLAT_CODE_MACX) {
err = -errno;
printf("parent locator %d points to missing file %s "
"(resolved to %s)\n", i, ppath, location);
goto out;
}
msg = vhd_util_check_validate_parent(vhd, location);
if (msg) {
err = -EINVAL;
printf("invalid parent %s: %s\n", location, msg);
goto out;
}
found++;
free(ppath);
free(location);
ppath = NULL;
location = NULL;
continue;
dup:
printf("duplicate platform code in locator %d: 0x%x\n",
i, loc->code);
err = -EINVAL;
goto out;
}
if (!found) {
err = -EINVAL;
printf("could not find parent %s\n", pname);
goto out;
}
err = 0;
out:
free(pname);
free(ppath);
free(location);
return err;
}
static void
vhd_util_dump_headers(const char *name)
{
char *argv[] = { "read", "-p", "-n", (char *)name };
int argc = sizeof(argv) / sizeof(argv[0]);
printf("%s appears invalid; dumping metadata\n", name);
vhd_util_read(argc, argv);
}
static int
vhd_util_check_vhd(const char *name, int ignore)
{
int fd, err;
vhd_context_t vhd;
struct stat stats;
vhd_footer_t footer;
fd = -1;
memset(&vhd, 0, sizeof(vhd));
err = stat(name, &stats);
if (err == -1) {
printf("cannot stat %s: %d\n", name, errno);
return -errno;
}
if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
printf("%s is not a regular file or block device\n", name);
return -EINVAL;
}
fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
if (fd == -1) {
printf("error opening %s\n", name);
return -errno;
}
err = vhd_util_check_footer(fd, &footer, ignore);
if (err)
goto out;
if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
goto out;
err = vhd_util_check_header(fd, &footer);
if (err)
goto out;
err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
if (err)
goto out;
err = vhd_util_check_differencing_header(&vhd);
if (err)
goto out;
err = vhd_util_check_bat(&vhd);
if (err)
goto out;
if (vhd_has_batmap(&vhd)) {
err = vhd_util_check_batmap(&vhd);
if (err)
goto out;
}
if (vhd.footer.type == HD_TYPE_DIFF) {
err = vhd_util_check_parent_locators(&vhd);
if (err)
goto out;
}
err = 0;
printf("%s is valid\n", name);
out:
if (err)
vhd_util_dump_headers(name);
if (fd != -1)
close(fd);
vhd_close(&vhd);
return err;
}
static int
vhd_util_check_parents(const char *name, int ignore)
{
int err;
vhd_context_t vhd;
char *cur, *parent;
cur = (char *)name;
for (;;) {
err = vhd_open(&vhd, cur,
VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
if (err)
goto out;
if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
vhd_close(&vhd);
goto out;
}
err = vhd_parent_locator_get(&vhd, &parent);
vhd_close(&vhd);
if (err) {
printf("error getting parent: %d\n", err);
goto out;
}
if (cur != name)
free(cur);
cur = parent;
err = vhd_util_check_vhd(cur, ignore);
if (err)
goto out;
}
out:
if (err)
printf("error checking parents: %d\n", err);
if (cur != name)
free(cur);
return err;
}
int
vhd_util_check(int argc, char **argv)
{
char *name;
vhd_context_t vhd;
int c, err, ignore, parents;
if (!argc || !argv) {
err = -EINVAL;
goto usage;
}
ignore = 0;
parents = 0;
name = NULL;
optind = 0;
while ((c = getopt(argc, argv, "n:iph")) != -1) {
switch (c) {
case 'n':
name = optarg;
break;
case 'i':
ignore = 1;
break;
case 'p':
parents = 1;
break;
case 'h':
err = 0;
goto usage;
default:
err = -EINVAL;
goto usage;
}
}
if (!name || optind != argc) {
err = -EINVAL;
goto usage;
}
err = vhd_util_check_vhd(name, ignore);
if (err)
goto out;
if (parents)
err = vhd_util_check_parents(name, ignore);
out:
return err;
usage:
printf("options: -n <file> [-i ignore missing primary footers] "
"[-p check parents] [-h help]\n");
return err;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -