1019d6b8fSAnthony Liguori /* 2cc2040f8SStefan Weil * Block driver for Connectix / Microsoft Virtual PC images 3019d6b8fSAnthony Liguori * 4019d6b8fSAnthony Liguori * Copyright (c) 2005 Alex Beregszaszi 5019d6b8fSAnthony Liguori * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de> 6019d6b8fSAnthony Liguori * 7019d6b8fSAnthony Liguori * Permission is hereby granted, free of charge, to any person obtaining a copy 8019d6b8fSAnthony Liguori * of this software and associated documentation files (the "Software"), to deal 9019d6b8fSAnthony Liguori * in the Software without restriction, including without limitation the rights 10019d6b8fSAnthony Liguori * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11019d6b8fSAnthony Liguori * copies of the Software, and to permit persons to whom the Software is 12019d6b8fSAnthony Liguori * furnished to do so, subject to the following conditions: 13019d6b8fSAnthony Liguori * 14019d6b8fSAnthony Liguori * The above copyright notice and this permission notice shall be included in 15019d6b8fSAnthony Liguori * all copies or substantial portions of the Software. 16019d6b8fSAnthony Liguori * 17019d6b8fSAnthony Liguori * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18019d6b8fSAnthony Liguori * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19019d6b8fSAnthony Liguori * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20019d6b8fSAnthony Liguori * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21019d6b8fSAnthony Liguori * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22019d6b8fSAnthony Liguori * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23019d6b8fSAnthony Liguori * THE SOFTWARE. 24019d6b8fSAnthony Liguori */ 25019d6b8fSAnthony Liguori #include "qemu-common.h" 26737e150eSPaolo Bonzini #include "block/block_int.h" 271de7afc9SPaolo Bonzini #include "qemu/module.h" 28caf71f86SPaolo Bonzini #include "migration/migration.h" 291fe1fa51SCharles Arnold #if defined(CONFIG_UUID) 301fe1fa51SCharles Arnold #include <uuid/uuid.h> 311fe1fa51SCharles Arnold #endif 32*4ab15590SChunyan Liu #ifdef __linux__ 33*4ab15590SChunyan Liu #include <linux/fs.h> 34*4ab15590SChunyan Liu #include <sys/ioctl.h> 35*4ab15590SChunyan Liu #ifndef FS_NOCOW_FL 36*4ab15590SChunyan Liu #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ 37*4ab15590SChunyan Liu #endif 38*4ab15590SChunyan Liu #endif 39019d6b8fSAnthony Liguori 40019d6b8fSAnthony Liguori /**************************************************************/ 41019d6b8fSAnthony Liguori 42019d6b8fSAnthony Liguori #define HEADER_SIZE 512 43019d6b8fSAnthony Liguori 44019d6b8fSAnthony Liguori //#define CACHE 45019d6b8fSAnthony Liguori 46019d6b8fSAnthony Liguori enum vhd_type { 47019d6b8fSAnthony Liguori VHD_FIXED = 2, 48019d6b8fSAnthony Liguori VHD_DYNAMIC = 3, 49019d6b8fSAnthony Liguori VHD_DIFFERENCING = 4, 50019d6b8fSAnthony Liguori }; 51019d6b8fSAnthony Liguori 52019d6b8fSAnthony Liguori // Seconds since Jan 1, 2000 0:00:00 (UTC) 53019d6b8fSAnthony Liguori #define VHD_TIMESTAMP_BASE 946684800 54019d6b8fSAnthony Liguori 5597f1c45cSJeff Cody #define VHD_MAX_SECTORS (65535LL * 255 * 255) 5697f1c45cSJeff Cody 57019d6b8fSAnthony Liguori // always big-endian 58e54835c0SJeff Cody typedef struct vhd_footer { 59019d6b8fSAnthony Liguori char creator[8]; // "conectix" 60019d6b8fSAnthony Liguori uint32_t features; 61019d6b8fSAnthony Liguori uint32_t version; 62019d6b8fSAnthony Liguori 63019d6b8fSAnthony Liguori // Offset of next header structure, 0xFFFFFFFF if none 64019d6b8fSAnthony Liguori uint64_t data_offset; 65019d6b8fSAnthony Liguori 66019d6b8fSAnthony Liguori // Seconds since Jan 1, 2000 0:00:00 (UTC) 67019d6b8fSAnthony Liguori uint32_t timestamp; 68019d6b8fSAnthony Liguori 69019d6b8fSAnthony Liguori char creator_app[4]; // "vpc " 70019d6b8fSAnthony Liguori uint16_t major; 71019d6b8fSAnthony Liguori uint16_t minor; 72019d6b8fSAnthony Liguori char creator_os[4]; // "Wi2k" 73019d6b8fSAnthony Liguori 74019d6b8fSAnthony Liguori uint64_t orig_size; 75019d6b8fSAnthony Liguori uint64_t size; 76019d6b8fSAnthony Liguori 77019d6b8fSAnthony Liguori uint16_t cyls; 78019d6b8fSAnthony Liguori uint8_t heads; 79019d6b8fSAnthony Liguori uint8_t secs_per_cyl; 80019d6b8fSAnthony Liguori 81019d6b8fSAnthony Liguori uint32_t type; 82019d6b8fSAnthony Liguori 83019d6b8fSAnthony Liguori // Checksum of the Hard Disk Footer ("one's complement of the sum of all 84019d6b8fSAnthony Liguori // the bytes in the footer without the checksum field") 85019d6b8fSAnthony Liguori uint32_t checksum; 86019d6b8fSAnthony Liguori 87019d6b8fSAnthony Liguori // UUID used to identify a parent hard disk (backing file) 88019d6b8fSAnthony Liguori uint8_t uuid[16]; 89019d6b8fSAnthony Liguori 90019d6b8fSAnthony Liguori uint8_t in_saved_state; 91e54835c0SJeff Cody } QEMU_PACKED VHDFooter; 92019d6b8fSAnthony Liguori 93e54835c0SJeff Cody typedef struct vhd_dyndisk_header { 94019d6b8fSAnthony Liguori char magic[8]; // "cxsparse" 95019d6b8fSAnthony Liguori 96019d6b8fSAnthony Liguori // Offset of next header structure, 0xFFFFFFFF if none 97019d6b8fSAnthony Liguori uint64_t data_offset; 98019d6b8fSAnthony Liguori 99019d6b8fSAnthony Liguori // Offset of the Block Allocation Table (BAT) 100019d6b8fSAnthony Liguori uint64_t table_offset; 101019d6b8fSAnthony Liguori 102019d6b8fSAnthony Liguori uint32_t version; 103019d6b8fSAnthony Liguori uint32_t max_table_entries; // 32bit/entry 104019d6b8fSAnthony Liguori 105019d6b8fSAnthony Liguori // 2 MB by default, must be a power of two 106019d6b8fSAnthony Liguori uint32_t block_size; 107019d6b8fSAnthony Liguori 108019d6b8fSAnthony Liguori uint32_t checksum; 109019d6b8fSAnthony Liguori uint8_t parent_uuid[16]; 110019d6b8fSAnthony Liguori uint32_t parent_timestamp; 111019d6b8fSAnthony Liguori uint32_t reserved; 112019d6b8fSAnthony Liguori 113019d6b8fSAnthony Liguori // Backing file name (in UTF-16) 114019d6b8fSAnthony Liguori uint8_t parent_name[512]; 115019d6b8fSAnthony Liguori 116019d6b8fSAnthony Liguori struct { 117019d6b8fSAnthony Liguori uint32_t platform; 118019d6b8fSAnthony Liguori uint32_t data_space; 119019d6b8fSAnthony Liguori uint32_t data_length; 120019d6b8fSAnthony Liguori uint32_t reserved; 121019d6b8fSAnthony Liguori uint64_t data_offset; 122019d6b8fSAnthony Liguori } parent_locator[8]; 123e54835c0SJeff Cody } QEMU_PACKED VHDDynDiskHeader; 124019d6b8fSAnthony Liguori 125019d6b8fSAnthony Liguori typedef struct BDRVVPCState { 126848c66e8SPaolo Bonzini CoMutex lock; 127019d6b8fSAnthony Liguori uint8_t footer_buf[HEADER_SIZE]; 128019d6b8fSAnthony Liguori uint64_t free_data_block_offset; 129019d6b8fSAnthony Liguori int max_table_entries; 130019d6b8fSAnthony Liguori uint32_t *pagetable; 131019d6b8fSAnthony Liguori uint64_t bat_offset; 132019d6b8fSAnthony Liguori uint64_t last_bitmap_offset; 133019d6b8fSAnthony Liguori 134019d6b8fSAnthony Liguori uint32_t block_size; 135019d6b8fSAnthony Liguori uint32_t bitmap_size; 136019d6b8fSAnthony Liguori 137019d6b8fSAnthony Liguori #ifdef CACHE 138019d6b8fSAnthony Liguori uint8_t *pageentry_u8; 139019d6b8fSAnthony Liguori uint32_t *pageentry_u32; 140019d6b8fSAnthony Liguori uint16_t *pageentry_u16; 141019d6b8fSAnthony Liguori 142019d6b8fSAnthony Liguori uint64_t last_bitmap; 143019d6b8fSAnthony Liguori #endif 144612ff3d8SKevin Wolf 145612ff3d8SKevin Wolf Error *migration_blocker; 146019d6b8fSAnthony Liguori } BDRVVPCState; 147019d6b8fSAnthony Liguori 148019d6b8fSAnthony Liguori static uint32_t vpc_checksum(uint8_t* buf, size_t size) 149019d6b8fSAnthony Liguori { 150019d6b8fSAnthony Liguori uint32_t res = 0; 151019d6b8fSAnthony Liguori int i; 152019d6b8fSAnthony Liguori 153019d6b8fSAnthony Liguori for (i = 0; i < size; i++) 154019d6b8fSAnthony Liguori res += buf[i]; 155019d6b8fSAnthony Liguori 156019d6b8fSAnthony Liguori return ~res; 157019d6b8fSAnthony Liguori } 158019d6b8fSAnthony Liguori 159019d6b8fSAnthony Liguori 160019d6b8fSAnthony Liguori static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) 161019d6b8fSAnthony Liguori { 162019d6b8fSAnthony Liguori if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) 163019d6b8fSAnthony Liguori return 100; 164019d6b8fSAnthony Liguori return 0; 165019d6b8fSAnthony Liguori } 166019d6b8fSAnthony Liguori 167015a1036SMax Reitz static int vpc_open(BlockDriverState *bs, QDict *options, int flags, 168015a1036SMax Reitz Error **errp) 169019d6b8fSAnthony Liguori { 170019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 17166f82ceeSKevin Wolf int i; 172e54835c0SJeff Cody VHDFooter *footer; 173e54835c0SJeff Cody VHDDynDiskHeader *dyndisk_header; 174019d6b8fSAnthony Liguori uint8_t buf[HEADER_SIZE]; 175019d6b8fSAnthony Liguori uint32_t checksum; 17697f1c45cSJeff Cody uint64_t computed_size; 17724da78dbSCharles Arnold int disk_type = VHD_DYNAMIC; 17859294e46SKevin Wolf int ret; 179019d6b8fSAnthony Liguori 18059294e46SKevin Wolf ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); 18159294e46SKevin Wolf if (ret < 0) { 182019d6b8fSAnthony Liguori goto fail; 18359294e46SKevin Wolf } 184019d6b8fSAnthony Liguori 185e54835c0SJeff Cody footer = (VHDFooter *) s->footer_buf; 18624da78dbSCharles Arnold if (strncmp(footer->creator, "conectix", 8)) { 18724da78dbSCharles Arnold int64_t offset = bdrv_getlength(bs->file); 18859294e46SKevin Wolf if (offset < 0) { 18959294e46SKevin Wolf ret = offset; 19059294e46SKevin Wolf goto fail; 19159294e46SKevin Wolf } else if (offset < HEADER_SIZE) { 19259294e46SKevin Wolf ret = -EINVAL; 193019d6b8fSAnthony Liguori goto fail; 19424da78dbSCharles Arnold } 19559294e46SKevin Wolf 19624da78dbSCharles Arnold /* If a fixed disk, the footer is found only at the end of the file */ 19759294e46SKevin Wolf ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, 19859294e46SKevin Wolf HEADER_SIZE); 19959294e46SKevin Wolf if (ret < 0) { 20024da78dbSCharles Arnold goto fail; 20124da78dbSCharles Arnold } 20224da78dbSCharles Arnold if (strncmp(footer->creator, "conectix", 8)) { 20376abe407SPaolo Bonzini error_setg(errp, "invalid VPC image"); 20476abe407SPaolo Bonzini ret = -EINVAL; 20524da78dbSCharles Arnold goto fail; 20624da78dbSCharles Arnold } 20724da78dbSCharles Arnold disk_type = VHD_FIXED; 20824da78dbSCharles Arnold } 209019d6b8fSAnthony Liguori 210019d6b8fSAnthony Liguori checksum = be32_to_cpu(footer->checksum); 211019d6b8fSAnthony Liguori footer->checksum = 0; 212019d6b8fSAnthony Liguori if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) 213019d6b8fSAnthony Liguori fprintf(stderr, "block-vpc: The header checksum of '%s' is " 21466f82ceeSKevin Wolf "incorrect.\n", bs->filename); 215019d6b8fSAnthony Liguori 216c088b691SZhang Shengju /* Write 'checksum' back to footer, or else will leave it with zero. */ 217c088b691SZhang Shengju footer->checksum = be32_to_cpu(checksum); 218c088b691SZhang Shengju 21933ccf667SStefan Hajnoczi // The visible size of a image in Virtual PC depends on the geometry 22033ccf667SStefan Hajnoczi // rather than on the size stored in the footer (the size in the footer 22133ccf667SStefan Hajnoczi // is too large usually) 22233ccf667SStefan Hajnoczi bs->total_sectors = (int64_t) 22333ccf667SStefan Hajnoczi be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; 224019d6b8fSAnthony Liguori 2250173e7bbSPeter Lieven /* images created with disk2vhd report a far higher virtual size 2260173e7bbSPeter Lieven * than expected with the cyls * heads * sectors_per_cyl formula. 2270173e7bbSPeter Lieven * use the footer->size instead if the image was created with 2280173e7bbSPeter Lieven * disk2vhd. 2290173e7bbSPeter Lieven */ 2300173e7bbSPeter Lieven if (!strncmp(footer->creator_app, "d2v", 4)) { 2310173e7bbSPeter Lieven bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE; 2320173e7bbSPeter Lieven } 2330173e7bbSPeter Lieven 234258d2edbSCharles Arnold /* Allow a maximum disk size of approximately 2 TB */ 23597f1c45cSJeff Cody if (bs->total_sectors >= VHD_MAX_SECTORS) { 23659294e46SKevin Wolf ret = -EFBIG; 237efc8243dSSerge E. Hallyn goto fail; 238efc8243dSSerge E. Hallyn } 239efc8243dSSerge E. Hallyn 24024da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 24159294e46SKevin Wolf ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, 24259294e46SKevin Wolf HEADER_SIZE); 24359294e46SKevin Wolf if (ret < 0) { 244019d6b8fSAnthony Liguori goto fail; 24524da78dbSCharles Arnold } 246019d6b8fSAnthony Liguori 247e54835c0SJeff Cody dyndisk_header = (VHDDynDiskHeader *) buf; 248019d6b8fSAnthony Liguori 24924da78dbSCharles Arnold if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { 25059294e46SKevin Wolf ret = -EINVAL; 251019d6b8fSAnthony Liguori goto fail; 25224da78dbSCharles Arnold } 253019d6b8fSAnthony Liguori 254019d6b8fSAnthony Liguori s->block_size = be32_to_cpu(dyndisk_header->block_size); 2555e71dfadSKevin Wolf if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) { 2565e71dfadSKevin Wolf error_setg(errp, "Invalid block size %" PRIu32, s->block_size); 2575e71dfadSKevin Wolf ret = -EINVAL; 2585e71dfadSKevin Wolf goto fail; 2595e71dfadSKevin Wolf } 260019d6b8fSAnthony Liguori s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; 261019d6b8fSAnthony Liguori 262019d6b8fSAnthony Liguori s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); 26397f1c45cSJeff Cody 26497f1c45cSJeff Cody if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) { 26597f1c45cSJeff Cody ret = -EINVAL; 26697f1c45cSJeff Cody goto fail; 26797f1c45cSJeff Cody } 26897f1c45cSJeff Cody if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) { 26997f1c45cSJeff Cody ret = -EINVAL; 27097f1c45cSJeff Cody goto fail; 27197f1c45cSJeff Cody } 27297f1c45cSJeff Cody 27397f1c45cSJeff Cody computed_size = (uint64_t) s->max_table_entries * s->block_size; 27497f1c45cSJeff Cody if (computed_size < bs->total_sectors * 512) { 27597f1c45cSJeff Cody ret = -EINVAL; 27697f1c45cSJeff Cody goto fail; 27797f1c45cSJeff Cody } 27897f1c45cSJeff Cody 27997f1c45cSJeff Cody s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4); 280019d6b8fSAnthony Liguori 281019d6b8fSAnthony Liguori s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); 28259294e46SKevin Wolf 28359294e46SKevin Wolf ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, 28459294e46SKevin Wolf s->max_table_entries * 4); 28559294e46SKevin Wolf if (ret < 0) { 286019d6b8fSAnthony Liguori goto fail; 28724da78dbSCharles Arnold } 288019d6b8fSAnthony Liguori 289019d6b8fSAnthony Liguori s->free_data_block_offset = 290019d6b8fSAnthony Liguori (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; 291019d6b8fSAnthony Liguori 292019d6b8fSAnthony Liguori for (i = 0; i < s->max_table_entries; i++) { 293019d6b8fSAnthony Liguori be32_to_cpus(&s->pagetable[i]); 294019d6b8fSAnthony Liguori if (s->pagetable[i] != 0xFFFFFFFF) { 295019d6b8fSAnthony Liguori int64_t next = (512 * (int64_t) s->pagetable[i]) + 296019d6b8fSAnthony Liguori s->bitmap_size + s->block_size; 297019d6b8fSAnthony Liguori 29824da78dbSCharles Arnold if (next > s->free_data_block_offset) { 299019d6b8fSAnthony Liguori s->free_data_block_offset = next; 300019d6b8fSAnthony Liguori } 301019d6b8fSAnthony Liguori } 30224da78dbSCharles Arnold } 303019d6b8fSAnthony Liguori 304fb8fe35fSPeter Lieven if (s->free_data_block_offset > bdrv_getlength(bs->file)) { 305fb8fe35fSPeter Lieven error_setg(errp, "block-vpc: free_data_block_offset points after " 306fb8fe35fSPeter Lieven "the end of file. The image has been truncated."); 307fb8fe35fSPeter Lieven ret = -EINVAL; 308fb8fe35fSPeter Lieven goto fail; 309fb8fe35fSPeter Lieven } 310fb8fe35fSPeter Lieven 311019d6b8fSAnthony Liguori s->last_bitmap_offset = (int64_t) -1; 312019d6b8fSAnthony Liguori 313019d6b8fSAnthony Liguori #ifdef CACHE 3147267c094SAnthony Liguori s->pageentry_u8 = g_malloc(512); 315019d6b8fSAnthony Liguori s->pageentry_u32 = s->pageentry_u8; 316019d6b8fSAnthony Liguori s->pageentry_u16 = s->pageentry_u8; 317019d6b8fSAnthony Liguori s->last_pagetable = -1; 318019d6b8fSAnthony Liguori #endif 31924da78dbSCharles Arnold } 320019d6b8fSAnthony Liguori 321848c66e8SPaolo Bonzini qemu_co_mutex_init(&s->lock); 322612ff3d8SKevin Wolf 323612ff3d8SKevin Wolf /* Disable migration when VHD images are used */ 324612ff3d8SKevin Wolf error_set(&s->migration_blocker, 325612ff3d8SKevin Wolf QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, 326612ff3d8SKevin Wolf "vpc", bs->device_name, "live migration"); 327612ff3d8SKevin Wolf migrate_add_blocker(s->migration_blocker); 328612ff3d8SKevin Wolf 329019d6b8fSAnthony Liguori return 0; 33059294e46SKevin Wolf 331019d6b8fSAnthony Liguori fail: 33297f1c45cSJeff Cody qemu_vfree(s->pagetable); 33359294e46SKevin Wolf #ifdef CACHE 33459294e46SKevin Wolf g_free(s->pageentry_u8); 33559294e46SKevin Wolf #endif 33659294e46SKevin Wolf return ret; 337019d6b8fSAnthony Liguori } 338019d6b8fSAnthony Liguori 3393fe4b700SJeff Cody static int vpc_reopen_prepare(BDRVReopenState *state, 3403fe4b700SJeff Cody BlockReopenQueue *queue, Error **errp) 3413fe4b700SJeff Cody { 3423fe4b700SJeff Cody return 0; 3433fe4b700SJeff Cody } 3443fe4b700SJeff Cody 345019d6b8fSAnthony Liguori /* 346019d6b8fSAnthony Liguori * Returns the absolute byte offset of the given sector in the image file. 347019d6b8fSAnthony Liguori * If the sector is not allocated, -1 is returned instead. 348019d6b8fSAnthony Liguori * 349019d6b8fSAnthony Liguori * The parameter write must be 1 if the offset will be used for a write 350019d6b8fSAnthony Liguori * operation (the block bitmaps is updated then), 0 otherwise. 351019d6b8fSAnthony Liguori */ 352019d6b8fSAnthony Liguori static inline int64_t get_sector_offset(BlockDriverState *bs, 353019d6b8fSAnthony Liguori int64_t sector_num, int write) 354019d6b8fSAnthony Liguori { 355019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 356019d6b8fSAnthony Liguori uint64_t offset = sector_num * 512; 357019d6b8fSAnthony Liguori uint64_t bitmap_offset, block_offset; 358019d6b8fSAnthony Liguori uint32_t pagetable_index, pageentry_index; 359019d6b8fSAnthony Liguori 360019d6b8fSAnthony Liguori pagetable_index = offset / s->block_size; 361019d6b8fSAnthony Liguori pageentry_index = (offset % s->block_size) / 512; 362019d6b8fSAnthony Liguori 363019d6b8fSAnthony Liguori if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) 364019d6b8fSAnthony Liguori return -1; // not allocated 365019d6b8fSAnthony Liguori 366019d6b8fSAnthony Liguori bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; 367019d6b8fSAnthony Liguori block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); 368019d6b8fSAnthony Liguori 369019d6b8fSAnthony Liguori // We must ensure that we don't write to any sectors which are marked as 370019d6b8fSAnthony Liguori // unused in the bitmap. We get away with setting all bits in the block 371019d6b8fSAnthony Liguori // bitmap each time we write to a new block. This might cause Virtual PC to 372019d6b8fSAnthony Liguori // miss sparse read optimization, but it's not a problem in terms of 373019d6b8fSAnthony Liguori // correctness. 374019d6b8fSAnthony Liguori if (write && (s->last_bitmap_offset != bitmap_offset)) { 375019d6b8fSAnthony Liguori uint8_t bitmap[s->bitmap_size]; 376019d6b8fSAnthony Liguori 377019d6b8fSAnthony Liguori s->last_bitmap_offset = bitmap_offset; 378019d6b8fSAnthony Liguori memset(bitmap, 0xff, s->bitmap_size); 379078a458eSKevin Wolf bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); 380019d6b8fSAnthony Liguori } 381019d6b8fSAnthony Liguori 382019d6b8fSAnthony Liguori // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", 383019d6b8fSAnthony Liguori // sector_num, pagetable_index, pageentry_index, 384019d6b8fSAnthony Liguori // bitmap_offset, block_offset); 385019d6b8fSAnthony Liguori 386019d6b8fSAnthony Liguori // disabled by reason 387019d6b8fSAnthony Liguori #if 0 388019d6b8fSAnthony Liguori #ifdef CACHE 389019d6b8fSAnthony Liguori if (bitmap_offset != s->last_bitmap) 390019d6b8fSAnthony Liguori { 391019d6b8fSAnthony Liguori lseek(s->fd, bitmap_offset, SEEK_SET); 392019d6b8fSAnthony Liguori 393019d6b8fSAnthony Liguori s->last_bitmap = bitmap_offset; 394019d6b8fSAnthony Liguori 395019d6b8fSAnthony Liguori // Scary! Bitmap is stored as big endian 32bit entries, 396019d6b8fSAnthony Liguori // while we used to look it up byte by byte 397019d6b8fSAnthony Liguori read(s->fd, s->pageentry_u8, 512); 398019d6b8fSAnthony Liguori for (i = 0; i < 128; i++) 399019d6b8fSAnthony Liguori be32_to_cpus(&s->pageentry_u32[i]); 400019d6b8fSAnthony Liguori } 401019d6b8fSAnthony Liguori 402019d6b8fSAnthony Liguori if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) 403019d6b8fSAnthony Liguori return -1; 404019d6b8fSAnthony Liguori #else 405019d6b8fSAnthony Liguori lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); 406019d6b8fSAnthony Liguori 407019d6b8fSAnthony Liguori read(s->fd, &bitmap_entry, 1); 408019d6b8fSAnthony Liguori 409019d6b8fSAnthony Liguori if ((bitmap_entry >> (pageentry_index % 8)) & 1) 410019d6b8fSAnthony Liguori return -1; // not allocated 411019d6b8fSAnthony Liguori #endif 412019d6b8fSAnthony Liguori #endif 413019d6b8fSAnthony Liguori 414019d6b8fSAnthony Liguori return block_offset; 415019d6b8fSAnthony Liguori } 416019d6b8fSAnthony Liguori 417019d6b8fSAnthony Liguori /* 418019d6b8fSAnthony Liguori * Writes the footer to the end of the image file. This is needed when the 419019d6b8fSAnthony Liguori * file grows as it overwrites the old footer 420019d6b8fSAnthony Liguori * 421019d6b8fSAnthony Liguori * Returns 0 on success and < 0 on error 422019d6b8fSAnthony Liguori */ 423019d6b8fSAnthony Liguori static int rewrite_footer(BlockDriverState* bs) 424019d6b8fSAnthony Liguori { 425019d6b8fSAnthony Liguori int ret; 426019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 427019d6b8fSAnthony Liguori int64_t offset = s->free_data_block_offset; 428019d6b8fSAnthony Liguori 429078a458eSKevin Wolf ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); 430019d6b8fSAnthony Liguori if (ret < 0) 431019d6b8fSAnthony Liguori return ret; 432019d6b8fSAnthony Liguori 433019d6b8fSAnthony Liguori return 0; 434019d6b8fSAnthony Liguori } 435019d6b8fSAnthony Liguori 436019d6b8fSAnthony Liguori /* 437019d6b8fSAnthony Liguori * Allocates a new block. This involves writing a new footer and updating 438019d6b8fSAnthony Liguori * the Block Allocation Table to use the space at the old end of the image 439019d6b8fSAnthony Liguori * file (overwriting the old footer) 440019d6b8fSAnthony Liguori * 441019d6b8fSAnthony Liguori * Returns the sectors' offset in the image file on success and < 0 on error 442019d6b8fSAnthony Liguori */ 443019d6b8fSAnthony Liguori static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) 444019d6b8fSAnthony Liguori { 445019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 446019d6b8fSAnthony Liguori int64_t bat_offset; 447019d6b8fSAnthony Liguori uint32_t index, bat_value; 448019d6b8fSAnthony Liguori int ret; 449019d6b8fSAnthony Liguori uint8_t bitmap[s->bitmap_size]; 450019d6b8fSAnthony Liguori 451019d6b8fSAnthony Liguori // Check if sector_num is valid 452019d6b8fSAnthony Liguori if ((sector_num < 0) || (sector_num > bs->total_sectors)) 453019d6b8fSAnthony Liguori return -1; 454019d6b8fSAnthony Liguori 455019d6b8fSAnthony Liguori // Write entry into in-memory BAT 456019d6b8fSAnthony Liguori index = (sector_num * 512) / s->block_size; 457019d6b8fSAnthony Liguori if (s->pagetable[index] != 0xFFFFFFFF) 458019d6b8fSAnthony Liguori return -1; 459019d6b8fSAnthony Liguori 460019d6b8fSAnthony Liguori s->pagetable[index] = s->free_data_block_offset / 512; 461019d6b8fSAnthony Liguori 462019d6b8fSAnthony Liguori // Initialize the block's bitmap 463019d6b8fSAnthony Liguori memset(bitmap, 0xff, s->bitmap_size); 4645bb1cbacSKevin Wolf ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, 465078a458eSKevin Wolf s->bitmap_size); 4665bb1cbacSKevin Wolf if (ret < 0) { 4675bb1cbacSKevin Wolf return ret; 4685bb1cbacSKevin Wolf } 469019d6b8fSAnthony Liguori 470019d6b8fSAnthony Liguori // Write new footer (the old one will be overwritten) 471019d6b8fSAnthony Liguori s->free_data_block_offset += s->block_size + s->bitmap_size; 472019d6b8fSAnthony Liguori ret = rewrite_footer(bs); 473019d6b8fSAnthony Liguori if (ret < 0) 474019d6b8fSAnthony Liguori goto fail; 475019d6b8fSAnthony Liguori 476019d6b8fSAnthony Liguori // Write BAT entry to disk 477019d6b8fSAnthony Liguori bat_offset = s->bat_offset + (4 * index); 478019d6b8fSAnthony Liguori bat_value = be32_to_cpu(s->pagetable[index]); 479078a458eSKevin Wolf ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); 480019d6b8fSAnthony Liguori if (ret < 0) 481019d6b8fSAnthony Liguori goto fail; 482019d6b8fSAnthony Liguori 483019d6b8fSAnthony Liguori return get_sector_offset(bs, sector_num, 0); 484019d6b8fSAnthony Liguori 485019d6b8fSAnthony Liguori fail: 486019d6b8fSAnthony Liguori s->free_data_block_offset -= (s->block_size + s->bitmap_size); 487019d6b8fSAnthony Liguori return -1; 488019d6b8fSAnthony Liguori } 489019d6b8fSAnthony Liguori 49097b00e28SPaolo Bonzini static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) 49197b00e28SPaolo Bonzini { 49297b00e28SPaolo Bonzini BDRVVPCState *s = (BDRVVPCState *)bs->opaque; 49397b00e28SPaolo Bonzini VHDFooter *footer = (VHDFooter *) s->footer_buf; 49497b00e28SPaolo Bonzini 49597b00e28SPaolo Bonzini if (cpu_to_be32(footer->type) != VHD_FIXED) { 49697b00e28SPaolo Bonzini bdi->cluster_size = s->block_size; 49797b00e28SPaolo Bonzini } 49897b00e28SPaolo Bonzini 49995de6d70SPaolo Bonzini bdi->unallocated_blocks_are_zero = true; 50097b00e28SPaolo Bonzini return 0; 50197b00e28SPaolo Bonzini } 50297b00e28SPaolo Bonzini 503019d6b8fSAnthony Liguori static int vpc_read(BlockDriverState *bs, int64_t sector_num, 504019d6b8fSAnthony Liguori uint8_t *buf, int nb_sectors) 505019d6b8fSAnthony Liguori { 5066c6ea921SKevin Wolf BDRVVPCState *s = bs->opaque; 507019d6b8fSAnthony Liguori int ret; 508019d6b8fSAnthony Liguori int64_t offset; 5096c6ea921SKevin Wolf int64_t sectors, sectors_per_block; 510e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) s->footer_buf; 511019d6b8fSAnthony Liguori 51224da78dbSCharles Arnold if (cpu_to_be32(footer->type) == VHD_FIXED) { 51324da78dbSCharles Arnold return bdrv_read(bs->file, sector_num, buf, nb_sectors); 51424da78dbSCharles Arnold } 515019d6b8fSAnthony Liguori while (nb_sectors > 0) { 516019d6b8fSAnthony Liguori offset = get_sector_offset(bs, sector_num, 0); 517019d6b8fSAnthony Liguori 5186c6ea921SKevin Wolf sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; 5196c6ea921SKevin Wolf sectors = sectors_per_block - (sector_num % sectors_per_block); 5206c6ea921SKevin Wolf if (sectors > nb_sectors) { 5216c6ea921SKevin Wolf sectors = nb_sectors; 522019d6b8fSAnthony Liguori } 523019d6b8fSAnthony Liguori 5246c6ea921SKevin Wolf if (offset == -1) { 5256c6ea921SKevin Wolf memset(buf, 0, sectors * BDRV_SECTOR_SIZE); 5266c6ea921SKevin Wolf } else { 5276c6ea921SKevin Wolf ret = bdrv_pread(bs->file, offset, buf, 5286c6ea921SKevin Wolf sectors * BDRV_SECTOR_SIZE); 5296c6ea921SKevin Wolf if (ret != sectors * BDRV_SECTOR_SIZE) { 5306c6ea921SKevin Wolf return -1; 5316c6ea921SKevin Wolf } 5326c6ea921SKevin Wolf } 5336c6ea921SKevin Wolf 5346c6ea921SKevin Wolf nb_sectors -= sectors; 5356c6ea921SKevin Wolf sector_num += sectors; 5366c6ea921SKevin Wolf buf += sectors * BDRV_SECTOR_SIZE; 537019d6b8fSAnthony Liguori } 538019d6b8fSAnthony Liguori return 0; 539019d6b8fSAnthony Liguori } 540019d6b8fSAnthony Liguori 5412914caa0SPaolo Bonzini static coroutine_fn int vpc_co_read(BlockDriverState *bs, int64_t sector_num, 5422914caa0SPaolo Bonzini uint8_t *buf, int nb_sectors) 5432914caa0SPaolo Bonzini { 5442914caa0SPaolo Bonzini int ret; 5452914caa0SPaolo Bonzini BDRVVPCState *s = bs->opaque; 5462914caa0SPaolo Bonzini qemu_co_mutex_lock(&s->lock); 5472914caa0SPaolo Bonzini ret = vpc_read(bs, sector_num, buf, nb_sectors); 5482914caa0SPaolo Bonzini qemu_co_mutex_unlock(&s->lock); 5492914caa0SPaolo Bonzini return ret; 5502914caa0SPaolo Bonzini } 5512914caa0SPaolo Bonzini 552019d6b8fSAnthony Liguori static int vpc_write(BlockDriverState *bs, int64_t sector_num, 553019d6b8fSAnthony Liguori const uint8_t *buf, int nb_sectors) 554019d6b8fSAnthony Liguori { 5556c6ea921SKevin Wolf BDRVVPCState *s = bs->opaque; 556019d6b8fSAnthony Liguori int64_t offset; 5576c6ea921SKevin Wolf int64_t sectors, sectors_per_block; 558019d6b8fSAnthony Liguori int ret; 559e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) s->footer_buf; 560019d6b8fSAnthony Liguori 56124da78dbSCharles Arnold if (cpu_to_be32(footer->type) == VHD_FIXED) { 56224da78dbSCharles Arnold return bdrv_write(bs->file, sector_num, buf, nb_sectors); 56324da78dbSCharles Arnold } 564019d6b8fSAnthony Liguori while (nb_sectors > 0) { 565019d6b8fSAnthony Liguori offset = get_sector_offset(bs, sector_num, 1); 566019d6b8fSAnthony Liguori 5676c6ea921SKevin Wolf sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; 5686c6ea921SKevin Wolf sectors = sectors_per_block - (sector_num % sectors_per_block); 5696c6ea921SKevin Wolf if (sectors > nb_sectors) { 5706c6ea921SKevin Wolf sectors = nb_sectors; 5716c6ea921SKevin Wolf } 5726c6ea921SKevin Wolf 573019d6b8fSAnthony Liguori if (offset == -1) { 574019d6b8fSAnthony Liguori offset = alloc_block(bs, sector_num); 575019d6b8fSAnthony Liguori if (offset < 0) 576019d6b8fSAnthony Liguori return -1; 577019d6b8fSAnthony Liguori } 578019d6b8fSAnthony Liguori 5796c6ea921SKevin Wolf ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); 5806c6ea921SKevin Wolf if (ret != sectors * BDRV_SECTOR_SIZE) { 581019d6b8fSAnthony Liguori return -1; 5826c6ea921SKevin Wolf } 583019d6b8fSAnthony Liguori 5846c6ea921SKevin Wolf nb_sectors -= sectors; 5856c6ea921SKevin Wolf sector_num += sectors; 5866c6ea921SKevin Wolf buf += sectors * BDRV_SECTOR_SIZE; 587019d6b8fSAnthony Liguori } 588019d6b8fSAnthony Liguori 589019d6b8fSAnthony Liguori return 0; 590019d6b8fSAnthony Liguori } 591019d6b8fSAnthony Liguori 592e183ef75SPaolo Bonzini static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, 593e183ef75SPaolo Bonzini const uint8_t *buf, int nb_sectors) 594e183ef75SPaolo Bonzini { 595e183ef75SPaolo Bonzini int ret; 596e183ef75SPaolo Bonzini BDRVVPCState *s = bs->opaque; 597e183ef75SPaolo Bonzini qemu_co_mutex_lock(&s->lock); 598e183ef75SPaolo Bonzini ret = vpc_write(bs, sector_num, buf, nb_sectors); 599e183ef75SPaolo Bonzini qemu_co_mutex_unlock(&s->lock); 600e183ef75SPaolo Bonzini return ret; 601e183ef75SPaolo Bonzini } 602e183ef75SPaolo Bonzini 603019d6b8fSAnthony Liguori /* 604019d6b8fSAnthony Liguori * Calculates the number of cylinders, heads and sectors per cylinder 605019d6b8fSAnthony Liguori * based on a given number of sectors. This is the algorithm described 606019d6b8fSAnthony Liguori * in the VHD specification. 607019d6b8fSAnthony Liguori * 608019d6b8fSAnthony Liguori * Note that the geometry doesn't always exactly match total_sectors but 609019d6b8fSAnthony Liguori * may round it down. 610019d6b8fSAnthony Liguori * 611258d2edbSCharles Arnold * Returns 0 on success, -EFBIG if the size is larger than ~2 TB. Override 612258d2edbSCharles Arnold * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB) 613258d2edbSCharles Arnold * and instead allow up to 255 heads. 614019d6b8fSAnthony Liguori */ 615019d6b8fSAnthony Liguori static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, 616019d6b8fSAnthony Liguori uint8_t* heads, uint8_t* secs_per_cyl) 617019d6b8fSAnthony Liguori { 618019d6b8fSAnthony Liguori uint32_t cyls_times_heads; 619019d6b8fSAnthony Liguori 620258d2edbSCharles Arnold /* Allow a maximum disk size of approximately 2 TB */ 621258d2edbSCharles Arnold if (total_sectors > 65535LL * 255 * 255) { 622019d6b8fSAnthony Liguori return -EFBIG; 623258d2edbSCharles Arnold } 624019d6b8fSAnthony Liguori 625019d6b8fSAnthony Liguori if (total_sectors > 65535 * 16 * 63) { 626019d6b8fSAnthony Liguori *secs_per_cyl = 255; 627258d2edbSCharles Arnold if (total_sectors > 65535 * 16 * 255) { 628258d2edbSCharles Arnold *heads = 255; 629258d2edbSCharles Arnold } else { 630019d6b8fSAnthony Liguori *heads = 16; 631258d2edbSCharles Arnold } 632019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 633019d6b8fSAnthony Liguori } else { 634019d6b8fSAnthony Liguori *secs_per_cyl = 17; 635019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 636019d6b8fSAnthony Liguori *heads = (cyls_times_heads + 1023) / 1024; 637019d6b8fSAnthony Liguori 638019d6b8fSAnthony Liguori if (*heads < 4) 639019d6b8fSAnthony Liguori *heads = 4; 640019d6b8fSAnthony Liguori 641019d6b8fSAnthony Liguori if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { 642019d6b8fSAnthony Liguori *secs_per_cyl = 31; 643019d6b8fSAnthony Liguori *heads = 16; 644019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 645019d6b8fSAnthony Liguori } 646019d6b8fSAnthony Liguori 647019d6b8fSAnthony Liguori if (cyls_times_heads >= (*heads * 1024)) { 648019d6b8fSAnthony Liguori *secs_per_cyl = 63; 649019d6b8fSAnthony Liguori *heads = 16; 650019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 651019d6b8fSAnthony Liguori } 652019d6b8fSAnthony Liguori } 653019d6b8fSAnthony Liguori 654dede4188SStefan Weil *cyls = cyls_times_heads / *heads; 655019d6b8fSAnthony Liguori 656019d6b8fSAnthony Liguori return 0; 657019d6b8fSAnthony Liguori } 658019d6b8fSAnthony Liguori 65924da78dbSCharles Arnold static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors) 660019d6b8fSAnthony Liguori { 661e54835c0SJeff Cody VHDDynDiskHeader *dyndisk_header = 662e54835c0SJeff Cody (VHDDynDiskHeader *) buf; 663019d6b8fSAnthony Liguori size_t block_size, num_bat_entries; 66424da78dbSCharles Arnold int i; 665f0ff243aSBlue Swirl int ret = -EIO; 666019d6b8fSAnthony Liguori 667019d6b8fSAnthony Liguori // Write the footer (twice: at the beginning and at the end) 668019d6b8fSAnthony Liguori block_size = 0x200000; 669019d6b8fSAnthony Liguori num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); 670019d6b8fSAnthony Liguori 671f0ff243aSBlue Swirl if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { 672f0ff243aSBlue Swirl goto fail; 673f0ff243aSBlue Swirl } 674019d6b8fSAnthony Liguori 675f0ff243aSBlue Swirl if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) { 676f0ff243aSBlue Swirl goto fail; 677f0ff243aSBlue Swirl } 678f0ff243aSBlue Swirl if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { 679f0ff243aSBlue Swirl goto fail; 680f0ff243aSBlue Swirl } 681019d6b8fSAnthony Liguori 682019d6b8fSAnthony Liguori // Write the initial BAT 683f0ff243aSBlue Swirl if (lseek(fd, 3 * 512, SEEK_SET) < 0) { 684f0ff243aSBlue Swirl goto fail; 685f0ff243aSBlue Swirl } 686019d6b8fSAnthony Liguori 687019d6b8fSAnthony Liguori memset(buf, 0xFF, 512); 688f0ff243aSBlue Swirl for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { 689f0ff243aSBlue Swirl if (write(fd, buf, 512) != 512) { 690f0ff243aSBlue Swirl goto fail; 691f0ff243aSBlue Swirl } 692f0ff243aSBlue Swirl } 693019d6b8fSAnthony Liguori 694019d6b8fSAnthony Liguori // Prepare the Dynamic Disk Header 695019d6b8fSAnthony Liguori memset(buf, 0, 1024); 696019d6b8fSAnthony Liguori 6975ec4d682SNathan Froyd memcpy(dyndisk_header->magic, "cxsparse", 8); 698019d6b8fSAnthony Liguori 69978439f6aSCharles Arnold /* 70078439f6aSCharles Arnold * Note: The spec is actually wrong here for data_offset, it says 70178439f6aSCharles Arnold * 0xFFFFFFFF, but MS tools expect all 64 bits to be set. 70278439f6aSCharles Arnold */ 70378439f6aSCharles Arnold dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); 704019d6b8fSAnthony Liguori dyndisk_header->table_offset = be64_to_cpu(3 * 512); 705019d6b8fSAnthony Liguori dyndisk_header->version = be32_to_cpu(0x00010000); 706019d6b8fSAnthony Liguori dyndisk_header->block_size = be32_to_cpu(block_size); 707019d6b8fSAnthony Liguori dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries); 708019d6b8fSAnthony Liguori 709019d6b8fSAnthony Liguori dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); 710019d6b8fSAnthony Liguori 711019d6b8fSAnthony Liguori // Write the header 712f0ff243aSBlue Swirl if (lseek(fd, 512, SEEK_SET) < 0) { 713f0ff243aSBlue Swirl goto fail; 714f0ff243aSBlue Swirl } 715019d6b8fSAnthony Liguori 716f0ff243aSBlue Swirl if (write(fd, buf, 1024) != 1024) { 717f0ff243aSBlue Swirl goto fail; 718f0ff243aSBlue Swirl } 719f0ff243aSBlue Swirl ret = 0; 720f0ff243aSBlue Swirl 721f0ff243aSBlue Swirl fail: 72224da78dbSCharles Arnold return ret; 72324da78dbSCharles Arnold } 72424da78dbSCharles Arnold 72524da78dbSCharles Arnold static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size) 72624da78dbSCharles Arnold { 72724da78dbSCharles Arnold int ret = -EIO; 72824da78dbSCharles Arnold 72924da78dbSCharles Arnold /* Add footer to total size */ 73024da78dbSCharles Arnold total_size += 512; 73124da78dbSCharles Arnold if (ftruncate(fd, total_size) != 0) { 73224da78dbSCharles Arnold ret = -errno; 73324da78dbSCharles Arnold goto fail; 73424da78dbSCharles Arnold } 73524da78dbSCharles Arnold if (lseek(fd, -512, SEEK_END) < 0) { 73624da78dbSCharles Arnold goto fail; 73724da78dbSCharles Arnold } 73824da78dbSCharles Arnold if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { 73924da78dbSCharles Arnold goto fail; 74024da78dbSCharles Arnold } 74124da78dbSCharles Arnold 74224da78dbSCharles Arnold ret = 0; 74324da78dbSCharles Arnold 74424da78dbSCharles Arnold fail: 74524da78dbSCharles Arnold return ret; 74624da78dbSCharles Arnold } 74724da78dbSCharles Arnold 748fec9921fSChunyan Liu static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) 74924da78dbSCharles Arnold { 75024da78dbSCharles Arnold uint8_t buf[1024]; 751e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) buf; 752fec9921fSChunyan Liu char *disk_type_param; 75324da78dbSCharles Arnold int fd, i; 75424da78dbSCharles Arnold uint16_t cyls = 0; 75524da78dbSCharles Arnold uint8_t heads = 0; 75624da78dbSCharles Arnold uint8_t secs_per_cyl = 0; 75724da78dbSCharles Arnold int64_t total_sectors; 75824da78dbSCharles Arnold int64_t total_size; 75924da78dbSCharles Arnold int disk_type; 76024da78dbSCharles Arnold int ret = -EIO; 761*4ab15590SChunyan Liu bool nocow = false; 76224da78dbSCharles Arnold 76324da78dbSCharles Arnold /* Read out options */ 764fec9921fSChunyan Liu total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); 765fec9921fSChunyan Liu disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); 766fec9921fSChunyan Liu if (disk_type_param) { 767fec9921fSChunyan Liu if (!strcmp(disk_type_param, "dynamic")) { 76824da78dbSCharles Arnold disk_type = VHD_DYNAMIC; 769fec9921fSChunyan Liu } else if (!strcmp(disk_type_param, "fixed")) { 77024da78dbSCharles Arnold disk_type = VHD_FIXED; 77124da78dbSCharles Arnold } else { 772fec9921fSChunyan Liu ret = -EINVAL; 773fec9921fSChunyan Liu goto out; 77424da78dbSCharles Arnold } 77524da78dbSCharles Arnold } else { 77624da78dbSCharles Arnold disk_type = VHD_DYNAMIC; 77724da78dbSCharles Arnold } 778*4ab15590SChunyan Liu nocow = qemu_opt_get_bool_del(opts, BLOCK_OPT_NOCOW, false); 77924da78dbSCharles Arnold 78024da78dbSCharles Arnold /* Create the file */ 7816165f4d8SCorey Bryant fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); 78224da78dbSCharles Arnold if (fd < 0) { 783fec9921fSChunyan Liu ret = -EIO; 784fec9921fSChunyan Liu goto out; 78524da78dbSCharles Arnold } 78624da78dbSCharles Arnold 787*4ab15590SChunyan Liu if (nocow) { 788*4ab15590SChunyan Liu #ifdef __linux__ 789*4ab15590SChunyan Liu /* Set NOCOW flag to solve performance issue on fs like btrfs. 790*4ab15590SChunyan Liu * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will 791*4ab15590SChunyan Liu * be ignored since any failure of this operation should not block the 792*4ab15590SChunyan Liu * left work. 793*4ab15590SChunyan Liu */ 794*4ab15590SChunyan Liu int attr; 795*4ab15590SChunyan Liu if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) { 796*4ab15590SChunyan Liu attr |= FS_NOCOW_FL; 797*4ab15590SChunyan Liu ioctl(fd, FS_IOC_SETFLAGS, &attr); 798*4ab15590SChunyan Liu } 799*4ab15590SChunyan Liu #endif 800*4ab15590SChunyan Liu } 801*4ab15590SChunyan Liu 802ecd880d9SKevin Wolf /* 803ecd880d9SKevin Wolf * Calculate matching total_size and geometry. Increase the number of 804ecd880d9SKevin Wolf * sectors requested until we get enough (or fail). This ensures that 805ecd880d9SKevin Wolf * qemu-img convert doesn't truncate images, but rather rounds up. 806ecd880d9SKevin Wolf */ 80724da78dbSCharles Arnold total_sectors = total_size / BDRV_SECTOR_SIZE; 808ecd880d9SKevin Wolf for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { 809ecd880d9SKevin Wolf if (calculate_geometry(total_sectors + i, &cyls, &heads, 810ecd880d9SKevin Wolf &secs_per_cyl)) 811ecd880d9SKevin Wolf { 81224da78dbSCharles Arnold ret = -EFBIG; 81324da78dbSCharles Arnold goto fail; 81424da78dbSCharles Arnold } 81524da78dbSCharles Arnold } 816ecd880d9SKevin Wolf 81724da78dbSCharles Arnold total_sectors = (int64_t) cyls * heads * secs_per_cyl; 81824da78dbSCharles Arnold 81924da78dbSCharles Arnold /* Prepare the Hard Disk Footer */ 82024da78dbSCharles Arnold memset(buf, 0, 1024); 82124da78dbSCharles Arnold 82224da78dbSCharles Arnold memcpy(footer->creator, "conectix", 8); 82324da78dbSCharles Arnold /* TODO Check if "qemu" creator_app is ok for VPC */ 82424da78dbSCharles Arnold memcpy(footer->creator_app, "qemu", 4); 82524da78dbSCharles Arnold memcpy(footer->creator_os, "Wi2k", 4); 82624da78dbSCharles Arnold 82724da78dbSCharles Arnold footer->features = be32_to_cpu(0x02); 82824da78dbSCharles Arnold footer->version = be32_to_cpu(0x00010000); 82924da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 83024da78dbSCharles Arnold footer->data_offset = be64_to_cpu(HEADER_SIZE); 83124da78dbSCharles Arnold } else { 83224da78dbSCharles Arnold footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); 83324da78dbSCharles Arnold } 83424da78dbSCharles Arnold footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); 83524da78dbSCharles Arnold 83624da78dbSCharles Arnold /* Version of Virtual PC 2007 */ 83724da78dbSCharles Arnold footer->major = be16_to_cpu(0x0005); 83824da78dbSCharles Arnold footer->minor = be16_to_cpu(0x0003); 83924da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 84024da78dbSCharles Arnold footer->orig_size = be64_to_cpu(total_sectors * 512); 84124da78dbSCharles Arnold footer->size = be64_to_cpu(total_sectors * 512); 84224da78dbSCharles Arnold } else { 84324da78dbSCharles Arnold footer->orig_size = be64_to_cpu(total_size); 84424da78dbSCharles Arnold footer->size = be64_to_cpu(total_size); 84524da78dbSCharles Arnold } 84624da78dbSCharles Arnold footer->cyls = be16_to_cpu(cyls); 84724da78dbSCharles Arnold footer->heads = heads; 84824da78dbSCharles Arnold footer->secs_per_cyl = secs_per_cyl; 84924da78dbSCharles Arnold 85024da78dbSCharles Arnold footer->type = be32_to_cpu(disk_type); 85124da78dbSCharles Arnold 8521fe1fa51SCharles Arnold #if defined(CONFIG_UUID) 8531fe1fa51SCharles Arnold uuid_generate(footer->uuid); 8541fe1fa51SCharles Arnold #endif 85524da78dbSCharles Arnold 85624da78dbSCharles Arnold footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); 85724da78dbSCharles Arnold 85824da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 85924da78dbSCharles Arnold ret = create_dynamic_disk(fd, buf, total_sectors); 86024da78dbSCharles Arnold } else { 86124da78dbSCharles Arnold ret = create_fixed_disk(fd, buf, total_size); 86224da78dbSCharles Arnold } 86324da78dbSCharles Arnold 86424da78dbSCharles Arnold fail: 8652e1e79daSCorey Bryant qemu_close(fd); 866fec9921fSChunyan Liu out: 867fec9921fSChunyan Liu g_free(disk_type_param); 868f0ff243aSBlue Swirl return ret; 869019d6b8fSAnthony Liguori } 870019d6b8fSAnthony Liguori 87172c6cc94SKevin Wolf static int vpc_has_zero_init(BlockDriverState *bs) 87272c6cc94SKevin Wolf { 87372c6cc94SKevin Wolf BDRVVPCState *s = bs->opaque; 874e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) s->footer_buf; 87572c6cc94SKevin Wolf 87672c6cc94SKevin Wolf if (cpu_to_be32(footer->type) == VHD_FIXED) { 87772c6cc94SKevin Wolf return bdrv_has_zero_init(bs->file); 87872c6cc94SKevin Wolf } else { 87972c6cc94SKevin Wolf return 1; 88072c6cc94SKevin Wolf } 88172c6cc94SKevin Wolf } 88272c6cc94SKevin Wolf 883019d6b8fSAnthony Liguori static void vpc_close(BlockDriverState *bs) 884019d6b8fSAnthony Liguori { 885019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 88697f1c45cSJeff Cody qemu_vfree(s->pagetable); 887019d6b8fSAnthony Liguori #ifdef CACHE 8887267c094SAnthony Liguori g_free(s->pageentry_u8); 889019d6b8fSAnthony Liguori #endif 890612ff3d8SKevin Wolf 891612ff3d8SKevin Wolf migrate_del_blocker(s->migration_blocker); 892612ff3d8SKevin Wolf error_free(s->migration_blocker); 893019d6b8fSAnthony Liguori } 894019d6b8fSAnthony Liguori 895fec9921fSChunyan Liu static QemuOptsList vpc_create_opts = { 896fec9921fSChunyan Liu .name = "vpc-create-opts", 897fec9921fSChunyan Liu .head = QTAILQ_HEAD_INITIALIZER(vpc_create_opts.head), 898fec9921fSChunyan Liu .desc = { 899db08adf5SKevin Wolf { 900db08adf5SKevin Wolf .name = BLOCK_OPT_SIZE, 901fec9921fSChunyan Liu .type = QEMU_OPT_SIZE, 902db08adf5SKevin Wolf .help = "Virtual disk size" 903db08adf5SKevin Wolf }, 90424da78dbSCharles Arnold { 90524da78dbSCharles Arnold .name = BLOCK_OPT_SUBFMT, 906fec9921fSChunyan Liu .type = QEMU_OPT_STRING, 90724da78dbSCharles Arnold .help = 90824da78dbSCharles Arnold "Type of virtual hard disk format. Supported formats are " 90924da78dbSCharles Arnold "{dynamic (default) | fixed} " 91024da78dbSCharles Arnold }, 911*4ab15590SChunyan Liu { 912*4ab15590SChunyan Liu .name = BLOCK_OPT_NOCOW, 913*4ab15590SChunyan Liu .type = QEMU_OPT_BOOL, 914*4ab15590SChunyan Liu .help = "Turn off copy-on-write (valid only on btrfs)" 915*4ab15590SChunyan Liu }, 916fec9921fSChunyan Liu { /* end of list */ } 917fec9921fSChunyan Liu } 9180e7e1989SKevin Wolf }; 9190e7e1989SKevin Wolf 920019d6b8fSAnthony Liguori static BlockDriver bdrv_vpc = { 921019d6b8fSAnthony Liguori .format_name = "vpc", 922019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVVPCState), 923c68b89acSKevin Wolf 924019d6b8fSAnthony Liguori .bdrv_probe = vpc_probe, 925019d6b8fSAnthony Liguori .bdrv_open = vpc_open, 926019d6b8fSAnthony Liguori .bdrv_close = vpc_close, 9273fe4b700SJeff Cody .bdrv_reopen_prepare = vpc_reopen_prepare, 928c282e1fdSChunyan Liu .bdrv_create = vpc_create, 9290e7e1989SKevin Wolf 930c68b89acSKevin Wolf .bdrv_read = vpc_co_read, 931c68b89acSKevin Wolf .bdrv_write = vpc_co_write, 932c68b89acSKevin Wolf 93397b00e28SPaolo Bonzini .bdrv_get_info = vpc_get_info, 93497b00e28SPaolo Bonzini 935fec9921fSChunyan Liu .create_opts = &vpc_create_opts, 93672c6cc94SKevin Wolf .bdrv_has_zero_init = vpc_has_zero_init, 937019d6b8fSAnthony Liguori }; 938019d6b8fSAnthony Liguori 939019d6b8fSAnthony Liguori static void bdrv_vpc_init(void) 940019d6b8fSAnthony Liguori { 941019d6b8fSAnthony Liguori bdrv_register(&bdrv_vpc); 942019d6b8fSAnthony Liguori } 943019d6b8fSAnthony Liguori 944019d6b8fSAnthony Liguori block_init(bdrv_vpc_init); 945