# HG changeset patch
# User Ryan Grimm <grimm@xxxxxxxxxx>
# Date 1156190590 18000
# Node ID 77a518e8f44d1c0c89dcb93e5a3713da6c5e6131
# Parent 6a7ea5a3554309b4e37e3e8bcbd6a11b8cdc4386
qcow plugin for dm-userspace userspace tool
Signed-off-by: Ryan Grimm <grimm@xxxxxxxxxx>
Signed-off-by: Dan Smith <danms@xxxxxxxxxx>
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/configure.in
--- a/tools/cowd/configure.in Mon Aug 21 15:03:09 2006 -0500
+++ b/tools/cowd/configure.in Mon Aug 21 15:03:10 2006 -0500
@@ -95,11 +95,13 @@ AC_SUBST(GLOBAL_CFLAGS)
AC_CONFIG_FILES([Makefile
plugins/Makefile
+ plugins/qcow/Makefile
plugins/dscow/Makefile])
# This just makes it easier to run cowd from the source directory
# for testing
mkdir -p lib
+ln -sf ../plugins/qcow/.libs/libcowd_qcow.so.0 lib/libcowd_qcow.so
ln -sf ../plugins/dscow/.libs/libcowd_dscow.so.0 lib/libcowd_dscow.so
ln -sf ../plugins/dscow/.libs/libcowd_dscow.la lib/libcowd_dscow.la
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/Makefile.am
--- a/tools/cowd/plugins/Makefile.am Mon Aug 21 15:03:09 2006 -0500
+++ b/tools/cowd/plugins/Makefile.am Mon Aug 21 15:03:10 2006 -0500
@@ -1,1 +1,1 @@ SUBDIRS = dscow
-SUBDIRS = dscow
+SUBDIRS = dscow qcow
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/qcow/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/Makefile.am Mon Aug 21 15:03:10 2006 -0500
@@ -0,0 +1,6 @@
+lib_LTLIBRARIES = libcowd_qcow.la
+libcowd_qcow_la_CFLAGS = -I../.. -I../../../../module @GLOBAL_CFLAGS@
+libcowd_qcow_la_SOURCES = qcow_plugin.c qcow_ops.c qcow.h qcow_ops.h
+
+#noinst_PROGRAMS = qcow_test
+#qcow_test_SOURCES = qcow_test.c qcow_ops.c ../../util.c
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/qcow/qcow.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow.h Mon Aug 21 15:03:10 2006 -0500
@@ -0,0 +1,167 @@
+#ifndef __QCOW_H
+#define __QCOW_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#define NAME "qcow: "
+#define L2_CACHE_SIZE 16
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xFB)
+
+typedef uint64_t qcow_te ;
+
+struct qcow_header {
+ uint32_t magic;
+ uint32_t version;
+ uint64_t backing_filename_offset;
+ uint32_t backing_filename_size;
+ uint32_t mtime;
+ uint64_t size;
+ uint8_t cluster_bits;
+ uint8_t l2_bits;
+ uint32_t crypto_method;
+ uint64_t l1_table_offset;
+};
+
+struct l1_entry {
+ int dirty;
+ uint64_t loc_on_disk;
+ qcow_te *table;
+};
+
+struct l2_cache {
+ uint64_t l1_index;
+ struct l1_entry *l2;
+ uint32_t hits;
+};
+
+struct qcow {
+ struct qcow_header header;
+ struct l1_entry *table;
+ int l1_dirty;
+
+ char *filename;
+
+ struct l2_cache *l2_cache[L2_CACHE_SIZE];
+ int l2_cache_counter;
+
+ uint64_t next_avail_block;
+
+ /* This is the offset we use for the file. The problem is,
+ the kernel module expects to remap whole aligned blocks.
+ Since we can interleave L2 blocks with differently-sized */
+ uint64_t offset;
+
+ int fd;
+
+};
+
+static inline uint64_t qcow_num_l1(struct qcow_header *h)
+{
+ uint64_t nonl1 = (1 << h->cluster_bits) * (1 << h->l2_bits);
+
+ if (h->size % nonl1)
+ return (h->size / nonl1) + 1;
+ else
+ return (h->size / nonl1);
+}
+
+static inline uint64_t qcow_num_l2(struct qcow_header *h)
+{
+ return 1 << h->l2_bits;
+}
+
+static inline uint64_t qcow_block_size(struct qcow_header *h)
+{
+ return 1 << h->cluster_bits;
+}
+
+static inline uint64_t qcow_cmask(struct qcow_header *h)
+{
+ return (1 << h->cluster_bits) - 1;
+}
+
+static inline uint64_t qcow_l2mask(struct qcow_header *h)
+{
+ return ((1 << h->l2_bits) - 1) << h->cluster_bits;
+}
+
+static inline uint64_t qcow_l1mask(struct qcow_header *h)
+{
+ return ~(qcow_cmask(h) | qcow_l2mask(h));
+}
+
+/* These are i386, little-endian.
+ * Clearly this needs to be generalized.
+ */
+static inline uint64_t ntohll(uint64_t value)
+{
+ uint32_t a, b;
+
+ a = value >> 32;
+ b = value & 0xFFFFFFFF;
+
+ return (((uint64_t)ntohl(b)) << 32) | ntohl(a);
+}
+
+static inline uint64_t htonll(uint64_t value)
+{
+ uint32_t a, b;
+
+ a = value >> 32;
+ b = value & 0xFFFFFFFF;
+
+ return (((uint64_t)htonl(b)) << 32) | htonl(a);
+}
+
+static inline uint64_t qcow_get_l1_entry(struct qcow *qcow, uint64_t sector)
+{
+ uint64_t entry;
+
+ entry = sector & qcow_l1mask(&qcow->header);
+ entry = entry >>
+ (qcow->header.l2_bits + qcow->header.cluster_bits);
+
+// printf("L1 entry: %llx %llx\n",
+// sector & qcow_l1mask(&qcow->header),
+// entry);
+
+ return entry;
+}
+
+static inline uint64_t qcow_get_l2_entry(struct qcow *qcow, uint64_t sector)
+{
+ return (sector & qcow_l2mask(&qcow->header)) >>
+ (qcow->header.cluster_bits);
+}
+
+static inline uint64_t qcow_make_te(struct qcow *qcow,
+ uint64_t l1_entry,
+ uint64_t l2_entry,
+ uint64_t index)
+{
+ uint64_t te = 0;
+
+ te |= (l1_entry << (qcow->header.l2_bits + qcow->header.cluster_bits));
+ te |= (l2_entry << (qcow->header.cluster_bits));
+ te |= (index & qcow_cmask(&qcow->header));
+
+ return te;
+}
+
+static inline uint64_t qcow_calc_offset(struct qcow *qcow,
+ uint64_t sector)
+{
+ return sector % qcow_block_size(&qcow->header);
+}
+
+static inline uint64_t qcow_align_to_block(struct qcow *qcow,
+ uint64_t sector)
+{
+ return sector / qcow_block_size(&qcow->header);
+}
+
+
+
+#endif
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/qcow/qcow_ops.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_ops.c Mon Aug 21 15:03:10 2006 -0500
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License. See the file COPYING in the main directory
+ * of this archive for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <netinet/in.h> /* for endian ops */
+
+#include "qcow.h"
+#include "qcow_ops.h"
+
+#define OPS_DEBUG 0
+
+#define LOG_ABOVE (128 << 20)
+
+/*
+ * Update our pointer to the end of the highest-block, if appropriate.
+ * This is used to allocate new blocks and tables, because it's the
+ * end of the last chunk of real data.
+ */
+static int qcow_seen_block(struct qcow *qcow,
+ qcow_te sector,
+ qcow_te block_size)
+{
+ uint64_t end;
+
+ end = sector + block_size;
+
+ if (end > qcow->header.size) {
+ fprintf(stderr,
+ "*** ERROR: Saw block %llu beyond end of %llu\n",
+ end, qcow->header.size);
+ return -1;
+ }
+
+ if (qcow->next_avail_block < end) {
+ qcow->next_avail_block = end;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void qcow_init_qcow(struct qcow *qcow)
+{
+ qcow->table = NULL;
+ qcow->filename = NULL;
+ qcow->l1_dirty = 0;
+ qcow->next_avail_block = 0;
+}
+
+/*
+ * Read in the qcow header from @fd, storing it in @header
+ * If @w!=0, then write instead. (Although not yet :)
+ */
+int qcow_rw_header(int fd, struct qcow_header *header, int w)
+{
+ int ret;
+ loff_t offset = 0;
+
+ if (dio_lseek(fd, offset, SEEK_SET) != offset) {
+ fprintf(stderr, "Offset was %lli instead of 0\n", offset);
+ perror("dio_lseek");
+ return 0;
+ }
+
+ if (w)
+ ret = dio_write(fd, header, sizeof(*header));
+ else
+ ret = dio_read(fd, header, sizeof(*header));
+
+ if (ret == 0) {
+ fprintf(stderr, "Read 0 bytes for header (%i)!\n",
+ sizeof(*header));
+ return 0;
+ } else if (ret < 0) {
+ perror("qcow-header");
+ return 0;
+ }
+
+ /* Convert endianess */
+ header->magic = ntohl(header->magic);
+ header->version = ntohl(header->version);
+ header->backing_filename_offset =
+ ntohll(header->backing_filename_offset);
+ header->backing_filename_size = ntohl(header->backing_filename_size);
+ header->mtime = ntohl(header->mtime);
+ header->size = ntohll(header->size);
+ header->crypto_method = ntohl(header->crypto_method);
+ header->l1_table_offset = ntohll(header->l1_table_offset);
+
+ return 1;
+}
+
+/*
+ * Read the backing filename information
+ */
+int qcow_read_backing_info(struct qcow *qcow)
+{
+ int ret;
+
+ qcow->filename = (char *)malloc(qcow->header.backing_filename_size+1);
+
+ ret = dio_lseek(qcow->fd,
+ qcow->header.backing_filename_offset,
+ SEEK_SET);
+ if (ret != qcow->header.backing_filename_offset) {
+ fprintf(stderr, "lseek to %llu\n",
+ qcow->header.backing_filename_offset);
+ perror("qcow-lseek");
+ return 0;
+ }
+
+ ret = dio_read(qcow->fd, qcow->filename,
+ qcow->header.backing_filename_size);
+ if (ret != qcow->header.backing_filename_size) {
+ fprintf(stderr, "qcow-read: EOF while reading filename\n");
+ return 0;
+ }
+ qcow->filename[qcow->header.backing_filename_size] = '\0';
+
+ return 1;
+}
+
+/*
+ * Allocate memory for the L1 table
+ */
+int qcow_init_from_header(struct qcow *qcow)
+{
+ qcow->table = (struct l1_entry *)calloc(qcow_num_l1(&qcow->header),
+ sizeof(struct l1_entry));
+
+ if (qcow->table == NULL) {
+ fprintf(stderr,
+ NAME "*** failed to calloc %llu x %i for table\n",
+ qcow_num_l1(&qcow->header), sizeof(qcow_te*));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Write @table to @fd:@pos, @length entries
+ */
+static int qcow_write_table(int fd, uint64_t pos,
+ uint64_t length, qcow_te *table)
+{
+ qcow_te *disk_table;
+ int64_t i;
+
+ disk_table = (qcow_te *)calloc(length,
+ sizeof(qcow_te));
+ if (!disk_table)
+ goto bad;
+
+ for (i=0; i < length; i++)
+ disk_table[i] = htonll(table[i]);
+
+ if (dio_lseek(fd, pos, SEEK_SET) != pos) {
+ perror("write_table lseek");
+ goto bad;
+ }
+
+ if (OPS_DEBUG)
+ fprintf(stderr, "Writing %llu bytes @ %llu\n",
+ length * sizeof(qcow_te), pos);
+
+ i = dio_write(fd, disk_table, length * sizeof(qcow_te));
+
+ if (i != (length * sizeof(qcow_te))) {
+ fprintf(stderr,
+ "Short write: %lli/%llu\n",
+ i,
+ length * sizeof(qcow_te));
+
+ goto bad;
+ } else if (i < 0) {
+ perror("write");
+ goto bad;
+ }
+
+ return 1;
+
+ bad:
+ return 0;
+}
+
+/*
+ * Read @table from @fd:@pos, @length entries
+ */
+static int qcow_read_table(int fd, uint64_t pos,
+ uint64_t length, qcow_te *table)
+{
+ qcow_te *disk_table;
+ int64_t i;
+
+ disk_table = (qcow_te *)calloc(length,
+ sizeof(qcow_te));
+ if (!disk_table)
+ goto bad;
+
+ if (dio_lseek(fd, pos, SEEK_SET) != pos) {
+ perror("lseek");
+ goto bad;
+ }
+
+ i = dio_read(fd, disk_table, length * sizeof(qcow_te));
+
+ if (i != (length * sizeof(qcow_te))) {
+ fprintf(stderr,
+ "Short read: %lli/%llu\n",
+ i,
+ length * sizeof(qcow_te));
+ goto bad;
+ } else if (i < 0) {
+ perror("read");
+ goto bad;
+ }
+
+ for (i=0; i < length; i++)
+ table[i] = htonll(disk_table[i]);
+
+ return 1;
+
+ bad:
+ return 0;
+}
+
+static int qcow_load_l2(struct qcow *qcow, qcow_te index)
+{
+ int ret;
+ struct l1_entry *entry;
+
+ entry = &qcow->table[index];
+
+ if (entry->loc_on_disk == 0) {
+ fprintf(stderr,
+ "*** ERROR: Trying to load non-existent L2 %llu\n",
+ index);
+ return 0;
+ }
+
+ if (entry->table != NULL)
+ fprintf(stderr,
+ "*** WARNING: Reloading L2 table %llu\n",
+ index);
+
+ entry->table = calloc(qcow_num_l2(&qcow->header),
+ sizeof(qcow_te));
+
+ if (entry->table == NULL)
+ return 0;
+
+ ret = qcow_read_table(qcow->fd,
+ entry->loc_on_disk,
+ qcow_num_l2(&qcow->header),
+ entry->table);
+
+ return ret;
+}
+
+/*
+ * Allocate a new L2 table on disk
+ */
+static int alloc_new_l2_on_disk(struct qcow *qcow, uint64_t l1_entry)
+{
+ uint64_t num, count, j;
+ struct l1_entry *entry;
+
+ if (qcow_num_l2(&qcow->header) < qcow_block_size(&qcow->header))
+ num = qcow_block_size(&qcow->header) /
+ qcow_num_l2(&qcow->header);
+
+ else
+ num = 1;
+
+ qcow->l1_dirty = 1;
+
+ entry = &qcow->table[l1_entry];
+
+ entry->dirty = 1;
+ entry->table = calloc(qcow_num_l2(&qcow->header), sizeof(qcow_te));
+ entry->loc_on_disk = qcow->next_avail_block;
+ qcow_seen_block(qcow, entry->loc_on_disk,
+ qcow_num_l2(&qcow->header) * sizeof(qcow_te));
+
+ printf("New L2 entry %llu @ %llu\n",
+ l1_entry, entry->loc_on_disk);
+
+ /* We try to distribute the extra to other non-allocated L2s,
+ so that we don't get too far away from our block
+ alignment */
+ count = num - 1;
+
+ /* Right now, we ignore the case that we might've allocated
+ too much space and didn't use it all. In the normal case
+ of 512-byte blocks, this isn't an issue */
+ return 1; /* FIXME */
+
+ j = 0;
+ while ((count > 0) && (j < qcow_num_l1(&qcow->header))) {
+ if (qcow->table[j].loc_on_disk == 0) {
+ qcow->table[j].loc_on_disk = qcow->next_avail_block;
+ qcow_seen_block(qcow,
+ qcow->table[j].loc_on_disk,
+ qcow_num_l2(&qcow->header) *
+ sizeof(qcow_te));
+ count--;
+ }
+ j++;
+ }
+
+ return 1;
+}
+
+/* Write out any dirty L2 tables, and the L1 table if needed */
+int qcow_write_tables(struct qcow *qcow)
+{
+ uint64_t l1_size;
+ uint64_t l2_size;
+ qcow_te *l1_table;
+ uint64_t i;
+ int ret;
+
+ l1_size = qcow_num_l1(&qcow->header);
+ l2_size = qcow_num_l2(&qcow->header);
+
+ for (i=0; i < l1_size; i++) {
+
+ if (qcow->table[i].dirty && qcow->table[i].loc_on_disk) {
+ qcow->table[i].dirty = 0;
+ if (OPS_DEBUG)
+ fprintf(stderr, "writing L2 %llu @ %llu\n",
+ i, qcow->table[i].loc_on_disk);
+ ret = qcow_write_table(qcow->fd,
+ qcow->table[i].loc_on_disk,
+ l2_size,
+ qcow->table[i].table);
+
+ if (ret != 1) {
+ fprintf(stderr,
+ NAME "failed to write L2 %i\n", i);
+ return 0;
+ }
+ }
+ }
+
+ if (qcow->l1_dirty) {
+ l1_table = (qcow_te *)calloc(l1_size,
+ sizeof(qcow_te));
+
+ for (i=0; i < l1_size; i++) {
+ l1_table[i] = qcow->table[i].loc_on_disk;
+ if (l1_table[i])
+ printf("Writing L1 entry %llu: %llu\n",
+ i, l1_table[i]);
+ }
+ ret = qcow_write_table(qcow->fd, qcow->header.l1_table_offset,
+ l1_size, l1_table);
+
+ free(l1_table);
+
+ qcow->l1_dirty = 0;
+
+ if (ret != 1) {
+ fprintf(stderr, NAME "failed to write L1\n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Read in the L1 table. We read L2 tables as needed based on the
+ * information in the l1.
+ */
+int qcow_read_tables(struct qcow *qcow)
+{
+ int i;
+ uint64_t numL1;
+ qcow_te *disk_table;
+
+ numL1 = qcow_num_l1(&qcow->header);
+
+ disk_table = (qcow_te *)calloc(numL1,
+ sizeof(qcow_te));
+
+ dio_lseek(qcow->fd, qcow->header.l1_table_offset, SEEK_SET);
+ dio_read(qcow->fd, disk_table, numL1 * sizeof(qcow_te));
+
+ for (i=0; i < numL1; i++) {
+
+ qcow->table[i].table = NULL;
+ qcow->table[i].dirty = 0;
+
+ qcow->table[i].loc_on_disk = ntohll(disk_table[i]);
+ }
+
+ if (OPS_DEBUG)
+ qcow_print_header(qcow);
+
+ return 1;
+}
+
+
+/* Map the block of logical to physical */
+int qcow_make_mapping(struct qcow *qcow, uint64_t logical, uint64_t physical)
+{
+ uint64_t l1_entry;
+ uint64_t l2_entry;
+
+ l1_entry = qcow_get_l1_entry(qcow, logical);
+ l2_entry = qcow_get_l2_entry(qcow, logical);
+
+ if (l1_entry >= qcow_num_l1(&qcow->header)) {
+ fprintf(stderr, "*** ERROR: L1 of %llu >= %llu, max\n",
+ l1_entry,
+ qcow_num_l1(&qcow->header));
+ fprintf(stderr, " Offending new map: %llu -> %llu\n",
+ logical, physical);
+ return 0;
+ }
+
+ if (l2_entry >= qcow_num_l2(&qcow->header)) {
+ fprintf(stderr, "*** ERRROR: L2 of %llu >= %llu, max\n",
+ l2_entry,
+ qcow_num_l2(&qcow->header));
+ return 0;
+ }
+
+ if (logical >= LOG_ABOVE) {
+ printf("Making mapping for %llu -> %llu\n"
+ " l1e: %llu l2e: %llu\n",
+ logical, physical, l1_entry, l2_entry);
+ }
+
+ if (qcow->table[l1_entry].table == NULL) {
+ if (qcow->table[l1_entry].loc_on_disk == 0) {
+
+ if (!alloc_new_l2_on_disk(qcow, l1_entry)) {
+ fprintf(stderr,
+ "Failed to alloc table for %llu\n",
+ l1_entry);
+ return 0;
+ }
+
+ qcow->l1_dirty = 1;
+ } else {
+ if (!qcow_load_l2(qcow, l1_entry)) {
+ fprintf(stderr,
+ "Failed to load L2 %llu\n",
+ l1_entry);
+ return 0;
+ }
+ }
+ }
+
+ qcow->table[l1_entry].table[l2_entry] = physical;
+ qcow->table[l1_entry].dirty = 1;
+
+ return 1;
+}
+
+/*
+ * Make a new mapping for @logical by selecting the next available
+ * block
+ */
+uint64_t qcow_make_new_mapping(struct qcow *qcow, uint64_t logical)
+{
+ uint64_t new_block;
+
+ new_block = qcow->next_avail_block;
+
+ /* Align to the next 512-byte boundary */
+ /* FIXME: Do we want to leak space like this? */
+ if (new_block % 512) {
+ new_block += (512 - (new_block % 512));
+ }
+
+ qcow_seen_block(qcow, new_block, qcow_block_size(&qcow->header));
+
+ if (qcow_make_mapping(qcow, logical, new_block))
+ return new_block;
+ else
+ return 0; /* Block 0 is error, because it contains the
+ header and L1, etc*/
+
+}
+
+/* Return the location that @logical maps to, 0 if unmapped */
+uint64_t qcow_get_mapping(struct qcow *qcow, uint64_t logical)
+{
+ uint64_t l1_entry;
+ uint64_t l2_entry;
+ uint64_t physical;
+ qcow_te *l2_table;
+
+ l1_entry = qcow_get_l1_entry(qcow, logical);
+ l2_entry = qcow_get_l2_entry(qcow, logical);
+
+ if (l1_entry >= qcow_num_l1(&qcow->header)) {
+ fprintf(stderr, "*** ERROR: L1 of %llu >= %llu, max\n",
+ l1_entry,
+ qcow_num_l1(&qcow->header));
+ fprintf(stderr, " Offending map request: %llu (%llx)\n",
+ logical, logical);
+ fprintf(stderr, " l1_entry: %llu\n", l1_entry);
+ fprintf(stderr, " l2_entry: %llu\n", l2_entry);
+
+ return 0;
+ }
+
+ if (l2_entry >= qcow_num_l2(&qcow->header)) {
+ fprintf(stderr, "*** ERROR: L2 of %llu >= %llu, max\n",
+ l2_entry,
+ qcow_num_l2(&qcow->header));
+ return 0;
+ }
+
+ if (qcow->table[l1_entry].table == NULL) {
+ if (qcow->table[l1_entry].loc_on_disk == 0) {
+ /* Not mapped if no L2 table */
+ return 0;
+ } else {
+ /* Need to load it in */
+ if (!qcow_load_l2(qcow, l1_entry))
+ return 0;
+ }
+ }
+
+ l2_table = qcow->table[l1_entry].table;
+
+ physical = l2_table[l2_entry];
+
+ if (OPS_DEBUG && 0) {
+ printf("L1 entry: %llu\n", l1_entry);
+ printf("L2 entry: %llu\n", l2_entry);
+ printf("Physical Block: %llu (%llx)\n", physical, physical);
+ printf("\n");
+ }
+
+ return physical;
+}
+
+/*
+ * Return 1 if any of the tables need flushing, 0 otherwise
+ */
+int qcow_is_anything_dirty(struct qcow *qcow)
+{
+ uint64_t i;
+
+ if (qcow->l1_dirty)
+ return 1;
+
+ for (i=0; i < qcow_num_l1(&qcow->header); i++) {
+ if (qcow->table[i].dirty)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Print out the header in human-readable format
+ */
+void qcow_print_header(struct qcow *qcow)
+{
+ printf("=== QCOW HEADER ===\n");
+ printf("Magic: %x (should be %x)\n", qcow->header.magic,
+ QCOW_MAGIC);
+ printf("Version: %x\n", qcow->header.version);
+ printf("File Offset: %llu\n", qcow->header.backing_filename_offset);
+ printf("File Size: %u\n", qcow->header.backing_filename_size);
+ printf("Mod Time: %u\n", qcow->header.mtime);
+ printf("Size: %llu\n", qcow->header.size);
+ printf("Cluster Bits: %hhu\n", qcow->header.cluster_bits);
+ printf("L2 Bits: %hhu\n", qcow->header.l2_bits);
+ printf("Crypto Method: %x\n", qcow->header.crypto_method);
+ printf("L1 Table @: %llu\n", qcow->header.l1_table_offset);
+ printf("Backing File: %s\n", qcow->filename);
+
+ printf("\nCalculated Information:\n");
+
+ printf("Next Avail: %llu\n", qcow->next_avail_block);
+ printf("Cluster Mask: %016llx\n", qcow_cmask(&qcow->header));
+ printf("L2 Mask: %016llx\n", qcow_l2mask(&qcow->header));
+ printf("L1 Mask: %016llx\n", qcow_l1mask(&qcow->header));
+ printf("Num L1: %llu\n", qcow_num_l1(&qcow->header));
+ printf("Num L2: %llu\n", qcow_num_l2(&qcow->header));
+}
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/qcow/qcow_ops.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_ops.h Mon Aug 21 15:03:10 2006 -0500
@@ -0,0 +1,15 @@
+#ifndef __QCOW_OPS_H
+#define __QCOW_OPS_H
+
+#include <stdint.h>
+
+#include "qcow.h"
+
+int qcow_rw_header(int fd, struct qcow_header *header, int write);
+int qcow_read_backing_info(struct qcow *qcow);
+int qcow_read_l1_table(struct qcow *qcow);
+uint64_t qcow_get_mapping(struct qcow *qcow, uint64_t logical);
+
+void qcow_print_header(struct qcow *qcow);
+
+#endif
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/qcow/qcow_plugin.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_plugin.c Mon Aug 21 15:03:10 2006 -0500
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License. See the file COPYING in the main directory
+ * of this archive for more details.
+ *
+ */
+
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdint.h>
+
+#include <cowd_plugin.h>
+
+#include <libdevmapper.h>
+
+#include "qcow.h"
+#include "qcow_ops.h"
+
+#define MAX_PATH 256
+#define ERR_LEN 256
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+
+char errmsg[ERR_LEN];
+
+struct qcow_private {
+ struct qcow qcow;
+ int init;
+
+ char base_path[MAX_PATH];
+ char qcow_path[MAX_PATH];
+
+ char base_dev[MAX_PATH];
+ char qcow_dev[MAX_PATH];
+
+ dev_t base_dev_t;
+ dev_t qcow_dev_t;
+
+ unsigned long qcow_size;
+
+ int debug;
+};
+
+static dev_t *qcow_get_devs(struct cow_device *dev, int *count)
+{
+ struct qcow_private *prv = dev->plugin_private;
+ struct stat s;
+ dev_t *devs;
+
+ devs = malloc(sizeof(*dev) * 2);
+ if (!devs) {
+ count = 0;
+ return NULL;
+ }
+
+ devs[0] = prv->base_dev_t;
+ devs[1] = prv->qcow_dev_t;
+
+ *count = 2;
+ return devs;
+}
+
+/*
+ * We use the loop driver to make the base and cow files available to
+ * the kernel. The problem is, we can't grow the cow file underneath
+ * the loop device. This function "inflates" the cow file to the
+ * maximum size it could be before we hook it up to the loop driver.
+ * This isn't *too* bad because it's just a sparse file.
+ */
+static int qcow_inflate(struct qcow_private *prv)
+{
+ struct qcow_header header;
+ int fd;
+ uint64_t offset;
+
+ fd = open(prv->qcow_path, O_RDWR | O_LARGEFILE);
+ if (fd < 0) {
+ perror("open");
+ return PLUGIN_FAIL;
+ }
+
+ if (read(fd, &header, sizeof(header)) != sizeof(header)) {
+ perror("read");
+ return PLUGIN_FAIL;
+ }
+
+ header.size = ntohll(header.size);
+
+ offset = lseek64(fd, header.size, SEEK_SET);
+
+ if (prv->debug)
+ printf("## Inflating %s to %llu (%llu)\n",
+ prv->qcow_path, offset, header.size);
+
+ if (offset != header.size) {
+ fprintf(stderr, "Failed to lseek to %llu\n", header.size);
+ return PLUGIN_FAIL;
+ }
+
+ if (write(fd, &fd, 1) != 1) {
+ perror("write");
+ close(fd);
+ return PLUGIN_FAIL;
+ }
+
+ close(fd);
+ return PLUGIN_OK;
+}
+
+/*
+ * This "deflates" the sparse cow file back to the appropriate size
+ */
+static void qcow_deflate(struct qcow_private *prv)
+{
+ printf("### Truncating %s to %llu\n",
+ prv->qcow_path, prv->qcow.next_avail_block);
+ truncate(prv->qcow_path, prv->qcow.next_avail_block);
+}
+
+/*
+ * Process the subset of arguments we were given
+ */
+static void qcow_process_args(int argc, char **argv, struct qcow_private *prv)
+{
+ int c, optidx = 0;
+ int i;
+ static struct option lopts[] = {
+ {"qcow", 1, 0, 'q'},
+ {"base", 1, 0, 'b'},
+ {"debug", 0, 0, 'd'},
+ {0, 0, 0, 0 }
+ };
+
+ strcpy(prv->qcow_path, argv[1]);
+ return;
+
+ /* getopt doesn't like the way I've arranged the strings for
+ some reason, so we just use the first arg as the qcow file
+ for now */
+
+ for (i=0; i<argc; i++)
+ fprintf(stderr, "Arg %i: %s\n", i, argv[i]);
+
+ while (1) {
+ fprintf(stderr, "Going... %s\n", argv[0]);
+ c = getopt_long(argc, argv, "db:q:", lopts, &optidx);
+ if (c == -1)
+ break;
+
+ fprintf(stderr, "Got: %c\n", c);
+
+ switch (c) {
+
+ case 'b':
+ strncpy(prv->base_path, optarg, MAX_PATH);
+ break;
+ case 'q':
+ strncpy(prv->qcow_path, optarg, MAX_PATH);
+ break;
+ case 'd':
+ prv->debug = 1;
+ break;
+ };
+
+ }
+}
+
+#if 0
+/*
+ * Hook up @path to @dev
+ *
+ * NB: This doesn't work at the moment!
+ */
+static int qcow_loop_setup(const char *path, char *dev)
+{
+ int fd, lfd;
+ int i, ret;
+ char ldevpath[256];
+
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return 0;
+
+ for (i = 0; i < 8; i++) {
+ snprintf(ldevpath, 256, "/dev/loop%i", i);
+ lfd = open(ldevpath, O_RDWR);
+ if (lfd < 0) {
+ fprintf(stderr, "Failed to open %s\n", ldevpath);
+ continue;
+ }
+ ret = ioctl(lfd, LOOP_SET_FD, fd);
+ close(lfd);
+ if (ret == 0) {
+ strcpy(dev, ldevpath);
+ close(fd);
+ return 1;
+ } else {
+ fprintf(stderr, "ioctl() failed:\n");
+ perror(ldevpath);
+ }
+ }
+
+ close(fd);
+
+ printf("No free loops for file: %s\n", path);
+
+ return 0;
+}
+
+/*
+ * Detach @dev from its backing file
+ *
+ * NB: This isn't used at the moment!
+ */
+static int qcow_loop_destroy(const char *dev)
+{
+ int lfd;
+ int ret;
+
+ lfd = open(dev, O_RDWR);
+ if (lfd < 0)
+ return 0;
+
+ ret = ioctl(lfd, LOOP_CLR_FD, 0);
+
+ if (ret == 0)
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+/*
+ * Call the appropriate qcow functions to initialize all our metadata
+ * and accounting information.
+ */
+static int qcow_read_metadata(struct cow_device *dev, int force_init)
+{
+ struct qcow_private *prv = dev->plugin_private;
+ int ret;
+
+ if (force_init) {
+ /* At some point, we want to be able to format a
+ qcow file outselves */
+ fprintf(stderr, "The QCOW plugin doesn't support init yet\n");
+ return PLUGIN_FAIL;
+ }
+
+ if (! prv->init) {
+ if (prv->debug)
+ printf("Init qcow from header\n");
+ ret = qcow_init_from_header(&prv->qcow);
+ if (! ret)
+ return PLUGIN_FAIL;
+
+ dev->block_size = 1 << prv->qcow.header.cluster_bits;
+ if (prv->debug)
+ fprintf(stderr, "Block size: %u\n", dev->block_size);
+
+ dev->blocks = get_device_blocks(prv->base_dev) /
+ (dev->block_size / 512);
+
+ if (prv->debug)
+ fprintf(stderr, "Blocks: %lu\n", dev->blocks);
+ }
+
+ if (prv->debug)
+ printf("Reading tables\n");
+
+ ret = qcow_read_tables(&prv->qcow);
+ if (! ret)
+ return PLUGIN_FAIL;
+
+ prv->init = 1;
+
+ return PLUGIN_OK;
+
+}
+
+static int qcow_init(struct cow_device *dev, int debug)
+{
+
+ struct qcow_private *prv;
+ struct stat s;
+
+ if (debug)
+ printf(NAME "init\n");
+
+ memset(errmsg, 0, ERR_LEN);
+
+ prv = (struct qcow_private *)malloc(sizeof(*prv));
+ prv->qcow.fd = -1;
+ prv->init = 0;
+ prv->debug = debug;
+ prv->base_path[0] = '\0';
+ prv->qcow_path[0] = '\0';
+ qcow_init_qcow(&prv->qcow);
+
+ qcow_process_args(dev->plugin_num_args, dev->plugin_args, prv);
+
+ if (prv->qcow_path[0] == '\0') {
+ snprintf(errmsg, ERR_LEN, "Path to qcow file is required!\n");
+ return PLUGIN_FAIL;
+ }
+
+ prv->qcow.next_avail_block = get_file_size(prv->qcow_path);
+ prv->qcow_size = get_file_size(prv->qcow_path);
+
+ /* Dirty hack to get around the fact that loop-backed files
+ can't grow */
+ qcow_inflate(prv);
+
+ if (prv->debug)
+ printf("Opening %s\n", prv->qcow_path);
+
+ prv->qcow.fd = dio_open(prv->qcow_path, O_RDWR);
+ if (prv->qcow.fd < 0) {
+ snprintf(errmsg, ERR_LEN,
+ "Failed to open %s with O_DIRECT: %s\n",
+ prv->qcow_path, strerror(prv->qcow.fd));
+ return PLUGIN_FAIL;
+ }
+
+ if (prv->debug)
+ printf("### Preset next_avail_block to %llu\n",
+ prv->qcow.next_avail_block);
+
+ /* Init the qcow header information */
+ qcow_rw_header(prv->qcow.fd, &prv->qcow.header, 0);
+ qcow_read_backing_info(&prv->qcow);
+ qcow_print_header(&prv->qcow);
+
+ /* FIXME: REMOVE */
+ qcow_print_header(&prv->qcow);
+
+ /* Get the files/devices setup for device-mapper */
+ if (prv->base_path[0] == '\0')
+ strcpy(prv->base_path, prv->qcow.filename);
+
+ /* FIXME: Need real loop support!! */
+
+ if (is_file(prv->base_path)) {
+ sprintf(prv->base_dev, "/dev/loop0");
+ loop_destroy(prv->base_dev);
+ loop_setup(prv->base_dev, prv->base_path);
+ }
+
+ if (is_file(prv->qcow_path)) {
+ sprintf(prv->qcow_dev, "/dev/loop1");
+ loop_destroy(prv->qcow_dev);
+ loop_setup(prv->qcow_dev, prv->qcow_path);
+ }
+
+#if 0
+ if (is_file(prv->qcow_path))
+ qcow_loop_setup(prv->qcow_path, prv->qcow_dev);
+ else
+ strcpy(prv->qcow_dev, prv->qcow_path);
+
+ if (is_file(prv->base_path))
+ qcow_loop_setup(prv->base_path, prv->base_dev);
+ else
+ strcpy(prv->base_dev, prv->base_path);
+#endif
+
+ stat(prv->base_dev, &s);
+ prv->base_dev_t = s.st_rdev;
+
+ stat(prv->qcow_dev, &s);
+ prv->qcow_dev_t = s.st_rdev;
+
+ if (prv->debug)
+ printf("Base Device: %s Cow Device: %s\n",
+ prv->base_dev, prv->qcow_dev);
+
+ dev->plugin_private = prv;
+
+ return qcow_read_metadata(dev, 0);
+
+}
+
+static void qcow_cleanup(struct cow_device *dev)
+{
+ /* FIXME: Do something more useful here */
+ struct qcow_private *prv = dev->plugin_private;
+
+ close(prv->qcow.fd);
+
+ /* FIXME: Need to check if these are actually loops */
+ loop_destroy(prv->base_dev);
+ loop_destroy(prv->qcow_dev);
+
+ qcow_deflate(prv);
+
+ /* Free some stuff */
+}
+
+
+static int qcow_write_metadata(struct cow_device *dev)
+{
+ struct qcow_private *prv = dev->plugin_private;
+ int ret;
+
+ if (prv->debug)
+ printf("Writing metadata!\n");
+
+ ret = qcow_write_tables(&prv->qcow);
+
+ if (! ret)
+ return PLUGIN_FAIL;
+ else
+ return PLUGIN_OK;
+}
+
+static int qcow_map_block(struct cow_device *dev,
+ struct dmu_map_data *map)
+{
+
+ struct qcow_private *prv = dev->plugin_private;
+ uint64_t tmp;
+ uint64_t org, org_block;
+
+ /*
+ * FIXME: This is super ugly
+ */
+
+ /* Convert to start byte position of cluster */
+ org_block = dmu_map_get_block(map);
+ org = org_block << prv->qcow.header.cluster_bits;
+
+ if (prv->debug && 0)
+ fprintf(stderr, "Looking for existing map for %llu\n",
+ org_block);
+
+ tmp = qcow_get_mapping(&prv->qcow, org);
+ if ((tmp == 0) && dmu_map_is_write(map)) {
+ /* NEW mapping for WRITE access:
+ Remap to somewhere in the cow device */
+ if (prv->debug && 0)
+ fprintf(stderr, "Not found, mapping...\n");
+
+ tmp = qcow_make_new_mapping(&prv->qcow, (uint64_t)org);
+
+ dmu_map_set_block(map, qcow_align_to_block(&prv->qcow, tmp));
+ dmu_map_set_offset(map, qcow_calc_offset(&prv->qcow, tmp));
+
+ dmu_map_set_copy_src_dev(map, prv->base_dev_t);
+ dmu_map_set_dest_dev(map, prv->qcow_dev_t);
+
+ } else if ((tmp == 0) && !dmu_map_is_write(map)) {
+ /* NEW mapping for READ access:
+ Remap to the same place in the base device */
+ dmu_map_set_block(map, org_block);
+
+ dmu_map_set_dest_dev(map, prv->base_dev_t);
+
+ } else if ((tmp == 0) && dmu_map_is_write(map)) {
+ /* This should not be allowed */
+ return PLUGIN_FAIL;
+ } else {
+ /* OLD mapping (access doesn't matter):
+ Remap to the correct location in the cow device */
+ dmu_map_set_block(map,
+ qcow_align_to_block(&prv->qcow, tmp));
+ dmu_map_set_offset(map,
+ qcow_calc_offset(&prv->qcow, tmp));
+ if (prv->debug)
+ fprintf(stderr,
+ "Found existing map for %llu: %llx (%llu)\n",
+ org, tmp, tmp);
+
+ dmu_map_set_dest_dev(map, prv->qcow_dev_t);
+ }
+
+ if (tmp > prv->qcow.header.size) {
+ snprintf(errmsg, ERR_LEN,
+ "Tried to map a block beyond end of device: "
+ "%llu > %llu\n",
+ tmp, prv->qcow.header.size);
+ return PLUGIN_FAIL;
+ }
+
+ return PLUGIN_OK;
+}
+
+static bool qcow_need_flush(struct cow_device *dev)
+{
+
+ struct qcow_private *prv = dev->plugin_private;
+
+ return qcow_is_anything_dirty(&prv->qcow);
+}
+
+int load_plugin(struct cowd_plugin *p)
+{
+
+ p->init_plugin = qcow_init;
+ p->write_metadata = qcow_write_metadata;
+ p->map_prepare = qcow_map_block;
+ p->cleanup_plugin = qcow_cleanup;
+ p->need_flush = qcow_need_flush;
+ p->errmsg = errmsg;
+ p->get_devs = qcow_get_devs;
+
+ return 1;
+
+}
diff -r 6a7ea5a35543 -r 77a518e8f44d tools/cowd/plugins/qcow/qcow_test.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow_test.c Mon Aug 21 15:03:10 2006 -0500
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License. See the file COPYING in the main directory
+ * of this archive for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "qcow.h"
+#include "qcow_ops.h"
+
+#define READ_COUNT 256
+
+int main(int argc, char **argv)
+{
+
+ int fd;
+ struct qcow qcow;
+ uint64_t physical;
+ uint64_t tmp;
+ char buf[READ_COUNT];
+ int i;
+
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ qcow_init_qcow(&qcow);
+
+ qcow.fd = fd;
+
+ qcow_rw_header(fd, &qcow.header, 0);
+
+ qcow_read_backing_info(&qcow);
+
+ qcow_init_from_header(&qcow);
+
+ qcow_read_tables(&qcow);
+
+ qcow_print_header(&qcow);
+
+// for (i=2; i < argc; i++) {
+// tmp = strtoull(argv[i], NULL, 10);
+// physical = qcow_get_mapping(&qcow, tmp);
+//
+// if (physical != 0) {
+// printf("Mapping for %llu is: %llu\n",
+// tmp, physical);
+//
+// lseek(fd, physical, SEEK_SET);
+// read(fd, buf, READ_COUNT);
+// buf[READ_COUNT-1] = 0;
+// printf(" Data is: %s ...\n", buf);
+// } else {
+// physical = qcow_make_new_mapping(&qcow, tmp);
+// printf("New mapping for %llu is: %llu\n",
+// tmp, physical);
+// }
+//
+// printf("\n");
+// }
+
+// qcow_write_tables(&qcow);
+
+}
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|