aboutsummaryrefslogtreecommitdiffstats
path: root/tools/blktap/drivers
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2008-03-27 17:13:37 +0000
committerKeir Fraser <keir.fraser@citrix.com>2008-03-27 17:13:37 +0000
commitb4f95a5c93a0b14825081d1c794b03ee77306b34 (patch)
tree1df08d72fd840a3882dc00278702a7b2ee90956a /tools/blktap/drivers
parent22b0cb305b796fd2f68dd1c7dd4f72f1e772e5dc (diff)
downloadxen-b4f95a5c93a0b14825081d1c794b03ee77306b34.tar.gz
xen-b4f95a5c93a0b14825081d1c794b03ee77306b34.tar.bz2
xen-b4f95a5c93a0b14825081d1c794b03ee77306b34.zip
tapdisk: Fix L1 table endianess of qcow images
Fix tapdisk to use big endian L1 tables as used by qemu/ioemu. Old tapdisk images with native endianess are automagically converted to big endian when the image file is opened for the first time. Signed-off-by: Kevin Wolf <kwolf@suse.de>
Diffstat (limited to 'tools/blktap/drivers')
-rw-r--r--tools/blktap/drivers/block-qcow.c62
1 files changed, 56 insertions, 6 deletions
diff --git a/tools/blktap/drivers/block-qcow.c b/tools/blktap/drivers/block-qcow.c
index f12fd90c4c..25838b44f9 100644
--- a/tools/blktap/drivers/block-qcow.c
+++ b/tools/blktap/drivers/block-qcow.c
@@ -76,6 +76,7 @@
#define QCOW_OFLAG_COMPRESSED (1LL << 63)
#define SPARSE_FILE 0x01
+#define EXTHDR_L1_BIG_ENDIAN 0x02
#ifndef O_BINARY
#define O_BINARY 0
@@ -147,19 +148,30 @@ static int decompress_cluster(struct tdqcow_state *s, uint64_t cluster_offset);
static uint32_t gen_cksum(char *ptr, int len)
{
+ int i;
unsigned char *md;
uint32_t ret;
md = malloc(MD5_DIGEST_LENGTH);
if(!md) return 0;
+
+ /* Convert L1 table to big endian */
+ for(i = 0; i < len / sizeof(uint64_t); i++) {
+ cpu_to_be64s(&((uint64_t*) ptr)[i]);
+ }
- if (MD5((unsigned char *)ptr, len, md) != md) {
- free(md);
- return 0;
+ /* Generate checksum */
+ if (MD5((unsigned char *)ptr, len, md) != md)
+ ret = 0;
+ else
+ memcpy(&ret, md, sizeof(uint32_t));
+
+ /* Convert L1 table back to native endianess */
+ for(i = 0; i < len / sizeof(uint64_t); i++) {
+ be64_to_cpus(&((uint64_t*) ptr)[i]);
}
- memcpy(&ret, md, sizeof(uint32_t));
free(md);
return ret;
}
@@ -354,7 +366,8 @@ static uint64_t get_cluster_offset(struct tdqcow_state *s,
int n_start, int n_end)
{
int min_index, i, j, l1_index, l2_index, l2_sector, l1_sector;
- char *tmp_ptr, *tmp_ptr2, *l2_ptr, *l1_ptr;
+ char *tmp_ptr2, *l2_ptr, *l1_ptr;
+ uint64_t *tmp_ptr;
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
uint32_t min_count;
int new_l2_table;
@@ -401,6 +414,11 @@ static uint64_t get_cluster_offset(struct tdqcow_state *s,
}
memcpy(tmp_ptr, l1_ptr, 4096);
+ /* Convert block to write to big endian */
+ for(i = 0; i < 4096 / sizeof(uint64_t); i++) {
+ cpu_to_be64s(&tmp_ptr[i]);
+ }
+
/*
* Issue non-asynchronous L1 write.
* For safety, we must ensure that
@@ -777,7 +795,7 @@ int tdqcow_open (struct disk_driver *dd, const char *name, td_flag_t flags)
goto fail;
for(i = 0; i < s->l1_size; i++) {
- //be64_to_cpus(&s->l1_table[i]);
+ be64_to_cpus(&s->l1_table[i]);
//DPRINTF("L1[%d] => %llu\n", i, s->l1_table[i]);
if (s->l1_table[i] > final_cluster)
final_cluster = s->l1_table[i];
@@ -810,6 +828,38 @@ int tdqcow_open (struct disk_driver *dd, const char *name, td_flag_t flags)
be32_to_cpus(&exthdr->xmagic);
if(exthdr->xmagic != XEN_MAGIC)
goto end_xenhdr;
+
+ /* Try to detect old tapdisk images. They have to be fixed because
+ * they don't use big endian but native endianess for the L1 table */
+ if ((exthdr->flags & EXTHDR_L1_BIG_ENDIAN) == 0) {
+
+ /*
+ The image is broken. Fix it. The L1 table has already been
+ byte-swapped, so we can write it to the image file as it is
+ currently in memory. Then swap it back to native endianess
+ for operation.
+ */
+
+ DPRINTF("qcow: Converting image to big endian L1 table\n");
+
+ lseek(fd, s->l1_table_offset, SEEK_SET);
+ if (write(fd, s->l1_table, l1_table_size) != l1_table_size) {
+ DPRINTF("qcow: Failed to write new L1 table\n");
+ goto fail;
+ }
+
+ for(i = 0;i < s->l1_size; i++) {
+ cpu_to_be64s(&s->l1_table[i]);
+ }
+
+ /* Write the big endian flag to the extended header */
+ exthdr->flags |= EXTHDR_L1_BIG_ENDIAN;
+
+ if (write(fd, buf, 512) != 512) {
+ DPRINTF("qcow: Failed to write extended header\n");
+ goto fail;
+ }
+ }
/*Finally check the L1 table cksum*/
be32_to_cpus(&exthdr->cksum);