From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50972) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZiQf-0004SL-0B for qemu-devel@nongnu.org; Thu, 02 Oct 2014 11:36:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XZiQT-0002ko-N6 for qemu-devel@nongnu.org; Thu, 02 Oct 2014 11:36:28 -0400 Received: from mail-wg0-x232.google.com ([2a00:1450:400c:c00::232]:37449) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZiQS-0002kQ-Pg for qemu-devel@nongnu.org; Thu, 02 Oct 2014 11:36:17 -0400 Received: by mail-wg0-f50.google.com with SMTP id a1so3600658wgh.9 for ; Thu, 02 Oct 2014 08:36:15 -0700 (PDT) Date: Thu, 2 Oct 2014 16:36:12 +0100 From: Stefan Hajnoczi Message-ID: <20141002153612.GI6250@stefanha-thinkpad.redhat.com> References: <1411738998-3019-1-git-send-email-gordongong0350@gmail.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="2nTeH+t2PBomgucg" Content-Disposition: inline In-Reply-To: <1411738998-3019-1-git-send-email-gordongong0350@gmail.com> Subject: Re: [Qemu-devel] [PATCH v5] Support vhd type VHD_DIFFERENCING List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Xiaodong Gong Cc: kwolf@redhat.com, petrutlucian94@gmail.com, lpetrut@cloudbasesolutions.com, hahn@univention.de, qemu-devel@nongnu.org, stefanha@redhat.com --2nTeH+t2PBomgucg Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Fri, Sep 26, 2014 at 09:43:18PM +0800, Xiaodong Gong wrote: > Now qemu only supports vhd type VHD_FIXED and VHD_DYNAMIC, so qemu > can't read snapshot volume of vhd, and can't support other storage > features of vhd file. >=20 > This patch add read parent information in function "vpc_open", read > bitmap in "vpc_read", and change bitmap in "vpc_write". >=20 > Signed-off-by: Xiaodong Gong > --- > block/vpc.c | 428 ++++++++++++++++++++++++++++++++++++++++++++++++++----= ------ > 1 file changed, 357 insertions(+), 71 deletions(-) Waiting for code review. I only consider patches for the block branch that have at least 1 Reviewed-by from another contributor. Anyone? > diff --git a/block/vpc.c b/block/vpc.c > index 4947369..1210542 100644 > --- a/block/vpc.c > +++ b/block/vpc.c > @@ -29,17 +29,27 @@ > #if defined(CONFIG_UUID) > #include > #endif > +#include > =20 > /**************************************************************/ > =20 > #define HEADER_SIZE 512 > +#define DYNAMIC_HEADER_SIZE 1024 > +#define PARENT_LOCATOR_NUM 8 > +#define MACX_PREFIX_LEN 7 /* file:// */ > +#define TBBATMAP_HEAD_SIZE 28 > + > +#define PLATFORM_MACX 0x5863614d /* big endian */ > +#define PLATFORM_W2RU 0x75723257 > + > +#define VHD_VERSION(major, minor) (((major) << 16) | ((minor) & 0x0000F= FFF)) > =20 > //#define CACHE > =20 > enum vhd_type { > VHD_FIXED =3D 2, > VHD_DYNAMIC =3D 3, > - VHD_DIFFERENCING =3D 4, > + VHD_DIFF =3D 4, > }; > =20 > // Seconds since Jan 1, 2000 0:00:00 (UTC) > @@ -138,6 +148,15 @@ typedef struct BDRVVPCState { > Error *migration_blocker; > } BDRVVPCState; > =20 > +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 =3D 0; > @@ -153,10 +172,107 @@ static uint32_t vpc_checksum(uint8_t* buf, size_t = size) > static int vpc_probe(const uint8_t *buf, int buf_size, const char *filen= ame) > { > if (buf_size >=3D 8 && !strncmp((char *)buf, "conectix", 8)) > - return 100; > + return 100; > return 0; > } > =20 > +static int vpc_read_backing_loc(VHDDynDiskHeader *dyndisk_header, > + BlockDriverState *bs, > + Error **errp) > +{ > + BDRVVPCState *s =3D bs->opaque; > + int64_t data_offset =3D 0; > + int data_length =3D 0; > + uint32_t platform; > + bool done =3D false; > + int parent_locator_offset =3D 0; > + int i; > + int ret =3D 0; > + > + for (i =3D 0; i < PARENT_LOCATOR_NUM; i++) { > + data_offset =3D > + be64_to_cpu(dyndisk_header->parent_locator[i].data_offset); > + data_length =3D > + be32_to_cpu(dyndisk_header->parent_locator[i].data_length); > + platform =3D dyndisk_header->parent_locator[i].platform; > + > + /* Extend the location offset */ > + if (parent_locator_offset < data_offset) { > + parent_locator_offset =3D data_offset; > + } > + > + if (done) { > + continue; > + } > + > + /* Skip "file://" in MacX platform */ > + if (platform =3D=3D PLATFORM_MACX) { > + data_offset +=3D MACX_PREFIX_LEN; > + data_length -=3D MACX_PREFIX_LEN; > + } > + > + /* Read location of backing file */ > + if (platform =3D=3D PLATFORM_MACX || platform =3D=3D PLATFORM_W2= RU) { > + if (data_offset > s->max_table_entries * s->block_size) { > + return -1; > + } > + if (data_length > BDRV_SECTOR_SIZE) { > + return -1; > + } > + ret =3D bdrv_pread(bs->file, data_offset, bs->backing_file, > + data_length); > + if (ret < 0) { > + return ret; > + } > + bs->backing_file[data_length] =3D '\0'; > + } > + > + /* Convert location to ACSII string */ > + if (platform =3D=3D PLATFORM_MACX) { > + done =3D true; > + > + } else if (platform =3D=3D PLATFORM_W2RU) { > + /* Must be UTF16-LE to ASCII */ > + char *out, *optr; > + int j; > + > + optr =3D out =3D (char *) malloc(data_length + 1); > + if (out =3D=3D NULL) { > + ret =3D -1; > + return ret; > + } > + memset(out, 0, data_length + 1); > + > + for (j =3D 0; j < data_length + 1; j++) { > + out[j] =3D bs->backing_file[2*j]; > + } > + out[data_length + 1] =3D '\0'; > + > + while (*optr !=3D '\0') { > + if (*optr =3D=3D '\\') { > + *optr =3D '/'; > + } > + optr++; > + } > + > + strncpy(bs->backing_file, out, data_length + 1); > + > + out =3D NULL; > + free(out); > + > + done =3D true; > + } > + } > + > + if (bs->backing_file[0] =3D=3D '\0') { > + error_setg(errp, "block-vpc: differencing is not support in w2ku= "); > + ret =3D -EINVAL; > + return ret; > + } > + > + return parent_locator_offset; > +} > + > static int vpc_open(BlockDriverState *bs, QDict *options, int flags, > Error **errp) > { > @@ -164,11 +280,14 @@ static int vpc_open(BlockDriverState *bs, QDict *op= tions, 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 =3D VHD_DYNAMIC; > + uint32_t disk_type; > int ret; > + VHDTdBatmapHeader *tdbatmap_header; > + int parent_locator_offset =3D 0; > =20 > ret =3D bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); > if (ret < 0) { > @@ -176,6 +295,8 @@ static int vpc_open(BlockDriverState *bs, QDict *opti= ons, int flags, > } > =20 > footer =3D (VHDFooter *) s->footer_buf; > + disk_type =3D be32_to_cpu(footer->type); > + > if (strncmp(footer->creator, "conectix", 8)) { > int64_t offset =3D bdrv_getlength(bs->file); > if (offset < 0) { > @@ -230,9 +351,9 @@ static int vpc_open(BlockDriverState *bs, QDict *opti= ons, int flags, > goto fail; > } > =20 > - if (disk_type =3D=3D VHD_DYNAMIC) { > + if (disk_type =3D=3D VHD_DYNAMIC || disk_type =3D=3D VHD_DIFF) { > ret =3D bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), b= uf, > - HEADER_SIZE); > + DYNAMIC_HEADER_SIZE); > if (ret < 0) { > goto fail; > } > @@ -286,6 +407,37 @@ static int vpc_open(BlockDriverState *bs, QDict *opt= ions, int flags, > s->free_data_block_offset =3D > (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; > =20 > + /* Read tdbatmap header by offset */ > + if (footer->version >=3D VHD_VERSION(1, 2)) { > + ret =3D bdrv_pread(bs->file, s->free_data_block_offset, > + tdbatmap_header_buf, TBBATMAP_HEAD_SIZE); > + if (ret < 0) { > + goto fail; > + } > + > + tdbatmap_header =3D (VHDTdBatmapHeader *) tdbatmap_header_bu= f; > + if (!strncmp(tdbatmap_header->magic, "tdbatmap", 8)) { > + s->free_data_block_offset =3D > + be32_to_cpu(tdbatmap_header->batmap_size) * 512 > + + be64_to_cpu(tdbatmap_header->batmap_offset); > + } > + } > + > + /* Read backing file location from dyn header table */ > + if (dyndisk_header->parent_name[0] || dyndisk_header->parent_nam= e[1]) { > + ret =3D parent_locator_offset =3D vpc_read_backing_loc(dyndi= sk_header, > + bs, errp); > + if (ret < 0) { > + goto fail; > + } > + } > + > + if (s->free_data_block_offset < parent_locator_offset > + + BDRV_SECTOR_SIZE) { > + s->free_data_block_offset =3D parent_locator_offset > + + BDRV_SECTOR_SIZE; > + } > + > for (i =3D 0; i < s->max_table_entries; i++) { > be32_to_cpus(&s->pagetable[i]); > if (s->pagetable[i] !=3D 0xFFFFFFFF) { > @@ -340,35 +492,76 @@ static int vpc_reopen_prepare(BDRVReopenState *stat= e, > } > =20 > /* > - * Returns the absolute byte offset of the given sector in the image fil= e. > - * If the sector is not allocated, -1 is returned instead. > + * Returns the absolute byte offset of the given sector in the differenc= ing > + * image file. > + * > + * If error happened, -1 is returned. > + * > + * 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. > * > - * 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 read diff. If the sector is not allocated, -2 is returned instea= d. > + * 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 =3D bs->opaque; > - uint64_t offset =3D sector_num * 512; > - uint64_t bitmap_offset, block_offset; > + uint64_t offset =3D sector_num << BDRV_SECTOR_BITS; > + uint64_t bitmap_offset; > uint32_t pagetable_index, pageentry_index; > + int64_t block_offset =3D LONG_MIN; > + int ret; > =20 > pagetable_index =3D offset / s->block_size; > - pageentry_index =3D (offset % s->block_size) / 512; > + pageentry_index =3D (offset % s->block_size) >> BDRV_SECTOR_BITS; > =20 > - if (pagetable_index >=3D s->max_table_entries || s->pagetable[pageta= ble_index] =3D=3D 0xffffffff) > - return -1; // not allocated > + if (pagetable_index >=3D s->max_table_entries) { > + return -2; > + } > + if (s->pagetable[pagetable_index] =3D=3D 0xffffffff) { > + if (!write && diff) { > + return -3; /* parent allocated */ > + } else { > + return -2; /* not allocated */ > + } > + } > =20 > - bitmap_offset =3D 512 * (uint64_t) s->pagetable[pagetable_index]; > - block_offset =3D bitmap_offset + s->bitmap_size + (512 * pageentry_i= ndex); > + bitmap_offset =3D (uint64_t) s->pagetable[pagetable_index] > + << BDRV_SECTOR_BITS; > + > + if (!diff || write) { > + block_offset =3D bitmap_offset + s->bitmap_size > + + (pageentry_index << BDRV_SECTOR_BITS); > + } else { > + uint32_t bitmap_index, bitmapentry_index; > + uint8_t bitmap[s->bitmap_size]; > =20 > + if (bitmap_offset > s->max_table_entries * s->block_size) { > + return -1; > + } > + ret =3D bdrv_pread(bs->file, bitmap_offset, bitmap, s->bitmap_si= ze); > + if (ret < 0) { > + return -1; > + } > + > + bitmap_index =3D pageentry_index / 8; > + bitmapentry_index =3D 7 - pageentry_index % 8; > + if (bitmap[bitmap_index] & 0x1 << bitmapentry_index) { > + block_offset =3D 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 marke= d as > // unused in the bitmap. We get away with setting all bits in the bl= ock > // bitmap each time we write to a new block. This might cause Virtua= l PC to > // miss sparse read optimization, but it's not a problem in terms of > // correctness. > - if (write && (s->last_bitmap_offset !=3D bitmap_offset)) { > + if (!diff && write && (s->last_bitmap_offset !=3D bitmap_offset)) { > uint8_t bitmap[s->bitmap_size]; > =20 > s->last_bitmap_offset =3D bitmap_offset; > @@ -376,7 +569,7 @@ static inline int64_t get_sector_offset(BlockDriverSt= ate *bs, > bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size= ); > } > =20 > -// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx= 64 ", bloff: %" PRIx64 "\n", > +// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64= ", bloff: %" PRIx64 "\n", > // sector_num, pagetable_index, pageentry_index, > // bitmap_offset, block_offset); > =20 > @@ -437,7 +630,8 @@ static int rewrite_footer(BlockDriverState* bs) > * > * Returns the sectors' offset in the image file on success and < 0 on e= rror > */ > -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 =3D bs->opaque; > int64_t bat_offset; > @@ -457,7 +651,11 @@ static int64_t alloc_block(BlockDriverState* bs, int= 64_t sector_num) > s->pagetable[index] =3D s->free_data_block_offset / 512; > =20 > // 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 =3D bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, > s->bitmap_size); > if (ret < 0) { > @@ -477,7 +675,7 @@ static int64_t alloc_block(BlockDriverState* bs, int6= 4_t sector_num) > if (ret < 0) > goto fail; > =20 > - return get_sector_offset(bs, sector_num, 0); > + return get_sector_offset(bs, sector_num, false, diff); > =20 > fail: > s->free_data_block_offset -=3D (s->block_size + s->bitmap_size); > @@ -501,36 +699,66 @@ static int vpc_read(BlockDriverState *bs, int64_t s= ector_num, > uint8_t *buf, int nb_sectors) > { > BDRVVPCState *s =3D bs->opaque; > - int ret; > - int64_t offset; > - int64_t sectors, sectors_per_block; > VHDFooter *footer =3D (VHDFooter *) s->footer_buf; > + int64_t sectors_per_block =3D s->block_size >> BDRV_SECTOR_BITS; > + int64_t offset, sectors; > + int ret; > =20 > - if (be32_to_cpu(footer->type) =3D=3D 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 =3D get_sector_offset(bs, sector_num, 0); > - > - sectors_per_block =3D s->block_size >> BDRV_SECTOR_BITS; > - sectors =3D sectors_per_block - (sector_num % sectors_per_block); > - if (sectors > nb_sectors) { > - sectors =3D nb_sectors; > - } > + case VHD_DYNAMIC: > + while (nb_sectors > 0) { > + sectors =3D sectors_per_block - (sector_num % sectors_per_bl= ock); > + if (sectors > nb_sectors) { > + sectors =3D nb_sectors; > + } > =20 > - if (offset =3D=3D -1) { > - memset(buf, 0, sectors * BDRV_SECTOR_SIZE); > - } else { > - ret =3D bdrv_pread(bs->file, offset, buf, > - sectors * BDRV_SECTOR_SIZE); > - if (ret !=3D sectors * BDRV_SECTOR_SIZE) { > + offset =3D get_sector_offset(bs, sector_num, false, false); > + if (offset =3D=3D -1) { > return -1; > + } else if (offset =3D=3D -2) { > + memset(buf, 0, sectors * BDRV_SECTOR_SIZE); > + } else { > + ret =3D bdrv_pread(bs->file, offset, buf, > + sectors * BDRV_SECTOR_SIZE); > + if (ret !=3D sectors * BDRV_SECTOR_SIZE) { > + return -1; > + } > } > + > + nb_sectors -=3D sectors; > + sector_num +=3D sectors; > + buf +=3D sectors * BDRV_SECTOR_SIZE; > } > + break; > + case VHD_DIFF: > + while (nb_sectors > 0) { > + offset =3D get_sector_offset(bs, sector_num, false, true); > + if (offset =3D=3D -1) { > + return -1; > + } else if (offset =3D=3D -2) { > + memset(buf, 0, BDRV_SECTOR_SIZE); > + } else if (offset =3D=3D -3) { > + ret =3D bdrv_pread(bs->backing_hd, sector_num << BDRV_SE= CTOR_BITS > + , buf, BDRV_SECTOR_SIZE); > + if (ret < 0) { > + return -1; > + } > + } else { > + ret =3D bdrv_pread(bs->file, offset, buf, BDRV_SECTOR_SI= ZE); > + if (ret !=3D BDRV_SECTOR_SIZE) { > + return -1; > + } > + } > =20 > - nb_sectors -=3D sectors; > - sector_num +=3D sectors; > - buf +=3D sectors * BDRV_SECTOR_SIZE; > + nb_sectors--; > + sector_num++; > + buf +=3D BDRV_SECTOR_SIZE; > + } > + break; > + default: > + return -1; > } > return 0; > } > @@ -546,44 +774,101 @@ static coroutine_fn int vpc_co_read(BlockDriverSta= te *bs, int64_t sector_num, > return ret; > } > =20 > -static int vpc_write(BlockDriverState *bs, int64_t sector_num, > - const uint8_t *buf, int nb_sectors) > +static inline int64_t write_bitmap(BlockDriverState *bs, int64_t sector_= num, > + int64_t sectors) > { > BDRVVPCState *s =3D bs->opaque; > - int64_t offset; > - int64_t sectors, sectors_per_block; > + uint64_t offset =3D 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; > - VHDFooter *footer =3D (VHDFooter *) s->footer_buf; > =20 > - if (be32_to_cpu(footer->type) =3D=3D VHD_FIXED) { > - return bdrv_write(bs->file, sector_num, buf, nb_sectors); > + pagetable_index =3D offset / s->block_size; > + pageentry_index =3D (offset % s->block_size) / 512; > + bitmap_offset =3D 512 * (uint64_t) s->pagetable[pagetable_index]; > + > + if (bitmap_offset > s->max_table_entries * s->block_size) { > + return -1; > + } > + ret =3D bdrv_pread(bs->file, bitmap_offset, bitmap, s->bitmap_size); > + if (ret < 0) { > + return -1; > } > - while (nb_sectors > 0) { > - offset =3D get_sector_offset(bs, sector_num, 1); > =20 > - sectors_per_block =3D s->block_size >> BDRV_SECTOR_BITS; > - sectors =3D sectors_per_block - (sector_num % sectors_per_block); > - if (sectors > nb_sectors) { > - sectors =3D nb_sectors; > + for (i =3D 0; i < sectors; i++) { > + bitmap_index =3D pageentry_index / 8; > + bitmapbit_index =3D 7 - pageentry_index % 8; > + bitmap[bitmap_index] |=3D (0x1 << bitmapbit_index); > + pageentry_index++; > + } > + ret =3D 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 =3D bs->opaque; > + VHDFooter *footer =3D (VHDFooter *) s->footer_buf; > + int64_t sectors_per_block =3D s->block_size >> BDRV_SECTOR_BITS; > + int64_t offset, sectors; > + bool diff =3D true; > + int ret =3D 0; > + > + switch (be32_to_cpu(footer->type)) { > + case VHD_FIXED: > + return bdrv_write(bs->file, sector_num, buf, nb_sectors); > + case VHD_DYNAMIC: > + case VHD_DIFF: > + if (be32_to_cpu(footer->type) =3D=3D VHD_DYNAMIC) { > + diff =3D false; > } > =20 > - if (offset =3D=3D -1) { > - offset =3D alloc_block(bs, sector_num); > - if (offset < 0) > + while (nb_sectors > 0) { > + sectors =3D sectors_per_block - (sector_num % sectors_per_bl= ock); > + if (sectors > nb_sectors) { > + sectors =3D nb_sectors; > + } > + > + offset =3D get_sector_offset(bs, sector_num, true, diff); > + if (offset =3D=3D -1) { > return -1; > - } > + } else if (offset =3D=3D -2) { > + offset =3D alloc_block(bs, sector_num, diff); > + if (offset < 0) { > + return -1; > + } > + } > =20 > - ret =3D bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR= _SIZE); > - if (ret !=3D sectors * BDRV_SECTOR_SIZE) { > - return -1; > - } > + ret =3D bdrv_pwrite(bs->file, offset, buf, > + sectors * BDRV_SECTOR_SIZE); > + if (ret !=3D sectors * BDRV_SECTOR_SIZE) { > + return -1; > + } > =20 > - nb_sectors -=3D sectors; > - sector_num +=3D sectors; > - buf +=3D sectors * BDRV_SECTOR_SIZE; > - } > + if (diff) { > + ret =3D write_bitmap(bs, sector_num, sectors); > + if (ret < 0) { > + return -1; > + } > + } > =20 > - return 0; > + nb_sectors -=3D sectors; > + sector_num +=3D sectors; > + buf +=3D sectors * BDRV_SECTOR_SIZE; > + } > + break; > + default: > + return -1; > + } > + return ret; > } > =20 > static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t secto= r_num, > @@ -911,6 +1196,7 @@ static BlockDriver bdrv_vpc =3D { > .bdrv_close =3D vpc_close, > .bdrv_reopen_prepare =3D vpc_reopen_prepare, > .bdrv_create =3D vpc_create, > + .supports_backing =3D true, > =20 > .bdrv_read =3D vpc_co_read, > .bdrv_write =3D vpc_co_write, > --=20 > 1.8.3.1 >=20 >=20 --2nTeH+t2PBomgucg Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAEBAgAGBQJULXDsAAoJEJykq7OBq3PIfCcH/2KqnMdRfQlhb5FePB6KFo1M aocoFFOW8qc7ZQqonTFku1sbT5nOjCF15qH1z5LGH5/W8N2usP+XFMiK2TBsg9Jt wiUaZM4uxIj/LAUIIhL6yKFvArJy/tRq1f5peyJMod7ozo2bTpCOV33Mop0tLz8A O39AcdDo/V8wOFGnP4N4UMuJsYb76X5PBCpJyMRPVyK+SWlngyEL2BCArKaQzk3x sWDUkujokkLoeEWqnIvhZpEjqKJGrmgbPvMXOv6qHFoMlqaW/V1F2/oGaVsAvKA3 2EObfhGUSBmM0zWyR0ckxHQDHFdwZKWLnzbAe8OTSQf6dYv0XhaDALT1lr5RDzs= =gZIR -----END PGP SIGNATURE----- --2nTeH+t2PBomgucg--