mirror of https://gitee.com/openkylin/qemu.git
block: Add bdrv_change_backing_file
Introduce the functions needed to change the backing file of an image. The function is implemented for qcow2. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
b783e409bf
commit
756e6736a1
20
block.c
20
block.c
|
@ -597,6 +597,26 @@ int bdrv_commit(BlockDriverState *bs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return values:
|
||||
* 0 - success
|
||||
* -EINVAL - backing format specified, but no file
|
||||
* -ENOSPC - can't update the backing file because no space is left in the
|
||||
* image file header
|
||||
* -ENOTSUP - format driver doesn't support changing the backing file
|
||||
*/
|
||||
int bdrv_change_backing_file(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (drv->bdrv_change_backing_file != NULL) {
|
||||
return drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
|
||||
size_t size)
|
||||
{
|
||||
|
|
2
block.h
2
block.h
|
@ -83,6 +83,8 @@ int64_t bdrv_getlength(BlockDriverState *bs);
|
|||
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
|
||||
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
|
||||
int bdrv_commit(BlockDriverState *bs);
|
||||
int bdrv_change_backing_file(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt);
|
||||
void bdrv_register(BlockDriver *bdrv);
|
||||
|
||||
/* async block I/O */
|
||||
|
|
101
block/qcow2.c
101
block/qcow2.c
|
@ -658,6 +658,105 @@ static void qcow_close(BlockDriverState *bs)
|
|||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the variable length parts of the qcow2 header, i.e. the backing file
|
||||
* name and all extensions. qcow2 was not designed to allow such changes, so if
|
||||
* we run out of space (we can only use the first cluster) this function may
|
||||
* fail.
|
||||
*
|
||||
* Returns 0 on success, -errno in error cases.
|
||||
*/
|
||||
static int qcow2_update_ext_header(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt)
|
||||
{
|
||||
size_t backing_file_len = 0;
|
||||
size_t backing_fmt_len = 0;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowExtension ext_backing_fmt = {0, 0};
|
||||
int ret;
|
||||
|
||||
/* Backing file format doesn't make sense without a backing file */
|
||||
if (backing_fmt && !backing_file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Prepare the backing file format extension if needed */
|
||||
if (backing_fmt) {
|
||||
ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt));
|
||||
ext_backing_fmt.magic = cpu_to_be32(QCOW_EXT_MAGIC_BACKING_FORMAT);
|
||||
backing_fmt_len = ((sizeof(ext_backing_fmt)
|
||||
+ strlen(backing_fmt) + 7) & ~7);
|
||||
}
|
||||
|
||||
/* Check if we can fit the new header into the first cluster */
|
||||
if (backing_file) {
|
||||
backing_file_len = strlen(backing_file);
|
||||
}
|
||||
|
||||
size_t header_size = sizeof(QCowHeader) + backing_file_len
|
||||
+ backing_fmt_len;
|
||||
|
||||
if (header_size > s->cluster_size) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Rewrite backing file name and qcow2 extensions */
|
||||
size_t ext_size = header_size - sizeof(QCowHeader);
|
||||
uint8_t buf[ext_size];
|
||||
size_t offset = 0;
|
||||
size_t backing_file_offset = 0;
|
||||
|
||||
if (backing_file) {
|
||||
if (backing_fmt) {
|
||||
int padding = backing_fmt_len -
|
||||
(sizeof(ext_backing_fmt) + strlen(backing_fmt));
|
||||
|
||||
memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt));
|
||||
offset += sizeof(ext_backing_fmt);
|
||||
|
||||
memcpy(buf + offset, backing_fmt, strlen(backing_fmt));
|
||||
offset += strlen(backing_fmt);
|
||||
|
||||
memset(buf + offset, 0, padding);
|
||||
offset += padding;
|
||||
}
|
||||
|
||||
memcpy(buf + offset, backing_file, backing_file_len);
|
||||
backing_file_offset = sizeof(QCowHeader) + offset;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(s->hd, sizeof(QCowHeader), buf, ext_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Update header fields */
|
||||
uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset);
|
||||
uint32_t be_backing_file_size = cpu_to_be32(backing_file_len);
|
||||
|
||||
ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, backing_file_offset),
|
||||
&be_backing_file_offset, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, backing_file_size),
|
||||
&be_backing_file_size, sizeof(uint32_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_change_backing_file(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt)
|
||||
{
|
||||
return qcow2_update_ext_header(bs, backing_file, backing_fmt);
|
||||
}
|
||||
|
||||
static int get_bits_from_size(size_t size)
|
||||
{
|
||||
int res = 0;
|
||||
|
@ -1137,6 +1236,8 @@ static BlockDriver bdrv_qcow2 = {
|
|||
.bdrv_save_vmstate = qcow_save_vmstate,
|
||||
.bdrv_load_vmstate = qcow_load_vmstate,
|
||||
|
||||
.bdrv_change_backing_file = qcow2_change_backing_file,
|
||||
|
||||
.create_options = qcow_create_options,
|
||||
.bdrv_check = qcow_check,
|
||||
};
|
||||
|
|
|
@ -98,6 +98,9 @@ struct BlockDriver {
|
|||
int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
|
||||
int (*bdrv_change_backing_file)(BlockDriverState *bs,
|
||||
const char *backing_file, const char *backing_fmt);
|
||||
|
||||
/* removable device specific */
|
||||
int (*bdrv_is_inserted)(BlockDriverState *bs);
|
||||
int (*bdrv_media_changed)(BlockDriverState *bs);
|
||||
|
|
Loading…
Reference in New Issue