|
|
1.1 root 1: /* 1.1.1.2 ! root 2: * Block driver for Connectix / Microsoft Virtual PC images 1.1 root 3: * 4: * Copyright (c) 2005 Alex Beregszaszi 5: * Copyright (c) 2009 Kevin Wolf <[email protected]> 6: * 7: * Permission is hereby granted, free of charge, to any person obtaining a copy 8: * of this software and associated documentation files (the "Software"), to deal 9: * in the Software without restriction, including without limitation the rights 10: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11: * copies of the Software, and to permit persons to whom the Software is 12: * furnished to do so, subject to the following conditions: 13: * 14: * The above copyright notice and this permission notice shall be included in 15: * all copies or substantial portions of the Software. 16: * 17: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23: * THE SOFTWARE. 24: */ 25: #include "qemu-common.h" 26: #include "block_int.h" 27: #include "module.h" 28: 29: /**************************************************************/ 30: 31: #define HEADER_SIZE 512 32: 33: //#define CACHE 34: 35: enum vhd_type { 36: VHD_FIXED = 2, 37: VHD_DYNAMIC = 3, 38: VHD_DIFFERENCING = 4, 39: }; 40: 41: // Seconds since Jan 1, 2000 0:00:00 (UTC) 42: #define VHD_TIMESTAMP_BASE 946684800 43: 44: // always big-endian 45: struct vhd_footer { 46: char creator[8]; // "conectix" 47: uint32_t features; 48: uint32_t version; 49: 50: // Offset of next header structure, 0xFFFFFFFF if none 51: uint64_t data_offset; 52: 53: // Seconds since Jan 1, 2000 0:00:00 (UTC) 54: uint32_t timestamp; 55: 56: char creator_app[4]; // "vpc " 57: uint16_t major; 58: uint16_t minor; 59: char creator_os[4]; // "Wi2k" 60: 61: uint64_t orig_size; 62: uint64_t size; 63: 64: uint16_t cyls; 65: uint8_t heads; 66: uint8_t secs_per_cyl; 67: 68: uint32_t type; 69: 70: // Checksum of the Hard Disk Footer ("one's complement of the sum of all 71: // the bytes in the footer without the checksum field") 72: uint32_t checksum; 73: 74: // UUID used to identify a parent hard disk (backing file) 75: uint8_t uuid[16]; 76: 77: uint8_t in_saved_state; 78: }; 79: 80: struct vhd_dyndisk_header { 81: char magic[8]; // "cxsparse" 82: 83: // Offset of next header structure, 0xFFFFFFFF if none 84: uint64_t data_offset; 85: 86: // Offset of the Block Allocation Table (BAT) 87: uint64_t table_offset; 88: 89: uint32_t version; 90: uint32_t max_table_entries; // 32bit/entry 91: 92: // 2 MB by default, must be a power of two 93: uint32_t block_size; 94: 95: uint32_t checksum; 96: uint8_t parent_uuid[16]; 97: uint32_t parent_timestamp; 98: uint32_t reserved; 99: 100: // Backing file name (in UTF-16) 101: uint8_t parent_name[512]; 102: 103: struct { 104: uint32_t platform; 105: uint32_t data_space; 106: uint32_t data_length; 107: uint32_t reserved; 108: uint64_t data_offset; 109: } parent_locator[8]; 110: }; 111: 112: typedef struct BDRVVPCState { 113: BlockDriverState *hd; 114: 115: uint8_t footer_buf[HEADER_SIZE]; 116: uint64_t free_data_block_offset; 117: int max_table_entries; 118: uint32_t *pagetable; 119: uint64_t bat_offset; 120: uint64_t last_bitmap_offset; 121: 122: uint32_t block_size; 123: uint32_t bitmap_size; 124: 125: #ifdef CACHE 126: uint8_t *pageentry_u8; 127: uint32_t *pageentry_u32; 128: uint16_t *pageentry_u16; 129: 130: uint64_t last_bitmap; 131: #endif 132: } BDRVVPCState; 133: 134: static uint32_t vpc_checksum(uint8_t* buf, size_t size) 135: { 136: uint32_t res = 0; 137: int i; 138: 139: for (i = 0; i < size; i++) 140: res += buf[i]; 141: 142: return ~res; 143: } 144: 145: 146: static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) 147: { 148: if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) 149: return 100; 150: return 0; 151: } 152: 153: static int vpc_open(BlockDriverState *bs, const char *filename, int flags) 154: { 155: BDRVVPCState *s = bs->opaque; 156: int ret, i; 157: struct vhd_footer* footer; 158: struct vhd_dyndisk_header* dyndisk_header; 159: uint8_t buf[HEADER_SIZE]; 160: uint32_t checksum; 161: 162: ret = bdrv_file_open(&s->hd, filename, flags); 163: if (ret < 0) 164: return ret; 165: 166: if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) 167: goto fail; 168: 169: footer = (struct vhd_footer*) s->footer_buf; 170: if (strncmp(footer->creator, "conectix", 8)) 171: goto fail; 172: 173: checksum = be32_to_cpu(footer->checksum); 174: footer->checksum = 0; 175: if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) 176: fprintf(stderr, "block-vpc: The header checksum of '%s' is " 177: "incorrect.\n", filename); 178: 179: // The visible size of a image in Virtual PC depends on the geometry 180: // rather than on the size stored in the footer (the size in the footer 181: // is too large usually) 182: bs->total_sectors = (int64_t) 183: be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; 184: 185: if (bdrv_pread(s->hd, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) 186: != HEADER_SIZE) 187: goto fail; 188: 189: dyndisk_header = (struct vhd_dyndisk_header*) buf; 190: 191: if (strncmp(dyndisk_header->magic, "cxsparse", 8)) 192: goto fail; 193: 194: 195: s->block_size = be32_to_cpu(dyndisk_header->block_size); 196: s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; 197: 198: s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); 199: s->pagetable = qemu_malloc(s->max_table_entries * 4); 200: 201: s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); 202: if (bdrv_pread(s->hd, s->bat_offset, s->pagetable, 203: s->max_table_entries * 4) != s->max_table_entries * 4) 204: goto fail; 205: 206: s->free_data_block_offset = 207: (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; 208: 209: for (i = 0; i < s->max_table_entries; i++) { 210: be32_to_cpus(&s->pagetable[i]); 211: if (s->pagetable[i] != 0xFFFFFFFF) { 212: int64_t next = (512 * (int64_t) s->pagetable[i]) + 213: s->bitmap_size + s->block_size; 214: 215: if (next> s->free_data_block_offset) 216: s->free_data_block_offset = next; 217: } 218: } 219: 220: s->last_bitmap_offset = (int64_t) -1; 221: 222: #ifdef CACHE 223: s->pageentry_u8 = qemu_malloc(512); 224: s->pageentry_u32 = s->pageentry_u8; 225: s->pageentry_u16 = s->pageentry_u8; 226: s->last_pagetable = -1; 227: #endif 228: 229: return 0; 230: fail: 231: bdrv_delete(s->hd); 232: return -1; 233: } 234: 235: /* 236: * Returns the absolute byte offset of the given sector in the image file. 237: * If the sector is not allocated, -1 is returned instead. 238: * 239: * The parameter write must be 1 if the offset will be used for a write 240: * operation (the block bitmaps is updated then), 0 otherwise. 241: */ 242: static inline int64_t get_sector_offset(BlockDriverState *bs, 243: int64_t sector_num, int write) 244: { 245: BDRVVPCState *s = bs->opaque; 246: uint64_t offset = sector_num * 512; 247: uint64_t bitmap_offset, block_offset; 248: uint32_t pagetable_index, pageentry_index; 249: 250: pagetable_index = offset / s->block_size; 251: pageentry_index = (offset % s->block_size) / 512; 252: 253: if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) 254: return -1; // not allocated 255: 256: bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; 257: block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); 258: 259: // We must ensure that we don't write to any sectors which are marked as 260: // unused in the bitmap. We get away with setting all bits in the block 261: // bitmap each time we write to a new block. This might cause Virtual PC to 262: // miss sparse read optimization, but it's not a problem in terms of 263: // correctness. 264: if (write && (s->last_bitmap_offset != bitmap_offset)) { 265: uint8_t bitmap[s->bitmap_size]; 266: 267: s->last_bitmap_offset = bitmap_offset; 268: memset(bitmap, 0xff, s->bitmap_size); 269: bdrv_pwrite(s->hd, bitmap_offset, bitmap, s->bitmap_size); 270: } 271: 272: // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", 273: // sector_num, pagetable_index, pageentry_index, 274: // bitmap_offset, block_offset); 275: 276: // disabled by reason 277: #if 0 278: #ifdef CACHE 279: if (bitmap_offset != s->last_bitmap) 280: { 281: lseek(s->fd, bitmap_offset, SEEK_SET); 282: 283: s->last_bitmap = bitmap_offset; 284: 285: // Scary! Bitmap is stored as big endian 32bit entries, 286: // while we used to look it up byte by byte 287: read(s->fd, s->pageentry_u8, 512); 288: for (i = 0; i < 128; i++) 289: be32_to_cpus(&s->pageentry_u32[i]); 290: } 291: 292: if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) 293: return -1; 294: #else 295: lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); 296: 297: read(s->fd, &bitmap_entry, 1); 298: 299: if ((bitmap_entry >> (pageentry_index % 8)) & 1) 300: return -1; // not allocated 301: #endif 302: #endif 303: 304: return block_offset; 305: } 306: 307: /* 308: * Writes the footer to the end of the image file. This is needed when the 309: * file grows as it overwrites the old footer 310: * 311: * Returns 0 on success and < 0 on error 312: */ 313: static int rewrite_footer(BlockDriverState* bs) 314: { 315: int ret; 316: BDRVVPCState *s = bs->opaque; 317: int64_t offset = s->free_data_block_offset; 318: 319: ret = bdrv_pwrite(s->hd, offset, s->footer_buf, HEADER_SIZE); 320: if (ret < 0) 321: return ret; 322: 323: return 0; 324: } 325: 326: /* 327: * Allocates a new block. This involves writing a new footer and updating 328: * the Block Allocation Table to use the space at the old end of the image 329: * file (overwriting the old footer) 330: * 331: * Returns the sectors' offset in the image file on success and < 0 on error 332: */ 333: static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) 334: { 335: BDRVVPCState *s = bs->opaque; 336: int64_t bat_offset; 337: uint32_t index, bat_value; 338: int ret; 339: uint8_t bitmap[s->bitmap_size]; 340: 341: // Check if sector_num is valid 342: if ((sector_num < 0) || (sector_num > bs->total_sectors)) 343: return -1; 344: 345: // Write entry into in-memory BAT 346: index = (sector_num * 512) / s->block_size; 347: if (s->pagetable[index] != 0xFFFFFFFF) 348: return -1; 349: 350: s->pagetable[index] = s->free_data_block_offset / 512; 351: 352: // Initialize the block's bitmap 353: memset(bitmap, 0xff, s->bitmap_size); 354: bdrv_pwrite(s->hd, s->free_data_block_offset, bitmap, s->bitmap_size); 355: 356: // Write new footer (the old one will be overwritten) 357: s->free_data_block_offset += s->block_size + s->bitmap_size; 358: ret = rewrite_footer(bs); 359: if (ret < 0) 360: goto fail; 361: 362: // Write BAT entry to disk 363: bat_offset = s->bat_offset + (4 * index); 364: bat_value = be32_to_cpu(s->pagetable[index]); 365: ret = bdrv_pwrite(s->hd, bat_offset, &bat_value, 4); 366: if (ret < 0) 367: goto fail; 368: 369: return get_sector_offset(bs, sector_num, 0); 370: 371: fail: 372: s->free_data_block_offset -= (s->block_size + s->bitmap_size); 373: return -1; 374: } 375: 376: static int vpc_read(BlockDriverState *bs, int64_t sector_num, 377: uint8_t *buf, int nb_sectors) 378: { 379: BDRVVPCState *s = bs->opaque; 380: int ret; 381: int64_t offset; 382: 383: while (nb_sectors > 0) { 384: offset = get_sector_offset(bs, sector_num, 0); 385: 386: if (offset == -1) { 387: memset(buf, 0, 512); 388: } else { 389: ret = bdrv_pread(s->hd, offset, buf, 512); 390: if (ret != 512) 391: return -1; 392: } 393: 394: nb_sectors--; 395: sector_num++; 396: buf += 512; 397: } 398: return 0; 399: } 400: 401: static int vpc_write(BlockDriverState *bs, int64_t sector_num, 402: const uint8_t *buf, int nb_sectors) 403: { 404: BDRVVPCState *s = bs->opaque; 405: int64_t offset; 406: int ret; 407: 408: while (nb_sectors > 0) { 409: offset = get_sector_offset(bs, sector_num, 1); 410: 411: if (offset == -1) { 412: offset = alloc_block(bs, sector_num); 413: if (offset < 0) 414: return -1; 415: } 416: 417: ret = bdrv_pwrite(s->hd, offset, buf, 512); 418: if (ret != 512) 419: return -1; 420: 421: nb_sectors--; 422: sector_num++; 423: buf += 512; 424: } 425: 426: return 0; 427: } 428: 429: 430: /* 431: * Calculates the number of cylinders, heads and sectors per cylinder 432: * based on a given number of sectors. This is the algorithm described 433: * in the VHD specification. 434: * 435: * Note that the geometry doesn't always exactly match total_sectors but 436: * may round it down. 437: * 438: * Returns 0 on success, -EFBIG if the size is larger than 127 GB 439: */ 440: static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, 441: uint8_t* heads, uint8_t* secs_per_cyl) 442: { 443: uint32_t cyls_times_heads; 444: 445: if (total_sectors > 65535 * 16 * 255) 446: return -EFBIG; 447: 448: if (total_sectors > 65535 * 16 * 63) { 449: *secs_per_cyl = 255; 450: *heads = 16; 451: cyls_times_heads = total_sectors / *secs_per_cyl; 452: } else { 453: *secs_per_cyl = 17; 454: cyls_times_heads = total_sectors / *secs_per_cyl; 455: *heads = (cyls_times_heads + 1023) / 1024; 456: 457: if (*heads < 4) 458: *heads = 4; 459: 460: if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { 461: *secs_per_cyl = 31; 462: *heads = 16; 463: cyls_times_heads = total_sectors / *secs_per_cyl; 464: } 465: 466: if (cyls_times_heads >= (*heads * 1024)) { 467: *secs_per_cyl = 63; 468: *heads = 16; 469: cyls_times_heads = total_sectors / *secs_per_cyl; 470: } 471: } 472: 473: // Note: Rounding up deviates from the Virtual PC behaviour 474: // However, we need this to avoid truncating images in qemu-img convert 475: *cyls = (cyls_times_heads + *heads - 1) / *heads; 476: 477: return 0; 478: } 479: 480: static int vpc_create(const char *filename, QEMUOptionParameter *options) 481: { 482: uint8_t buf[1024]; 483: struct vhd_footer* footer = (struct vhd_footer*) buf; 484: struct vhd_dyndisk_header* dyndisk_header = 485: (struct vhd_dyndisk_header*) buf; 486: int fd, i; 487: uint16_t cyls; 488: uint8_t heads; 489: uint8_t secs_per_cyl; 490: size_t block_size, num_bat_entries; 491: int64_t total_sectors = 0; 492: 493: // Read out options 494: while (options && options->name) { 495: if (!strcmp(options->name, "size")) { 496: total_sectors = options->value.n / 512; 497: } 498: options++; 499: } 500: 501: // Create the file 502: fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); 503: if (fd < 0) 504: return -EIO; 505: 506: // Calculate matching total_size and geometry 507: if (calculate_geometry(total_sectors, &cyls, &heads, &secs_per_cyl)) 508: return -EFBIG; 509: total_sectors = (int64_t) cyls * heads * secs_per_cyl; 510: 511: // Prepare the Hard Disk Footer 512: memset(buf, 0, 1024); 513: 1.1.1.2 ! root 514: memcpy(footer->creator, "conectix", 8); 1.1 root 515: // TODO Check if "qemu" creator_app is ok for VPC 1.1.1.2 ! root 516: memcpy(footer->creator_app, "qemu", 4); ! 517: memcpy(footer->creator_os, "Wi2k", 4); 1.1 root 518: 519: footer->features = be32_to_cpu(0x02); 520: footer->version = be32_to_cpu(0x00010000); 521: footer->data_offset = be64_to_cpu(HEADER_SIZE); 522: footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); 523: 524: // Version of Virtual PC 2007 525: footer->major = be16_to_cpu(0x0005); 526: footer->minor =be16_to_cpu(0x0003); 527: 528: footer->orig_size = be64_to_cpu(total_sectors * 512); 529: footer->size = be64_to_cpu(total_sectors * 512); 530: 531: footer->cyls = be16_to_cpu(cyls); 532: footer->heads = heads; 533: footer->secs_per_cyl = secs_per_cyl; 534: 535: footer->type = be32_to_cpu(VHD_DYNAMIC); 536: 537: // TODO uuid is missing 538: 539: footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); 540: 541: // Write the footer (twice: at the beginning and at the end) 542: block_size = 0x200000; 543: num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); 544: 545: if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) 546: return -EIO; 547: 548: if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) 549: return -EIO; 550: if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) 551: return -EIO; 552: 553: // Write the initial BAT 554: if (lseek(fd, 3 * 512, SEEK_SET) < 0) 555: return -EIO; 556: 557: memset(buf, 0xFF, 512); 558: for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) 559: if (write(fd, buf, 512) != 512) 560: return -EIO; 561: 562: 563: // Prepare the Dynamic Disk Header 564: memset(buf, 0, 1024); 565: 1.1.1.2 ! root 566: memcpy(dyndisk_header->magic, "cxsparse", 8); 1.1 root 567: 568: dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF); 569: dyndisk_header->table_offset = be64_to_cpu(3 * 512); 570: dyndisk_header->version = be32_to_cpu(0x00010000); 571: dyndisk_header->block_size = be32_to_cpu(block_size); 572: dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries); 573: 574: dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); 575: 576: // Write the header 577: if (lseek(fd, 512, SEEK_SET) < 0) 578: return -EIO; 579: if (write(fd, buf, 1024) != 1024) 580: return -EIO; 581: 582: close(fd); 583: return 0; 584: } 585: 586: static void vpc_close(BlockDriverState *bs) 587: { 588: BDRVVPCState *s = bs->opaque; 589: qemu_free(s->pagetable); 590: #ifdef CACHE 591: qemu_free(s->pageentry_u8); 592: #endif 593: bdrv_delete(s->hd); 594: } 595: 596: static QEMUOptionParameter vpc_create_options[] = { 597: { 598: .name = BLOCK_OPT_SIZE, 599: .type = OPT_SIZE, 600: .help = "Virtual disk size" 601: }, 602: { NULL } 603: }; 604: 605: static BlockDriver bdrv_vpc = { 606: .format_name = "vpc", 607: .instance_size = sizeof(BDRVVPCState), 608: .bdrv_probe = vpc_probe, 609: .bdrv_open = vpc_open, 610: .bdrv_read = vpc_read, 611: .bdrv_write = vpc_write, 612: .bdrv_close = vpc_close, 613: .bdrv_create = vpc_create, 614: 615: .create_options = vpc_create_options, 616: }; 617: 618: static void bdrv_vpc_init(void) 619: { 620: bdrv_register(&bdrv_vpc); 621: } 622: 623: block_init(bdrv_vpc_init);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.