xref: /openbmc/qemu/block/vdi.c (revision e67b7aef7c7f67ecd0282e903e0daff806d5d680)
19aebd98aSStefan Weil /*
29aebd98aSStefan Weil  * Block driver for the Virtual Disk Image (VDI) format
39aebd98aSStefan Weil  *
4641543b7SStefan Weil  * Copyright (c) 2009, 2012 Stefan Weil
59aebd98aSStefan Weil  *
6dc86dd55SPhilippe Mathieu-Daudé  * SPDX-License-Identifier: GPL-2.0-or-later
7dc86dd55SPhilippe Mathieu-Daudé  *
89aebd98aSStefan Weil  * This program is free software: you can redistribute it and/or modify
99aebd98aSStefan Weil  * it under the terms of the GNU General Public License as published by
109aebd98aSStefan Weil  * the Free Software Foundation, either version 2 of the License, or
11dc86dd55SPhilippe Mathieu-Daudé  * (at your option) any later version.
129aebd98aSStefan Weil  *
139aebd98aSStefan Weil  * This program is distributed in the hope that it will be useful,
149aebd98aSStefan Weil  * but WITHOUT ANY WARRANTY; without even the implied warranty of
159aebd98aSStefan Weil  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
169aebd98aSStefan Weil  * GNU General Public License for more details.
179aebd98aSStefan Weil  *
189aebd98aSStefan Weil  * You should have received a copy of the GNU General Public License
199aebd98aSStefan Weil  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
209aebd98aSStefan Weil  *
219aebd98aSStefan Weil  * Reference:
229aebd98aSStefan Weil  * http://forums.virtualbox.org/viewtopic.php?t=8046
239aebd98aSStefan Weil  *
249aebd98aSStefan Weil  * This driver supports create / read / write operations on VDI images.
259aebd98aSStefan Weil  *
269aebd98aSStefan Weil  * Todo (see also TODO in code):
279aebd98aSStefan Weil  *
289aebd98aSStefan Weil  * Some features like snapshots are still missing.
299aebd98aSStefan Weil  *
309aebd98aSStefan Weil  * Deallocation of zero-filled blocks and shrinking images are missing, too
319aebd98aSStefan Weil  * (might be added to common block layer).
329aebd98aSStefan Weil  *
339aebd98aSStefan Weil  * Allocation of blocks could be optimized (less writes to block map and
349aebd98aSStefan Weil  * header).
359aebd98aSStefan Weil  *
36dc6fb73dSDeepak Kathayat  * Read and write of adjacent blocks could be done in one operation
379aebd98aSStefan Weil  * (current code uses one operation per block (1 MiB).
389aebd98aSStefan Weil  *
399aebd98aSStefan Weil  * The code is not thread safe (missing locks for changes in header and
409aebd98aSStefan Weil  * block table, no problem with current QEMU).
419aebd98aSStefan Weil  *
429aebd98aSStefan Weil  * Hints:
439aebd98aSStefan Weil  *
449aebd98aSStefan Weil  * Blocks (VDI documentation) correspond to clusters (QEMU).
459aebd98aSStefan Weil  * QEMU's backing files could be implemented using VDI snapshot files (TODO).
469aebd98aSStefan Weil  * VDI snapshot files may also contain the complete machine state.
479aebd98aSStefan Weil  * Maybe this machine state can be converted to QEMU PC machine snapshot data.
489aebd98aSStefan Weil  *
499aebd98aSStefan Weil  * The driver keeps a block cache (little endian entries) in memory.
509aebd98aSStefan Weil  * For the standard block size (1 MiB), a 1 TiB disk will use 4 MiB RAM,
519aebd98aSStefan Weil  * so this seems to be reasonable.
529aebd98aSStefan Weil  */
539aebd98aSStefan Weil 
5480c71a24SPeter Maydell #include "qemu/osdep.h"
55f043568fSPhilippe Mathieu-Daudé #include "qemu/units.h"
56da34e65cSMarkus Armbruster #include "qapi/error.h"
5749858b50SMax Reitz #include "qapi/qobject-input-visitor.h"
5849858b50SMax Reitz #include "qapi/qapi-visit-block-core.h"
59737e150eSPaolo Bonzini #include "block/block_int.h"
60f853465aSMarkus Armbruster #include "block/qdict.h"
61a08f0c3bSKevin Wolf #include "sysemu/block-backend.h"
621de7afc9SPaolo Bonzini #include "qemu/module.h"
63922a01a0SMarkus Armbruster #include "qemu/option.h"
6458369e22SPaolo Bonzini #include "qemu/bswap.h"
65795c40b8SJuan Quintela #include "migration/blocker.h"
6610817bf0SDaniel P. Berrange #include "qemu/coroutine.h"
67f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
687c6f55b6SFam Zheng #include "qemu/uuid.h"
695df022cfSPeter Maydell #include "qemu/memalign.h"
709aebd98aSStefan Weil 
719aebd98aSStefan Weil /* Code configuration options. */
729aebd98aSStefan Weil 
739aebd98aSStefan Weil /* Enable debug messages. */
749aebd98aSStefan Weil //~ #define CONFIG_VDI_DEBUG
759aebd98aSStefan Weil 
769aebd98aSStefan Weil /* Support write operations on VDI images. */
779aebd98aSStefan Weil #define CONFIG_VDI_WRITE
789aebd98aSStefan Weil 
799aebd98aSStefan Weil /* Support non-standard block (cluster) size. This is untested.
809aebd98aSStefan Weil  * Maybe it will be needed for very large images.
819aebd98aSStefan Weil  */
829aebd98aSStefan Weil //~ #define CONFIG_VDI_BLOCK_SIZE
839aebd98aSStefan Weil 
849aebd98aSStefan Weil /* Support static (fixed, pre-allocated) images. */
859aebd98aSStefan Weil #define CONFIG_VDI_STATIC_IMAGE
869aebd98aSStefan Weil 
879aebd98aSStefan Weil /* Command line option for static images. */
889aebd98aSStefan Weil #define BLOCK_OPT_STATIC "static"
899aebd98aSStefan Weil 
90*4d7c5f83SPeter Maydell #define SECTOR_SIZE 512ULL
9114632122SMarkus Armbruster #define DEFAULT_CLUSTER_SIZE 1048576
9214632122SMarkus Armbruster /* Note: can't use 1 * MiB, because it's passed to stringify() */
939aebd98aSStefan Weil 
949aebd98aSStefan Weil #if defined(CONFIG_VDI_DEBUG)
95b80666bfSEric Blake #define VDI_DEBUG 1
969aebd98aSStefan Weil #else
97b80666bfSEric Blake #define VDI_DEBUG 0
989aebd98aSStefan Weil #endif
999aebd98aSStefan Weil 
100b80666bfSEric Blake #define logout(fmt, ...) \
101b80666bfSEric Blake     do {                                                                \
102b80666bfSEric Blake         if (VDI_DEBUG) {                                                \
103b80666bfSEric Blake             fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__); \
104b80666bfSEric Blake         }                                                               \
105b80666bfSEric Blake     } while (0)
106b80666bfSEric Blake 
1079aebd98aSStefan Weil /* Image signature. */
1089aebd98aSStefan Weil #define VDI_SIGNATURE 0xbeda107f
1099aebd98aSStefan Weil 
1109aebd98aSStefan Weil /* Image version. */
1119aebd98aSStefan Weil #define VDI_VERSION_1_1 0x00010001
1129aebd98aSStefan Weil 
1139aebd98aSStefan Weil /* Image type. */
1149aebd98aSStefan Weil #define VDI_TYPE_DYNAMIC 1
1159aebd98aSStefan Weil #define VDI_TYPE_STATIC  2
1169aebd98aSStefan Weil 
1179aebd98aSStefan Weil /* Innotek / SUN images use these strings in header.text:
1189aebd98aSStefan Weil  * "<<< innotek VirtualBox Disk Image >>>\n"
1199aebd98aSStefan Weil  * "<<< Sun xVM VirtualBox Disk Image >>>\n"
1209aebd98aSStefan Weil  * "<<< Sun VirtualBox Disk Image >>>\n"
1219aebd98aSStefan Weil  * The value does not matter, so QEMU created images use a different text.
1229aebd98aSStefan Weil  */
1239aebd98aSStefan Weil #define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
1249aebd98aSStefan Weil 
125c794b4e0SEric Sunshine /* A never-allocated block; semantically arbitrary content. */
126c794b4e0SEric Sunshine #define VDI_UNALLOCATED 0xffffffffU
127c794b4e0SEric Sunshine 
128c794b4e0SEric Sunshine /* A discarded (no longer allocated) block; semantically zero-filled. */
129c794b4e0SEric Sunshine #define VDI_DISCARDED   0xfffffffeU
130c794b4e0SEric Sunshine 
131c794b4e0SEric Sunshine #define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
1329aebd98aSStefan Weil 
133d20418eeSMax Reitz /* The bmap will take up VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) bytes; since
134d20418eeSMax Reitz  * the bmap is read and written in a single operation, its size needs to be
135d20418eeSMax Reitz  * limited to INT_MAX; furthermore, when opening an image, the bmap size is
136d20418eeSMax Reitz  * rounded up to be aligned on BDRV_SECTOR_SIZE.
137d20418eeSMax Reitz  * Therefore this should satisfy the following:
138d20418eeSMax Reitz  * VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) + BDRV_SECTOR_SIZE == INT_MAX + 1
139d20418eeSMax Reitz  * (INT_MAX + 1 is the first value not representable as an int)
140d20418eeSMax Reitz  * This guarantees that any value below or equal to the constant will, when
141d20418eeSMax Reitz  * multiplied by sizeof(uint32_t) and rounded up to a BDRV_SECTOR_SIZE boundary,
142d20418eeSMax Reitz  * still be below or equal to INT_MAX. */
143d20418eeSMax Reitz #define VDI_BLOCKS_IN_IMAGE_MAX \
144d20418eeSMax Reitz     ((unsigned)((INT_MAX + 1u - BDRV_SECTOR_SIZE) / sizeof(uint32_t)))
14563fa06dcSJeff Cody #define VDI_DISK_SIZE_MAX        ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
14663fa06dcSJeff Cody                                   (uint64_t)DEFAULT_CLUSTER_SIZE)
14763fa06dcSJeff Cody 
14849858b50SMax Reitz static QemuOptsList vdi_create_opts;
14949858b50SMax Reitz 
1509aebd98aSStefan Weil typedef struct {
1519aebd98aSStefan Weil     char text[0x40];
1529aebd98aSStefan Weil     uint32_t signature;
1539aebd98aSStefan Weil     uint32_t version;
1549aebd98aSStefan Weil     uint32_t header_size;
1559aebd98aSStefan Weil     uint32_t image_type;
1569aebd98aSStefan Weil     uint32_t image_flags;
1579aebd98aSStefan Weil     char description[256];
1589aebd98aSStefan Weil     uint32_t offset_bmap;
1599aebd98aSStefan Weil     uint32_t offset_data;
1609aebd98aSStefan Weil     uint32_t cylinders;         /* disk geometry, unused here */
1619aebd98aSStefan Weil     uint32_t heads;             /* disk geometry, unused here */
1629aebd98aSStefan Weil     uint32_t sectors;           /* disk geometry, unused here */
1639aebd98aSStefan Weil     uint32_t sector_size;
1649aebd98aSStefan Weil     uint32_t unused1;
1659aebd98aSStefan Weil     uint64_t disk_size;
1669aebd98aSStefan Weil     uint32_t block_size;
1679aebd98aSStefan Weil     uint32_t block_extra;       /* unused here */
1689aebd98aSStefan Weil     uint32_t blocks_in_image;
1699aebd98aSStefan Weil     uint32_t blocks_allocated;
1707c6f55b6SFam Zheng     QemuUUID uuid_image;
1717c6f55b6SFam Zheng     QemuUUID uuid_last_snap;
1727c6f55b6SFam Zheng     QemuUUID uuid_link;
1737c6f55b6SFam Zheng     QemuUUID uuid_parent;
1749aebd98aSStefan Weil     uint64_t unused2[7];
1758368febdSJeff Cody } QEMU_PACKED VdiHeader;
1769aebd98aSStefan Weil 
177d4f18971SAlberto Garcia QEMU_BUILD_BUG_ON(sizeof(VdiHeader) != 512);
178d4f18971SAlberto Garcia 
1799aebd98aSStefan Weil typedef struct {
1809aebd98aSStefan Weil     /* The block map entries are little endian (even in memory). */
1819aebd98aSStefan Weil     uint32_t *bmap;
1829aebd98aSStefan Weil     /* Size of block (bytes). */
1839aebd98aSStefan Weil     uint32_t block_size;
1849aebd98aSStefan Weil     /* First sector of block map. */
1859aebd98aSStefan Weil     uint32_t bmap_sector;
1864ff9786cSStefan Weil     /* VDI header (converted to host endianness). */
1879aebd98aSStefan Weil     VdiHeader header;
188fc9d106cSKevin Wolf 
1891e886639SPaolo Bonzini     CoRwlock bmap_lock;
190f0ab6f10SMax Reitz 
191fc9d106cSKevin Wolf     Error *migration_blocker;
1929aebd98aSStefan Weil } BDRVVdiState;
1939aebd98aSStefan Weil 
vdi_header_to_cpu(VdiHeader * header)1949aebd98aSStefan Weil static void vdi_header_to_cpu(VdiHeader *header)
1959aebd98aSStefan Weil {
19609190184SPeter Maydell     header->signature = le32_to_cpu(header->signature);
19709190184SPeter Maydell     header->version = le32_to_cpu(header->version);
19809190184SPeter Maydell     header->header_size = le32_to_cpu(header->header_size);
19909190184SPeter Maydell     header->image_type = le32_to_cpu(header->image_type);
20009190184SPeter Maydell     header->image_flags = le32_to_cpu(header->image_flags);
20109190184SPeter Maydell     header->offset_bmap = le32_to_cpu(header->offset_bmap);
20209190184SPeter Maydell     header->offset_data = le32_to_cpu(header->offset_data);
20309190184SPeter Maydell     header->cylinders = le32_to_cpu(header->cylinders);
20409190184SPeter Maydell     header->heads = le32_to_cpu(header->heads);
20509190184SPeter Maydell     header->sectors = le32_to_cpu(header->sectors);
20609190184SPeter Maydell     header->sector_size = le32_to_cpu(header->sector_size);
20709190184SPeter Maydell     header->disk_size = le64_to_cpu(header->disk_size);
20809190184SPeter Maydell     header->block_size = le32_to_cpu(header->block_size);
20909190184SPeter Maydell     header->block_extra = le32_to_cpu(header->block_extra);
21009190184SPeter Maydell     header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
21109190184SPeter Maydell     header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
2121324f063SPeter Maydell     header->uuid_image = qemu_uuid_bswap(header->uuid_image);
2131324f063SPeter Maydell     header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
2141324f063SPeter Maydell     header->uuid_link = qemu_uuid_bswap(header->uuid_link);
2151324f063SPeter Maydell     header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
2169aebd98aSStefan Weil }
2179aebd98aSStefan Weil 
vdi_header_to_le(VdiHeader * header)2189aebd98aSStefan Weil static void vdi_header_to_le(VdiHeader *header)
2199aebd98aSStefan Weil {
22009190184SPeter Maydell     header->signature = cpu_to_le32(header->signature);
22109190184SPeter Maydell     header->version = cpu_to_le32(header->version);
22209190184SPeter Maydell     header->header_size = cpu_to_le32(header->header_size);
22309190184SPeter Maydell     header->image_type = cpu_to_le32(header->image_type);
22409190184SPeter Maydell     header->image_flags = cpu_to_le32(header->image_flags);
22509190184SPeter Maydell     header->offset_bmap = cpu_to_le32(header->offset_bmap);
22609190184SPeter Maydell     header->offset_data = cpu_to_le32(header->offset_data);
22709190184SPeter Maydell     header->cylinders = cpu_to_le32(header->cylinders);
22809190184SPeter Maydell     header->heads = cpu_to_le32(header->heads);
22909190184SPeter Maydell     header->sectors = cpu_to_le32(header->sectors);
23009190184SPeter Maydell     header->sector_size = cpu_to_le32(header->sector_size);
23109190184SPeter Maydell     header->disk_size = cpu_to_le64(header->disk_size);
23209190184SPeter Maydell     header->block_size = cpu_to_le32(header->block_size);
23309190184SPeter Maydell     header->block_extra = cpu_to_le32(header->block_extra);
23409190184SPeter Maydell     header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
23509190184SPeter Maydell     header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
2361324f063SPeter Maydell     header->uuid_image = qemu_uuid_bswap(header->uuid_image);
2371324f063SPeter Maydell     header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
2381324f063SPeter Maydell     header->uuid_link = qemu_uuid_bswap(header->uuid_link);
2391324f063SPeter Maydell     header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
2409aebd98aSStefan Weil }
2419aebd98aSStefan Weil 
vdi_header_print(VdiHeader * header)2429aebd98aSStefan Weil static void vdi_header_print(VdiHeader *header)
2439aebd98aSStefan Weil {
244721da039SCédric Le Goater     char uuidstr[UUID_STR_LEN];
245ac928b8eSPeter Maydell     QemuUUID uuid;
2469aebd98aSStefan Weil     logout("text        %s", header->text);
2479f0470bbSStefan Weil     logout("signature   0x%08x\n", header->signature);
2489aebd98aSStefan Weil     logout("header size 0x%04x\n", header->header_size);
2499aebd98aSStefan Weil     logout("image type  0x%04x\n", header->image_type);
2509aebd98aSStefan Weil     logout("image flags 0x%04x\n", header->image_flags);
2519aebd98aSStefan Weil     logout("description %s\n", header->description);
2529aebd98aSStefan Weil     logout("offset bmap 0x%04x\n", header->offset_bmap);
2539aebd98aSStefan Weil     logout("offset data 0x%04x\n", header->offset_data);
2549aebd98aSStefan Weil     logout("cylinders   0x%04x\n", header->cylinders);
2559aebd98aSStefan Weil     logout("heads       0x%04x\n", header->heads);
2569aebd98aSStefan Weil     logout("sectors     0x%04x\n", header->sectors);
2579aebd98aSStefan Weil     logout("sector size 0x%04x\n", header->sector_size);
2589aebd98aSStefan Weil     logout("image size  0x%" PRIx64 " B (%" PRIu64 " MiB)\n",
2599aebd98aSStefan Weil            header->disk_size, header->disk_size / MiB);
2609aebd98aSStefan Weil     logout("block size  0x%04x\n", header->block_size);
2619aebd98aSStefan Weil     logout("block extra 0x%04x\n", header->block_extra);
2629aebd98aSStefan Weil     logout("blocks tot. 0x%04x\n", header->blocks_in_image);
2639aebd98aSStefan Weil     logout("blocks all. 0x%04x\n", header->blocks_allocated);
264ac928b8eSPeter Maydell     uuid = header->uuid_image;
265ac928b8eSPeter Maydell     qemu_uuid_unparse(&uuid, uuidstr);
266ac928b8eSPeter Maydell     logout("uuid image  %s\n", uuidstr);
267ac928b8eSPeter Maydell     uuid = header->uuid_last_snap;
268ac928b8eSPeter Maydell     qemu_uuid_unparse(&uuid, uuidstr);
269ac928b8eSPeter Maydell     logout("uuid snap   %s\n", uuidstr);
270ac928b8eSPeter Maydell     uuid = header->uuid_link;
271ac928b8eSPeter Maydell     qemu_uuid_unparse(&uuid, uuidstr);
272ac928b8eSPeter Maydell     logout("uuid link   %s\n", uuidstr);
273ac928b8eSPeter Maydell     uuid = header->uuid_parent;
274ac928b8eSPeter Maydell     qemu_uuid_unparse(&uuid, uuidstr);
275ac928b8eSPeter Maydell     logout("uuid parent %s\n", uuidstr);
2769aebd98aSStefan Weil }
2779aebd98aSStefan Weil 
vdi_co_check(BlockDriverState * bs,BdrvCheckResult * res,BdrvCheckMode fix)2782fd61638SPaolo Bonzini static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
2794534ff54SKevin Wolf                                      BdrvCheckMode fix)
2809aebd98aSStefan Weil {
2819aebd98aSStefan Weil     /* TODO: additional checks possible. */
2829aebd98aSStefan Weil     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
2839aebd98aSStefan Weil     uint32_t blocks_allocated = 0;
2849aebd98aSStefan Weil     uint32_t block;
2859aebd98aSStefan Weil     uint32_t *bmap;
2869aebd98aSStefan Weil     logout("\n");
2879aebd98aSStefan Weil 
2884534ff54SKevin Wolf     if (fix) {
2894534ff54SKevin Wolf         return -ENOTSUP;
2904534ff54SKevin Wolf     }
2914534ff54SKevin Wolf 
2925839e53bSMarkus Armbruster     bmap = g_try_new(uint32_t, s->header.blocks_in_image);
29317cce735SKevin Wolf     if (s->header.blocks_in_image && bmap == NULL) {
29417cce735SKevin Wolf         res->check_errors++;
29517cce735SKevin Wolf         return -ENOMEM;
29617cce735SKevin Wolf     }
29717cce735SKevin Wolf 
2989aebd98aSStefan Weil     memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t));
2999aebd98aSStefan Weil 
3009aebd98aSStefan Weil     /* Check block map and value of blocks_allocated. */
3019aebd98aSStefan Weil     for (block = 0; block < s->header.blocks_in_image; block++) {
3029aebd98aSStefan Weil         uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
303c794b4e0SEric Sunshine         if (VDI_IS_ALLOCATED(bmap_entry)) {
3049aebd98aSStefan Weil             if (bmap_entry < s->header.blocks_in_image) {
3059aebd98aSStefan Weil                 blocks_allocated++;
306c794b4e0SEric Sunshine                 if (!VDI_IS_ALLOCATED(bmap[bmap_entry])) {
3079aebd98aSStefan Weil                     bmap[bmap_entry] = bmap_entry;
3089aebd98aSStefan Weil                 } else {
3099aebd98aSStefan Weil                     fprintf(stderr, "ERROR: block index %" PRIu32
3109aebd98aSStefan Weil                             " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry);
3119ac228e0SKevin Wolf                     res->corruptions++;
3129aebd98aSStefan Weil                 }
3139aebd98aSStefan Weil             } else {
3149aebd98aSStefan Weil                 fprintf(stderr, "ERROR: block index %" PRIu32
3159aebd98aSStefan Weil                         " too large, is %" PRIu32 "\n", block, bmap_entry);
3169ac228e0SKevin Wolf                 res->corruptions++;
3179aebd98aSStefan Weil             }
3189aebd98aSStefan Weil         }
3199aebd98aSStefan Weil     }
3209aebd98aSStefan Weil     if (blocks_allocated != s->header.blocks_allocated) {
3219aebd98aSStefan Weil         fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32
3229aebd98aSStefan Weil                ", should be %" PRIu32 "\n",
3239aebd98aSStefan Weil                blocks_allocated, s->header.blocks_allocated);
3249ac228e0SKevin Wolf         res->corruptions++;
3259aebd98aSStefan Weil     }
3269aebd98aSStefan Weil 
3277267c094SAnthony Liguori     g_free(bmap);
3289aebd98aSStefan Weil 
3299ac228e0SKevin Wolf     return 0;
3309aebd98aSStefan Weil }
3319aebd98aSStefan Weil 
3323d47eb0aSEmanuele Giuseppe Esposito static int coroutine_fn
vdi_co_get_info(BlockDriverState * bs,BlockDriverInfo * bdi)3333d47eb0aSEmanuele Giuseppe Esposito vdi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
3349aebd98aSStefan Weil {
3353d47eb0aSEmanuele Giuseppe Esposito     /* TODO: vdi_co_get_info would be needed for machine snapshots.
3369aebd98aSStefan Weil        vm_state_offset is still missing. */
3379aebd98aSStefan Weil     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
3389aebd98aSStefan Weil     logout("\n");
3399aebd98aSStefan Weil     bdi->cluster_size = s->block_size;
3409aebd98aSStefan Weil     bdi->vm_state_offset = 0;
3419aebd98aSStefan Weil     return 0;
3429aebd98aSStefan Weil }
3439aebd98aSStefan Weil 
vdi_make_empty(BlockDriverState * bs)3449aebd98aSStefan Weil static int vdi_make_empty(BlockDriverState *bs)
3459aebd98aSStefan Weil {
3469aebd98aSStefan Weil     /* TODO: missing code. */
3479aebd98aSStefan Weil     logout("\n");
3489aebd98aSStefan Weil     /* The return value for missing code must be 0, see block.c. */
3499aebd98aSStefan Weil     return 0;
3509aebd98aSStefan Weil }
3519aebd98aSStefan Weil 
vdi_probe(const uint8_t * buf,int buf_size,const char * filename)3529aebd98aSStefan Weil static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
3539aebd98aSStefan Weil {
3549aebd98aSStefan Weil     const VdiHeader *header = (const VdiHeader *)buf;
355dddc7750SJeff Cody     int ret = 0;
3569aebd98aSStefan Weil 
3579aebd98aSStefan Weil     logout("\n");
3589aebd98aSStefan Weil 
3599aebd98aSStefan Weil     if (buf_size < sizeof(*header)) {
3609aebd98aSStefan Weil         /* Header too small, no VDI. */
3619aebd98aSStefan Weil     } else if (le32_to_cpu(header->signature) == VDI_SIGNATURE) {
362dddc7750SJeff Cody         ret = 100;
3639aebd98aSStefan Weil     }
3649aebd98aSStefan Weil 
365dddc7750SJeff Cody     if (ret == 0) {
3669aebd98aSStefan Weil         logout("no vdi image\n");
3679aebd98aSStefan Weil     } else {
3689aebd98aSStefan Weil         logout("%s", header->text);
3699aebd98aSStefan Weil     }
3709aebd98aSStefan Weil 
371dddc7750SJeff Cody     return ret;
3729aebd98aSStefan Weil }
3739aebd98aSStefan Weil 
vdi_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)374015a1036SMax Reitz static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
375015a1036SMax Reitz                     Error **errp)
3769aebd98aSStefan Weil {
3779aebd98aSStefan Weil     BDRVVdiState *s = bs->opaque;
3789aebd98aSStefan Weil     VdiHeader header;
3799aebd98aSStefan Weil     size_t bmap_size;
3808937f822SStefan Weil     int ret;
381ac928b8eSPeter Maydell     QemuUUID uuid_link, uuid_parent;
3829aebd98aSStefan Weil 
38383930780SVladimir Sementsov-Ogievskiy     ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
38483930780SVladimir Sementsov-Ogievskiy     if (ret < 0) {
38583930780SVladimir Sementsov-Ogievskiy         return ret;
3864e4bf5c4SKevin Wolf     }
3874e4bf5c4SKevin Wolf 
388a4b740dbSKevin Wolf     GRAPH_RDLOCK_GUARD_MAINLOOP();
389a4b740dbSKevin Wolf 
3909aebd98aSStefan Weil     logout("\n");
3919aebd98aSStefan Weil 
39232cc71deSAlberto Faria     ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0);
3938937f822SStefan Weil     if (ret < 0) {
3949aebd98aSStefan Weil         goto fail;
3959aebd98aSStefan Weil     }
3969aebd98aSStefan Weil 
3979aebd98aSStefan Weil     vdi_header_to_cpu(&header);
39895a14d51SKevin Wolf     if (VDI_DEBUG) {
3999aebd98aSStefan Weil         vdi_header_print(&header);
40095a14d51SKevin Wolf     }
4019aebd98aSStefan Weil 
40263fa06dcSJeff Cody     if (header.disk_size > VDI_DISK_SIZE_MAX) {
40363fa06dcSJeff Cody         error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
40463fa06dcSJeff Cody                           ", max supported is 0x%" PRIx64 ")",
40563fa06dcSJeff Cody                           header.disk_size, VDI_DISK_SIZE_MAX);
40663fa06dcSJeff Cody         ret = -ENOTSUP;
40763fa06dcSJeff Cody         goto fail;
40863fa06dcSJeff Cody     }
40963fa06dcSJeff Cody 
410ac928b8eSPeter Maydell     uuid_link = header.uuid_link;
411ac928b8eSPeter Maydell     uuid_parent = header.uuid_parent;
412ac928b8eSPeter Maydell 
413f21dc3a4SStefan Weil     if (header.disk_size % SECTOR_SIZE != 0) {
414f21dc3a4SStefan Weil         /* 'VBoxManage convertfromraw' can create images with odd disk sizes.
415f21dc3a4SStefan Weil            We accept them but round the disk size to the next multiple of
416f21dc3a4SStefan Weil            SECTOR_SIZE. */
417f21dc3a4SStefan Weil         logout("odd disk size %" PRIu64 " B, round up\n", header.disk_size);
418e9082e47SMax Reitz         header.disk_size = ROUND_UP(header.disk_size, SECTOR_SIZE);
419f21dc3a4SStefan Weil     }
420f21dc3a4SStefan Weil 
4210e87ba2cSStefan Weil     if (header.signature != VDI_SIGNATURE) {
422521b2b5dSMax Reitz         error_setg(errp, "Image not in VDI format (bad signature %08" PRIx32
423521b2b5dSMax Reitz                    ")", header.signature);
42476abe407SPaolo Bonzini         ret = -EINVAL;
4250e87ba2cSStefan Weil         goto fail;
4260e87ba2cSStefan Weil     } else if (header.version != VDI_VERSION_1_1) {
427521b2b5dSMax Reitz         error_setg(errp, "unsupported VDI image (version %" PRIu32 ".%" PRIu32
428521b2b5dSMax Reitz                    ")", header.version >> 16, header.version & 0xffff);
4298937f822SStefan Weil         ret = -ENOTSUP;
4309aebd98aSStefan Weil         goto fail;
4319aebd98aSStefan Weil     } else if (header.offset_bmap % SECTOR_SIZE != 0) {
4329aebd98aSStefan Weil         /* We only support block maps which start on a sector boundary. */
4335b7aa9b5SPaolo Bonzini         error_setg(errp, "unsupported VDI image (unaligned block map offset "
434521b2b5dSMax Reitz                    "0x%" PRIx32 ")", header.offset_bmap);
4358937f822SStefan Weil         ret = -ENOTSUP;
4369aebd98aSStefan Weil         goto fail;
4379aebd98aSStefan Weil     } else if (header.offset_data % SECTOR_SIZE != 0) {
4389aebd98aSStefan Weil         /* We only support data blocks which start on a sector boundary. */
439521b2b5dSMax Reitz         error_setg(errp, "unsupported VDI image (unaligned data offset 0x%"
440521b2b5dSMax Reitz                    PRIx32 ")", header.offset_data);
4418937f822SStefan Weil         ret = -ENOTSUP;
4429aebd98aSStefan Weil         goto fail;
4439aebd98aSStefan Weil     } else if (header.sector_size != SECTOR_SIZE) {
444521b2b5dSMax Reitz         error_setg(errp, "unsupported VDI image (sector size %" PRIu32
445*4d7c5f83SPeter Maydell                    " is not %llu)", header.sector_size, SECTOR_SIZE);
4468937f822SStefan Weil         ret = -ENOTSUP;
4479aebd98aSStefan Weil         goto fail;
44863fa06dcSJeff Cody     } else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
449521b2b5dSMax Reitz         error_setg(errp, "unsupported VDI image (block size %" PRIu32
4503dd5b8f4SLeonid Bloch                          " is not %" PRIu32 ")",
451f043568fSPhilippe Mathieu-Daudé                    header.block_size, DEFAULT_CLUSTER_SIZE);
4528937f822SStefan Weil         ret = -ENOTSUP;
4539aebd98aSStefan Weil         goto fail;
454f21dc3a4SStefan Weil     } else if (header.disk_size >
455f21dc3a4SStefan Weil                (uint64_t)header.blocks_in_image * header.block_size) {
4565b7aa9b5SPaolo Bonzini         error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", "
4575b7aa9b5SPaolo Bonzini                    "image bitmap has room for %" PRIu64 ")",
4585b7aa9b5SPaolo Bonzini                    header.disk_size,
4595b7aa9b5SPaolo Bonzini                    (uint64_t)header.blocks_in_image * header.block_size);
4608937f822SStefan Weil         ret = -ENOTSUP;
4619aebd98aSStefan Weil         goto fail;
462ac928b8eSPeter Maydell     } else if (!qemu_uuid_is_null(&uuid_link)) {
4635b7aa9b5SPaolo Bonzini         error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
4648937f822SStefan Weil         ret = -ENOTSUP;
4659aebd98aSStefan Weil         goto fail;
466ac928b8eSPeter Maydell     } else if (!qemu_uuid_is_null(&uuid_parent)) {
4675b7aa9b5SPaolo Bonzini         error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
4688937f822SStefan Weil         ret = -ENOTSUP;
4699aebd98aSStefan Weil         goto fail;
47063fa06dcSJeff Cody     } else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) {
47163fa06dcSJeff Cody         error_setg(errp, "unsupported VDI image "
47263fa06dcSJeff Cody                          "(too many blocks %u, max is %u)",
47363fa06dcSJeff Cody                           header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX);
47463fa06dcSJeff Cody         ret = -ENOTSUP;
47563fa06dcSJeff Cody         goto fail;
4769aebd98aSStefan Weil     }
4779aebd98aSStefan Weil 
4789aebd98aSStefan Weil     bs->total_sectors = header.disk_size / SECTOR_SIZE;
4799aebd98aSStefan Weil 
4809aebd98aSStefan Weil     s->block_size = header.block_size;
4819aebd98aSStefan Weil     s->bmap_sector = header.offset_bmap / SECTOR_SIZE;
4829aebd98aSStefan Weil     s->header = header;
4839aebd98aSStefan Weil 
4849aebd98aSStefan Weil     bmap_size = header.blocks_in_image * sizeof(uint32_t);
485e9082e47SMax Reitz     bmap_size = DIV_ROUND_UP(bmap_size, SECTOR_SIZE);
4869a4f4c31SKevin Wolf     s->bmap = qemu_try_blockalign(bs->file->bs, bmap_size * SECTOR_SIZE);
48717cce735SKevin Wolf     if (s->bmap == NULL) {
48817cce735SKevin Wolf         ret = -ENOMEM;
48917cce735SKevin Wolf         goto fail;
49017cce735SKevin Wolf     }
49117cce735SKevin Wolf 
49232cc71deSAlberto Faria     ret = bdrv_pread(bs->file, header.offset_bmap, bmap_size * SECTOR_SIZE,
49332cc71deSAlberto Faria                      s->bmap, 0);
4948937f822SStefan Weil     if (ret < 0) {
4959aebd98aSStefan Weil         goto fail_free_bmap;
4969aebd98aSStefan Weil     }
4979aebd98aSStefan Weil 
498fc9d106cSKevin Wolf     /* Disable migration when vdi images are used */
49981e5f78aSAlberto Garcia     error_setg(&s->migration_blocker, "The vdi format used by node '%s' "
50081e5f78aSAlberto Garcia                "does not support live migration",
50181e5f78aSAlberto Garcia                bdrv_get_device_or_node_name(bs));
5024026f1c4SKevin Wolf 
503e0ee3a8fSSteve Sistare     ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
504386f6c07SMarkus Armbruster     if (ret < 0) {
505fe44dc91SAshijeet Acharya         goto fail_free_bmap;
506fe44dc91SAshijeet Acharya     }
507fc9d106cSKevin Wolf 
5081e886639SPaolo Bonzini     qemu_co_rwlock_init(&s->bmap_lock);
509f0ab6f10SMax Reitz 
5109aebd98aSStefan Weil     return 0;
5119aebd98aSStefan Weil 
5129aebd98aSStefan Weil  fail_free_bmap:
51317cce735SKevin Wolf     qemu_vfree(s->bmap);
5149aebd98aSStefan Weil 
5159aebd98aSStefan Weil  fail:
5168937f822SStefan Weil     return ret;
5179aebd98aSStefan Weil }
5189aebd98aSStefan Weil 
vdi_reopen_prepare(BDRVReopenState * state,BlockReopenQueue * queue,Error ** errp)519ecfe2bbaSJeff Cody static int vdi_reopen_prepare(BDRVReopenState *state,
520ecfe2bbaSJeff Cody                               BlockReopenQueue *queue, Error **errp)
521ecfe2bbaSJeff Cody {
522ecfe2bbaSJeff Cody     return 0;
523ecfe2bbaSJeff Cody }
524ecfe2bbaSJeff Cody 
52579a55866SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
vdi_co_block_status(BlockDriverState * bs,bool want_zero,int64_t offset,int64_t bytes,int64_t * pnum,int64_t * map,BlockDriverState ** file)52679a55866SKevin Wolf vdi_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
52779a55866SKevin Wolf                     int64_t bytes, int64_t *pnum, int64_t *map,
52867635f6aSEric Blake                     BlockDriverState **file)
5299aebd98aSStefan Weil {
5309aebd98aSStefan Weil     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
53167635f6aSEric Blake     size_t bmap_index = offset / s->block_size;
53267635f6aSEric Blake     size_t index_in_block = offset % s->block_size;
5339aebd98aSStefan Weil     uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
5344bc74be9SPaolo Bonzini     int result;
5354bc74be9SPaolo Bonzini 
53667635f6aSEric Blake     logout("%p, %" PRId64 ", %" PRId64 ", %p\n", bs, offset, bytes, pnum);
53767635f6aSEric Blake     *pnum = MIN(s->block_size - index_in_block, bytes);
5384bc74be9SPaolo Bonzini     result = VDI_IS_ALLOCATED(bmap_entry);
5394bc74be9SPaolo Bonzini     if (!result) {
5402ea0332fSVladimir Sementsov-Ogievskiy         return BDRV_BLOCK_ZERO;
5414bc74be9SPaolo Bonzini     }
5424bc74be9SPaolo Bonzini 
54367635f6aSEric Blake     *map = s->header.offset_data + (uint64_t)bmap_entry * s->block_size +
54467635f6aSEric Blake         index_in_block;
5458bfb1371SFam Zheng     *file = bs->file->bs;
546ad6434dcSMax Reitz     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID |
547ad6434dcSMax Reitz         (s->header.image_type == VDI_TYPE_STATIC ? BDRV_BLOCK_RECURSE : 0);
5489aebd98aSStefan Weil }
5499aebd98aSStefan Weil 
550b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
vdi_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)551f7ef38ddSVladimir Sementsov-Ogievskiy vdi_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
552f7ef38ddSVladimir Sementsov-Ogievskiy               QEMUIOVector *qiov, BdrvRequestFlags flags)
5539aebd98aSStefan Weil {
5549aebd98aSStefan Weil     BDRVVdiState *s = bs->opaque;
5550865bb6fSKevin Wolf     QEMUIOVector local_qiov;
5569aebd98aSStefan Weil     uint32_t bmap_entry;
5579aebd98aSStefan Weil     uint32_t block_index;
5580865bb6fSKevin Wolf     uint32_t offset_in_block;
5590865bb6fSKevin Wolf     uint32_t n_bytes;
5600865bb6fSKevin Wolf     uint64_t bytes_done = 0;
561eb9566d1SPaolo Bonzini     int ret = 0;
5624de659e8SPaolo Bonzini 
5634de659e8SPaolo Bonzini     logout("\n");
5649aebd98aSStefan Weil 
5650865bb6fSKevin Wolf     qemu_iovec_init(&local_qiov, qiov->niov);
5669aebd98aSStefan Weil 
5670865bb6fSKevin Wolf     while (ret >= 0 && bytes > 0) {
5680865bb6fSKevin Wolf         block_index = offset / s->block_size;
5690865bb6fSKevin Wolf         offset_in_block = offset % s->block_size;
5700865bb6fSKevin Wolf         n_bytes = MIN(bytes, s->block_size - offset_in_block);
5710865bb6fSKevin Wolf 
5720865bb6fSKevin Wolf         logout("will read %u bytes starting at offset %" PRIu64 "\n",
5730865bb6fSKevin Wolf                n_bytes, offset);
5749aebd98aSStefan Weil 
5759aebd98aSStefan Weil         /* prepare next AIO request */
5761e886639SPaolo Bonzini         qemu_co_rwlock_rdlock(&s->bmap_lock);
5779aebd98aSStefan Weil         bmap_entry = le32_to_cpu(s->bmap[block_index]);
5781e886639SPaolo Bonzini         qemu_co_rwlock_unlock(&s->bmap_lock);
579c794b4e0SEric Sunshine         if (!VDI_IS_ALLOCATED(bmap_entry)) {
5809aebd98aSStefan Weil             /* Block not allocated, return zeros, no need to wait. */
5810865bb6fSKevin Wolf             qemu_iovec_memset(qiov, bytes_done, 0, n_bytes);
5823d46a75aSPaolo Bonzini             ret = 0;
5839aebd98aSStefan Weil         } else {
5840865bb6fSKevin Wolf             uint64_t data_offset = s->header.offset_data +
5850865bb6fSKevin Wolf                                    (uint64_t)bmap_entry * s->block_size +
5860865bb6fSKevin Wolf                                    offset_in_block;
5870c7bfc32SPaolo Bonzini 
5880865bb6fSKevin Wolf             qemu_iovec_reset(&local_qiov);
5890865bb6fSKevin Wolf             qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes);
5900865bb6fSKevin Wolf 
591a03ef88fSKevin Wolf             ret = bdrv_co_preadv(bs->file, data_offset, n_bytes,
5920865bb6fSKevin Wolf                                  &local_qiov, 0);
5933d46a75aSPaolo Bonzini         }
5940865bb6fSKevin Wolf         logout("%u bytes read\n", n_bytes);
5950865bb6fSKevin Wolf 
5960865bb6fSKevin Wolf         bytes -= n_bytes;
5970865bb6fSKevin Wolf         offset += n_bytes;
5980865bb6fSKevin Wolf         bytes_done += n_bytes;
5990865bb6fSKevin Wolf     }
6000865bb6fSKevin Wolf 
6010865bb6fSKevin Wolf     qemu_iovec_destroy(&local_qiov);
6023d46a75aSPaolo Bonzini 
6033d46a75aSPaolo Bonzini     return ret;
6049aebd98aSStefan Weil }
6059aebd98aSStefan Weil 
606b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
vdi_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)607e75abedaSVladimir Sementsov-Ogievskiy vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
608e75abedaSVladimir Sementsov-Ogievskiy                QEMUIOVector *qiov, BdrvRequestFlags flags)
6099aebd98aSStefan Weil {
6109aebd98aSStefan Weil     BDRVVdiState *s = bs->opaque;
611fde9d56fSKevin Wolf     QEMUIOVector local_qiov;
6129aebd98aSStefan Weil     uint32_t bmap_entry;
6139aebd98aSStefan Weil     uint32_t block_index;
614fde9d56fSKevin Wolf     uint32_t offset_in_block;
615fde9d56fSKevin Wolf     uint32_t n_bytes;
6161e886639SPaolo Bonzini     uint64_t data_offset;
617bfc45fc1SPaolo Bonzini     uint32_t bmap_first = VDI_UNALLOCATED;
618bfc45fc1SPaolo Bonzini     uint32_t bmap_last = VDI_UNALLOCATED;
619bfc45fc1SPaolo Bonzini     uint8_t *block = NULL;
620fde9d56fSKevin Wolf     uint64_t bytes_done = 0;
621eb9566d1SPaolo Bonzini     int ret = 0;
6224de659e8SPaolo Bonzini 
6234de659e8SPaolo Bonzini     logout("\n");
6249aebd98aSStefan Weil 
625fde9d56fSKevin Wolf     qemu_iovec_init(&local_qiov, qiov->niov);
6269aebd98aSStefan Weil 
627fde9d56fSKevin Wolf     while (ret >= 0 && bytes > 0) {
628fde9d56fSKevin Wolf         block_index = offset / s->block_size;
629fde9d56fSKevin Wolf         offset_in_block = offset % s->block_size;
630fde9d56fSKevin Wolf         n_bytes = MIN(bytes, s->block_size - offset_in_block);
631fde9d56fSKevin Wolf 
632fde9d56fSKevin Wolf         logout("will write %u bytes starting at offset %" PRIu64 "\n",
633fde9d56fSKevin Wolf                n_bytes, offset);
6349aebd98aSStefan Weil 
6359aebd98aSStefan Weil         /* prepare next AIO request */
6361e886639SPaolo Bonzini         qemu_co_rwlock_rdlock(&s->bmap_lock);
6379aebd98aSStefan Weil         bmap_entry = le32_to_cpu(s->bmap[block_index]);
638c794b4e0SEric Sunshine         if (!VDI_IS_ALLOCATED(bmap_entry)) {
6399aebd98aSStefan Weil             /* Allocate new block and write to it. */
6401e886639SPaolo Bonzini             qemu_co_rwlock_upgrade(&s->bmap_lock);
6411e886639SPaolo Bonzini             bmap_entry = le32_to_cpu(s->bmap[block_index]);
6421e886639SPaolo Bonzini             if (VDI_IS_ALLOCATED(bmap_entry)) {
6431e886639SPaolo Bonzini                 /* A concurrent allocation did the work for us.  */
6441e886639SPaolo Bonzini                 qemu_co_rwlock_downgrade(&s->bmap_lock);
6451e886639SPaolo Bonzini                 goto nonallocating_write;
6461e886639SPaolo Bonzini             }
6471e886639SPaolo Bonzini 
6489aebd98aSStefan Weil             bmap_entry = s->header.blocks_allocated;
6499aebd98aSStefan Weil             s->bmap[block_index] = cpu_to_le32(bmap_entry);
6509aebd98aSStefan Weil             s->header.blocks_allocated++;
651fde9d56fSKevin Wolf             data_offset = s->header.offset_data +
652fde9d56fSKevin Wolf                           (uint64_t)bmap_entry * s->block_size;
6539aebd98aSStefan Weil             if (block == NULL) {
654641543b7SStefan Weil                 block = g_malloc(s->block_size);
655bfc45fc1SPaolo Bonzini                 bmap_first = block_index;
6569aebd98aSStefan Weil             }
657bfc45fc1SPaolo Bonzini             bmap_last = block_index;
658641543b7SStefan Weil             /* Copy data to be written to new block and zero unused parts. */
659fde9d56fSKevin Wolf             memset(block, 0, offset_in_block);
660fde9d56fSKevin Wolf             qemu_iovec_to_buf(qiov, bytes_done, block + offset_in_block,
661fde9d56fSKevin Wolf                               n_bytes);
662fde9d56fSKevin Wolf             memset(block + offset_in_block + n_bytes, 0,
663fde9d56fSKevin Wolf                    s->block_size - n_bytes - offset_in_block);
664f0ab6f10SMax Reitz 
6651e886639SPaolo Bonzini             /* Write the new block under CoRwLock write-side protection,
6661e886639SPaolo Bonzini              * so this full-cluster write does not overlap a partial write
6671e886639SPaolo Bonzini              * of the same cluster, issued from the "else" branch.
6681e886639SPaolo Bonzini              */
6693f653028SAlberto Faria             ret = bdrv_co_pwrite(bs->file, data_offset, s->block_size, block,
6703f653028SAlberto Faria                                  0);
6711e886639SPaolo Bonzini             qemu_co_rwlock_unlock(&s->bmap_lock);
6729aebd98aSStefan Weil         } else {
6731e886639SPaolo Bonzini nonallocating_write:
6741e886639SPaolo Bonzini             data_offset = s->header.offset_data +
675fde9d56fSKevin Wolf                            (uint64_t)bmap_entry * s->block_size +
676fde9d56fSKevin Wolf                            offset_in_block;
6771e886639SPaolo Bonzini             qemu_co_rwlock_unlock(&s->bmap_lock);
678fde9d56fSKevin Wolf 
679fde9d56fSKevin Wolf             qemu_iovec_reset(&local_qiov);
680fde9d56fSKevin Wolf             qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes);
681fde9d56fSKevin Wolf 
682a03ef88fSKevin Wolf             ret = bdrv_co_pwritev(bs->file, data_offset, n_bytes,
683fde9d56fSKevin Wolf                                   &local_qiov, 0);
6849aebd98aSStefan Weil         }
6850c7bfc32SPaolo Bonzini 
686fde9d56fSKevin Wolf         bytes -= n_bytes;
687fde9d56fSKevin Wolf         offset += n_bytes;
688fde9d56fSKevin Wolf         bytes_done += n_bytes;
6890c7bfc32SPaolo Bonzini 
690fde9d56fSKevin Wolf         logout("%u bytes written\n", n_bytes);
6913d46a75aSPaolo Bonzini     }
6929aebd98aSStefan Weil 
693fde9d56fSKevin Wolf     qemu_iovec_destroy(&local_qiov);
694fde9d56fSKevin Wolf 
6950c7bfc32SPaolo Bonzini     logout("finished data write\n");
6964eea78e6SPaolo Bonzini     if (ret < 0) {
697574b8304SDavid Edmondson         g_free(block);
6984eea78e6SPaolo Bonzini         return ret;
6994eea78e6SPaolo Bonzini     }
7004eea78e6SPaolo Bonzini 
701bfc45fc1SPaolo Bonzini     if (block) {
7024eea78e6SPaolo Bonzini         /* One or more new blocks were allocated. */
70307ee2ab4SDavid Edmondson         VdiHeader *header;
7044eea78e6SPaolo Bonzini         uint8_t *base;
705d25b99c7SMarkus Armbruster         uint64_t bmap_offset;
706fde9d56fSKevin Wolf         uint32_t n_sectors;
7074eea78e6SPaolo Bonzini 
70807ee2ab4SDavid Edmondson         g_free(block);
70907ee2ab4SDavid Edmondson         header = g_malloc(sizeof(*header));
71007ee2ab4SDavid Edmondson 
7110c7bfc32SPaolo Bonzini         logout("now writing modified header\n");
712bfc45fc1SPaolo Bonzini         assert(VDI_IS_ALLOCATED(bmap_first));
7130c7bfc32SPaolo Bonzini         *header = s->header;
7140c7bfc32SPaolo Bonzini         vdi_header_to_le(header);
7153f653028SAlberto Faria         ret = bdrv_co_pwrite(bs->file, 0, sizeof(*header), header, 0);
71607ee2ab4SDavid Edmondson         g_free(header);
7174eea78e6SPaolo Bonzini 
7184eea78e6SPaolo Bonzini         if (ret < 0) {
7194eea78e6SPaolo Bonzini             return ret;
7204eea78e6SPaolo Bonzini         }
7214eea78e6SPaolo Bonzini 
7220c7bfc32SPaolo Bonzini         logout("now writing modified block map entry %u...%u\n",
7230c7bfc32SPaolo Bonzini                bmap_first, bmap_last);
7240c7bfc32SPaolo Bonzini         /* Write modified sectors from block map. */
7250c7bfc32SPaolo Bonzini         bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
7260c7bfc32SPaolo Bonzini         bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
7270c7bfc32SPaolo Bonzini         n_sectors = bmap_last - bmap_first + 1;
728d25b99c7SMarkus Armbruster         bmap_offset = s->bmap_sector + bmap_first;
7294eea78e6SPaolo Bonzini         base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE;
7300c7bfc32SPaolo Bonzini         logout("will write %u block map sectors starting from entry %u\n",
7310c7bfc32SPaolo Bonzini                n_sectors, bmap_first);
732d25b99c7SMarkus Armbruster         ret = bdrv_co_pwrite(bs->file, bmap_offset * SECTOR_SIZE,
73332cc71deSAlberto Faria                              n_sectors * SECTOR_SIZE, base, 0);
7340c7bfc32SPaolo Bonzini     }
7350c7bfc32SPaolo Bonzini 
736353a5d84SAlberto Faria     return ret;
7379aebd98aSStefan Weil }
7389aebd98aSStefan Weil 
7394db7ba3bSKevin Wolf static int coroutine_fn GRAPH_UNLOCKED
vdi_co_do_create(BlockdevCreateOptions * create_options,size_t block_size,Error ** errp)7404db7ba3bSKevin Wolf vdi_co_do_create(BlockdevCreateOptions *create_options, size_t block_size,
7414db7ba3bSKevin Wolf                  Error **errp)
7429aebd98aSStefan Weil {
743f5ec96c9SZhao Liu     ERRP_GUARD();
744e3810574SMax Reitz     BlockdevCreateOptionsVdi *vdi_opts;
745dddc7750SJeff Cody     int ret = 0;
7469aebd98aSStefan Weil     uint64_t bytes = 0;
7479aebd98aSStefan Weil     uint32_t blocks;
74861fa6487SKevin Wolf     uint32_t image_type;
7499aebd98aSStefan Weil     VdiHeader header;
7509aebd98aSStefan Weil     size_t i;
7519aebd98aSStefan Weil     size_t bmap_size;
75270747862SJeff Cody     int64_t offset = 0;
753ec73f060SMax Reitz     BlockDriverState *bs_file = NULL;
754a08f0c3bSKevin Wolf     BlockBackend *blk = NULL;
75570747862SJeff Cody     uint32_t *bmap = NULL;
756ac928b8eSPeter Maydell     QemuUUID uuid;
7579aebd98aSStefan Weil 
758e3810574SMax Reitz     assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
759e3810574SMax Reitz     vdi_opts = &create_options->u.vdi;
760e3810574SMax Reitz 
7619aebd98aSStefan Weil     logout("\n");
7629aebd98aSStefan Weil 
763da23248fSKevin Wolf     /* Validate options and set default values */
76449858b50SMax Reitz     bytes = vdi_opts->size;
76561fa6487SKevin Wolf 
76661fa6487SKevin Wolf     if (!vdi_opts->has_preallocation) {
76761fa6487SKevin Wolf         vdi_opts->preallocation = PREALLOC_MODE_OFF;
7686eea90ebSStefan Weil     }
76961fa6487SKevin Wolf     switch (vdi_opts->preallocation) {
77061fa6487SKevin Wolf     case PREALLOC_MODE_OFF:
77161fa6487SKevin Wolf         image_type = VDI_TYPE_DYNAMIC;
77261fa6487SKevin Wolf         break;
77361fa6487SKevin Wolf     case PREALLOC_MODE_METADATA:
77461fa6487SKevin Wolf         image_type = VDI_TYPE_STATIC;
77561fa6487SKevin Wolf         break;
77661fa6487SKevin Wolf     default:
77761fa6487SKevin Wolf         error_setg(errp, "Preallocation mode not supported for vdi");
77861fa6487SKevin Wolf         return -EINVAL;
77961fa6487SKevin Wolf     }
78061fa6487SKevin Wolf 
78149858b50SMax Reitz #ifndef CONFIG_VDI_STATIC_IMAGE
78249858b50SMax Reitz     if (image_type == VDI_TYPE_STATIC) {
78349858b50SMax Reitz         ret = -ENOTSUP;
78449858b50SMax Reitz         error_setg(errp, "Statically allocated images cannot be created in "
78549858b50SMax Reitz                    "this build");
78649858b50SMax Reitz         goto exit;
78749858b50SMax Reitz     }
78849858b50SMax Reitz #endif
78949858b50SMax Reitz #ifndef CONFIG_VDI_BLOCK_SIZE
79049858b50SMax Reitz     if (block_size != DEFAULT_CLUSTER_SIZE) {
79149858b50SMax Reitz         ret = -ENOTSUP;
79249858b50SMax Reitz         error_setg(errp,
79349858b50SMax Reitz                    "A non-default cluster size is not supported in this build");
79449858b50SMax Reitz         goto exit;
79549858b50SMax Reitz     }
7969aebd98aSStefan Weil #endif
7979aebd98aSStefan Weil 
79863fa06dcSJeff Cody     if (bytes > VDI_DISK_SIZE_MAX) {
799dddc7750SJeff Cody         ret = -ENOTSUP;
80063fa06dcSJeff Cody         error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
80163fa06dcSJeff Cody                           ", max supported is 0x%" PRIx64 ")",
80263fa06dcSJeff Cody                           bytes, VDI_DISK_SIZE_MAX);
80363fa06dcSJeff Cody         goto exit;
80463fa06dcSJeff Cody     }
80563fa06dcSJeff Cody 
806da23248fSKevin Wolf     /* Create BlockBackend to write to the image */
80713dd6327SKevin Wolf     bs_file = bdrv_co_open_blockdev_ref(vdi_opts->file, errp);
808ec73f060SMax Reitz     if (!bs_file) {
809ec73f060SMax Reitz         ret = -EIO;
81063fa06dcSJeff Cody         goto exit;
8119aebd98aSStefan Weil     }
812a08f0c3bSKevin Wolf 
81313dd6327SKevin Wolf     blk = blk_co_new_with_bs(bs_file, BLK_PERM_WRITE | BLK_PERM_RESIZE,
814a3aeeab5SEric Blake                              BLK_PERM_ALL, errp);
815a3aeeab5SEric Blake     if (!blk) {
816a3aeeab5SEric Blake         ret = -EPERM;
81770747862SJeff Cody         goto exit;
8184ab15590SChunyan Liu     }
8194ab15590SChunyan Liu 
820a08f0c3bSKevin Wolf     blk_set_allow_write_beyond_eof(blk, true);
821a08f0c3bSKevin Wolf 
822f21dc3a4SStefan Weil     /* We need enough blocks to store the given disk size,
823f21dc3a4SStefan Weil        so always round up. */
824e9082e47SMax Reitz     blocks = DIV_ROUND_UP(bytes, block_size);
825f21dc3a4SStefan Weil 
8269aebd98aSStefan Weil     bmap_size = blocks * sizeof(uint32_t);
827e9082e47SMax Reitz     bmap_size = ROUND_UP(bmap_size, SECTOR_SIZE);
8289aebd98aSStefan Weil 
8299aebd98aSStefan Weil     memset(&header, 0, sizeof(header));
8301786dc15SBlue Swirl     pstrcpy(header.text, sizeof(header.text), VDI_TEXT);
8319aebd98aSStefan Weil     header.signature = VDI_SIGNATURE;
8329aebd98aSStefan Weil     header.version = VDI_VERSION_1_1;
8339aebd98aSStefan Weil     header.header_size = 0x180;
8349aebd98aSStefan Weil     header.image_type = image_type;
8359aebd98aSStefan Weil     header.offset_bmap = 0x200;
8369aebd98aSStefan Weil     header.offset_data = 0x200 + bmap_size;
8379aebd98aSStefan Weil     header.sector_size = SECTOR_SIZE;
8389aebd98aSStefan Weil     header.disk_size = bytes;
8399aebd98aSStefan Weil     header.block_size = block_size;
8409aebd98aSStefan Weil     header.blocks_in_image = blocks;
8416eea90ebSStefan Weil     if (image_type == VDI_TYPE_STATIC) {
8426eea90ebSStefan Weil         header.blocks_allocated = blocks;
8436eea90ebSStefan Weil     }
844ac928b8eSPeter Maydell     qemu_uuid_generate(&uuid);
845ac928b8eSPeter Maydell     header.uuid_image = uuid;
846ac928b8eSPeter Maydell     qemu_uuid_generate(&uuid);
847ac928b8eSPeter Maydell     header.uuid_last_snap = uuid;
8489aebd98aSStefan Weil     /* There is no need to set header.uuid_link or header.uuid_parent here. */
84995a14d51SKevin Wolf     if (VDI_DEBUG) {
8509aebd98aSStefan Weil         vdi_header_print(&header);
85195a14d51SKevin Wolf     }
8529aebd98aSStefan Weil     vdi_header_to_le(&header);
8533f653028SAlberto Faria     ret = blk_co_pwrite(blk, offset, sizeof(header), &header, 0);
854dddc7750SJeff Cody     if (ret < 0) {
855ec73f060SMax Reitz         error_setg(errp, "Error writing header");
85670747862SJeff Cody         goto exit;
8579aebd98aSStefan Weil     }
85870747862SJeff Cody     offset += sizeof(header);
8599aebd98aSStefan Weil 
860b76b6e95SStefan Weil     if (bmap_size > 0) {
86117cce735SKevin Wolf         bmap = g_try_malloc0(bmap_size);
86217cce735SKevin Wolf         if (bmap == NULL) {
86317cce735SKevin Wolf             ret = -ENOMEM;
86417cce735SKevin Wolf             error_setg(errp, "Could not allocate bmap");
86517cce735SKevin Wolf             goto exit;
86617cce735SKevin Wolf         }
8679aebd98aSStefan Weil         for (i = 0; i < blocks; i++) {
8689aebd98aSStefan Weil             if (image_type == VDI_TYPE_STATIC) {
8699aebd98aSStefan Weil                 bmap[i] = i;
8709aebd98aSStefan Weil             } else {
8719aebd98aSStefan Weil                 bmap[i] = VDI_UNALLOCATED;
8729aebd98aSStefan Weil             }
8739aebd98aSStefan Weil         }
8743f653028SAlberto Faria         ret = blk_co_pwrite(blk, offset, bmap_size, bmap, 0);
875dddc7750SJeff Cody         if (ret < 0) {
876ec73f060SMax Reitz             error_setg(errp, "Error writing bmap");
87770747862SJeff Cody             goto exit;
8789aebd98aSStefan Weil         }
87970747862SJeff Cody         offset += bmap_size;
880514f21a5SStefan Weil     }
881514f21a5SStefan Weil 
8829aebd98aSStefan Weil     if (image_type == VDI_TYPE_STATIC) {
8833f653028SAlberto Faria         ret = blk_co_truncate(blk, offset + blocks * block_size, false,
8848c6242b6SKevin Wolf                               PREALLOC_MODE_OFF, 0, errp);
885dddc7750SJeff Cody         if (ret < 0) {
886ec73f060SMax Reitz             error_prepend(errp, "Failed to statically allocate file");
88770747862SJeff Cody             goto exit;
8889aebd98aSStefan Weil         }
8899aebd98aSStefan Weil     }
8909aebd98aSStefan Weil 
89153618dd8SKevin Wolf     ret = 0;
89263fa06dcSJeff Cody exit:
893b2ab5f54SKevin Wolf     blk_co_unref(blk);
894b2ab5f54SKevin Wolf     bdrv_co_unref(bs_file);
89570747862SJeff Cody     g_free(bmap);
896dddc7750SJeff Cody     return ret;
8979aebd98aSStefan Weil }
8989aebd98aSStefan Weil 
8994db7ba3bSKevin Wolf static int coroutine_fn GRAPH_UNLOCKED
vdi_co_create(BlockdevCreateOptions * create_options,Error ** errp)9004db7ba3bSKevin Wolf vdi_co_create(BlockdevCreateOptions *create_options, Error **errp)
901e3810574SMax Reitz {
902e3810574SMax Reitz     return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp);
903e3810574SMax Reitz }
904e3810574SMax Reitz 
9054db7ba3bSKevin Wolf static int coroutine_fn GRAPH_UNLOCKED
vdi_co_create_opts(BlockDriver * drv,const char * filename,QemuOpts * opts,Error ** errp)9064ec8df01SKevin Wolf vdi_co_create_opts(BlockDriver *drv, const char *filename,
9074ec8df01SKevin Wolf                    QemuOpts *opts, Error **errp)
90849858b50SMax Reitz {
90949858b50SMax Reitz     QDict *qdict = NULL;
910e3810574SMax Reitz     BlockdevCreateOptions *create_options = NULL;
911ec73f060SMax Reitz     BlockDriverState *bs_file = NULL;
91249858b50SMax Reitz     uint64_t block_size = DEFAULT_CLUSTER_SIZE;
91361fa6487SKevin Wolf     bool is_static = false;
91449858b50SMax Reitz     Visitor *v;
91549858b50SMax Reitz     int ret;
91649858b50SMax Reitz 
917da23248fSKevin Wolf     /* Parse options and convert legacy syntax.
918da23248fSKevin Wolf      *
919da23248fSKevin Wolf      * Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
92049858b50SMax Reitz      * cluster-size is not part of the QAPI schema; therefore we have
92149858b50SMax Reitz      * to parse it before creating the QAPI object. */
92249858b50SMax Reitz #if defined(CONFIG_VDI_BLOCK_SIZE)
92349858b50SMax Reitz     block_size = qemu_opt_get_size_del(opts,
92449858b50SMax Reitz                                        BLOCK_OPT_CLUSTER_SIZE,
92549858b50SMax Reitz                                        DEFAULT_CLUSTER_SIZE);
92649858b50SMax Reitz     if (block_size < BDRV_SECTOR_SIZE || block_size > UINT32_MAX ||
92749858b50SMax Reitz         !is_power_of_2(block_size))
92849858b50SMax Reitz     {
92949858b50SMax Reitz         error_setg(errp, "Invalid cluster size");
93049858b50SMax Reitz         ret = -EINVAL;
93149858b50SMax Reitz         goto done;
93249858b50SMax Reitz     }
93349858b50SMax Reitz #endif
93461fa6487SKevin Wolf     if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
93561fa6487SKevin Wolf         is_static = true;
93661fa6487SKevin Wolf     }
93749858b50SMax Reitz 
93849858b50SMax Reitz     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
93949858b50SMax Reitz 
940da23248fSKevin Wolf     /* Create and open the file (protocol layer) */
9412475a0d0SEmanuele Giuseppe Esposito     ret = bdrv_co_create_file(filename, opts, errp);
942ec73f060SMax Reitz     if (ret < 0) {
943ec73f060SMax Reitz         goto done;
944ec73f060SMax Reitz     }
945ec73f060SMax Reitz 
94613dd6327SKevin Wolf     bs_file = bdrv_co_open(filename, NULL, NULL,
947ec73f060SMax Reitz                            BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
948ec73f060SMax Reitz     if (!bs_file) {
949ec73f060SMax Reitz         ret = -EIO;
950ec73f060SMax Reitz         goto done;
951ec73f060SMax Reitz     }
952ec73f060SMax Reitz 
953e3810574SMax Reitz     qdict_put_str(qdict, "driver", "vdi");
954ec73f060SMax Reitz     qdict_put_str(qdict, "file", bs_file->node_name);
95561fa6487SKevin Wolf     if (is_static) {
95661fa6487SKevin Wolf         qdict_put_str(qdict, "preallocation", "metadata");
95761fa6487SKevin Wolf     }
95849858b50SMax Reitz 
95949858b50SMax Reitz     /* Get the QAPI object */
960f853465aSMarkus Armbruster     v = qobject_input_visitor_new_flat_confused(qdict, errp);
961f853465aSMarkus Armbruster     if (!v) {
962f853465aSMarkus Armbruster         ret = -EINVAL;
963f853465aSMarkus Armbruster         goto done;
964f853465aSMarkus Armbruster     }
965b11a093cSMarkus Armbruster     visit_type_BlockdevCreateOptions(v, NULL, &create_options, errp);
96649858b50SMax Reitz     visit_free(v);
967b11a093cSMarkus Armbruster     if (!create_options) {
96849858b50SMax Reitz         ret = -EINVAL;
96949858b50SMax Reitz         goto done;
97049858b50SMax Reitz     }
97149858b50SMax Reitz 
972da23248fSKevin Wolf     /* Silently round up size */
973e3810574SMax Reitz     assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
974e3810574SMax Reitz     create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size,
975e3810574SMax Reitz                                           BDRV_SECTOR_SIZE);
97649858b50SMax Reitz 
977da23248fSKevin Wolf     /* Create the vdi image (format layer) */
978ec73f060SMax Reitz     ret = vdi_co_do_create(create_options, block_size, errp);
97949858b50SMax Reitz done:
980cb3e7f08SMarc-André Lureau     qobject_unref(qdict);
981e3810574SMax Reitz     qapi_free_BlockdevCreateOptions(create_options);
982b2ab5f54SKevin Wolf     bdrv_co_unref(bs_file);
98349858b50SMax Reitz     return ret;
98449858b50SMax Reitz }
98549858b50SMax Reitz 
vdi_close(BlockDriverState * bs)9869aebd98aSStefan Weil static void vdi_close(BlockDriverState *bs)
9879aebd98aSStefan Weil {
988fc9d106cSKevin Wolf     BDRVVdiState *s = bs->opaque;
9896ac5f388SKevin Wolf 
99017cce735SKevin Wolf     qemu_vfree(s->bmap);
9916ac5f388SKevin Wolf 
992c8a7fc51SSteve Sistare     migrate_del_blocker(&s->migration_blocker);
9939aebd98aSStefan Weil }
9949aebd98aSStefan Weil 
vdi_has_zero_init(BlockDriverState * bs)99506717986SKevin Wolf static int GRAPH_RDLOCK vdi_has_zero_init(BlockDriverState *bs)
9960a28bf28SMax Reitz {
9970a28bf28SMax Reitz     BDRVVdiState *s = bs->opaque;
9980a28bf28SMax Reitz 
9990a28bf28SMax Reitz     if (s->header.image_type == VDI_TYPE_STATIC) {
10000a28bf28SMax Reitz         return bdrv_has_zero_init(bs->file->bs);
10010a28bf28SMax Reitz     } else {
10020a28bf28SMax Reitz         return 1;
10030a28bf28SMax Reitz     }
10040a28bf28SMax Reitz }
10050a28bf28SMax Reitz 
1006004b7f25SChunyan Liu static QemuOptsList vdi_create_opts = {
1007004b7f25SChunyan Liu     .name = "vdi-create-opts",
1008004b7f25SChunyan Liu     .head = QTAILQ_HEAD_INITIALIZER(vdi_create_opts.head),
1009004b7f25SChunyan Liu     .desc = {
10109aebd98aSStefan Weil         {
10119aebd98aSStefan Weil             .name = BLOCK_OPT_SIZE,
1012004b7f25SChunyan Liu             .type = QEMU_OPT_SIZE,
10139aebd98aSStefan Weil             .help = "Virtual disk size"
10149aebd98aSStefan Weil         },
10159aebd98aSStefan Weil #if defined(CONFIG_VDI_BLOCK_SIZE)
10169aebd98aSStefan Weil         {
10179aebd98aSStefan Weil             .name = BLOCK_OPT_CLUSTER_SIZE,
1018004b7f25SChunyan Liu             .type = QEMU_OPT_SIZE,
101999cce9faSKevin Wolf             .help = "VDI cluster (block) size",
1020004b7f25SChunyan Liu             .def_value_str = stringify(DEFAULT_CLUSTER_SIZE)
10219aebd98aSStefan Weil         },
10229aebd98aSStefan Weil #endif
10239aebd98aSStefan Weil #if defined(CONFIG_VDI_STATIC_IMAGE)
10249aebd98aSStefan Weil         {
10259aebd98aSStefan Weil             .name = BLOCK_OPT_STATIC,
1026004b7f25SChunyan Liu             .type = QEMU_OPT_BOOL,
1027004b7f25SChunyan Liu             .help = "VDI static (pre-allocated) image",
1028004b7f25SChunyan Liu             .def_value_str = "off"
10299aebd98aSStefan Weil         },
10309aebd98aSStefan Weil #endif
10319aebd98aSStefan Weil         /* TODO: An additional option to set UUID values might be useful. */
1032004b7f25SChunyan Liu         { /* end of list */ }
1033004b7f25SChunyan Liu     }
10349aebd98aSStefan Weil };
10359aebd98aSStefan Weil 
10369aebd98aSStefan Weil static BlockDriver bdrv_vdi = {
10379aebd98aSStefan Weil     .format_name = "vdi",
10389aebd98aSStefan Weil     .instance_size = sizeof(BDRVVdiState),
10399aebd98aSStefan Weil     .bdrv_probe = vdi_probe,
10409aebd98aSStefan Weil     .bdrv_open = vdi_open,
10419aebd98aSStefan Weil     .bdrv_close = vdi_close,
1042ecfe2bbaSJeff Cody     .bdrv_reopen_prepare = vdi_reopen_prepare,
104369dca43dSMax Reitz     .bdrv_child_perm          = bdrv_default_perms,
1044e3810574SMax Reitz     .bdrv_co_create      = vdi_co_create,
1045da23248fSKevin Wolf     .bdrv_co_create_opts = vdi_co_create_opts,
10460a28bf28SMax Reitz     .bdrv_has_zero_init  = vdi_has_zero_init,
104767635f6aSEric Blake     .bdrv_co_block_status = vdi_co_block_status,
10489aebd98aSStefan Weil     .bdrv_make_empty = vdi_make_empty,
10499aebd98aSStefan Weil 
10500865bb6fSKevin Wolf     .bdrv_co_preadv     = vdi_co_preadv,
10519aebd98aSStefan Weil #if defined(CONFIG_VDI_WRITE)
1052fde9d56fSKevin Wolf     .bdrv_co_pwritev    = vdi_co_pwritev,
10539aebd98aSStefan Weil #endif
10549aebd98aSStefan Weil 
10553d47eb0aSEmanuele Giuseppe Esposito     .bdrv_co_get_info = vdi_co_get_info,
10569aebd98aSStefan Weil 
1057d67066d8SMax Reitz     .is_format = true,
1058004b7f25SChunyan Liu     .create_opts = &vdi_create_opts,
10592fd61638SPaolo Bonzini     .bdrv_co_check = vdi_co_check,
10609aebd98aSStefan Weil };
10619aebd98aSStefan Weil 
bdrv_vdi_init(void)10629aebd98aSStefan Weil static void bdrv_vdi_init(void)
10639aebd98aSStefan Weil {
10649aebd98aSStefan Weil     logout("\n");
10659aebd98aSStefan Weil     bdrv_register(&bdrv_vdi);
10669aebd98aSStefan Weil }
10679aebd98aSStefan Weil 
10689aebd98aSStefan Weil block_init(bdrv_vdi_init);
1069