emu-img 是 QEMU 虚拟机管理工具套件中的一个工具,用于创建、转换、修改和管理磁盘映像文件。
常用命令
- 创建映像
qemu-img create 命令创建一个新的磁盘映像文件。qemu-img create -f qcow2 mydisk.img 10G
- 查看磁盘映像信息
使用 qemu-img info 命令查看磁盘映像的详细信息。qemu-img info mydisk.img
- 转换磁盘映像格式
qemu-img convert 命令将磁盘映像从一个格式转换为另一个格式。qemu-img convert -f raw -O qcow2 myrawdisk.img myqcow2disk.img
- 调整磁盘映像大小
qemu-img resize 命令调整磁盘映像的大小。注意,这只会更改映像的元数据,不会实际写入新数据。qemu-img resize mydisk.img +2G
实际使用
目前对resize进行验证,首先进入虚机查看分区信息:
Filesystem Size Used Avail Use% Mounted on
/dev/root 3.0G 2.4G 627M 80% /
tmpfs 3.8G 460K 3.8G 1% /dev
tmpfs 3.8G 4.0K 3.8G 1% /mnt
/dev/block/vde1 0.9G 321M 671M 33% /vendor
/dev/block/vdc 63G 61M 62G 1% /data
在虚机外部,执行resize操作:
bash-4.2# qemu-img resize userdata-qemu.img.qcow2 +5G
Image resized.
最后查看实际空间信息,可以看到data分区已经增加。
/dev/root 3.0G 2.4G 627M 80% /
tmpfs 3.8G 456K 3.8G 1% /dev
tmpfs 3.8G 4.0K 3.8G 1% /mnt
/dev/block/vde1 0.9G 321M 671M 33% /vendor
/dev/block/vdc 68G 484M 67G 1% /data
代码相关
create、info、convert和reszie分别对应qemu-img.c中的img_create
、img_info
、img_convert
和img_resize
。
例如示例中的resize实现如下:
static int img_resize(int argc, char **argv)
{
Error *err = NULL;
int c, ret, relative;
const char *filename, *fmt, *size;
int64_t n, total_size, current_size;
bool quiet = false;
BlockBackend *blk = NULL;
PreallocMode prealloc = PREALLOC_MODE_OFF;
QemuOpts *param;
static QemuOptsList resize_options = {
.name = "resize_options",
.head = QTAILQ_HEAD_INITIALIZER(resize_options.head),
.desc = {
{
.name = BLOCK_OPT_SIZE,
.type = QEMU_OPT_SIZE,
.help = "Virtual disk size"
}, {
/* end of list */
}
},
};
bool image_opts = false;
bool shrink = false;
/* Remove size from argv manually so that negative numbers are not treated
* as options by getopt. */
if (argc < 3) {
error_exit("Not enough arguments");
return 1;
}
size = argv[--argc];
/* Parse getopt arguments */
fmt = NULL;
for(;;) {
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
{"preallocation", required_argument, 0, OPTION_PREALLOCATION},
{"shrink", no_argument, 0, OPTION_SHRINK},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, ":f:hq",
long_options, NULL);
if (c == -1) {
break;
}
switch(c) {
case ':':
missing_argument(argv[optind - 1]);
break;
case '?':
unrecognized_option(argv[optind - 1]);
break;
case 'h':
help();
break;
case 'f':
fmt = optarg;
break;
case 'q':
quiet = true;
break;
case OPTION_OBJECT:
user_creatable_process_cmdline(optarg);
break;
case OPTION_IMAGE_OPTS:
image_opts = true;
break;
case OPTION_PREALLOCATION:
prealloc = qapi_enum_parse(&PreallocMode_lookup, optarg,
PREALLOC_MODE__MAX, NULL);
if (prealloc == PREALLOC_MODE__MAX) {
error_report("Invalid preallocation mode '%s'", optarg);
return 1;
}
break;
case OPTION_SHRINK:
shrink = true;
break;
}
}
if (optind != argc - 1) {
error_exit("Expecting image file name and size");
}
filename = argv[optind++];
/* Choose grow, shrink, or absolute resize mode */
switch (size[0]) {
case '+':
relative = 1;
size++;
break;
case '-':
relative = -1;
size++;
break;
default:
relative = 0;
break;
}
/* Parse size */
param = qemu_opts_create(&resize_options, NULL, 0, &error_abort);
if (!qemu_opt_set(param, BLOCK_OPT_SIZE, size, &err)) {
error_report_err(err);
ret = -1;
qemu_opts_del(param);
goto out;
}
n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0);
qemu_opts_del(param);
blk = img_open(image_opts, filename, fmt,
BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet,
false);
if (!blk) {
ret = -1;
goto out;
}
current_size = blk_getlength(blk);
if (current_size < 0) {
error_report("Failed to inquire current image length: %s",
strerror(-current_size));
ret = -1;
goto out;
}
if (relative) {
total_size = current_size + n * relative;
} else {
total_size = n;
}
if (total_size <= 0) {
error_report("New image size must be positive");
ret = -1;
goto out;
}
if (total_size <= current_size && prealloc != PREALLOC_MODE_OFF) {
error_report("Preallocation can only be used for growing images");
ret = -1;
goto out;
}
if (total_size < current_size && !shrink) {
error_report("Use the --shrink option to perform a shrink operation.");
warn_report("Shrinking an image will delete all data beyond the "
"shrunken image's end. Before performing such an "
"operation, make sure there is no important data there.");
ret = -1;
goto out;
}
/*
* The user expects the image to have the desired size after
* resizing, so pass @exact=true. It is of no use to report
* success when the image has not actually been resized.
*/
ret = blk_truncate(blk, total_size, true, prealloc, 0, &err);
if (!ret) {
qprintf(quiet, "Image resized.\n");
} else {
error_report_err(err);
}
out:
blk_unref(blk);
if (ret) {
return 1;
}
return 0;
}
这里核心调用的就是blk_truncate(blk, total_size, true, prealloc, 0, &err)
函数。