kvm tools: Add read-only support for QCOW2 images

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>
diff --git a/qcow.c b/qcow.c
index 11f6454..6d847d0 100644
--- a/qcow.c
+++ b/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 @@
 	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 @@
 	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 @@
 		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 @@
 
 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 @@
 	return 0;
 }
 
-static void *qcow1_read_header(int fd)
+static void *qcow2_read_header(int fd)
 {
-	struct qcow1_header *header;
+	struct qcow2_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 qcow2_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.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_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 qcow1_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.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 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 @@
 	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);
 
-	return qcow1_probe(fd);
+	if (qcow2_check_image(fd))
+		return qcow2_probe(fd);
+
+	return NULL;
 }