static int vhd_util_find_snapshot_target(const char *name, char **result, int *parent_raw) { int i, err; char *target; vhd_context_t vhd; *parent_raw = 0; *result = NULL; target = strdup(name); if (!target) return -ENOMEM; for (;;) { err = vhd_open(&vhd, target, VHD_OPEN_RDONLY); if (err) { free(target); return err; } if (vhd.footer.type != HD_TYPE_DIFF) goto out; err = vhd_get_bat(&vhd); if (err) goto out; for (i = 0; i < vhd.bat.entries; i++) if (vhd.bat.bat[i] != DD_BLK_UNUSED) goto out; free(target); err = vhd_parent_locator_get(&vhd, &target); if (err) goto out; if (vhd_parent_raw(&vhd)) { *parent_raw = 1; goto out; } vhd_close(&vhd); } out: vhd_close(&vhd); if (err) free(target); else *result = target; return err; }
int vhd_journal_close(vhd_journal_t *j) { if (j->jfd) close(j->jfd); vhd_close(&j->vhd); free(j->jname); return 0; }
static int vhd_util_check_depth(const char *name, int *depth) { int err; vhd_context_t vhd; err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); if (err) return err; err = vhd_chain_depth(&vhd, depth); vhd_close(&vhd); return err; }
int vhd_util_repair(int argc, char **argv) { char *name; int err, c; off64_t eof; vhd_context_t vhd; name = NULL; if (!argc || !argv) goto usage; optind = 0; while ((c = getopt(argc, argv, "n:h")) != -1) { switch (c) { case 'n': name = optarg; break; case 'h': default: goto usage; } } if (!name || optind != argc) goto usage; err = vhd_open(&vhd, name, VHD_OPEN_RDWR); if (err) { printf("error opening %s: %d\n", name, err); return err; } err = vhd_write_footer(&vhd, &vhd.footer); if (err) printf("error writing footer: %d\n", err); vhd_close(&vhd); return err; usage: printf("options: <-n name> [-h help]\n"); return -EINVAL; }
int vhd_journal_remove(vhd_journal_t *j) { int err; err = vhd_journal_enable_vhd(j); if (err) return err; if (j->jfd) { close(j->jfd); if (!j->is_block) unlink(j->jname); } vhd_close(&j->vhd); free(j->jname); return 0; }
int vhd_util_read(int argc, char **argv) { char *name; vhd_context_t vhd; int c, err, headers, hex, bat_str, cache, flags; uint64_t bat, bitmap, tbitmap, ebitmap, batmap, tbatmap, data, lsec, count, read; uint64_t bread; err = 0; hex = 0; cache = 0; headers = 0; bat_str = 0; count = 1; bat = -1; bitmap = -1; tbitmap = -1; ebitmap = -1; batmap = -1; tbatmap = -1; data = -1; lsec = -1; read = -1; bread = -1; name = NULL; if (!argc || !argv) goto usage; optind = 0; while ((c = getopt(argc, argv, "n:pt:b:Bm:i:e:aj:d:c:r:R:xCh")) != -1) { switch(c) { case 'n': name = optarg; break; case 'p': headers = 1; break; case 'C': cache = 1; break; case 'B': bat_str = 1; break; case 't': lsec = strtoul(optarg, NULL, 10); break; case 'b': bat = strtoull(optarg, NULL, 10); break; case 'm': bitmap = strtoull(optarg, NULL, 10); break; case 'i': tbitmap = strtoul(optarg, NULL, 10); break; case 'e': ebitmap = strtoul(optarg, NULL, 10); break; case 'a': batmap = 1; break; case 'j': tbatmap = strtoull(optarg, NULL, 10); break; case 'd': data = strtoull(optarg, NULL, 10); break; case 'r': read = strtoull(optarg, NULL, 10); break; case 'R': bread = strtoull(optarg, NULL, 10); break; case 'c': count = strtoul(optarg, NULL, 10); break; case 'x': hex = 1; break; case 'h': default: goto usage; } } if (!name || optind != argc) goto usage; flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED; if (cache) flags |= VHD_OPEN_CACHED | VHD_OPEN_FAST; err = vhd_open(&vhd, name, flags); if (err) { printf("Failed to open %s: %d\n", name, err); vhd_dump_headers(name, hex); return err; } err = vhd_get_bat(&vhd); if (err) { printf("Failed to get bat for %s: %d\n", name, err); goto out; } if (headers) vhd_print_headers(&vhd, hex); if (lsec != -1) { err = vhd_print_logical_to_physical(&vhd, lsec, count, hex); if (err) goto out; } if (bat != -1) { err = vhd_print_bat(&vhd, bat, count, hex); if (err) goto out; } if (bat_str) { err = vhd_print_bat_str(&vhd); if (err) goto out; } if (bitmap != -1) { err = vhd_print_bitmap(&vhd, bitmap, count, hex); if (err) goto out; } if (tbitmap != -1) { err = vhd_test_bitmap(&vhd, tbitmap, count, hex); if (err) goto out; } if (ebitmap != -1) { err = vhd_print_bitmap_extents(&vhd, ebitmap, count, hex); if (err) goto out; } if (batmap != -1) { err = vhd_print_batmap(&vhd); if (err) goto out; } if (tbatmap != -1) { err = vhd_test_batmap(&vhd, tbatmap, count, hex); if (err) goto out; } if (data != -1) { err = vhd_print_data(&vhd, data, count, hex); if (err) goto out; } if (read != -1) { err = vhd_read_data(&vhd, read, count, hex); if (err) goto out; } if (bread != -1) { err = vhd_read_bytes(&vhd, bread, count, hex); if (err) goto out; } err = 0; out: vhd_close(&vhd); return err; usage: printf("options:\n" "-h help\n" "-n name\n" "-p print VHD headers\n" "-t sec translate logical sector to VHD location\n" "-b blk print bat entry\n" "-B print entire bat as a bitmap\n" "-m blk print bitmap\n" "-i sec test bitmap for logical sector\n" "-e sec output extent list of allocated logical sectors\n" "-a print batmap\n" "-j blk test batmap for block\n" "-d blk print data\n" "-c num num units\n" "-r sec read num sectors at sec\n" "-R byte read num bytes at byte\n" "-x print in hex\n"); return EINVAL; }
int vhd_util_snapshot(int argc, char **argv) { vhd_flag_creat_t flags; int c, err, prt_raw, limit, empty_check; char *name, *pname, *backing; char *ppath, __ppath[PATH_MAX]; uint64_t size, msize; vhd_context_t vhd; name = NULL; pname = NULL; ppath = NULL; backing = NULL; size = 0; msize = 0; flags = 0; limit = 0; empty_check = 1; if (!argc || !argv) { err = -EINVAL; goto usage; } optind = 0; while ((c = getopt(argc, argv, "n:p:S:l:meh")) != -1) { switch (c) { case 'n': name = optarg; break; case 'p': pname = optarg; break; case 'S': msize = strtoull(optarg, NULL, 10); case 'l': limit = strtol(optarg, NULL, 10); break; case 'm': vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW); break; case 'e': empty_check = 0; break; case 'h': err = 0; goto usage; default: err = -EINVAL; goto usage; } } if (!name || !pname || optind != argc) { err = -EINVAL; goto usage; } ppath = realpath(pname, __ppath); if (!ppath) return -errno; if (vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW) || !empty_check) { backing = strdup(ppath); if (!backing) { err = -ENOMEM; goto out; } } else { err = vhd_util_find_snapshot_target(ppath, &backing, &prt_raw); if (err) { backing = NULL; goto out; } /* * if the sizes of the parent chain are non-uniform, we need to * pick the right size: that of the supplied parent */ if (strcmp(ppath, backing)) { err = vhd_open(&vhd, ppath, VHD_OPEN_RDONLY); if (err) goto out; size = vhd.footer.curr_size; vhd_close(&vhd); } if (prt_raw) vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW); } if (limit && !vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) { int depth; err = vhd_util_check_depth(backing, &depth); if (err) printf("error checking snapshot depth: %d\n", err); else if (depth + 1 > limit) { err = -ENOSPC; printf("snapshot depth exceeded: " "current depth: %d, limit: %d\n", depth, limit); } if (err) goto out; } err = vhd_snapshot(name, size, backing, msize << 20, flags); out: free(backing); return err; usage: printf("options: <-n name> <-p parent name> [-l snapshot depth limit]" " [-m parent_is_raw] [-S size (MB) for metadata preallocation " "(see vhd-util resize)] [-e link to supplied parent name even " "if it's empty] [-h help]\n"); return err; }
int vhd_util_fill(int argc, char **argv) { int err, c; char *name; void *buf; vhd_context_t vhd; uint64_t i, sec, secs, from_sector, to_sector; int init_bat; bool ignore_2tb_limit; buf = NULL; name = NULL; init_bat = 0; from_sector = ULLONG_MAX; to_sector = ULLONG_MAX; ignore_2tb_limit = false; if (!argc || !argv) goto usage; optind = 0; while ((c = getopt(argc, argv, "n:f:t:bBh")) != -1) { switch (c) { case 'n': name = optarg; break; case 'f': from_sector = strtoull(optarg, NULL, 10); break; case 't': to_sector = strtoull(optarg, NULL, 10); break; case 'b': init_bat = 1; break; case 'B': ignore_2tb_limit = true; break; case 'h': default: goto usage; } } if (!name || optind != argc) goto usage; if ((from_sector != ULLONG_MAX || to_sector != ULLONG_MAX) && !init_bat) { printf("-f/-t can only be used with -b\n"); goto usage; } if (from_sector != ULLONG_MAX && to_sector != ULLONG_MAX) { if (to_sector < from_sector) { printf("invalid sector range %llu-%llu\n", (unsigned long long)from_sector, (unsigned long long)to_sector); goto usage; } } if (ignore_2tb_limit && !init_bat) { printf("-B can only be used with -b\n"); goto usage; } err = vhd_open(&vhd, name, VHD_OPEN_RDWR); if (err) { printf("error opening %s: %d\n", name, err); return err; } err = vhd_get_bat(&vhd); if (err) goto done; if (init_bat) { uint32_t from_extent; uint32_t to_extent; if (from_sector != ULLONG_MAX) from_extent = from_sector / vhd.spb; else from_extent = 0; if (to_sector != ULLONG_MAX) to_extent = to_sector / vhd.spb; else to_extent = vhd.bat.entries - 1; err = vhd_io_allocate_blocks_fast(&vhd, from_extent, to_extent, ignore_2tb_limit); if (err) goto done; } else { err = posix_memalign(&buf, 4096, vhd.header.block_size); if (err) { err = -err; goto done; } sec = 0; secs = vhd.header.block_size >> VHD_SECTOR_SHIFT; for (i = 0; i < vhd.header.max_bat_size; i++) { err = vhd_io_read(&vhd, buf, sec, secs); if (err) goto done; err = vhd_io_write(&vhd, buf, sec, secs); if (err) goto done; sec += secs; } err = 0; } done: free(buf); vhd_close(&vhd); return err; usage: printf("options: <-n name> [-h help] [-b initialise the BAT and bitmaps, " "don't write to the data blocks (much faster)] [-f start " "intialisation from this sector, only usable with -b] [-t " "intialise up to this sector (inclusive), only usable with -b] " "[-B ignore the 2 TB limit, only usable with -b]\n"); return -EINVAL; }
/* * revert indicates the transaction failed * and we should revert any changes via the undo log */ int vhd_journal_revert(vhd_journal_t *j) { int i, err; char *file; void *buf; vhd_context_t *vhd; vhd_journal_entry_t entry; err = 0; vhd = &j->vhd; buf = NULL; file = strdup(vhd->file); if (!file) return -ENOMEM; vhd_close(&j->vhd); j->vhd.fd = open(file, O_RDWR | O_DIRECT | O_LARGEFILE); if (j->vhd.fd == -1) { free(file); return -errno; } err = vhd_test_file_fixed(file, &vhd->is_block); if (err) { free(file); return err; } err = vhd_journal_restore_metadata(j); if (err) { free(file); return err; } close(vhd->fd); free(vhd->bat.bat); free(vhd->batmap.map); err = vhd_open(vhd, file, VHD_OPEN_RDWR); free(file); if (err) return err; err = vhd_journal_seek(j, j->header.journal_data_offset, SEEK_SET); if (err) return err; for (i = 0; i < j->header.journal_data_entries; i++) { err = vhd_journal_read_entry(j, &entry); if (err) goto end; err = posix_memalign(&buf, VHD_SECTOR_SIZE, entry.size); if (err) { err = -err; buf = NULL; goto end; } err = vhd_journal_read(j, buf, entry.size); if (err) goto end; err = vhd_journal_validate_entry_data(&entry, buf); if (err) goto end; err = vhd_seek(vhd, entry.offset, SEEK_SET); if (err) goto end; err = vhd_write(vhd, buf, entry.size); if (err) goto end; err = 0; end: free(buf); buf = NULL; if (err) break; } if (err) return err; if (!vhd->is_block) { err = ftruncate(vhd->fd, j->header.vhd_footer_offset + sizeof(vhd_footer_t)); if (err) return -errno; } return vhd_journal_sync(j); }
int vhd_util_fill(int argc, char **argv) { int err, c; char *name; void *buf; vhd_context_t vhd; uint64_t i, sec, secs; buf = NULL; name = NULL; if (!argc || !argv) goto usage; optind = 0; while ((c = getopt(argc, argv, "n:h")) != -1) { switch (c) { case 'n': name = optarg; break; case 'h': default: goto usage; } } if (!name || optind != argc) goto usage; err = vhd_open(&vhd, name, VHD_OPEN_RDWR); if (err) { printf("error opening %s: %d\n", name, err); return err; } err = vhd_get_bat(&vhd); if (err) goto done; err = posix_memalign(&buf, 4096, vhd.header.block_size); if (err) { err = -err; goto done; } sec = 0; secs = vhd.header.block_size >> VHD_SECTOR_SHIFT; for (i = 0; i < vhd.header.max_bat_size; i++) { err = vhd_io_read(&vhd, buf, sec, secs); if (err) goto done; err = vhd_io_write(&vhd, buf, sec, secs); if (err) goto done; sec += secs; } err = 0; done: free(buf); vhd_close(&vhd); return err; usage: printf("options: <-n name> [-h help]\n"); return -EINVAL; }
int vhd_util_set_field(int argc, char **argv) { long value; int err, c; vhd_context_t vhd; char *name, *field; err = -EINVAL; value = 0; name = NULL; field = NULL; if (!argc || !argv) goto usage; optind = 0; while ((c = getopt(argc, argv, "n:f:v:h")) != -1) { switch (c) { case 'n': name = optarg; break; case 'f': field = optarg; break; case 'v': err = 0; value = strtol(optarg, NULL, 10); break; case 'h': default: goto usage; } } if (!name || !field || optind != argc || err) goto usage; if (strnlen(field, 25) >= 25) { printf("invalid field\n"); goto usage; } if (strcmp(field, "hidden") && strcmp(field, "marker")) { printf("invalid field %s\n", field); goto usage; } if (value < 0 || value > 255) { printf("invalid value %ld\n", value); goto usage; } err = vhd_open(&vhd, name, VHD_OPEN_RDWR); if (err) { printf("error opening %s: %d\n", name, err); return err; } if (!strcmp(field, "hidden")) { vhd.footer.hidden = (char)value; err = vhd_write_footer(&vhd, &vhd.footer); if (err == -ENOSPC && vhd_type_dynamic(&vhd) && value) /* if no space to write the primary footer, at least write the * backup footer so that it's possible to delete the VDI */ err = vhd_write_footer_at(&vhd, &vhd.footer, 0); } else { err = vhd_set_marker(&vhd, (char)value); } vhd_close(&vhd); return err; usage: printf("options: <-n name> <-f field> <-v value> [-h help]\n"); return -EINVAL; }