All of lore.kernel.org
 help / color / mirror / Atom feed
* [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.