Merge remote branch 'kwolf/for-anthony' into staging

This commit is contained in:
Anthony Liguori 2010-10-26 09:50:58 -05:00
commit 21bcc5907f
15 changed files with 269 additions and 217 deletions

16
block.c
View file

@ -1899,6 +1899,22 @@ int bdrv_snapshot_list(BlockDriverState *bs,
return -ENOTSUP;
}
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
const char *snapshot_name)
{
BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
}
if (!bs->read_only) {
return -EINVAL;
}
if (drv->bdrv_snapshot_load_tmp) {
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
}
return -ENOTSUP;
}
#define NB_SUFFIXES 4
char *get_human_readable_size(char *buf, int buf_size, int64_t size)

View file

@ -211,6 +211,8 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
const char *snapshot_name);
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
char *get_human_readable_size(char *buf, int buf_size, int64_t size);

View file

@ -53,7 +53,8 @@ static AIOPool blkverify_aio_pool = {
.cancel = blkverify_aio_cancel,
};
static void blkverify_err(BlkverifyAIOCB *acb, const char *fmt, ...)
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
const char *fmt, ...)
{
va_list ap;
@ -299,7 +300,7 @@ static void blkverify_verify_readv(BlkverifyAIOCB *acb)
{
ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
if (offset != -1) {
blkverify_err(acb, "contents mismatch in sector %ld",
blkverify_err(acb, "contents mismatch in sector %lld",
acb->sector_num + (offset / BDRV_SECTOR_SIZE));
}
}

View file

@ -28,7 +28,7 @@
#include "block_int.h"
#include "block/qcow2.h"
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
{
BDRVQcowState *s = bs->opaque;
int new_l1_size, new_l1_size2, ret, i;
@ -36,15 +36,22 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
int64_t new_l1_table_offset;
uint8_t data[12];
new_l1_size = s->l1_size;
if (min_size <= new_l1_size)
if (min_size <= s->l1_size)
return 0;
if (new_l1_size == 0) {
new_l1_size = 1;
}
while (min_size > new_l1_size) {
new_l1_size = (new_l1_size * 3 + 1) / 2;
if (exact_size) {
new_l1_size = min_size;
} else {
/* Bump size up to reduce the number of times we have to grow */
new_l1_size = s->l1_size;
if (new_l1_size == 0) {
new_l1_size = 1;
}
while (min_size > new_l1_size) {
new_l1_size = (new_l1_size * 3 + 1) / 2;
}
}
#ifdef DEBUG_ALLOC2
printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
#endif
@ -550,7 +557,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
l1_index = offset >> (s->l2_bits + s->cluster_bits);
if (l1_index >= s->l1_size) {
ret = qcow2_grow_l1_table(bs, l1_index + 1);
ret = qcow2_grow_l1_table(bs, l1_index + 1, false);
if (ret < 0) {
return ret;
}

View file

@ -327,7 +327,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
goto fail;
if (qcow2_grow_l1_table(bs, sn->l1_size) < 0)
if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0)
goto fail;
s->l1_size = sn->l1_size;
@ -418,3 +418,34 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
return s->nb_snapshots;
}
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
{
int i, snapshot_index, l1_size2;
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
if (snapshot_index < 0) {
return -ENOENT;
}
sn = &s->snapshots[snapshot_index];
s->l1_size = sn->l1_size;
l1_size2 = s->l1_size * sizeof(uint64_t);
if (s->l1_table != NULL) {
qemu_free(s->l1_table);
}
s->l1_table_offset = sn->l1_table_offset;
s->l1_table = qemu_mallocz(align_offset(l1_size2, 512));
if (bdrv_pread(bs->file, sn->l1_table_offset,
s->l1_table, l1_size2) != l1_size2) {
return -1;
}
for(i = 0;i < s->l1_size; i++) {
be64_to_cpus(&s->l1_table[i]);
}
return 0;
}

View file

@ -27,6 +27,7 @@
#include <zlib.h>
#include "aes.h"
#include "block/qcow2.h"
#include "qemu-error.h"
/*
Differences with QCOW:
@ -794,28 +795,6 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
return qcow2_update_ext_header(bs, backing_file, backing_fmt);
}
static int get_bits_from_size(size_t size)
{
int res = 0;
if (size == 0) {
return -1;
}
while (size != 1) {
/* Not a power of two */
if (size & 1) {
return -1;
}
size >>= 1;
res++;
}
return res;
}
static int preallocate(BlockDriverState *bs)
{
uint64_t nb_sectors;
@ -871,199 +850,127 @@ static int preallocate(BlockDriverState *bs)
static int qcow_create2(const char *filename, int64_t total_size,
const char *backing_file, const char *backing_format,
int flags, size_t cluster_size, int prealloc)
int flags, size_t cluster_size, int prealloc,
QEMUOptionParameter *options)
{
/* Calulate cluster_bits */
int cluster_bits;
cluster_bits = ffs(cluster_size) - 1;
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
(1 << cluster_bits) != cluster_size)
{
error_report(
"Cluster size must be a power of two between %d and %dk\n",
1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
return -EINVAL;
}
int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits;
int ref_clusters, reftable_clusters, backing_format_len = 0;
int rounded_ext_bf_len = 0;
/*
* Open the image file and write a minimal qcow2 header.
*
* We keep things simple and start with a zero-sized image. We also
* do without refcount blocks or a L1 table for now. We'll fix the
* inconsistency later.
*
* We do need a refcount table because growing the refcount table means
* allocating two new refcount blocks - the seconds of which would be at
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
* size for any qcow2 image.
*/
BlockDriverState* bs;
QCowHeader header;
uint64_t tmp, offset;
uint64_t old_ref_clusters;
QCowCreateState s1, *s = &s1;
QCowExtension ext_bf = {0, 0};
uint8_t* refcount_table;
int ret;
memset(s, 0, sizeof(*s));
ret = bdrv_create_file(filename, options);
if (ret < 0) {
return ret;
}
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0)
return -errno;
ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
if (ret < 0) {
return ret;
}
/* Write the header */
memset(&header, 0, sizeof(header));
header.magic = cpu_to_be32(QCOW_MAGIC);
header.version = cpu_to_be32(QCOW_VERSION);
header.size = cpu_to_be64(total_size * 512);
header_size = sizeof(header);
backing_filename_len = 0;
if (backing_file) {
if (backing_format) {
ext_bf.magic = QCOW_EXT_MAGIC_BACKING_FORMAT;
backing_format_len = strlen(backing_format);
ext_bf.len = backing_format_len;
rounded_ext_bf_len = (sizeof(ext_bf) + ext_bf.len + 7) & ~7;
header_size += rounded_ext_bf_len;
}
header.backing_file_offset = cpu_to_be64(header_size);
backing_filename_len = strlen(backing_file);
header.backing_file_size = cpu_to_be32(backing_filename_len);
header_size += backing_filename_len;
}
header.cluster_bits = cpu_to_be32(cluster_bits);
header.size = cpu_to_be64(0);
header.l1_table_offset = cpu_to_be64(0);
header.l1_size = cpu_to_be32(0);
header.refcount_table_offset = cpu_to_be64(cluster_size);
header.refcount_table_clusters = cpu_to_be32(1);
/* Cluster size */
s->cluster_bits = get_bits_from_size(cluster_size);
if (s->cluster_bits < MIN_CLUSTER_BITS ||
s->cluster_bits > MAX_CLUSTER_BITS)
{
fprintf(stderr, "Cluster size must be a power of two between "
"%d and %dk\n",
1 << MIN_CLUSTER_BITS,
1 << (MAX_CLUSTER_BITS - 10));
return -EINVAL;
}
s->cluster_size = 1 << s->cluster_bits;
header.cluster_bits = cpu_to_be32(s->cluster_bits);
header_size = (header_size + 7) & ~7;
if (flags & BLOCK_FLAG_ENCRYPT) {
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
} else {
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
}
l2_bits = s->cluster_bits - 3;
shift = s->cluster_bits + l2_bits;
l1_size = (((total_size * 512) + (1LL << shift) - 1) >> shift);
offset = align_offset(header_size, s->cluster_size);
s->l1_table_offset = offset;
header.l1_table_offset = cpu_to_be64(s->l1_table_offset);
header.l1_size = cpu_to_be32(l1_size);
offset += align_offset(l1_size * sizeof(uint64_t), s->cluster_size);
/* count how many refcount blocks needed */
#define NUM_CLUSTERS(bytes) \
(((bytes) + (s->cluster_size) - 1) / (s->cluster_size))
ref_clusters = NUM_CLUSTERS(NUM_CLUSTERS(offset) * sizeof(uint16_t));
do {
uint64_t image_clusters;
old_ref_clusters = ref_clusters;
/* Number of clusters used for the refcount table */
reftable_clusters = NUM_CLUSTERS(ref_clusters * sizeof(uint64_t));
/* Number of clusters that the whole image will have */
image_clusters = NUM_CLUSTERS(offset) + ref_clusters
+ reftable_clusters;
/* Number of refcount blocks needed for the image */
ref_clusters = NUM_CLUSTERS(image_clusters * sizeof(uint16_t));
} while (ref_clusters != old_ref_clusters);
s->refcount_table = qemu_mallocz(reftable_clusters * s->cluster_size);
s->refcount_table_offset = offset;
header.refcount_table_offset = cpu_to_be64(offset);
header.refcount_table_clusters = cpu_to_be32(reftable_clusters);
offset += (reftable_clusters * s->cluster_size);
s->refcount_block_offset = offset;
for (i=0; i < ref_clusters; i++) {
s->refcount_table[i] = cpu_to_be64(offset);
offset += s->cluster_size;
ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
if (ret < 0) {
goto out;
}
s->refcount_block = qemu_mallocz(ref_clusters * s->cluster_size);
/* Write an empty refcount table */
refcount_table = qemu_mallocz(cluster_size);
ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
qemu_free(refcount_table);
/* update refcounts */
qcow2_create_refcount_update(s, 0, header_size);
qcow2_create_refcount_update(s, s->l1_table_offset,
l1_size * sizeof(uint64_t));
qcow2_create_refcount_update(s, s->refcount_table_offset,
reftable_clusters * s->cluster_size);
qcow2_create_refcount_update(s, s->refcount_block_offset,
ref_clusters * s->cluster_size);
/* write all the data */
ret = qemu_write_full(fd, &header, sizeof(header));
if (ret != sizeof(header)) {
ret = -errno;
goto exit;
if (ret < 0) {
goto out;
}
bdrv_close(bs);
/*
* And now open the image and make it consistent first (i.e. increase the
* refcount of the cluster that is occupied by the header and the refcount
* table)
*/
BlockDriver* drv = bdrv_find_format("qcow2");
assert(drv != NULL);
ret = bdrv_open(bs, filename, BDRV_O_RDWR | BDRV_O_NO_FLUSH, drv);
if (ret < 0) {
goto out;
}
ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
if (ret < 0) {
goto out;
} else if (ret != 0) {
error_report("Huh, first cluster in empty image is already in use?");
abort();
}
/* Okay, now that we have a valid image, let's give it the right size */
ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
if (ret < 0) {
goto out;
}
/* Want a backing file? There you go.*/
if (backing_file) {
if (backing_format_len) {
char zero[16];
int padding = rounded_ext_bf_len - (ext_bf.len + sizeof(ext_bf));
memset(zero, 0, sizeof(zero));
cpu_to_be32s(&ext_bf.magic);
cpu_to_be32s(&ext_bf.len);
ret = qemu_write_full(fd, &ext_bf, sizeof(ext_bf));
if (ret != sizeof(ext_bf)) {
ret = -errno;
goto exit;
}
ret = qemu_write_full(fd, backing_format, backing_format_len);
if (ret != backing_format_len) {
ret = -errno;
goto exit;
}
if (padding > 0) {
ret = qemu_write_full(fd, zero, padding);
if (ret != padding) {
ret = -errno;
goto exit;
}
}
ret = bdrv_change_backing_file(bs, backing_file, backing_format);
if (ret < 0) {
goto out;
}
ret = qemu_write_full(fd, backing_file, backing_filename_len);
if (ret != backing_filename_len) {
ret = -errno;
goto exit;
}
}
lseek(fd, s->l1_table_offset, SEEK_SET);
tmp = 0;
for(i = 0;i < l1_size; i++) {
ret = qemu_write_full(fd, &tmp, sizeof(tmp));
if (ret != sizeof(tmp)) {
ret = -errno;
goto exit;
}
}
lseek(fd, s->refcount_table_offset, SEEK_SET);
ret = qemu_write_full(fd, s->refcount_table,
reftable_clusters * s->cluster_size);
if (ret != reftable_clusters * s->cluster_size) {
ret = -errno;
goto exit;
}
lseek(fd, s->refcount_block_offset, SEEK_SET);
ret = qemu_write_full(fd, s->refcount_block,
ref_clusters * s->cluster_size);
if (ret != ref_clusters * s->cluster_size) {
ret = -errno;
goto exit;
/* And if we're supposed to preallocate metadata, do that now */
if (prealloc) {
ret = preallocate(bs);
if (ret < 0) {
goto out;
}
}
ret = 0;
exit:
qemu_free(s->refcount_table);
qemu_free(s->refcount_block);
close(fd);
/* Preallocate metadata */
if (ret == 0 && prealloc) {
BlockDriverState *bs;
BlockDriver *drv = bdrv_find_format("qcow2");
bs = bdrv_new("");
bdrv_open(bs, filename, BDRV_O_CACHE_WB | BDRV_O_RDWR, drv);
ret = preallocate(bs);
bdrv_close(bs);
}
out:
bdrv_delete(bs);
return ret;
}
@ -1111,7 +1018,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
}
return qcow_create2(filename, sectors, backing_file, backing_fmt, flags,
cluster_size, prealloc);
cluster_size, prealloc, options);
}
static int qcow_make_empty(BlockDriverState *bs)
@ -1154,7 +1061,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
}
new_l1_size = size_to_l1(s, offset);
ret = qcow2_grow_l1_table(bs, new_l1_size);
ret = qcow2_grow_l1_table(bs, new_l1_size, true);
if (ret < 0) {
return ret;
}
@ -1379,6 +1286,7 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_snapshot_goto = qcow2_snapshot_goto,
.bdrv_snapshot_delete = qcow2_snapshot_delete,
.bdrv_snapshot_list = qcow2_snapshot_list,
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
.bdrv_get_info = qcow_get_info,
.bdrv_save_vmstate = qcow_save_vmstate,

View file

@ -188,7 +188,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
/* qcow2-cluster.c functions */
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size);
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);
void qcow2_l2_cache_reset(BlockDriverState *bs);
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
@ -211,6 +211,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
void qcow2_free_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs);

View file

@ -93,6 +93,8 @@ struct BlockDriver {
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
int (*bdrv_snapshot_list)(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
const char *snapshot_name);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,

View file

@ -65,6 +65,7 @@ static void ide_dma_start(IDEState *s, BlockDriverCompletionFunc *dma_cb);
static void ide_dma_restart(IDEState *s, int is_read);
static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret);
static int ide_handle_rw_error(IDEState *s, int error, int op);
static void ide_flush_cache(IDEState *s);
static void padstr(char *str, const char *src, int len)
{
@ -146,8 +147,8 @@ static void ide_identify(IDEState *s)
put_le16(p + 68, 120);
put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
put_le16(p + 81, 0x16); /* conforms to ata5 */
/* 14=NOP supported, 0=SMART supported */
put_le16(p + 82, (1 << 14) | 1);
/* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */
put_le16(p + 82, (1 << 14) | (1 << 5) | 1);
/* 13=flush_cache_ext,12=flush_cache,10=lba48 */
put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
/* 14=set to 1, 1=SMART self test, 0=SMART error logging */
@ -688,6 +689,8 @@ static void ide_dma_restart_bh(void *opaque)
} else {
ide_sector_write(bmdma_active_if(bm));
}
} else if (bm->status & BM_STATUS_RETRY_FLUSH) {
ide_flush_cache(bmdma_active_if(bm));
}
}
@ -795,12 +798,26 @@ static void ide_flush_cb(void *opaque, int ret)
{
IDEState *s = opaque;
/* XXX: how do we signal I/O errors here? */
if (ret < 0) {
/* XXX: What sector number to set here? */
if (ide_handle_rw_error(s, -ret, BM_STATUS_RETRY_FLUSH)) {
return;
}
}
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s->bus);
}
static void ide_flush_cache(IDEState *s)
{
if (s->bs) {
bdrv_aio_flush(s->bs, ide_flush_cb, s);
} else {
ide_flush_cb(s, 0);
}
}
static inline void cpu_to_ube16(uint8_t *buf, int val)
{
buf[0] = val >> 8;
@ -2031,10 +2048,7 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
break;
case WIN_FLUSH_CACHE:
case WIN_FLUSH_CACHE_EXT:
if (s->bs)
bdrv_aio_flush(s->bs, ide_flush_cb, s);
else
ide_flush_cb(s, 0);
ide_flush_cache(s);
break;
case WIN_STANDBY:
case WIN_STANDBY2:

View file

@ -472,7 +472,8 @@ struct IDEDeviceInfo {
#define BM_STATUS_INT 0x04
#define BM_STATUS_DMA_RETRY 0x08
#define BM_STATUS_PIO_RETRY 0x10
#define BM_STATUS_RETRY_READ 0x20
#define BM_STATUS_RETRY_READ 0x20
#define BM_STATUS_RETRY_FLUSH 0x40
#define BM_CMD_START 0x01
#define BM_CMD_READ 0x08

View file

@ -106,7 +106,13 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
{
VirtIOBlockReq *req = opaque;
virtio_blk_req_complete(req, ret ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK);
if (ret) {
if (virtio_blk_handle_rw_error(req, -ret, 0)) {
return;
}
}
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
}
static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)

View file

@ -28,9 +28,9 @@ STEXI
ETEXI
DEF("convert", img_convert,
"convert [-c] [-f fmt] [-O output_fmt] [-o options] filename [filename2 [...]] output_filename")
"convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
STEXI
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename}
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("info", img_info,

View file

@ -645,14 +645,16 @@ static int img_convert(int argc, char **argv)
const uint8_t *buf1;
BlockDriverInfo bdi;
QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *out_baseimg_param;
char *options = NULL;
const char *snapshot_name = NULL;
fmt = NULL;
out_fmt = "raw";
out_baseimg = NULL;
flags = 0;
for(;;) {
c = getopt(argc, argv, "f:O:B:hce6o:");
c = getopt(argc, argv, "f:O:B:s:hce6o:");
if (c == -1)
break;
switch(c) {
@ -680,6 +682,9 @@ static int img_convert(int argc, char **argv)
case 'o':
options = optarg;
break;
case 's':
snapshot_name = optarg;
break;
}
}
@ -711,6 +716,19 @@ static int img_convert(int argc, char **argv)
total_sectors += bs_sectors;
}
if (snapshot_name != NULL) {
if (bs_n > 1) {
error("No support for concatenating multiple snapshot\n");
ret = -1;
goto out;
}
if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) {
error("Failed to load snapshot\n");
ret = -1;
goto out;
}
}
/* Find driver and parse its options */
drv = bdrv_find_format(out_fmt);
if (!drv) {
@ -752,6 +770,12 @@ static int img_convert(int argc, char **argv)
goto out;
}
/* Get backing file name if -o backing_file was used */
out_baseimg_param = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
if (out_baseimg_param) {
out_baseimg = out_baseimg_param->value.s;
}
/* Check if compression is supported */
if (flags & BLOCK_FLAG_COMPRESS) {
QEMUOptionParameter *encryption =

View file

@ -77,9 +77,9 @@ it doesn't need to be specified separately in this case.
Commit the changes recorded in @var{filename} in its base image.
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename}
@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
Convert the disk image @var{filename} to disk image @var{output_filename}
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
option) or use any format specific options like encryption (@code{-o} option).

View file

@ -1442,6 +1442,44 @@ static const cmdinfo_t alloc_cmd = {
.oneline = "checks if a sector is present in the file",
};
static int
map_f(int argc, char **argv)
{
int64_t offset;
int64_t nb_sectors;
char s1[64];
int num, num_checked;
int ret;
const char *retstr;
offset = 0;
nb_sectors = bs->total_sectors;
do {
num_checked = MIN(nb_sectors, INT_MAX);
ret = bdrv_is_allocated(bs, offset, num_checked, &num);
retstr = ret ? " allocated" : "not allocated";
cvtstr(offset << 9ULL, s1, sizeof(s1));
printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
offset << 9ULL, num, num_checked, retstr, s1, ret);
offset += num;
nb_sectors -= num;
} while(offset < bs->total_sectors);
return 0;
}
static const cmdinfo_t map_cmd = {
.name = "map",
.argmin = 0,
.argmax = 0,
.cfunc = map_f,
.args = "",
.oneline = "prints the allocated areas of a file",
};
static int
close_f(int argc, char **argv)
{
@ -1680,6 +1718,7 @@ int main(int argc, char **argv)
add_command(&length_cmd);
add_command(&info_cmd);
add_command(&alloc_cmd);
add_command(&map_cmd);
add_args_command(init_args_command);
add_check_command(init_check_command);