* [PATCH] kvm tools: Add read-only support for QCOW2 images
@ 2011-04-19 21:07 Pekka Enberg
[not found] ` <BANLkTi=Oi1MigCncsejnFSF7uZ=Y4CebCw@mail.gmail.com>
0 siblings, 1 reply; 2+ messages in thread
From: Pekka Enberg @ 2011-04-19 21:07 UTC (permalink / raw)
To: kvm
Cc: Pekka Enberg, Asias He, Cyrill Gorcunov, Prasad Joshi,
Sasha Levin, Ingo Molnar
This patch extends the QCOW1 format to also support QCOW2 images as specified
by the following document:
http://people.gnome.org/~markmc/qcow-image-format.html
Cc: Asias He <asias.hejun@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Prasad Joshi <prasadjoshi124@gmail.com>
Cc: Sasha Levin <levinsasha928@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
---
tools/kvm/include/kvm/qcow.h | 42 ++++++++++-
tools/kvm/qcow.c | 177 +++++++++++++++++++++++++++++++++---------
2 files changed, 181 insertions(+), 38 deletions(-)
diff --git a/tools/kvm/include/kvm/qcow.h b/tools/kvm/include/kvm/qcow.h
index 4be2597..afd776d 100644
--- a/tools/kvm/include/kvm/qcow.h
+++ b/tools/kvm/include/kvm/qcow.h
@@ -4,9 +4,17 @@
#include <linux/types.h>
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+
#define QCOW1_VERSION 1
+#define QCOW2_VERSION 2
+
+#define QCOW1_OFLAG_COMPRESSED (1LL << 63)
+
+#define QCOW1_OFLAG_MASK QCOW1_OFLAG_COMPRESSED
-#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+#define QCOW2_OFLAG_COPIED (1LL << 63)
+#define QCOW2_OFLAG_COMPRESSED (1LL << 62)
+#define QCOW2_OFLAG_MASK (QCOW2_OFLAG_COPIED|QCOW2_OFLAG_COMPRESSED)
struct qcow_table {
u32 table_size;
@@ -19,7 +27,16 @@ struct qcow {
int fd;
};
-struct qcow1_header {
+struct qcow_header {
+ u64 size; /* in bytes */
+ u64 l1_table_offset;
+ u32 l1_size;
+ u8 cluster_bits;
+ u8 l2_bits;
+ uint64_t oflag_mask;
+};
+
+struct qcow1_header_disk {
u32 magic;
u32 version;
@@ -36,6 +53,27 @@ struct qcow1_header {
u64 l1_table_offset;
};
+struct qcow2_header_disk {
+ u32 magic;
+ u32 version;
+
+ u64 backing_file_offset;
+ u32 backing_file_size;
+
+ u32 cluster_bits;
+ u64 size; /* in bytes */
+ u32 crypt_method;
+
+ u32 l1_size;
+ u64 l1_table_offset;
+
+ u64 refcount_table_offset;
+ u32 refcount_table_clusters;
+
+ u32 nb_snapshots;
+ u64 snapshots_offset;
+};
+
struct disk_image *qcow_probe(int fd);
#endif /* KVM__QCOW_H */
diff --git a/tools/kvm/qcow.c b/tools/kvm/qcow.c
index 11f6454..6d847d0 100644
--- a/tools/kvm/qcow.c
+++ b/tools/kvm/qcow.c
@@ -17,28 +17,28 @@
static inline u64 get_l1_index(struct qcow *q, u64 offset)
{
- struct qcow1_header *header = q->header;
+ struct qcow_header *header = q->header;
return offset >> (header->l2_bits + header->cluster_bits);
}
static inline u64 get_l2_index(struct qcow *q, u64 offset)
{
- struct qcow1_header *header = q->header;
+ struct qcow_header *header = q->header;
return (offset >> (header->cluster_bits)) & ((1 << header->l2_bits)-1);
}
static inline u64 get_cluster_offset(struct qcow *q, u64 offset)
{
- struct qcow1_header *header = q->header;
+ struct qcow_header *header = q->header;
return offset & ((1 << header->cluster_bits)-1);
}
static ssize_t qcow1_read_cluster(struct qcow *q, u64 offset, void *dst, u32 dst_len)
{
- struct qcow1_header *header = q->header;
+ struct qcow_header *header = q->header;
struct qcow_table *table = &q->table;
u64 *l2_table = NULL;
u64 l2_table_offset;
@@ -64,7 +64,7 @@ static ssize_t qcow1_read_cluster(struct qcow *q, u64 offset, void *dst, u32 dst
if (length > dst_len)
length = dst_len;
- l2_table_offset = table->l1_table[l1_idx];
+ l2_table_offset = table->l1_table[l1_idx] & ~header->oflag_mask;
if (!l2_table_offset)
goto zero_cluster;
@@ -81,7 +81,7 @@ static ssize_t qcow1_read_cluster(struct qcow *q, u64 offset, void *dst, u32 dst
if (l2_idx >= l2_table_size)
goto out_error;
- clust_start = be64_to_cpu(l2_table[l2_idx]);
+ clust_start = be64_to_cpu(l2_table[l2_idx]) & ~header->oflag_mask;
if (!clust_start)
goto zero_cluster;
@@ -105,7 +105,7 @@ static int qcow1_read_sector(struct disk_image *self, uint64_t sector,
void *dst, uint32_t dst_len)
{
struct qcow *q = self->priv;
- struct qcow1_header *header = q->header;
+ struct qcow_header *header = q->header;
char *buf = dst;
u64 offset;
u32 nr_read;
@@ -157,12 +157,11 @@ struct disk_image_operations qcow1_disk_ops = {
static int qcow_read_l1_table(struct qcow *q)
{
- struct qcow1_header *header = q->header;
+ struct qcow_header *header = q->header;
struct qcow_table *table = &q->table;
u64 i;
- table->table_size = header->size / ((1 << header->l2_bits) *
- (1 << header->cluster_bits));
+ table->table_size = header->l1_size;
table->l1_table = calloc(table->table_size, sizeof(u64));
if (!table->l1_table)
@@ -178,25 +177,128 @@ static int qcow_read_l1_table(struct qcow *q)
return 0;
}
+static void *qcow2_read_header(int fd)
+{
+ struct qcow2_header_disk f_header;
+ struct qcow_header *header;
+
+ header = malloc(sizeof(struct qcow_header));
+ if (!header)
+ return NULL;
+
+ if (pread_in_full(fd, &f_header, sizeof(struct qcow2_header_disk), 0) < 0)
+ return NULL;
+
+ be32_to_cpus(&f_header.magic);
+ be32_to_cpus(&f_header.version);
+ be64_to_cpus(&f_header.backing_file_offset);
+ be32_to_cpus(&f_header.backing_file_size);
+ be32_to_cpus(&f_header.cluster_bits);
+ be64_to_cpus(&f_header.size);
+ be32_to_cpus(&f_header.crypt_method);
+ be32_to_cpus(&f_header.l1_size);
+ be64_to_cpus(&f_header.l1_table_offset);
+ be64_to_cpus(&f_header.refcount_table_offset);
+ be32_to_cpus(&f_header.refcount_table_clusters);
+ be32_to_cpus(&f_header.nb_snapshots);
+ be64_to_cpus(&f_header.snapshots_offset);
+
+ *header = (struct qcow_header) {
+ .size = f_header.size,
+ .l1_table_offset = f_header.l1_table_offset,
+ .l1_size = f_header.l1_size,
+ .cluster_bits = f_header.cluster_bits,
+ .l2_bits = f_header.cluster_bits - 3,
+ .oflag_mask = QCOW2_OFLAG_MASK,
+ };
+
+ return header;
+}
+
+static struct disk_image *qcow2_probe(int fd)
+{
+ struct qcow *q;
+ struct qcow_header *h;
+ struct disk_image *disk_image;
+
+ q = calloc(1, sizeof(struct qcow));
+ if (!q)
+ goto error;
+
+ q->fd = fd;
+
+ h = q->header = qcow2_read_header(fd);
+ if (!h)
+ goto error;
+
+ if (qcow_read_l1_table(q) < 0)
+ goto error;
+
+ disk_image = disk_image__new(fd, h->size, &qcow1_disk_ops);
+ if (!disk_image)
+ goto error;
+ disk_image->priv = q;
+
+ return disk_image;
+error:
+ if (!q)
+ return NULL;
+
+ free(q->table.l1_table);
+ free(q->header);
+ free(q);
+
+ return NULL;
+}
+
+static bool qcow2_check_image(int fd)
+{
+ struct qcow2_header_disk f_header;
+
+ if (pread_in_full(fd, &f_header, sizeof(struct qcow2_header_disk), 0) < 0)
+ return false;
+
+ be32_to_cpus(&f_header.magic);
+ be32_to_cpus(&f_header.version);
+
+ if (f_header.magic != QCOW_MAGIC)
+ return false;
+
+ if (f_header.version != QCOW2_VERSION)
+ return false;
+
+ return true;
+}
+
static void *qcow1_read_header(int fd)
{
- struct qcow1_header *header;
+ struct qcow1_header_disk f_header;
+ struct qcow_header *header;
- header = malloc(sizeof(struct qcow1_header));
+ header = malloc(sizeof(struct qcow_header));
if (!header)
return NULL;
- if (pread_in_full(fd, header, sizeof(struct qcow1_header), 0) < 0)
+ if (pread_in_full(fd, &f_header, sizeof(struct qcow1_header_disk), 0) < 0)
return NULL;
- be32_to_cpus(&header->magic);
- be32_to_cpus(&header->version);
- be64_to_cpus(&header->backing_file_offset);
- be32_to_cpus(&header->backing_file_size);
- be32_to_cpus(&header->mtime);
- be64_to_cpus(&header->size);
- be32_to_cpus(&header->crypt_method);
- be64_to_cpus(&header->l1_table_offset);
+ be32_to_cpus(&f_header.magic);
+ be32_to_cpus(&f_header.version);
+ be64_to_cpus(&f_header.backing_file_offset);
+ be32_to_cpus(&f_header.backing_file_size);
+ be32_to_cpus(&f_header.mtime);
+ be64_to_cpus(&f_header.size);
+ be32_to_cpus(&f_header.crypt_method);
+ be64_to_cpus(&f_header.l1_table_offset);
+
+ *header = (struct qcow_header) {
+ .size = f_header.size,
+ .l1_table_offset = f_header.l1_table_offset,
+ .l1_size = f_header.size / ((1 << f_header.l2_bits) * (1 << f_header.cluster_bits)),
+ .cluster_bits = f_header.cluster_bits,
+ .l2_bits = f_header.l2_bits,
+ .oflag_mask = QCOW1_OFLAG_MASK,
+ };
return header;
}
@@ -204,7 +306,7 @@ static void *qcow1_read_header(int fd)
static struct disk_image *qcow1_probe(int fd)
{
struct qcow *q;
- struct qcow1_header *h;
+ struct qcow_header *h;
struct disk_image *disk_image;
q = calloc(1, sizeof(struct qcow));
@@ -237,29 +339,32 @@ error:
return NULL;
}
-static int qcow_check_image(int fd)
+static bool qcow1_check_image(int fd)
{
- struct qcow1_header header;
+ struct qcow1_header_disk f_header;
- if (pread_in_full(fd, &header, sizeof(struct qcow1_header), 0) < 0)
- return -1;
+ if (pread_in_full(fd, &f_header, sizeof(struct qcow1_header_disk), 0) < 0)
+ return false;
- be32_to_cpus(&header.magic);
- be32_to_cpus(&header.version);
+ be32_to_cpus(&f_header.magic);
+ be32_to_cpus(&f_header.version);
- if (header.magic != QCOW_MAGIC)
- return -1;
+ if (f_header.magic != QCOW_MAGIC)
+ return false;
- if (header.version != QCOW1_VERSION)
- return -1;
+ if (f_header.version != QCOW1_VERSION)
+ return false;
- return 0;
+ return true;
}
struct disk_image *qcow_probe(int fd)
{
- if (qcow_check_image(fd) < 0)
- return NULL;
+ if (qcow1_check_image(fd))
+ return qcow1_probe(fd);
+
+ if (qcow2_check_image(fd))
+ return qcow2_probe(fd);
- return qcow1_probe(fd);
+ return NULL;
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] kvm tools: Add read-only support for QCOW2 images
[not found] ` <BANLkTi=Oi1MigCncsejnFSF7uZ=Y4CebCw@mail.gmail.com>
@ 2011-04-20 15:57 ` Pekka Enberg
0 siblings, 0 replies; 2+ messages in thread
From: Pekka Enberg @ 2011-04-20 15:57 UTC (permalink / raw)
To: Prasad Joshi; +Cc: kvm, Asias He, Cyrill Gorcunov, Sasha Levin, Ingo Molnar
[-- Attachment #1: Type: TEXT/PLAIN, Size: 3796 bytes --]
On Tue, 19 Apr 2011, Prasad Joshi wrote:
> On Tue, Apr 19, 2011 at 10:07 PM, Pekka Enberg <penberg@kernel.org> wrote:
> This patch extends the QCOW1 format to also support QCOW2 images as specified
> by the following document:
>
> http://people.gnome.org/~markmc/qcow-image-format.html
>
> Cc: Asias He <asias.hejun@gmail.com>
> Cc: Cyrill Gorcunov <gorcunov@gmail.com>
> Cc: Prasad Joshi <prasadjoshi124@gmail.com>
> Cc: Sasha Levin <levinsasha928@gmail.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Signed-off-by: Pekka Enberg <penberg@kernel.org>
> ---
> tools/kvm/include/kvm/qcow.h | 42 ++++++++++-
> tools/kvm/qcow.c | 177 +++++++++++++++++++++++++++++++++---------
> 2 files changed, 181 insertions(+), 38 deletions(-)
>
> diff --git a/tools/kvm/include/kvm/qcow.h b/tools/kvm/include/kvm/qcow.h
> index 4be2597..afd776d 100644
> --- a/tools/kvm/include/kvm/qcow.h
> +++ b/tools/kvm/include/kvm/qcow.h
> @@ -4,9 +4,17 @@
> #include <linux/types.h>
>
> #define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
> +
> #define QCOW1_VERSION 1
> +#define QCOW2_VERSION 2
> +
> +#define QCOW1_OFLAG_COMPRESSED (1LL << 63)
> +
> +#define QCOW1_OFLAG_MASK QCOW1_OFLAG_COMPRESSED
>
> -#define QCOW_OFLAG_COMPRESSED (1LL << 63)
> +#define QCOW2_OFLAG_COPIED (1LL << 63)
> +#define QCOW2_OFLAG_COMPRESSED (1LL << 62)
> +#define QCOW2_OFLAG_MASK (QCOW2_OFLAG_COPIED|QCOW2_OFLAG_COMPRESSED)
>
> struct qcow_table {
> u32 table_size;
> @@ -19,7 +27,16 @@ struct qcow {
> int fd;
> };
>
> -struct qcow1_header {
> +struct qcow_header {
> + u64 size; /* in bytes */
> + u64 l1_table_offset;
> + u32 l1_size;
> + u8 cluster_bits;
> + u8 l2_bits;
> + uint64_t oflag_mask;
> +};
> +
> +struct qcow1_header_disk {
> u32 magic;
> u32 version;
>
> @@ -36,6 +53,27 @@ struct qcow1_header {
> u64 l1_table_offset;
> };
>
> +struct qcow2_header_disk {
> + u32 magic;
> + u32 version;
> +
> + u64 backing_file_offset;
> + u32 backing_file_size;
> +
> + u32 cluster_bits;
> + u64 size; /* in bytes */
> + u32 crypt_method;
> +
> + u32 l1_size;
> + u64 l1_table_offset;
> +
> + u64 refcount_table_offset;
> + u32 refcount_table_clusters;
> +
> + u32 nb_snapshots;
> + u64 snapshots_offset;
> +};
>
> IMHO, as we start adding other features of QCOW, the two structures qcow2_header_disk and qcow_header might eventually become the same.
No, the point of 'struct qcow2_header_disk' is to map to the on-disk
representation. 'struct qcow_header' is the in-memory version of the data.
> + disk_image = disk_image__new(fd, h->size, &qcow1_disk_ops);
>
>
> qcow1_disk_ops can be changed to qcow_disk_ops.
Sure, there's more qcow1 prefixes that need fixing now as well.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2011-04-20 15:57 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-19 21:07 [PATCH] kvm tools: Add read-only support for QCOW2 images Pekka Enberg
[not found] ` <BANLkTi=Oi1MigCncsejnFSF7uZ=Y4CebCw@mail.gmail.com>
2011-04-20 15:57 ` Pekka Enberg
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.