From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56426) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YioUj-0004Ua-Ku for qemu-devel@nongnu.org; Thu, 16 Apr 2015 14:26:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YioUd-0000T7-6R for qemu-devel@nongnu.org; Thu, 16 Apr 2015 14:26:33 -0400 Received: from maverick.spineless.org ([98.118.126.100]:55356 helo=spineless.org) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YioUd-0000SQ-1W for qemu-devel@nongnu.org; Thu, 16 Apr 2015 14:26:27 -0400 Received: from [216.57.91.130] (helo=[10.204.241.11]) by spineless.org with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84) (envelope-from ) id 1YioUV-00006S-Ch for qemu-devel@nongnu.org; Thu, 16 Apr 2015 14:26:20 -0400 Message-ID: <552FFEC5.5090900@spineless.org> Date: Thu, 16 Apr 2015 14:26:13 -0400 From: John Baboval MIME-Version: 1.0 References: <1425478712-4146-1-git-send-email-gongxiaodong1@huawei.com> In-Reply-To: <1425478712-4146-1-git-send-email-gongxiaodong1@huawei.com> Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH v10] Support vhd type VHD_DIFFERENCING List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Thank you for this patch. I found a few places where you have 32-bit integer overflows that result in failure. They are marked inline below. On 03/04/2015 09:18 AM, Xiaodong Gong wrote: > diff --git a/block/vpc.c b/block/vpc.c > index 46803b1..d9a8d19 100644 > --- a/block/vpc.c > +++ b/block/vpc.c > @@ -29,17 +29,29 @@ > #if defined(CONFIG_UUID) > #include > #endif > +#include > > /**************************************************************/ > > #define HEADER_SIZE 512 > +#define DYNAMIC_HEADER_SIZE 1024 > +#define PARENT_LOCATOR_NUM 8 > +#define TBBATMAP_HEAD_SIZE 28 > + > +#define MACX_PREFIX_LEN 7 /* file:// */ > + > +#define PLATFORM_MACX 0x4D616358 > +#define PLATFORM_W2RU 0x57327275 > +#define PLATFORM_W2KU 0x57326B75 > + > +#define VHD_VERSION(major, minor) (((major) << 16) | ((minor) & 0x0000FFFF)) > > //#define CACHE > > enum vhd_type { > VHD_FIXED = 2, > VHD_DYNAMIC = 3, > - VHD_DIFFERENCING = 4, > + VHD_DIFF = 4, > }; > > // Seconds since Jan 1, 2000 0:00:00 (UTC) > @@ -138,6 +150,15 @@ typedef struct BDRVVPCState { > Error *migration_blocker; > } BDRVVPCState; > > +typedef struct vhd_tdbatmap_header { > + char magic[8]; /* always "tdbatmap" */ > + > + uint64_t batmap_offset; > + uint32_t batmap_size; > + uint32_t batmap_version; > + uint32_t checksum; > +} QEMU_PACKED VHDTdBatmapHeader; > + > static uint32_t vpc_checksum(uint8_t* buf, size_t size) > { > uint32_t res = 0; > @@ -157,6 +178,224 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) > return 0; > } > > +static int vpc_decode_maxc_loc(BlockDriverState *bs, uint32_t data_length) > +{ > + GIConv cd; > + gchar *inbuf, *outbuf, *buf; > + gsize inbyteleft, outbyteleft, outbyte; > + int ret = 0; > + gsize ret1; > + gint ret2; > + > + if (!bs || !bs->backing_file || !data_length) { > + return -1; > + } > + > + cd = g_iconv_open("ASCII", "UTF8"); > + if (cd == (GIConv) -1) { > + return -1; > + } > + > + inbuf = bs->backing_file; > + outbuf = buf = (gchar *) g_malloc(data_length + 1); > + if (!outbuf) { > + ret = -1; > + goto fail2; > + } > + inbyteleft = outbyteleft = data_length; > + > + ret1 = g_iconv(cd, &inbuf, &inbyteleft, &outbuf, &outbyteleft); > + if (ret1 == (gsize) -1 || inbyteleft) { > + ret = -1; > + goto fail1; > + } > + outbyte = data_length - outbyteleft; > + if (outbyte > sizeof(bs->backing_file) - 1) { > + ret = -1; > + goto fail1; > + } > + if (outbyte < MACX_PREFIX_LEN) { > + ret = -1; > + goto fail1; > + } > + buf[outbyte] = '\0'; > + > + pstrcpy(bs->backing_file, sizeof(bs->backing_file), buf + MACX_PREFIX_LEN); > + > +fail1: > + g_free(buf); > + buf = NULL; > + > +fail2: > + ret2 = g_iconv_close(cd); > + if (ret2 == (gint) -1) { > + ret = -1; > + } > + > + return ret; > +} > + > +static int vpc_decode_w2u_loc(BlockDriverState *bs, uint32_t data_length) > +{ > + GIConv cd; > + gchar *buf, *inbuf, *outbuf; > + gsize inbyteleft, outbyteleft, outbyte; > + gchar *ptr; > + char len = 0; > + int ret = 0; > + gsize ret1; > + gint ret2; > + > + if (!bs || !bs->backing_file || !data_length) { > + return -1; > + } > + > + cd = g_iconv_open("ASCII", "UTF-16LE"); > + if (cd == (GIConv) -1) { > + return -1; > + } > + > + inbuf = bs->backing_file; > + outbuf = buf = ptr = (char *) g_malloc(data_length + 1); > + if (!buf) { > + ret = -1; > + goto fail2; > + } > + inbyteleft = outbyteleft = data_length; > + > + ret1 = g_iconv(cd, &inbuf, &inbyteleft, &outbuf, &outbyteleft); > + if (ret1 == (gsize) -1 || inbyteleft) { > + ret = -1; > + goto fail1; > + } > + outbyte = data_length - outbyteleft; > + if (outbyte > sizeof(bs->backing_file) - 1) { > + ret = -1; > + goto fail1; > + } > + buf[outbyte] = '\0'; > + > + while (ptr != outbuf) { > + if (*ptr == '\\') { > + *ptr = '/'; > + } > + ptr++; > + } > + ptr = strstr(buf, ":"); > + if (ptr) { > + *ptr = '.'; > + len = ptr - buf; > + } > + > + pstrcpy(bs->backing_file, sizeof(bs->backing_file), buf + len); > + > +fail1: > + g_free(buf); > + buf = NULL; > + > +fail2: > + ret2 = g_iconv_close(cd); > + if (ret2 == (gint) -1) { > + return -1; > + } > + > + return ret; > +} > + > +static int vpc_decode_parent_loc(uint32_t platform, > + BlockDriverState *bs, > + uint32_t data_length) > +{ > + int ret; > + > + switch (platform) { > + case PLATFORM_MACX: > + ret = vpc_decode_maxc_loc(bs, data_length); > + if (ret < 0) { > + return ret; > + } > + break; > + > + case PLATFORM_W2RU: > + /* fall through! */ > + case PLATFORM_W2KU: > + ret = vpc_decode_w2u_loc(bs, data_length); > + if (ret < 0) { > + return ret; > + } > + break; > + > + default: > + return -1; > + } > + > + return 0; > +} > + > +static int vpc_read_backing_loc(VHDDynDiskHeader *dyndisk_header, > + BlockDriverState *bs, > + Error **errp) > +{ > + BDRVVPCState *s = bs->opaque; > + uint64_t data_offset; > + uint32_t data_length; > + uint32_t platform; > + bool done = false; > + uint64_t parent_locator_offset = 0; > + int i; > + int ret = 0; > + > + for (i = 0; i < PARENT_LOCATOR_NUM; i++) { > + platform = > + be32_to_cpu(dyndisk_header->parent_locator[i].platform); > + data_offset = > + be64_to_cpu(dyndisk_header->parent_locator[i].data_offset); > + data_length = > + be32_to_cpu(dyndisk_header->parent_locator[i].data_length); > + > + /* Extend the location offset */ > + if (parent_locator_offset < data_offset) { > + parent_locator_offset = data_offset; > + } > + > + if (done) { > + continue; > + } > + > + /* Read location of backing file */ > + if (data_offset > s->max_table_entries * s->block_size) { This multiplication overflows resulting in failure... > + return -1; > + } > + if (data_length > sizeof(bs->backing_file) - 1) { > + return -1; > + } > + ret = bdrv_pread(bs->file, data_offset, bs->backing_file, > + data_length); > + if (ret < 0) { > + return ret; > + } > + > + bs->backing_file[data_length] = '\0'; > + > + /* Decode the parent location */ > + ret = vpc_decode_parent_loc(platform, bs, data_length); > + if (ret < 0) { > + return ret; > + } > + > + /* Right parent location is got */ > + if (bs->backing_file[0] != '\0') { > + done = true; > + } > + } > + > + if (!done) { > + return -1; > + } > + > + return parent_locator_offset; > +} > + > static int vpc_open(BlockDriverState *bs, QDict *options, int flags, > Error **errp) > { > @@ -164,11 +403,14 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, > int i; > VHDFooter *footer; > VHDDynDiskHeader *dyndisk_header; > - uint8_t buf[HEADER_SIZE]; > + uint8_t buf[DYNAMIC_HEADER_SIZE]; > + uint8_t tdbatmap_header_buf[TBBATMAP_HEAD_SIZE]; > uint32_t checksum; > uint64_t computed_size; > - int disk_type = VHD_DYNAMIC; > + uint32_t disk_type; > int ret; > + VHDTdBatmapHeader *tdbatmap_header; > + uint64_t parent_locator_offset = 0; > > ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); > if (ret < 0) { > @@ -176,6 +418,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, > } > > footer = (VHDFooter *) s->footer_buf; > + disk_type = be32_to_cpu(footer->type); > + > if (strncmp(footer->creator, "conectix", 8)) { > int64_t offset = bdrv_getlength(bs->file); > if (offset < 0) { > @@ -230,9 +474,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, > goto fail; > } > > - if (disk_type == VHD_DYNAMIC) { > + if (disk_type == VHD_DYNAMIC || disk_type == VHD_DIFF) { > ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, > - HEADER_SIZE); > + DYNAMIC_HEADER_SIZE); > if (ret < 0) { > goto fail; > } > @@ -286,6 +530,47 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, > s->free_data_block_offset = > (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; > > + /* Read tdbatmap header by offset */ > + if (be32_to_cpu(footer->version) >= VHD_VERSION(1, 2)) { > + ret = bdrv_pread(bs->file, s->free_data_block_offset, > + tdbatmap_header_buf, TBBATMAP_HEAD_SIZE); > + if (ret < 0) { > + goto fail; > + } > + > + tdbatmap_header = (VHDTdBatmapHeader *) tdbatmap_header_buf; > + if (!strncmp(tdbatmap_header->magic, "tdbatmap", 8)) { > + s->free_data_block_offset = > + be32_to_cpu(tdbatmap_header->batmap_size) * 512 > + + be64_to_cpu(tdbatmap_header->batmap_offset); > + } > + } > + > + if (dyndisk_header->parent_name[0] || dyndisk_header->parent_name[1]) { > + int len; > + > + /* Read parent location from dyn header table */ > + ret = parent_locator_offset = vpc_read_backing_loc(dyndisk_header, > + bs, errp); > + if (ret < 0) { > + goto fail; > + } > + > + /* Fix me : Set parent format to avoid probing to raw in > + * format probe framework */ > + len = strlen("vpc"); > + if (sizeof(bs->backing_format) - 1 < len) { > + goto fail; > + } > + pstrcpy(bs->backing_format, sizeof(bs->backing_format), "vpc"); > + } > + > + if (s->free_data_block_offset < parent_locator_offset > + + BDRV_SECTOR_SIZE) { > + s->free_data_block_offset = parent_locator_offset > + + BDRV_SECTOR_SIZE; > + } > + > for (i = 0; i < s->max_table_entries; i++) { > be32_to_cpus(&s->pagetable[i]); > if (s->pagetable[i] != 0xFFFFFFFF) { > @@ -340,35 +625,76 @@ static int vpc_reopen_prepare(BDRVReopenState *state, > } > > /* > - * Returns the absolute byte offset of the given sector in the image file. > - * If the sector is not allocated, -1 is returned instead. > + * Returns the absolute byte offset of the given sector in the differencing > + * image file. > + * > + * If error happened, -1 is returned. > * > - * The parameter write must be 1 if the offset will be used for a write > - * operation (the block bitmaps is updated then), 0 otherwise. > + * When write all type or read dynamic, if the sector is not allocated, -2 > + * is returned instead. If the sector is allocated in current file, the block > + * offset is returned. > + * > + * When read diff. If the sector is not allocated, -2 is returned instead. > + * If the sector is allocated in the backing file, -3 is returned. If the > + * sector is allocated in current file, the block offset is returned. > */ > static inline int64_t get_sector_offset(BlockDriverState *bs, > - int64_t sector_num, int write) > + int64_t sector_num, bool write, bool diff) > { > BDRVVPCState *s = bs->opaque; > - uint64_t offset = sector_num * 512; > - uint64_t bitmap_offset, block_offset; > + uint64_t offset = sector_num << BDRV_SECTOR_BITS; > + uint64_t bitmap_offset; > uint32_t pagetable_index, pageentry_index; > + int64_t block_offset = LONG_MIN; > + int ret; > > pagetable_index = offset / s->block_size; > - pageentry_index = (offset % s->block_size) / 512; > + pageentry_index = (offset % s->block_size) >> BDRV_SECTOR_BITS; > > - if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) > - return -1; // not allocated > + if (pagetable_index >= s->max_table_entries) { > + return -2; > + } > + if (s->pagetable[pagetable_index] == 0xffffffff) { > + if (!write && diff) { > + return -3; /* parent allocated */ > + } else { > + return -2; /* not allocated */ > + } > + } > > - bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; > - block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); > + bitmap_offset = (uint64_t) s->pagetable[pagetable_index] > + << BDRV_SECTOR_BITS; > + > + if (!diff || write) { > + block_offset = bitmap_offset + s->bitmap_size > + + (pageentry_index << BDRV_SECTOR_BITS); > + } else { > + uint32_t bitmap_index, bitmapentry_index; > + uint8_t bitmap[s->bitmap_size]; > > + if (bitmap_offset > s->max_table_entries * s->block_size) { This one too.... > + return -1; > + } > + ret = bdrv_pread(bs->file, bitmap_offset, bitmap, s->bitmap_size); > + if (ret < 0) { > + return -1; > + } > + > + bitmap_index = pageentry_index / 8; > + bitmapentry_index = 7 - pageentry_index % 8; > + if (bitmap[bitmap_index] & 0x1 << bitmapentry_index) { > + block_offset = bitmap_offset + s->bitmap_size > + + (pageentry_index << BDRV_SECTOR_BITS); > + } else { > + return -3; > + } > + } > // We must ensure that we don't write to any sectors which are marked as > // unused in the bitmap. We get away with setting all bits in the block > // bitmap each time we write to a new block. This might cause Virtual PC to > // miss sparse read optimization, but it's not a problem in terms of > // correctness. > - if (write && (s->last_bitmap_offset != bitmap_offset)) { > + if (!diff && write && (s->last_bitmap_offset != bitmap_offset)) { > uint8_t bitmap[s->bitmap_size]; > > s->last_bitmap_offset = bitmap_offset; > @@ -437,7 +763,8 @@ static int rewrite_footer(BlockDriverState* bs) > * > * Returns the sectors' offset in the image file on success and < 0 on error > */ > -static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) > +static int64_t alloc_block(BlockDriverState *bs, int64_t sector_num, > + bool diff) > { > BDRVVPCState *s = bs->opaque; > int64_t bat_offset; > @@ -457,7 +784,11 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) > s->pagetable[index] = s->free_data_block_offset / 512; > > // Initialize the block's bitmap > - memset(bitmap, 0xff, s->bitmap_size); > + if (diff) { > + memset(bitmap, 0x0, s->bitmap_size); > + } else { > + memset(bitmap, 0xff, s->bitmap_size); > + } > ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, > s->bitmap_size); > if (ret < 0) { > @@ -477,7 +808,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) > if (ret < 0) > goto fail; > > - return get_sector_offset(bs, sector_num, 0); > + return get_sector_offset(bs, sector_num, false, diff); > > fail: > s->free_data_block_offset -= (s->block_size + s->bitmap_size); > @@ -501,37 +832,71 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, > uint8_t *buf, int nb_sectors) > { > BDRVVPCState *s = bs->opaque; > - int ret; > - int64_t offset; > - int64_t sectors, sectors_per_block; > VHDFooter *footer = (VHDFooter *) s->footer_buf; > + int64_t sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; > + int64_t offset, sectors; > + int ret; > > - if (be32_to_cpu(footer->type) == VHD_FIXED) { > + switch (be32_to_cpu(footer->type)) { > + case VHD_FIXED: > return bdrv_read(bs->file, sector_num, buf, nb_sectors); > - } > - while (nb_sectors > 0) { > - offset = get_sector_offset(bs, sector_num, 0); > > - sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; > - sectors = sectors_per_block - (sector_num % sectors_per_block); > - if (sectors > nb_sectors) { > - sectors = nb_sectors; > + case VHD_DYNAMIC: > + while (nb_sectors > 0) { > + sectors = sectors_per_block - (sector_num % sectors_per_block); > + if (sectors > nb_sectors) { > + sectors = nb_sectors; > + } > + > + offset = get_sector_offset(bs, sector_num, false, false); > + if (offset == -1) { > + return -1; > + } else if (offset == -2) { > + memset(buf, 0, sectors * BDRV_SECTOR_SIZE); > + } else { > + ret = bdrv_pread(bs->file, offset, buf, > + sectors * BDRV_SECTOR_SIZE); > + if (ret != sectors * BDRV_SECTOR_SIZE) { > + return -1; > + } > + } > + > + nb_sectors -= sectors; > + sector_num += sectors; > + buf += sectors * BDRV_SECTOR_SIZE; > } > + break; > > - if (offset == -1) { > - memset(buf, 0, sectors * BDRV_SECTOR_SIZE); > - } else { > - ret = bdrv_pread(bs->file, offset, buf, > - sectors * BDRV_SECTOR_SIZE); > - if (ret != sectors * BDRV_SECTOR_SIZE) { > + case VHD_DIFF: > + while (nb_sectors > 0) { > + offset = get_sector_offset(bs, sector_num, false, true); > + if (offset == -1) { > return -1; > + } else if (offset == -2) { > + memset(buf, 0, BDRV_SECTOR_SIZE); > + } else if (offset == -3) { > + ret = bdrv_pread(bs->backing_hd, sector_num << BDRV_SECTOR_BITS > + , buf, BDRV_SECTOR_SIZE); > + if (ret < 0) { > + return -1; > + } > + } else { > + ret = bdrv_pread(bs->file, offset, buf, BDRV_SECTOR_SIZE); > + if (ret != BDRV_SECTOR_SIZE) { > + return -1; > + } > } > + > + nb_sectors--; > + sector_num++; > + buf += BDRV_SECTOR_SIZE; > } > + break; > > - nb_sectors -= sectors; > - sector_num += sectors; > - buf += sectors * BDRV_SECTOR_SIZE; > + default: > + return -1; > } > + > return 0; > } > > @@ -546,41 +911,101 @@ static coroutine_fn int vpc_co_read(BlockDriverState *bs, int64_t sector_num, > return ret; > } > > +static inline int64_t write_bitmap(BlockDriverState *bs, int64_t sector_num, > + int64_t sectors) > +{ > + BDRVVPCState *s = bs->opaque; > + uint64_t offset = sector_num << BDRV_SECTOR_BITS; > + uint64_t bitmap_offset; > + uint32_t pagetable_index, pageentry_index; > + uint8_t bitmap[s->bitmap_size]; > + uint32_t bitmap_index, bitmapbit_index; > + int i; > + int ret; > + > + pagetable_index = offset / s->block_size; > + pageentry_index = (offset % s->block_size) / 512; > + bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; > + > + if (bitmap_offset > s->max_table_entries * s->block_size) { And here... > + return -1; > + } > + ret = bdrv_pread(bs->file, bitmap_offset, bitmap, s->bitmap_size); > + if (ret < 0) { > + return -1; > + } > + > + for (i = 0; i < sectors; i++) { > + bitmap_index = pageentry_index / 8; > + bitmapbit_index = 7 - pageentry_index % 8; > + bitmap[bitmap_index] |= (0x1 << bitmapbit_index); > + pageentry_index++; > + } > + ret = bdrv_pwrite(bs->file, bitmap_offset, bitmap, s->bitmap_size); > + if (ret < 0) { > + return -1; > + } > + > + return 0; > +} > + > static int vpc_write(BlockDriverState *bs, int64_t sector_num, > const uint8_t *buf, int nb_sectors) > { > BDRVVPCState *s = bs->opaque; > - int64_t offset; > - int64_t sectors, sectors_per_block; > - int ret; > - VHDFooter *footer = (VHDFooter *) s->footer_buf; > + VHDFooter *footer = (VHDFooter *) s->footer_buf; > + int64_t sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; > + int64_t offset, sectors; > + bool diff = true; > + int ret = 0; > > - if (be32_to_cpu(footer->type) == VHD_FIXED) { > + switch (be32_to_cpu(footer->type)) { > + case VHD_FIXED: > return bdrv_write(bs->file, sector_num, buf, nb_sectors); > - } > - while (nb_sectors > 0) { > - offset = get_sector_offset(bs, sector_num, 1); > > - sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; > - sectors = sectors_per_block - (sector_num % sectors_per_block); > - if (sectors > nb_sectors) { > - sectors = nb_sectors; > - } > + case VHD_DYNAMIC: > + diff = false; > + /* fall-through ! */ > + > + case VHD_DIFF: > + while (nb_sectors > 0) { > + sectors = sectors_per_block - (sector_num % sectors_per_block); > + if (sectors > nb_sectors) { > + sectors = nb_sectors; > + } > > - if (offset == -1) { > - offset = alloc_block(bs, sector_num); > - if (offset < 0) > + offset = get_sector_offset(bs, sector_num, true, diff); > + if (offset == -1) { > return -1; > - } > + } else if (offset == -2) { > + offset = alloc_block(bs, sector_num, diff); > + if (offset < 0) { > + return -1; > + } > + } > > - ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); > - if (ret != sectors * BDRV_SECTOR_SIZE) { > - return -1; > + ret = bdrv_pwrite(bs->file, offset, buf, > + sectors * BDRV_SECTOR_SIZE); > + if (ret != sectors * BDRV_SECTOR_SIZE) { > + return -1; > + } > + > + if (diff) { > + ret = write_bitmap(bs, sector_num, sectors); > + if (ret < 0) { > + return -1; > + } > + } > + > + nb_sectors -= sectors; > + sector_num += sectors; > + buf += sectors * BDRV_SECTOR_SIZE; > } > > - nb_sectors -= sectors; > - sector_num += sectors; > - buf += sectors * BDRV_SECTOR_SIZE; > + break; > + > + default: > + return -1; > } > > return 0; > @@ -906,6 +1331,7 @@ static BlockDriver bdrv_vpc = { > .bdrv_close = vpc_close, > .bdrv_reopen_prepare = vpc_reopen_prepare, > .bdrv_create = vpc_create, > + .supports_backing = true, > > .bdrv_read = vpc_co_read, > .bdrv_write = vpc_co_write,