* [PATCH] kvm tools: Add QCOW version 1 read-only support
@ 2011-04-14 17:21 Pekka Enberg
0 siblings, 0 replies; only message in thread
From: Pekka Enberg @ 2011-04-14 17:21 UTC (permalink / raw)
To: kvm
Cc: Prasad Joshi, Asias He, Cyrill Gorcunov, Ingo Molnar, Kevin Wolf,
Sasha Levin, Stefan Hajnoczi, Pekka Enberg
From: Prasad Joshi <prasadjoshi124@gmail.com>
The patch only implements the basic read-only support for QCOW version 1
images. Many of the QCOW features are not implemented:
- write
- image creation
- snapshot
- copy-on-write
- encryption
The code is based on the following QCOW 1 image format specification:
http://people.gnome.org/~markmc/qcow-image-format-version-1.html
Cc: Asias He <asias.hejun@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Kevin Wolf <kwolf@redhat.com>
Cc: Sasha Levin <levinsasha928@gmail.com>
Cc: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Prasad Joshi <prasadjoshi124@gmail.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
---
FYI, I dropped commit 910c791 ("kvm tools: Add QCOW version 1 read/write
support") from the GIT repository and re-implemented read-only QCOW1 support
from scratch.
tools/kvm/Makefile | 1 +
tools/kvm/disk-image.c | 5 +
tools/kvm/include/kvm/qcow.h | 41 +++++++
tools/kvm/include/linux/byteorder.h | 7 +
tools/kvm/include/linux/types.h | 19 +++
tools/kvm/qcow.c | 227 +++++++++++++++++++++++++++++++++++
6 files changed, 300 insertions(+), 0 deletions(-)
create mode 100644 tools/kvm/include/kvm/qcow.h
create mode 100644 tools/kvm/include/linux/byteorder.h
create mode 100644 tools/kvm/qcow.c
diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 6895113..28dd369 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -34,6 +34,7 @@ OBJS += util/strbuf.o
OBJS += kvm-help.o
OBJS += kvm-cmd.o
OBJS += kvm-run.o
+OBJS += qcow.o
DEPS := $(patsubst %.o,%.d,$(OBJS))
diff --git a/tools/kvm/disk-image.c b/tools/kvm/disk-image.c
index 5d0f342..fff71b4 100644
--- a/tools/kvm/disk-image.c
+++ b/tools/kvm/disk-image.c
@@ -1,6 +1,7 @@
#include "kvm/disk-image.h"
#include "kvm/read-write.h"
+#include "kvm/qcow.h"
#include "kvm/util.h"
#include <sys/types.h>
@@ -131,6 +132,10 @@ struct disk_image *disk_image__open(const char *filename, bool readonly)
if (fd < 0)
return NULL;
+ self = qcow_probe(fd);
+ if (self)
+ return self;
+
self = raw_image__probe(fd, readonly);
if (self)
return self;
diff --git a/tools/kvm/include/kvm/qcow.h b/tools/kvm/include/kvm/qcow.h
new file mode 100644
index 0000000..4be2597
--- /dev/null
+++ b/tools/kvm/include/kvm/qcow.h
@@ -0,0 +1,41 @@
+#ifndef KVM__QCOW_H
+#define KVM__QCOW_H
+
+#include <linux/types.h>
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW1_VERSION 1
+
+#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+
+struct qcow_table {
+ u32 table_size;
+ u64 *l1_table;
+};
+
+struct qcow {
+ void *header;
+ struct qcow_table table;
+ int fd;
+};
+
+struct qcow1_header {
+ u32 magic;
+ u32 version;
+
+ u64 backing_file_offset;
+ u32 backing_file_size;
+ u32 mtime;
+
+ u64 size; /* in bytes */
+
+ u8 cluster_bits;
+ u8 l2_bits;
+ u32 crypt_method;
+
+ u64 l1_table_offset;
+};
+
+struct disk_image *qcow_probe(int fd);
+
+#endif /* KVM__QCOW_H */
diff --git a/tools/kvm/include/linux/byteorder.h b/tools/kvm/include/linux/byteorder.h
new file mode 100644
index 0000000..c490de8
--- /dev/null
+++ b/tools/kvm/include/linux/byteorder.h
@@ -0,0 +1,7 @@
+#ifndef __BYTE_ORDER_H__
+#define __BYTE_ORDER_H__
+
+#include <asm/byteorder.h>
+#include <linux/byteorder/generic.h>
+
+#endif
diff --git a/tools/kvm/include/linux/types.h b/tools/kvm/include/linux/types.h
index 8b608e7..efd8519 100644
--- a/tools/kvm/include/linux/types.h
+++ b/tools/kvm/include/linux/types.h
@@ -27,4 +27,23 @@ typedef __s16 s16;
typedef __u8 u8;
typedef __s8 s8;
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
#endif /* LINUX_TYPES_H */
diff --git a/tools/kvm/qcow.c b/tools/kvm/qcow.c
new file mode 100644
index 0000000..c4e3e48
--- /dev/null
+++ b/tools/kvm/qcow.c
@@ -0,0 +1,227 @@
+#include "kvm/qcow.h"
+
+#include "kvm/disk-image.h"
+#include "kvm/read-write.h"
+#include "kvm/util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <linux/byteorder.h>
+#include <linux/types.h>
+
+static inline uint64_t sect_to_l1_offset(struct qcow *q, uint64_t offset)
+{
+ struct qcow1_header *header = q->header;
+
+ return offset >> (header->l2_bits + header->cluster_bits);
+}
+
+static inline uint64_t sect_to_l2_offset(struct qcow *q, uint64_t offset)
+{
+ struct qcow1_header *header = q->header;
+
+ return (offset >> (header->cluster_bits)) & ((1 << header->l2_bits)-1);
+}
+
+static inline uint64_t sect_to_cluster_offset(struct qcow *q, uint64_t offset)
+{
+ struct qcow1_header *header = q->header;
+
+ return offset & ((1 << header->cluster_bits)-1);
+}
+
+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;
+ uint64_t l2_table_offset;
+ uint64_t l2_table_size;
+ uint64_t clust_offset;
+ uint64_t clust_start;
+ uint64_t *l2_table;
+ uint64_t l1_idx;
+ uint64_t l2_idx;
+ uint64_t offset;
+
+ offset = sector << SECTOR_SHIFT;
+ if (offset >= header->size)
+ goto out_error;
+
+ l1_idx = sect_to_l1_offset(self->priv, offset);
+
+ if (l1_idx >= q->table.table_size)
+ goto out_error;
+
+ l2_table_offset = be64_to_cpu(q->table.l1_table[l1_idx]);
+ if (!l2_table_offset)
+ goto zero_sector;
+
+ l2_table_size = 1 << header->l2_bits;
+
+ l2_table = calloc(l2_table_size, sizeof(uint64_t));
+ if (!l2_table)
+ goto out_error;
+
+ if (pread_in_full(q->fd, l2_table, sizeof(uint64_t) * l2_table_size, l2_table_offset) < 0)
+ goto out_error_free_l2;
+
+ l2_idx = sect_to_l2_offset(self->priv, offset);
+
+ if (l2_idx >= l2_table_size)
+ goto out_error_free_l2;
+
+ clust_start = be64_to_cpu(l2_table[l2_idx]);
+
+ if (!clust_start)
+ goto zero_sector;
+
+ clust_offset = sect_to_cluster_offset(self->priv, offset);
+
+ if (pread_in_full(q->fd, dst, dst_len, clust_start + clust_offset) < 0)
+ goto out_error_free_l2;
+
+ free(l2_table);
+
+ return 0;
+
+zero_sector:
+ memset(dst, 0, dst_len);
+
+ return 0;
+
+out_error_free_l2:
+ free(l2_table);
+out_error:
+ return -1;
+}
+
+static int qcow1_write_sector(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len)
+{
+ return -1;
+}
+
+static void qcow1_disk_close(struct disk_image *self)
+{
+ struct qcow *q;
+
+ if (!self)
+ return;
+
+ q = self->priv;
+
+ free(q->header);
+ free(q);
+}
+
+struct disk_image_operations qcow1_disk_ops = {
+ .read_sector = qcow1_read_sector,
+ .write_sector = qcow1_write_sector,
+ .close = qcow1_disk_close
+};
+
+static int qcow_read_l1_table(struct qcow *q)
+{
+ struct qcow1_header *header = q->header;
+
+ q->table.table_size = header->size / ((1 << header->l2_bits) * (1 << header->cluster_bits));
+
+ q->table.l1_table = calloc(q->table.table_size, sizeof(uint64_t));
+ if (!q->table.l1_table)
+ return -1;
+
+ if (pread_in_full(q->fd, q->table.l1_table, sizeof(uint64_t) * q->table.table_size, header->l1_table_offset) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void *qcow1_read_header(int fd)
+{
+ struct qcow1_header *header;
+
+ header = malloc(sizeof(struct qcow1_header));
+ if (!header)
+ return NULL;
+
+ if (pread_in_full(fd, header, sizeof(struct qcow1_header), 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);
+
+ return header;
+}
+
+static struct disk_image *qcow1_probe(int fd)
+{
+ struct qcow *q;
+ struct qcow1_header *h;
+ struct disk_image *disk_image;
+
+ q = calloc(1, sizeof(struct qcow));
+ if (!q)
+ goto error;
+
+ q->fd = fd;
+
+ h = q->header = qcow1_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->header);
+ free(q);
+
+ return NULL;
+}
+
+static int qcow_check_image(int fd)
+{
+ struct qcow1_header header;
+
+ if (pread_in_full(fd, &header, sizeof(struct qcow1_header), 0) < 0)
+ return -1;
+
+ be32_to_cpus(&header.magic);
+ be32_to_cpus(&header.version);
+
+ if (header.magic != QCOW_MAGIC)
+ return -1;
+
+ if (header.version != QCOW1_VERSION)
+ return -1;
+
+ return 0;
+}
+
+struct disk_image *qcow_probe(int fd)
+{
+ if (qcow_check_image(fd) < 0)
+ return NULL;
+
+ return qcow1_probe(fd);
+}
--
1.7.0.4
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2011-04-14 17:21 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-14 17:21 [PATCH] kvm tools: Add QCOW version 1 read-only support 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.