📄 vhd-util-resize.c
字号:
next = 0;
spp = getpagesize() >> VHD_SECTOR_SHIFT;
for (i = 0; i < vhd->bat.entries; i++) {
blk = vhd->bat.bat[i];
if (blk != DD_BLK_UNUSED) {
end = blk + vhd->spb + vhd->bm_secs;
next = MAX(next, end);
}
}
return next;
}
static inline int
in_range(off64_t off, off64_t start, off64_t size)
{
return (start < off && start + size > off);
}
#define SKIP_HEADER 0x01
#define SKIP_BAT 0x02
#define SKIP_BATMAP 0x04
#define SKIP_PLOC 0x08
#define SKIP_DATA 0x10
static inline int
skip_check(int mode, int type)
{
return mode & type;
}
static int
vhd_check_for_clobber(vhd_context_t *vhd, off64_t off, int mode)
{
int i, n;
char *msg;
size_t size;
vhd_block_t fb;
vhd_parent_locator_t *loc;
msg = NULL;
if (!vhd_type_dynamic(vhd))
return 0;
if (off < VHD_SECTOR_SIZE) {
msg = "backup footer";
goto fail;
}
if (!skip_check(mode, SKIP_HEADER))
if (in_range(off,
vhd->footer.data_offset, sizeof(vhd_header_t))) {
msg = "header";
goto fail;
}
if (!skip_check(mode, SKIP_BAT))
if (in_range(off, vhd->header.table_offset,
vhd_bytes_padded(vhd->header.max_bat_size *
sizeof(uint32_t)))) {
msg = "bat";
goto fail;
}
if (!skip_check(mode, SKIP_BATMAP))
if (vhd_has_batmap(vhd) &&
in_range(off, vhd->batmap.header.batmap_offset,
vhd_bytes_padded(vhd->batmap.header.batmap_size))) {
msg = "batmap";
goto fail;
}
if (!skip_check(mode, SKIP_PLOC)) {
n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
for (i = 0; i < n; i++) {
loc = vhd->header.loc + i;
if (loc->code == PLAT_CODE_NONE)
continue;
size = vhd_parent_locator_size(loc);
if (in_range(off, loc->data_offset, size)) {
msg = "parent locator";
goto fail;
}
}
}
if (!skip_check(mode, SKIP_DATA)) {
vhd_first_data_block(vhd, &fb);
if (fb.offset && in_range(off,
vhd_sectors_to_bytes(fb.offset),
VHD_BLOCK_SIZE)) {
msg = "data block";
goto fail;
}
}
return 0;
fail:
EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg);
return -EINVAL;
}
/*
* take any metadata after the bat (@eob) and shift it
*/
static int
vhd_shift_metadata(vhd_journal_t *journal, off64_t eob,
size_t bat_needed, size_t map_needed)
{
int i, n, err;
vhd_context_t *vhd;
size_t size_needed;
char *buf, **locators;
vhd_parent_locator_t *loc;
vhd = &journal->vhd;
size_needed = bat_needed + map_needed;
n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
locators = calloc(n, sizeof(char *));
if (!locators)
return -ENOMEM;
for (i = 0; i < n; i++) {
size_t size;
loc = vhd->header.loc + i;
if (loc->code == PLAT_CODE_NONE)
continue;
if (loc->data_offset < eob)
continue;
size = vhd_parent_locator_size(loc);
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
if (err) {
err = -err;
buf = NULL;
goto out;
}
err = vhd_seek(vhd, loc->data_offset, SEEK_SET);
if (err)
goto out;
err = vhd_read(vhd, buf, size);
if (err)
goto out;
locators[i] = buf;
}
for (i = 0; i < n; i++) {
off64_t off;
size_t size;
if (!locators[i])
continue;
loc = vhd->header.loc + i;
off = loc->data_offset + size_needed;
size = vhd_parent_locator_size(loc);
if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) {
EPRINTF("%s: shifting locator %d would clobber data\n",
vhd->file, i);
return -EINVAL;
}
err = vhd_seek(vhd, off, SEEK_SET);
if (err)
goto out;
err = vhd_write(vhd, locators[i], size);
if (err)
goto out;
free(locators[i]);
locators[i] = NULL;
loc->data_offset = off;
/* write the new header after writing the new bat */
}
if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) {
vhd->batmap.header.batmap_offset += bat_needed;
/* write the new batmap after writing the new bat */
}
err = 0;
out:
for (i = 0; i < n; i++)
free(locators[i]);
free(locators);
return err;
}
static int
vhd_add_bat_entries(vhd_journal_t *journal, int entries)
{
int i, err;
off64_t off;
vhd_bat_t new_bat;
vhd_context_t *vhd;
uint32_t new_entries;
vhd_batmap_t new_batmap;
uint64_t bat_size, new_bat_size, map_size, new_map_size;
vhd = &journal->vhd;
new_entries = vhd->header.max_bat_size + entries;
bat_size = vhd_bytes_padded(vhd->header.max_bat_size *
sizeof(uint32_t));
new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t));
map_size = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3);
new_map_size = vhd_bytes_padded((new_entries + 7) >> 3);
off = vhd->header.table_offset + new_bat_size;
if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) {
EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes "
"at 0x%08llx would clobber data\n",
vhd->file, new_bat_size, vhd->header.table_offset);
return -EINVAL;
}
if (vhd_has_batmap(vhd)) {
off = vhd->batmap.header.batmap_offset + new_map_size;
if (vhd_check_for_clobber(vhd, off, 0)) {
EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes"
" at 0x%08llx would clobber data\n", vhd->file,
new_map_size, vhd->batmap.header.batmap_offset);
return -EINVAL;
}
}
/* update header */
vhd->header.max_bat_size = new_entries;
err = vhd_write_header(vhd, &vhd->header);
if (err)
return err;
/* update footer */
vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
err = vhd_write_footer(vhd, &vhd->footer);
if (err)
return err;
/* allocate new bat */
err = posix_memalign((void **)&new_bat.bat, VHD_SECTOR_SIZE, new_bat_size);
if (err)
return -err;
new_bat.spb = vhd->bat.spb;
new_bat.entries = new_entries;
memcpy(new_bat.bat, vhd->bat.bat, bat_size);
for (i = vhd->bat.entries; i < new_entries; i++)
new_bat.bat[i] = DD_BLK_UNUSED;
/* write new bat */
err = vhd_write_bat(vhd, &new_bat);
if (err) {
free(new_bat.bat);
return err;
}
/* update in-memory bat */
free(vhd->bat.bat);
vhd->bat = new_bat;
if (!vhd_has_batmap(vhd))
return 0;
/* allocate new batmap */
err = posix_memalign((void **)&new_batmap.map,
VHD_SECTOR_SIZE, new_map_size);
if (err)
return err;
new_batmap.header = vhd->batmap.header;
new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size);
memcpy(new_batmap.map, vhd->batmap.map, map_size);
memset(new_batmap.map + map_size, 0, new_map_size - map_size);
/* write new batmap */
err = vhd_write_batmap(vhd, &new_batmap);
if (err) {
free(new_batmap.map);
return err;
}
/* update in-memory batmap */
free(vhd->batmap.map);
vhd->batmap = new_batmap;
return 0;
}
static int
vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs)
{
int i, err;
off64_t eob, eom;
vhd_context_t *vhd;
vhd_block_t first_block;
uint64_t blocks, size_needed;
uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs;
uint64_t map_needed, map_size, map_avail, map_bytes, map_secs;
vhd = &journal->vhd;
size_needed = 0;
bat_needed = 0;
map_needed = 0;
/* number of vhd blocks to add */
blocks = secs_to_blocks_up(vhd, secs);
/* size in bytes needed for new bat entries */
bat_needed = blocks * sizeof(uint32_t);
map_needed = (blocks >> 3) + 1;
/* available bytes in current bat */
bat_bytes = vhd->header.max_bat_size * sizeof(uint32_t);
bat_secs = secs_round_up_no_zero(bat_bytes);
bat_size = vhd_sectors_to_bytes(bat_secs);
bat_avail = bat_size - bat_bytes;
if (vhd_has_batmap(vhd)) {
/* avaliable bytes in current batmap */
map_bytes = (vhd->header.max_bat_size + 7) >> 3;
map_secs = vhd->batmap.header.batmap_size;
map_size = vhd_sectors_to_bytes(map_secs);
map_avail = map_size - map_bytes;
} else {
map_needed = 0;
map_avail = 0;
}
/* we have enough space already; just extend the bat */
if (bat_needed <= bat_avail && map_needed <= map_avail)
goto add_entries;
/* we need to add new sectors to the bat */
if (bat_needed > bat_avail) {
bat_needed -= bat_avail;
bat_needed = vhd_bytes_padded(bat_needed);
} else
bat_needed = 0;
/* we need to add new sectors to the batmap */
if (map_needed > map_avail) {
map_needed -= map_avail;
map_needed = vhd_bytes_padded(map_needed);
} else
map_needed = 0;
/* how many additional bytes do we need? */
size_needed = bat_needed + map_needed;
/* calculate space between end of headers and beginning of data */
err = vhd_end_of_headers(vhd, &eom);
if (err)
return err;
eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs);
vhd_first_data_block(vhd, &first_block);
/* no blocks allocated; just shift post-bat metadata */
if (!first_block.offset)
goto shift_metadata;
/*
* not enough space --
* move vhd data blocks to the end of the file to make room
*/
do {
off64_t new_off, bm_size, gap_size;
new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd));
/* data region of segment should begin on page boundary */
bm_size = vhd_sectors_to_bytes(vhd->bm_secs);
if ((new_off + bm_size) % 4096) {
gap_size = 4096 - ((new_off + bm_size) % 4096);
err = vhd_write_zeros(journal, new_off, gap_size);
if (err)
return err;
new_off += gap_size;
}
err = vhd_move_block(journal, first_block.block, new_off);
if (err)
return err;
vhd_first_data_block(vhd, &first_block);
} while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset));
TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED);
shift_metadata:
/* shift any metadata after the bat to make room for new bat sectors */
err = vhd_shift_metadata(journal, eob, bat_needed, map_needed);
if (err)
return err;
TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED);
add_entries:
return vhd_add_bat_entries(journal, blocks);
}
static int
vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size)
{
int err;
vhd_context_t *vhd;
uint64_t cur_secs, new_secs;
vhd = &journal->vhd;
cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
new_secs = size << (20 - VHD_SECTOR_SHIFT);
if (cur_secs == new_secs)
return 0;
err = vhd_get_header(vhd);
if (err)
return err;
err = vhd_get_bat(vhd);
if (err)
return err;
if (vhd_has_batmap(vhd)) {
err = vhd_get_batmap(vhd);
if (err)
return err;
}
if (cur_secs > new_secs)
err = vhd_dynamic_shrink(journal, cur_secs - new_secs);
else
err = vhd_dynamic_grow(journal, new_secs - cur_secs);
return err;
}
static int
vhd_util_resize_check_creator(const char *name)
{
int err;
vhd_context_t vhd;
err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT);
if (err) {
printf("error opening %s: %d\n", name, err);
return err;
}
if (!vhd_creator_tapdisk(&vhd)) {
printf("%s not created by xen; resize not supported\n", name);
err = -EINVAL;
}
vhd_close(&vhd);
return err;
}
int
vhd_util_resize(int argc, char **argv)
{
char *name, *jname;
uint64_t size;
int c, err, jerr;
vhd_journal_t journal;
vhd_context_t *vhd;
err = -EINVAL;
size = 0;
name = NULL;
jname = NULL;
optind = 0;
while ((c = getopt(argc, argv, "n:j:s:h")) != -1) {
switch (c) {
case 'n':
name = optarg;
break;
case 'j':
jname = optarg;
break;
case 's':
err = 0;
size = strtoull(optarg, NULL, 10);
break;
case 'h':
default:
goto usage;
}
}
if (err || !name || !jname || argc != optind)
goto usage;
err = vhd_util_resize_check_creator(name);
if (err)
return err;
libvhd_set_log_level(1);
err = vhd_journal_create(&journal, name, jname);
if (err) {
printf("creating journal failed: %d\n", err);
return err;
}
vhd = &journal.vhd;
err = vhd_get_footer(vhd);
if (err)
goto out;
TEST_FAIL_AT(FAIL_RESIZE_BEGIN);
if (vhd_type_dynamic(vhd))
err = vhd_dynamic_resize(&journal, size);
else
err = vhd_fixed_resize(&journal, size);
TEST_FAIL_AT(FAIL_RESIZE_END);
out:
if (err) {
printf("resize failed: %d\n", err);
jerr = vhd_journal_revert(&journal);
} else
jerr = vhd_journal_commit(&journal);
if (jerr) {
printf("closing journal failed: %d\n", jerr);
vhd_journal_close(&journal);
} else
vhd_journal_remove(&journal);
return (err ? : jerr);
usage:
printf("options: <-n name> <-j journal> <-s size (in MB)> [-h help]\n");
return -EINVAL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -