summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoredison <edison@cloud.com>2010-09-21 19:58:41 -0700
committerKevin Wolf <kwolf@redhat.com>2010-10-22 14:49:35 +0200
commit51ef67270b1d10e1fcf3de7368dccad1ba0bf9d1 (patch)
treea06b3ce03bae7ed77a1e1e9bebc6ead157c7a4e5
parenta58b8d5401b6064d52113f243456115d046bdd12 (diff)
Copy snapshots out of QCOW2 disk
In order to backup snapshots, created from QCOW2 iamge, we want to copy snapshots out of QCOW2 disk to a seperate storage. The following patch adds a new option in "qemu-img": qemu-img convert -f qcow2 -O qcow2 -s snapshot_name src_img bck_img. Right now, it only supports to copy the full snapshot, delta snapshot is on the way. Changes from V1: all the comments from Kevin are addressed: Add read-only checking Fix coding style Change the name from bdrv_snapshot_load to bdrv_snapshot_load_tmp Signed-off-by: Disheng Su <edison@cloud.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-rw-r--r--block.c16
-rw-r--r--block.h2
-rw-r--r--block/qcow2-snapshot.c31
-rw-r--r--block/qcow2.c1
-rw-r--r--block/qcow2.h1
-rw-r--r--block_int.h2
-rw-r--r--qemu-img-cmds.hx4
-rw-r--r--qemu-img.c19
-rw-r--r--qemu-img.texi4
9 files changed, 75 insertions, 5 deletions
diff --git a/block.c b/block.c
index a19374dfc..985d0b7e3 100644
--- a/block.c
+++ b/block.c
@@ -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)
diff --git a/block.h b/block.h
index 5f6438010..a4facf2fa 100644
--- a/block.h
+++ b/block.h
@@ -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);
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index 8dd5df0bc..aacf35782 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -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;
+}
diff --git a/block/qcow2.c b/block/qcow2.c
index 45ed073db..b816d8733 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1286,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,
diff --git a/block/qcow2.h b/block/qcow2.h
index add710ba7..2d22e5ec4 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -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);
diff --git a/block_int.h b/block_int.h
index e8e7156c9..87e60b859 100644
--- a/block_int.h
+++ b/block_int.h
@@ -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,
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 6d3e5f8e6..6c7176f8b 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -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,
diff --git a/qemu-img.c b/qemu-img.c
index 578b8ebe8..d4a3b4ebc 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -646,13 +646,14 @@ static int img_convert(int argc, char **argv)
BlockDriverInfo bdi;
QEMUOptionParameter *param = NULL, *create_options = NULL;
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 +681,9 @@ static int img_convert(int argc, char **argv)
case 'o':
options = optarg;
break;
+ case 's':
+ snapshot_name = optarg;
+ break;
}
}
@@ -711,6 +715,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) {
diff --git a/qemu-img.texi b/qemu-img.texi
index c1b1f2717..1b90ddbcf 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -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).