Annotation of qemu/block/vdi.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Block driver for the Virtual Disk Image (VDI) format
        !             3:  *
        !             4:  * Copyright (c) 2009 Stefan Weil
        !             5:  *
        !             6:  * This program is free software: you can redistribute it and/or modify
        !             7:  * it under the terms of the GNU General Public License as published by
        !             8:  * the Free Software Foundation, either version 2 of the License, or
        !             9:  * (at your option) version 3 or any later version.
        !            10:  *
        !            11:  * This program is distributed in the hope that it will be useful,
        !            12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            14:  * GNU General Public License for more details.
        !            15:  *
        !            16:  * You should have received a copy of the GNU General Public License
        !            17:  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
        !            18:  *
        !            19:  * Reference:
        !            20:  * http://forums.virtualbox.org/viewtopic.php?t=8046
        !            21:  *
        !            22:  * This driver supports create / read / write operations on VDI images.
        !            23:  *
        !            24:  * Todo (see also TODO in code):
        !            25:  *
        !            26:  * Some features like snapshots are still missing.
        !            27:  *
        !            28:  * Deallocation of zero-filled blocks and shrinking images are missing, too
        !            29:  * (might be added to common block layer).
        !            30:  *
        !            31:  * Allocation of blocks could be optimized (less writes to block map and
        !            32:  * header).
        !            33:  *
        !            34:  * Read and write of adjacents blocks could be done in one operation
        !            35:  * (current code uses one operation per block (1 MiB).
        !            36:  *
        !            37:  * The code is not thread safe (missing locks for changes in header and
        !            38:  * block table, no problem with current QEMU).
        !            39:  *
        !            40:  * Hints:
        !            41:  *
        !            42:  * Blocks (VDI documentation) correspond to clusters (QEMU).
        !            43:  * QEMU's backing files could be implemented using VDI snapshot files (TODO).
        !            44:  * VDI snapshot files may also contain the complete machine state.
        !            45:  * Maybe this machine state can be converted to QEMU PC machine snapshot data.
        !            46:  *
        !            47:  * The driver keeps a block cache (little endian entries) in memory.
        !            48:  * For the standard block size (1 MiB), a 1 TiB disk will use 4 MiB RAM,
        !            49:  * so this seems to be reasonable.
        !            50:  */
        !            51: 
        !            52: #include "qemu-common.h"
        !            53: #include "block_int.h"
        !            54: #include "module.h"
        !            55: 
        !            56: #if defined(CONFIG_UUID)
        !            57: #include <uuid/uuid.h>
        !            58: #else
        !            59: /* TODO: move uuid emulation to some central place in QEMU. */
        !            60: #include "sysemu.h"     /* UUID_FMT */
        !            61: typedef unsigned char uuid_t[16];
        !            62: void uuid_generate(uuid_t out);
        !            63: int uuid_is_null(const uuid_t uu);
        !            64: void uuid_unparse(const uuid_t uu, char *out);
        !            65: #endif
        !            66: 
        !            67: /* Code configuration options. */
        !            68: 
        !            69: /* Enable debug messages. */
        !            70: //~ #define CONFIG_VDI_DEBUG
        !            71: 
        !            72: /* Support write operations on VDI images. */
        !            73: #define CONFIG_VDI_WRITE
        !            74: 
        !            75: /* Support non-standard block (cluster) size. This is untested.
        !            76:  * Maybe it will be needed for very large images.
        !            77:  */
        !            78: //~ #define CONFIG_VDI_BLOCK_SIZE
        !            79: 
        !            80: /* Support static (fixed, pre-allocated) images. */
        !            81: #define CONFIG_VDI_STATIC_IMAGE
        !            82: 
        !            83: /* Command line option for static images. */
        !            84: #define BLOCK_OPT_STATIC "static"
        !            85: 
        !            86: #define KiB     1024
        !            87: #define MiB     (KiB * KiB)
        !            88: 
        !            89: #define SECTOR_SIZE 512
        !            90: 
        !            91: #if defined(CONFIG_VDI_DEBUG)
        !            92: #define logout(fmt, ...) \
        !            93:                 fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__)
        !            94: #else
        !            95: #define logout(fmt, ...) ((void)0)
        !            96: #endif
        !            97: 
        !            98: /* Image signature. */
        !            99: #define VDI_SIGNATURE 0xbeda107f
        !           100: 
        !           101: /* Image version. */
        !           102: #define VDI_VERSION_1_1 0x00010001
        !           103: 
        !           104: /* Image type. */
        !           105: #define VDI_TYPE_DYNAMIC 1
        !           106: #define VDI_TYPE_STATIC  2
        !           107: 
        !           108: /* Innotek / SUN images use these strings in header.text:
        !           109:  * "<<< innotek VirtualBox Disk Image >>>\n"
        !           110:  * "<<< Sun xVM VirtualBox Disk Image >>>\n"
        !           111:  * "<<< Sun VirtualBox Disk Image >>>\n"
        !           112:  * The value does not matter, so QEMU created images use a different text.
        !           113:  */
        !           114: #define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
        !           115: 
        !           116: /* Unallocated blocks use this index (no need to convert endianess). */
        !           117: #define VDI_UNALLOCATED UINT32_MAX
        !           118: 
        !           119: #if !defined(CONFIG_UUID)
        !           120: void uuid_generate(uuid_t out)
        !           121: {
        !           122:     memset(out, 0, sizeof(out));
        !           123: }
        !           124: 
        !           125: int uuid_is_null(const uuid_t uu)
        !           126: {
        !           127:     uuid_t null_uuid = { 0 };
        !           128:     return memcmp(uu, null_uuid, sizeof(uu)) == 0;
        !           129: }
        !           130: 
        !           131: void uuid_unparse(const uuid_t uu, char *out)
        !           132: {
        !           133:     snprintf(out, 37, UUID_FMT,
        !           134:             uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
        !           135:             uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
        !           136: }
        !           137: #endif
        !           138: 
        !           139: typedef struct {
        !           140:     BlockDriverAIOCB common;
        !           141:     int64_t sector_num;
        !           142:     QEMUIOVector *qiov;
        !           143:     uint8_t *buf;
        !           144:     /* Total number of sectors. */
        !           145:     int nb_sectors;
        !           146:     /* Number of sectors for current AIO. */
        !           147:     int n_sectors;
        !           148:     /* New allocated block map entry. */
        !           149:     uint32_t bmap_first;
        !           150:     uint32_t bmap_last;
        !           151:     /* Buffer for new allocated block. */
        !           152:     void *block_buffer;
        !           153:     void *orig_buf;
        !           154:     int header_modified;
        !           155:     BlockDriverAIOCB *hd_aiocb;
        !           156:     struct iovec hd_iov;
        !           157:     QEMUIOVector hd_qiov;
        !           158:     QEMUBH *bh;
        !           159: } VdiAIOCB;
        !           160: 
        !           161: typedef struct {
        !           162:     char text[0x40];
        !           163:     uint32_t signature;
        !           164:     uint32_t version;
        !           165:     uint32_t header_size;
        !           166:     uint32_t image_type;
        !           167:     uint32_t image_flags;
        !           168:     char description[256];
        !           169:     uint32_t offset_bmap;
        !           170:     uint32_t offset_data;
        !           171:     uint32_t cylinders;         /* disk geometry, unused here */
        !           172:     uint32_t heads;             /* disk geometry, unused here */
        !           173:     uint32_t sectors;           /* disk geometry, unused here */
        !           174:     uint32_t sector_size;
        !           175:     uint32_t unused1;
        !           176:     uint64_t disk_size;
        !           177:     uint32_t block_size;
        !           178:     uint32_t block_extra;       /* unused here */
        !           179:     uint32_t blocks_in_image;
        !           180:     uint32_t blocks_allocated;
        !           181:     uuid_t uuid_image;
        !           182:     uuid_t uuid_last_snap;
        !           183:     uuid_t uuid_link;
        !           184:     uuid_t uuid_parent;
        !           185:     uint64_t unused2[7];
        !           186: } VdiHeader;
        !           187: 
        !           188: typedef struct {
        !           189:     BlockDriverState *hd;
        !           190:     /* The block map entries are little endian (even in memory). */
        !           191:     uint32_t *bmap;
        !           192:     /* Size of block (bytes). */
        !           193:     uint32_t block_size;
        !           194:     /* Size of block (sectors). */
        !           195:     uint32_t block_sectors;
        !           196:     /* First sector of block map. */
        !           197:     uint32_t bmap_sector;
        !           198:     /* VDI header (converted to host endianess). */
        !           199:     VdiHeader header;
        !           200: } BDRVVdiState;
        !           201: 
        !           202: /* Change UUID from little endian (IPRT = VirtualBox format) to big endian
        !           203:  * format (network byte order, standard, see RFC 4122) and vice versa.
        !           204:  */
        !           205: static void uuid_convert(uuid_t uuid)
        !           206: {
        !           207:     bswap32s((uint32_t *)&uuid[0]);
        !           208:     bswap16s((uint16_t *)&uuid[4]);
        !           209:     bswap16s((uint16_t *)&uuid[6]);
        !           210: }
        !           211: 
        !           212: static void vdi_header_to_cpu(VdiHeader *header)
        !           213: {
        !           214:     le32_to_cpus(&header->signature);
        !           215:     le32_to_cpus(&header->version);
        !           216:     le32_to_cpus(&header->header_size);
        !           217:     le32_to_cpus(&header->image_type);
        !           218:     le32_to_cpus(&header->image_flags);
        !           219:     le32_to_cpus(&header->offset_bmap);
        !           220:     le32_to_cpus(&header->offset_data);
        !           221:     le32_to_cpus(&header->cylinders);
        !           222:     le32_to_cpus(&header->heads);
        !           223:     le32_to_cpus(&header->sectors);
        !           224:     le32_to_cpus(&header->sector_size);
        !           225:     le64_to_cpus(&header->disk_size);
        !           226:     le32_to_cpus(&header->block_size);
        !           227:     le32_to_cpus(&header->block_extra);
        !           228:     le32_to_cpus(&header->blocks_in_image);
        !           229:     le32_to_cpus(&header->blocks_allocated);
        !           230:     uuid_convert(header->uuid_image);
        !           231:     uuid_convert(header->uuid_last_snap);
        !           232:     uuid_convert(header->uuid_link);
        !           233:     uuid_convert(header->uuid_parent);
        !           234: }
        !           235: 
        !           236: static void vdi_header_to_le(VdiHeader *header)
        !           237: {
        !           238:     cpu_to_le32s(&header->signature);
        !           239:     cpu_to_le32s(&header->version);
        !           240:     cpu_to_le32s(&header->header_size);
        !           241:     cpu_to_le32s(&header->image_type);
        !           242:     cpu_to_le32s(&header->image_flags);
        !           243:     cpu_to_le32s(&header->offset_bmap);
        !           244:     cpu_to_le32s(&header->offset_data);
        !           245:     cpu_to_le32s(&header->cylinders);
        !           246:     cpu_to_le32s(&header->heads);
        !           247:     cpu_to_le32s(&header->sectors);
        !           248:     cpu_to_le32s(&header->sector_size);
        !           249:     cpu_to_le64s(&header->disk_size);
        !           250:     cpu_to_le32s(&header->block_size);
        !           251:     cpu_to_le32s(&header->block_extra);
        !           252:     cpu_to_le32s(&header->blocks_in_image);
        !           253:     cpu_to_le32s(&header->blocks_allocated);
        !           254:     cpu_to_le32s(&header->blocks_allocated);
        !           255:     uuid_convert(header->uuid_image);
        !           256:     uuid_convert(header->uuid_last_snap);
        !           257:     uuid_convert(header->uuid_link);
        !           258:     uuid_convert(header->uuid_parent);
        !           259: }
        !           260: 
        !           261: #if defined(CONFIG_VDI_DEBUG)
        !           262: static void vdi_header_print(VdiHeader *header)
        !           263: {
        !           264:     char uuid[37];
        !           265:     logout("text        %s", header->text);
        !           266:     logout("signature   0x%04x\n", header->signature);
        !           267:     logout("header size 0x%04x\n", header->header_size);
        !           268:     logout("image type  0x%04x\n", header->image_type);
        !           269:     logout("image flags 0x%04x\n", header->image_flags);
        !           270:     logout("description %s\n", header->description);
        !           271:     logout("offset bmap 0x%04x\n", header->offset_bmap);
        !           272:     logout("offset data 0x%04x\n", header->offset_data);
        !           273:     logout("cylinders   0x%04x\n", header->cylinders);
        !           274:     logout("heads       0x%04x\n", header->heads);
        !           275:     logout("sectors     0x%04x\n", header->sectors);
        !           276:     logout("sector size 0x%04x\n", header->sector_size);
        !           277:     logout("image size  0x%" PRIx64 " B (%" PRIu64 " MiB)\n",
        !           278:            header->disk_size, header->disk_size / MiB);
        !           279:     logout("block size  0x%04x\n", header->block_size);
        !           280:     logout("block extra 0x%04x\n", header->block_extra);
        !           281:     logout("blocks tot. 0x%04x\n", header->blocks_in_image);
        !           282:     logout("blocks all. 0x%04x\n", header->blocks_allocated);
        !           283:     uuid_unparse(header->uuid_image, uuid);
        !           284:     logout("uuid image  %s\n", uuid);
        !           285:     uuid_unparse(header->uuid_last_snap, uuid);
        !           286:     logout("uuid snap   %s\n", uuid);
        !           287:     uuid_unparse(header->uuid_link, uuid);
        !           288:     logout("uuid link   %s\n", uuid);
        !           289:     uuid_unparse(header->uuid_parent, uuid);
        !           290:     logout("uuid parent %s\n", uuid);
        !           291: }
        !           292: #endif
        !           293: 
        !           294: static int vdi_check(BlockDriverState *bs)
        !           295: {
        !           296:     /* TODO: additional checks possible. */
        !           297:     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
        !           298:     int n_errors = 0;
        !           299:     uint32_t blocks_allocated = 0;
        !           300:     uint32_t block;
        !           301:     uint32_t *bmap;
        !           302:     logout("\n");
        !           303: 
        !           304:     bmap = qemu_malloc(s->header.blocks_in_image * sizeof(uint32_t));
        !           305:     memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t));
        !           306: 
        !           307:     /* Check block map and value of blocks_allocated. */
        !           308:     for (block = 0; block < s->header.blocks_in_image; block++) {
        !           309:         uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
        !           310:         if (bmap_entry != VDI_UNALLOCATED) {
        !           311:             if (bmap_entry < s->header.blocks_in_image) {
        !           312:                 blocks_allocated++;
        !           313:                 if (bmap[bmap_entry] == VDI_UNALLOCATED) {
        !           314:                     bmap[bmap_entry] = bmap_entry;
        !           315:                 } else {
        !           316:                     fprintf(stderr, "ERROR: block index %" PRIu32
        !           317:                             " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry);
        !           318:                 }
        !           319:             } else {
        !           320:                 fprintf(stderr, "ERROR: block index %" PRIu32
        !           321:                         " too large, is %" PRIu32 "\n", block, bmap_entry);
        !           322:                 n_errors++;
        !           323:             }
        !           324:         }
        !           325:     }
        !           326:     if (blocks_allocated != s->header.blocks_allocated) {
        !           327:         fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32
        !           328:                ", should be %" PRIu32 "\n",
        !           329:                blocks_allocated, s->header.blocks_allocated);
        !           330:         n_errors++;
        !           331:     }
        !           332: 
        !           333:     qemu_free(bmap);
        !           334: 
        !           335:     return n_errors;
        !           336: }
        !           337: 
        !           338: static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
        !           339: {
        !           340:     /* TODO: vdi_get_info would be needed for machine snapshots.
        !           341:        vm_state_offset is still missing. */
        !           342:     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
        !           343:     logout("\n");
        !           344:     bdi->cluster_size = s->block_size;
        !           345:     bdi->vm_state_offset = 0;
        !           346:     return 0;
        !           347: }
        !           348: 
        !           349: static int vdi_make_empty(BlockDriverState *bs)
        !           350: {
        !           351:     /* TODO: missing code. */
        !           352:     logout("\n");
        !           353:     /* The return value for missing code must be 0, see block.c. */
        !           354:     return 0;
        !           355: }
        !           356: 
        !           357: static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
        !           358: {
        !           359:     const VdiHeader *header = (const VdiHeader *)buf;
        !           360:     int result = 0;
        !           361: 
        !           362:     logout("\n");
        !           363: 
        !           364:     if (buf_size < sizeof(*header)) {
        !           365:         /* Header too small, no VDI. */
        !           366:     } else if (le32_to_cpu(header->signature) == VDI_SIGNATURE) {
        !           367:         result = 100;
        !           368:     }
        !           369: 
        !           370:     if (result == 0) {
        !           371:         logout("no vdi image\n");
        !           372:     } else {
        !           373:         logout("%s", header->text);
        !           374:     }
        !           375: 
        !           376:     return result;
        !           377: }
        !           378: 
        !           379: static int vdi_open(BlockDriverState *bs, const char *filename, int flags)
        !           380: {
        !           381:     BDRVVdiState *s = bs->opaque;
        !           382:     VdiHeader header;
        !           383:     size_t bmap_size;
        !           384:     int ret;
        !           385: 
        !           386:     logout("\n");
        !           387: 
        !           388:     ret = bdrv_file_open(&s->hd, filename, flags);
        !           389:     if (ret < 0) {
        !           390:         return ret;
        !           391:     }
        !           392: 
        !           393:     if (bdrv_read(s->hd, 0, (uint8_t *)&header, 1) < 0) {
        !           394:         goto fail;
        !           395:     }
        !           396: 
        !           397:     vdi_header_to_cpu(&header);
        !           398: #if defined(CONFIG_VDI_DEBUG)
        !           399:     vdi_header_print(&header);
        !           400: #endif
        !           401: 
        !           402:     if (header.version != VDI_VERSION_1_1) {
        !           403:         logout("unsupported version %u.%u\n",
        !           404:                header.version >> 16, header.version & 0xffff);
        !           405:         goto fail;
        !           406:     } else if (header.offset_bmap % SECTOR_SIZE != 0) {
        !           407:         /* We only support block maps which start on a sector boundary. */
        !           408:         logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
        !           409:         goto fail;
        !           410:     } else if (header.offset_data % SECTOR_SIZE != 0) {
        !           411:         /* We only support data blocks which start on a sector boundary. */
        !           412:         logout("unsupported data offset 0x%x B\n", header.offset_data);
        !           413:         goto fail;
        !           414:     } else if (header.sector_size != SECTOR_SIZE) {
        !           415:         logout("unsupported sector size %u B\n", header.sector_size);
        !           416:         goto fail;
        !           417:     } else if (header.block_size != 1 * MiB) {
        !           418:         logout("unsupported block size %u B\n", header.block_size);
        !           419:         goto fail;
        !           420:     } else if (header.disk_size !=
        !           421:                (uint64_t)header.blocks_in_image * header.block_size) {
        !           422:         logout("unexpected block number %u B\n", header.blocks_in_image);
        !           423:         goto fail;
        !           424:     } else if (!uuid_is_null(header.uuid_link)) {
        !           425:         logout("link uuid != 0, unsupported\n");
        !           426:         goto fail;
        !           427:     } else if (!uuid_is_null(header.uuid_parent)) {
        !           428:         logout("parent uuid != 0, unsupported\n");
        !           429:         goto fail;
        !           430:     }
        !           431: 
        !           432:     bs->total_sectors = header.disk_size / SECTOR_SIZE;
        !           433: 
        !           434:     s->block_size = header.block_size;
        !           435:     s->block_sectors = header.block_size / SECTOR_SIZE;
        !           436:     s->bmap_sector = header.offset_bmap / SECTOR_SIZE;
        !           437:     s->header = header;
        !           438: 
        !           439:     bmap_size = header.blocks_in_image * sizeof(uint32_t);
        !           440:     bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
        !           441:     s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE);
        !           442:     if (bdrv_read(s->hd, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) {
        !           443:         goto fail_free_bmap;
        !           444:     }
        !           445: 
        !           446:     return 0;
        !           447: 
        !           448:  fail_free_bmap:
        !           449:     qemu_free(s->bmap);
        !           450: 
        !           451:  fail:
        !           452:     bdrv_delete(s->hd);
        !           453:     return -1;
        !           454: }
        !           455: 
        !           456: static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
        !           457:                              int nb_sectors, int *pnum)
        !           458: {
        !           459:     /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
        !           460:     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
        !           461:     size_t bmap_index = sector_num / s->block_sectors;
        !           462:     size_t sector_in_block = sector_num % s->block_sectors;
        !           463:     int n_sectors = s->block_sectors - sector_in_block;
        !           464:     uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
        !           465:     logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum);
        !           466:     if (n_sectors > nb_sectors) {
        !           467:         n_sectors = nb_sectors;
        !           468:     }
        !           469:     *pnum = n_sectors;
        !           470:     return bmap_entry != VDI_UNALLOCATED;
        !           471: }
        !           472: 
        !           473: static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
        !           474: {
        !           475:     /* TODO: This code is untested. How can I get it executed? */
        !           476:     VdiAIOCB *acb = (VdiAIOCB *)blockacb;
        !           477:     logout("\n");
        !           478:     if (acb->hd_aiocb) {
        !           479:         bdrv_aio_cancel(acb->hd_aiocb);
        !           480:     }
        !           481:     qemu_aio_release(acb);
        !           482: }
        !           483: 
        !           484: static AIOPool vdi_aio_pool = {
        !           485:     .aiocb_size = sizeof(VdiAIOCB),
        !           486:     .cancel = vdi_aio_cancel,
        !           487: };
        !           488: 
        !           489: static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num,
        !           490:         QEMUIOVector *qiov, int nb_sectors,
        !           491:         BlockDriverCompletionFunc *cb, void *opaque, int is_write)
        !           492: {
        !           493:     VdiAIOCB *acb;
        !           494: 
        !           495:     logout("%p, %" PRId64 ", %p, %d, %p, %p, %d\n",
        !           496:            bs, sector_num, qiov, nb_sectors, cb, opaque, is_write);
        !           497: 
        !           498:     acb = qemu_aio_get(&vdi_aio_pool, bs, cb, opaque);
        !           499:     if (acb) {
        !           500:         acb->hd_aiocb = NULL;
        !           501:         acb->sector_num = sector_num;
        !           502:         acb->qiov = qiov;
        !           503:         if (qiov->niov > 1) {
        !           504:             acb->buf = qemu_blockalign(bs, qiov->size);
        !           505:             acb->orig_buf = acb->buf;
        !           506:             if (is_write) {
        !           507:                 qemu_iovec_to_buffer(qiov, acb->buf);
        !           508:             }
        !           509:         } else {
        !           510:             acb->buf = (uint8_t *)qiov->iov->iov_base;
        !           511:         }
        !           512:         acb->nb_sectors = nb_sectors;
        !           513:         acb->n_sectors = 0;
        !           514:         acb->bmap_first = VDI_UNALLOCATED;
        !           515:         acb->bmap_last = VDI_UNALLOCATED;
        !           516:         acb->block_buffer = NULL;
        !           517:         acb->header_modified = 0;
        !           518:     }
        !           519:     return acb;
        !           520: }
        !           521: 
        !           522: static int vdi_schedule_bh(QEMUBHFunc *cb, VdiAIOCB *acb)
        !           523: {
        !           524:     logout("\n");
        !           525: 
        !           526:     if (acb->bh) {
        !           527:         return -EIO;
        !           528:     }
        !           529: 
        !           530:     acb->bh = qemu_bh_new(cb, acb);
        !           531:     if (!acb->bh) {
        !           532:         return -EIO;
        !           533:     }
        !           534: 
        !           535:     qemu_bh_schedule(acb->bh);
        !           536: 
        !           537:     return 0;
        !           538: }
        !           539: 
        !           540: static void vdi_aio_read_cb(void *opaque, int ret);
        !           541: 
        !           542: static void vdi_aio_read_bh(void *opaque)
        !           543: {
        !           544:     VdiAIOCB *acb = opaque;
        !           545:     logout("\n");
        !           546:     qemu_bh_delete(acb->bh);
        !           547:     acb->bh = NULL;
        !           548:     vdi_aio_read_cb(opaque, 0);
        !           549: }
        !           550: 
        !           551: static void vdi_aio_read_cb(void *opaque, int ret)
        !           552: {
        !           553:     VdiAIOCB *acb = opaque;
        !           554:     BlockDriverState *bs = acb->common.bs;
        !           555:     BDRVVdiState *s = bs->opaque;
        !           556:     uint32_t bmap_entry;
        !           557:     uint32_t block_index;
        !           558:     uint32_t sector_in_block;
        !           559:     uint32_t n_sectors;
        !           560: 
        !           561:     logout("%u sectors read\n", acb->n_sectors);
        !           562: 
        !           563:     acb->hd_aiocb = NULL;
        !           564: 
        !           565:     if (ret < 0) {
        !           566:         goto done;
        !           567:     }
        !           568: 
        !           569:     acb->nb_sectors -= acb->n_sectors;
        !           570: 
        !           571:     if (acb->nb_sectors == 0) {
        !           572:         /* request completed */
        !           573:         ret = 0;
        !           574:         goto done;
        !           575:     }
        !           576: 
        !           577:     acb->sector_num += acb->n_sectors;
        !           578:     acb->buf += acb->n_sectors * SECTOR_SIZE;
        !           579: 
        !           580:     block_index = acb->sector_num / s->block_sectors;
        !           581:     sector_in_block = acb->sector_num % s->block_sectors;
        !           582:     n_sectors = s->block_sectors - sector_in_block;
        !           583:     if (n_sectors > acb->nb_sectors) {
        !           584:         n_sectors = acb->nb_sectors;
        !           585:     }
        !           586: 
        !           587:     logout("will read %u sectors starting at sector %" PRIu64 "\n",
        !           588:            n_sectors, acb->sector_num);
        !           589: 
        !           590:     /* prepare next AIO request */
        !           591:     acb->n_sectors = n_sectors;
        !           592:     bmap_entry = le32_to_cpu(s->bmap[block_index]);
        !           593:     if (bmap_entry == VDI_UNALLOCATED) {
        !           594:         /* Block not allocated, return zeros, no need to wait. */
        !           595:         memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
        !           596:         ret = vdi_schedule_bh(vdi_aio_read_bh, acb);
        !           597:         if (ret < 0) {
        !           598:             goto done;
        !           599:         }
        !           600:     } else {
        !           601:         uint64_t offset = s->header.offset_data / SECTOR_SIZE +
        !           602:                           (uint64_t)bmap_entry * s->block_sectors +
        !           603:                           sector_in_block;
        !           604:         acb->hd_iov.iov_base = (void *)acb->buf;
        !           605:         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
        !           606:         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
        !           607:         acb->hd_aiocb = bdrv_aio_readv(s->hd, offset, &acb->hd_qiov,
        !           608:                                        n_sectors, vdi_aio_read_cb, acb);
        !           609:         if (acb->hd_aiocb == NULL) {
        !           610:             goto done;
        !           611:         }
        !           612:     }
        !           613:     return;
        !           614: done:
        !           615:     if (acb->qiov->niov > 1) {
        !           616:         qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
        !           617:         qemu_vfree(acb->orig_buf);
        !           618:     }
        !           619:     acb->common.cb(acb->common.opaque, ret);
        !           620:     qemu_aio_release(acb);
        !           621: }
        !           622: 
        !           623: static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs,
        !           624:         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
        !           625:         BlockDriverCompletionFunc *cb, void *opaque)
        !           626: {
        !           627:     VdiAIOCB *acb;
        !           628:     logout("\n");
        !           629:     acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
        !           630:     if (!acb) {
        !           631:         return NULL;
        !           632:     }
        !           633:     vdi_aio_read_cb(acb, 0);
        !           634:     return &acb->common;
        !           635: }
        !           636: 
        !           637: static void vdi_aio_write_cb(void *opaque, int ret)
        !           638: {
        !           639:     VdiAIOCB *acb = opaque;
        !           640:     BlockDriverState *bs = acb->common.bs;
        !           641:     BDRVVdiState *s = bs->opaque;
        !           642:     uint32_t bmap_entry;
        !           643:     uint32_t block_index;
        !           644:     uint32_t sector_in_block;
        !           645:     uint32_t n_sectors;
        !           646: 
        !           647:     acb->hd_aiocb = NULL;
        !           648: 
        !           649:     if (ret < 0) {
        !           650:         goto done;
        !           651:     }
        !           652: 
        !           653:     acb->nb_sectors -= acb->n_sectors;
        !           654:     acb->sector_num += acb->n_sectors;
        !           655:     acb->buf += acb->n_sectors * SECTOR_SIZE;
        !           656: 
        !           657:     if (acb->nb_sectors == 0) {
        !           658:         logout("finished data write\n");
        !           659:         acb->n_sectors = 0;
        !           660:         if (acb->header_modified) {
        !           661:             VdiHeader *header = acb->block_buffer;
        !           662:             logout("now writing modified header\n");
        !           663:             assert(acb->bmap_first != VDI_UNALLOCATED);
        !           664:             *header = s->header;
        !           665:             vdi_header_to_le(header);
        !           666:             acb->header_modified = 0;
        !           667:             acb->hd_iov.iov_base = acb->block_buffer;
        !           668:             acb->hd_iov.iov_len = SECTOR_SIZE;
        !           669:             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
        !           670:             acb->hd_aiocb = bdrv_aio_writev(s->hd, 0, &acb->hd_qiov, 1,
        !           671:                                             vdi_aio_write_cb, acb);
        !           672:             if (acb->hd_aiocb == NULL) {
        !           673:                 goto done;
        !           674:             }
        !           675:             return;
        !           676:         } else if (acb->bmap_first != VDI_UNALLOCATED) {
        !           677:             /* One or more new blocks were allocated. */
        !           678:             uint64_t offset;
        !           679:             uint32_t bmap_first;
        !           680:             uint32_t bmap_last;
        !           681:             qemu_free(acb->block_buffer);
        !           682:             acb->block_buffer = NULL;
        !           683:             bmap_first = acb->bmap_first;
        !           684:             bmap_last = acb->bmap_last;
        !           685:             logout("now writing modified block map entry %u...%u\n",
        !           686:                    bmap_first, bmap_last);
        !           687:             /* Write modified sectors from block map. */
        !           688:             bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
        !           689:             bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
        !           690:             n_sectors = bmap_last - bmap_first + 1;
        !           691:             offset = s->bmap_sector + bmap_first;
        !           692:             acb->bmap_first = VDI_UNALLOCATED;
        !           693:             acb->hd_iov.iov_base = (void *)((uint8_t *)&s->bmap[0] +
        !           694:                                             bmap_first * SECTOR_SIZE);
        !           695:             acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
        !           696:             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
        !           697:             logout("will write %u block map sectors starting from entry %u\n",
        !           698:                    n_sectors, bmap_first);
        !           699:             acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov,
        !           700:                                             n_sectors, vdi_aio_write_cb, acb);
        !           701:             if (acb->hd_aiocb == NULL) {
        !           702:                 goto done;
        !           703:             }
        !           704:             return;
        !           705:         }
        !           706:         ret = 0;
        !           707:         goto done;
        !           708:     }
        !           709: 
        !           710:     logout("%u sectors written\n", acb->n_sectors);
        !           711: 
        !           712:     block_index = acb->sector_num / s->block_sectors;
        !           713:     sector_in_block = acb->sector_num % s->block_sectors;
        !           714:     n_sectors = s->block_sectors - sector_in_block;
        !           715:     if (n_sectors > acb->nb_sectors) {
        !           716:         n_sectors = acb->nb_sectors;
        !           717:     }
        !           718: 
        !           719:     logout("will write %u sectors starting at sector %" PRIu64 "\n",
        !           720:            n_sectors, acb->sector_num);
        !           721: 
        !           722:     /* prepare next AIO request */
        !           723:     acb->n_sectors = n_sectors;
        !           724:     bmap_entry = le32_to_cpu(s->bmap[block_index]);
        !           725:     if (bmap_entry == VDI_UNALLOCATED) {
        !           726:         /* Allocate new block and write to it. */
        !           727:         uint64_t offset;
        !           728:         uint8_t *block;
        !           729:         bmap_entry = s->header.blocks_allocated;
        !           730:         s->bmap[block_index] = cpu_to_le32(bmap_entry);
        !           731:         s->header.blocks_allocated++;
        !           732:         offset = s->header.offset_data / SECTOR_SIZE +
        !           733:                  (uint64_t)bmap_entry * s->block_sectors;
        !           734:         block = acb->block_buffer;
        !           735:         if (block == NULL) {
        !           736:             block = qemu_mallocz(s->block_size);
        !           737:             acb->block_buffer = block;
        !           738:             acb->bmap_first = block_index;
        !           739:             assert(!acb->header_modified);
        !           740:             acb->header_modified = 1;
        !           741:         }
        !           742:         acb->bmap_last = block_index;
        !           743:         memcpy(block + sector_in_block * SECTOR_SIZE,
        !           744:                acb->buf, n_sectors * SECTOR_SIZE);
        !           745:         acb->hd_iov.iov_base = (void *)block;
        !           746:         acb->hd_iov.iov_len = s->block_size;
        !           747:         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
        !           748:         acb->hd_aiocb = bdrv_aio_writev(s->hd, offset,
        !           749:                                         &acb->hd_qiov, s->block_sectors,
        !           750:                                         vdi_aio_write_cb, acb);
        !           751:         if (acb->hd_aiocb == NULL) {
        !           752:             goto done;
        !           753:         }
        !           754:     } else {
        !           755:         uint64_t offset = s->header.offset_data / SECTOR_SIZE +
        !           756:                           (uint64_t)bmap_entry * s->block_sectors +
        !           757:                           sector_in_block;
        !           758:         acb->hd_iov.iov_base = (void *)acb->buf;
        !           759:         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
        !           760:         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
        !           761:         acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov,
        !           762:                                         n_sectors, vdi_aio_write_cb, acb);
        !           763:         if (acb->hd_aiocb == NULL) {
        !           764:             goto done;
        !           765:         }
        !           766:     }
        !           767: 
        !           768:     return;
        !           769: 
        !           770: done:
        !           771:     if (acb->qiov->niov > 1) {
        !           772:         qemu_vfree(acb->orig_buf);
        !           773:     }
        !           774:     acb->common.cb(acb->common.opaque, ret);
        !           775:     qemu_aio_release(acb);
        !           776: }
        !           777: 
        !           778: static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs,
        !           779:         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
        !           780:         BlockDriverCompletionFunc *cb, void *opaque)
        !           781: {
        !           782:     VdiAIOCB *acb;
        !           783:     logout("\n");
        !           784:     acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
        !           785:     if (!acb) {
        !           786:         return NULL;
        !           787:     }
        !           788:     vdi_aio_write_cb(acb, 0);
        !           789:     return &acb->common;
        !           790: }
        !           791: 
        !           792: static int vdi_create(const char *filename, QEMUOptionParameter *options)
        !           793: {
        !           794:     int fd;
        !           795:     int result = 0;
        !           796:     uint64_t bytes = 0;
        !           797:     uint32_t blocks;
        !           798:     size_t block_size = 1 * MiB;
        !           799:     uint32_t image_type = VDI_TYPE_DYNAMIC;
        !           800:     VdiHeader header;
        !           801:     size_t i;
        !           802:     size_t bmap_size;
        !           803:     uint32_t *bmap;
        !           804: 
        !           805:     logout("\n");
        !           806: 
        !           807:     /* Read out options. */
        !           808:     while (options && options->name) {
        !           809:         if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
        !           810:             bytes = options->value.n;
        !           811: #if defined(CONFIG_VDI_BLOCK_SIZE)
        !           812:         } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
        !           813:             if (options->value.n) {
        !           814:                 /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
        !           815:                 block_size = options->value.n;
        !           816:             }
        !           817: #endif
        !           818: #if defined(CONFIG_VDI_STATIC_IMAGE)
        !           819:         } else if (!strcmp(options->name, BLOCK_OPT_STATIC)) {
        !           820:             if (options->value.n) {
        !           821:                 image_type = VDI_TYPE_STATIC;
        !           822:             }
        !           823: #endif
        !           824:         }
        !           825:         options++;
        !           826:     }
        !           827: 
        !           828:     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
        !           829:               0644);
        !           830:     if (fd < 0) {
        !           831:         return -errno;
        !           832:     }
        !           833: 
        !           834:     blocks = bytes / block_size;
        !           835:     bmap_size = blocks * sizeof(uint32_t);
        !           836:     bmap_size = ((bmap_size + SECTOR_SIZE - 1) & ~(SECTOR_SIZE -1));
        !           837: 
        !           838:     memset(&header, 0, sizeof(header));
        !           839:     pstrcpy(header.text, sizeof(header.text), VDI_TEXT);
        !           840:     header.signature = VDI_SIGNATURE;
        !           841:     header.version = VDI_VERSION_1_1;
        !           842:     header.header_size = 0x180;
        !           843:     header.image_type = image_type;
        !           844:     header.offset_bmap = 0x200;
        !           845:     header.offset_data = 0x200 + bmap_size;
        !           846:     header.sector_size = SECTOR_SIZE;
        !           847:     header.disk_size = bytes;
        !           848:     header.block_size = block_size;
        !           849:     header.blocks_in_image = blocks;
        !           850:     if (image_type == VDI_TYPE_STATIC) {
        !           851:         header.blocks_allocated = blocks;
        !           852:     }
        !           853:     uuid_generate(header.uuid_image);
        !           854:     uuid_generate(header.uuid_last_snap);
        !           855:     /* There is no need to set header.uuid_link or header.uuid_parent here. */
        !           856: #if defined(CONFIG_VDI_DEBUG)
        !           857:     vdi_header_print(&header);
        !           858: #endif
        !           859:     vdi_header_to_le(&header);
        !           860:     if (write(fd, &header, sizeof(header)) < 0) {
        !           861:         result = -errno;
        !           862:     }
        !           863: 
        !           864:     bmap = (uint32_t *)qemu_mallocz(bmap_size);
        !           865:     for (i = 0; i < blocks; i++) {
        !           866:         if (image_type == VDI_TYPE_STATIC) {
        !           867:             bmap[i] = i;
        !           868:         } else {
        !           869:             bmap[i] = VDI_UNALLOCATED;
        !           870:         }
        !           871:     }
        !           872:     if (write(fd, bmap, bmap_size) < 0) {
        !           873:         result = -errno;
        !           874:     }
        !           875:     qemu_free(bmap);
        !           876:     if (image_type == VDI_TYPE_STATIC) {
        !           877:         if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) {
        !           878:             result = -errno;
        !           879:         }
        !           880:     }
        !           881: 
        !           882:     if (close(fd) < 0) {
        !           883:         result = -errno;
        !           884:     }
        !           885: 
        !           886:     return result;
        !           887: }
        !           888: 
        !           889: static void vdi_close(BlockDriverState *bs)
        !           890: {
        !           891:     BDRVVdiState *s = bs->opaque;
        !           892:     logout("\n");
        !           893:     bdrv_delete(s->hd);
        !           894: }
        !           895: 
        !           896: static void vdi_flush(BlockDriverState *bs)
        !           897: {
        !           898:     BDRVVdiState *s = bs->opaque;
        !           899:     logout("\n");
        !           900:     bdrv_flush(s->hd);
        !           901: }
        !           902: 
        !           903: 
        !           904: static QEMUOptionParameter vdi_create_options[] = {
        !           905:     {
        !           906:         .name = BLOCK_OPT_SIZE,
        !           907:         .type = OPT_SIZE,
        !           908:         .help = "Virtual disk size"
        !           909:     },
        !           910: #if defined(CONFIG_VDI_BLOCK_SIZE)
        !           911:     {
        !           912:         .name = BLOCK_OPT_CLUSTER_SIZE,
        !           913:         .type = OPT_SIZE,
        !           914:         .help = "VDI cluster (block) size"
        !           915:     },
        !           916: #endif
        !           917: #if defined(CONFIG_VDI_STATIC_IMAGE)
        !           918:     {
        !           919:         .name = BLOCK_OPT_STATIC,
        !           920:         .type = OPT_FLAG,
        !           921:         .help = "VDI static (pre-allocated) image"
        !           922:     },
        !           923: #endif
        !           924:     /* TODO: An additional option to set UUID values might be useful. */
        !           925:     { NULL }
        !           926: };
        !           927: 
        !           928: static BlockDriver bdrv_vdi = {
        !           929:     .format_name = "vdi",
        !           930:     .instance_size = sizeof(BDRVVdiState),
        !           931:     .bdrv_probe = vdi_probe,
        !           932:     .bdrv_open = vdi_open,
        !           933:     .bdrv_close = vdi_close,
        !           934:     .bdrv_create = vdi_create,
        !           935:     .bdrv_flush = vdi_flush,
        !           936:     .bdrv_is_allocated = vdi_is_allocated,
        !           937:     .bdrv_make_empty = vdi_make_empty,
        !           938: 
        !           939:     .bdrv_aio_readv = vdi_aio_readv,
        !           940: #if defined(CONFIG_VDI_WRITE)
        !           941:     .bdrv_aio_writev = vdi_aio_writev,
        !           942: #endif
        !           943: 
        !           944:     .bdrv_get_info = vdi_get_info,
        !           945: 
        !           946:     .create_options = vdi_create_options,
        !           947:     .bdrv_check = vdi_check,
        !           948: };
        !           949: 
        !           950: static void bdrv_vdi_init(void)
        !           951: {
        !           952:     logout("\n");
        !           953:     bdrv_register(&bdrv_vdi);
        !           954: }
        !           955: 
        !           956: block_init(bdrv_vdi_init);

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.