WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 5 of 6] qcow plugin for dm-userspace userspace tool

To: Xen Devel <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH 5 of 6] qcow plugin for dm-userspace userspace tool
From: Ryan Grimm <grimm@xxxxxxxxxx>
Date: Fri, 25 Aug 2006 16:24:08 -0500
Cc: Dan Smith <danms@xxxxxxxxxx>
Delivery-date: Fri, 25 Aug 2006 14:28:45 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
In-reply-to: <patchbomb.1156540578@venkman-64>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.5.6+20040907i
Signed-off-by: Ryan Grimm <grimm@xxxxxxxxxx>
Signed-off-by: Dan Smith <danms@xxxxxxxxxx>

# HG changeset patch
# User Ryan Grimm <grimm@xxxxxxxxxx>
# Date 1156536096 18000
# Node ID be4574d288030b64d4623dd33505d0990185a6b9
# Parent  a3656acd770b4f21ad54fa961032ff39562058eb
qcow plugin for dm-userspace userspace tool

diff -r a3656acd770b -r be4574d28803 tools/cowd/configure.in
--- a/tools/cowd/configure.in   Fri Aug 25 15:01:35 2006 -0500
+++ b/tools/cowd/configure.in   Fri Aug 25 15:01:36 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 a3656acd770b -r be4574d28803 tools/cowd/plugins/Makefile.am
--- a/tools/cowd/plugins/Makefile.am    Fri Aug 25 15:01:35 2006 -0500
+++ b/tools/cowd/plugins/Makefile.am    Fri Aug 25 15:01:36 2006 -0500
@@ -1,1 +1,1 @@ SUBDIRS = dscow
-SUBDIRS = dscow
+SUBDIRS = dscow qcow
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/Makefile.am
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/Makefile.am       Fri Aug 25 15:01:36 2006 -0500
@@ -0,0 +1,3 @@
+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
diff -r a3656acd770b -r be4574d28803 tools/cowd/plugins/qcow/qcow.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/plugins/qcow/qcow.h    Fri Aug 25 15:01:36 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 a3656acd770b -r be4574d28803 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        Fri Aug 25 15:01:36 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 a3656acd770b -r be4574d28803 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        Fri Aug 25 15:01:36 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 a3656acd770b -r be4574d28803 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     Fri Aug 25 15:01:36 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;
+
+}

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>