⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vhd-util-resize.c

📁 xen source 推出最新的VHD操作工具VHD-UTIL 实现源码,超强
💻 C
📖 第 1 页 / 共 2 页
字号:
	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 + -