ia64/xen-unstable

changeset 17331:c02deeae7432

ioemu: Fix L1 table endianess of qcow images created by tapdisk

The qemu/ioemu implementation of the qcow format uses a big endian L1
table. tapdisk omits the necessary conversion, so qcow images have the
wrong endianess and cannot be read by correct implementations of qcow.

This patch detects broken tapdisk images and converts their L1 tables
to big endian when the image file is opened in ioemu for the first
time. The fixed image has a new flag EXTHDR_L1_BIG_ENDIAN set in the
extended header.

Note that a converted image cannot be opened by tapdisk again.

Signed-off-by: Kevin Wolf <kwolf@suse.de>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Mar 26 14:44:21 2008 +0000 (2008-03-26)
parents 145eff6c96fc
children 89121c8b3c0d
files tools/ioemu/block-qcow.c
line diff
     1.1 --- a/tools/ioemu/block-qcow.c	Wed Mar 26 14:41:47 2008 +0000
     1.2 +++ b/tools/ioemu/block-qcow.c	Wed Mar 26 14:44:21 2008 +0000
     1.3 @@ -37,6 +37,11 @@
     1.4  
     1.5  #define QCOW_OFLAG_COMPRESSED (1LL << 63)
     1.6  
     1.7 +#define XEN_MAGIC  (('X' << 24) | ('E' << 16) | ('N' << 8) | 0xfb)
     1.8 +
     1.9 +#define EXTHDR_SPARSE_FILE      0x01
    1.10 +#define EXTHDR_L1_BIG_ENDIAN    0x02
    1.11 +
    1.12  typedef struct QCowHeader {
    1.13      uint32_t magic;
    1.14      uint32_t version;
    1.15 @@ -50,6 +55,14 @@ typedef struct QCowHeader {
    1.16      uint64_t l1_table_offset;
    1.17  } QCowHeader;
    1.18  
    1.19 +/*Extended header for Xen enhancements*/
    1.20 +typedef struct QCowHeader_ext {
    1.21 +    uint32_t xmagic;
    1.22 +    uint32_t cksum;
    1.23 +    uint32_t min_cluster_alloc;
    1.24 +    uint32_t flags;
    1.25 +} QCowHeader_ext;
    1.26 +
    1.27  #define L2_CACHE_SIZE 16
    1.28  
    1.29  typedef struct BDRVQcowState {
    1.30 @@ -137,6 +150,51 @@ static int qcow_open(BlockDriverState *b
    1.31      if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != 
    1.32          s->l1_size * sizeof(uint64_t))
    1.33          goto fail;
    1.34 +
    1.35 +    /* Try to detect old tapdisk images. They have to be fixed because they
    1.36 +     * don't use big endian but native endianess for the L1 table */
    1.37 +    if (header.backing_file_offset == 0 && s->l1_table_offset % 4096 == 0) {
    1.38 +
    1.39 +        QCowHeader_ext exthdr;
    1.40 +        uint64_t l1_bytes = s->l1_size * sizeof(uint64_t);
    1.41 +
    1.42 +        if (bdrv_pread(s->hd, sizeof(header), &exthdr, sizeof(exthdr)) 
    1.43 +                != sizeof(exthdr))
    1.44 +            goto end_xenhdr;
    1.45 +
    1.46 +        be32_to_cpus(&exthdr.xmagic);
    1.47 +        if (exthdr.xmagic != XEN_MAGIC) 
    1.48 +            goto end_xenhdr;
    1.49 +
    1.50 +        be32_to_cpus(&exthdr.flags);
    1.51 +        if (exthdr.flags & EXTHDR_L1_BIG_ENDIAN)
    1.52 +            goto end_xenhdr;
    1.53 +
    1.54 +        /* The image is broken. Fix it. */
    1.55 +        fprintf(stderr, "qcow: Converting image to big endian L1 table\n");
    1.56 +
    1.57 +        for(i = 0;i < s->l1_size; i++) {
    1.58 +            cpu_to_be64s(&s->l1_table[i]);
    1.59 +        }
    1.60 +    
    1.61 +        if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, 
    1.62 +                l1_bytes) != l1_bytes) {
    1.63 +            fprintf(stderr, "qcow: Failed to write new L1 table\n");
    1.64 +            goto fail;
    1.65 +        }
    1.66 +
    1.67 +        exthdr.flags |= EXTHDR_L1_BIG_ENDIAN;
    1.68 +        cpu_to_be32s(&exthdr.flags);
    1.69 +        
    1.70 +        if (bdrv_pwrite(s->hd, sizeof(header), &exthdr, sizeof(exthdr)) 
    1.71 +                != sizeof(exthdr)) {
    1.72 +            fprintf(stderr, "qcow: Failed to write extended header\n");
    1.73 +            goto fail;
    1.74 +        }
    1.75 +    }
    1.76 +end_xenhdr:
    1.77 +
    1.78 +    /* L1 table is big endian now */
    1.79      for(i = 0;i < s->l1_size; i++) {
    1.80          be64_to_cpus(&s->l1_table[i]);
    1.81      }