/* All VHDX structures on disk are little endian */ static void vhdx_header_le_import(VHDXHeader *h) { assert(h != NULL); le32_to_cpus(&h->signature); le32_to_cpus(&h->checksum); le64_to_cpus(&h->sequence_number); leguid_to_cpus(&h->file_write_guid); leguid_to_cpus(&h->data_write_guid); leguid_to_cpus(&h->log_guid); le16_to_cpus(&h->log_version); le16_to_cpus(&h->version); le32_to_cpus(&h->log_length); le64_to_cpus(&h->log_offset); }
static void mptsas_fix_sgentry_endianness(MPISGEntry *sge) { le32_to_cpus(&sge->FlagsLength); if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { le64_to_cpus(&sge->u.Address64); } else { le32_to_cpus(&sge->u.Address32); } }
static void vdi_header_to_cpu(VdiHeader *header) { le32_to_cpus(&header->signature); le32_to_cpus(&header->version); le32_to_cpus(&header->header_size); le32_to_cpus(&header->image_type); le32_to_cpus(&header->image_flags); le32_to_cpus(&header->offset_bmap); le32_to_cpus(&header->offset_data); le32_to_cpus(&header->cylinders); le32_to_cpus(&header->heads); le32_to_cpus(&header->sectors); le32_to_cpus(&header->sector_size); le64_to_cpus(&header->disk_size); le32_to_cpus(&header->block_size); le32_to_cpus(&header->block_extra); le32_to_cpus(&header->blocks_in_image); le32_to_cpus(&header->blocks_allocated); uuid_convert(header->uuid_image); uuid_convert(header->uuid_last_snap); uuid_convert(header->uuid_link); uuid_convert(header->uuid_parent); }
static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVVHDXState *s = bs->opaque; int ret = 0; uint32_t i; uint64_t signature; uint32_t data_blocks_cnt, bitmap_blocks_cnt; s->bat = NULL; qemu_co_mutex_init(&s->lock); /* validate the file signature */ ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); if (ret < 0) { goto fail; } if (memcmp(&signature, "vhdxfile", 8)) { ret = -EINVAL; goto fail; } ret = vhdx_parse_header(bs, s); if (ret) { goto fail; } ret = vhdx_parse_log(bs, s); if (ret) { goto fail; } ret = vhdx_open_region_tables(bs, s); if (ret) { goto fail; } ret = vhdx_parse_metadata(bs, s); if (ret) { goto fail; } s->block_size = s->params.block_size; /* the VHDX spec dictates that virtual_disk_size is always a multiple of * logical_sector_size */ bs->total_sectors = s->virtual_disk_size >> s->logical_sector_size_bits; data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits; if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) { data_blocks_cnt++; } bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits; if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) { bitmap_blocks_cnt++; } if (s->parent_entries) { s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1); } else { s->bat_entries = data_blocks_cnt + ((data_blocks_cnt - 1) >> s->chunk_ratio_bits); } s->bat_offset = s->bat_rt.file_offset; if (s->bat_entries > s->bat_rt.length / sizeof(VHDXBatEntry)) { /* BAT allocation is not large enough for all entries */ ret = -EINVAL; goto fail; } s->bat = qemu_blockalign(bs, s->bat_rt.length); ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); if (ret < 0) { goto fail; } for (i = 0; i < s->bat_entries; i++) { le64_to_cpus(&s->bat[i]); } if (flags & BDRV_O_RDWR) { ret = -ENOTSUP; goto fail; } /* TODO: differencing files, write */ /* Disable migration when VHDX images are used */ error_set(&s->migration_blocker, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, "vhdx", bs->device_name, "live migration"); migrate_add_blocker(s->migration_blocker); return 0; fail: qemu_vfree(s->headers[0]); qemu_vfree(s->headers[1]); qemu_vfree(s->bat); qemu_vfree(s->parent_entries); return ret; }
/* Metadata initial parser * * This loads all the metadata entry fields. This may cause additional * fields to be processed (e.g. parent locator, etc..). * * There are 5 Metadata items that are always required: * - File Parameters (block size, has a parent) * - Virtual Disk Size (size, in bytes, of the virtual drive) * - Page 83 Data (scsi page 83 guid) * - Logical Sector Size (logical sector size in bytes, either 512 or * 4096. We only support 512 currently) * - Physical Sector Size (512 or 4096) * * Also, if the File Parameters indicate this is a differencing file, * we must also look for the Parent Locator metadata item. */ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) { int ret = 0; uint8_t *buffer; int offset = 0; uint32_t i = 0; VHDXMetadataTableEntry md_entry; buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE); ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer, VHDX_METADATA_TABLE_MAX_SIZE); if (ret < 0) { goto exit; } memcpy(&s->metadata_hdr, buffer, sizeof(s->metadata_hdr)); offset += sizeof(s->metadata_hdr); le64_to_cpus(&s->metadata_hdr.signature); le16_to_cpus(&s->metadata_hdr.reserved); le16_to_cpus(&s->metadata_hdr.entry_count); if (memcmp(&s->metadata_hdr.signature, "metadata", 8)) { ret = -EINVAL; goto exit; } s->metadata_entries.present = 0; if ((s->metadata_hdr.entry_count * sizeof(md_entry)) > (VHDX_METADATA_TABLE_MAX_SIZE - offset)) { ret = -EINVAL; goto exit; } for (i = 0; i < s->metadata_hdr.entry_count; i++) { memcpy(&md_entry, buffer + offset, sizeof(md_entry)); offset += sizeof(md_entry); leguid_to_cpus(&md_entry.item_id); le32_to_cpus(&md_entry.offset); le32_to_cpus(&md_entry.length); le32_to_cpus(&md_entry.data_bits); le32_to_cpus(&md_entry.reserved2); if (guid_eq(md_entry.item_id, file_param_guid)) { if (s->metadata_entries.present & META_FILE_PARAMETER_PRESENT) { ret = -EINVAL; goto exit; } s->metadata_entries.file_parameters_entry = md_entry; s->metadata_entries.present |= META_FILE_PARAMETER_PRESENT; continue; } if (guid_eq(md_entry.item_id, virtual_size_guid)) { if (s->metadata_entries.present & META_VIRTUAL_DISK_SIZE_PRESENT) { ret = -EINVAL; goto exit; } s->metadata_entries.virtual_disk_size_entry = md_entry; s->metadata_entries.present |= META_VIRTUAL_DISK_SIZE_PRESENT; continue; } if (guid_eq(md_entry.item_id, page83_guid)) { if (s->metadata_entries.present & META_PAGE_83_PRESENT) { ret = -EINVAL; goto exit; } s->metadata_entries.page83_data_entry = md_entry; s->metadata_entries.present |= META_PAGE_83_PRESENT; continue; } if (guid_eq(md_entry.item_id, logical_sector_guid)) { if (s->metadata_entries.present & META_LOGICAL_SECTOR_SIZE_PRESENT) { ret = -EINVAL; goto exit; } s->metadata_entries.logical_sector_size_entry = md_entry; s->metadata_entries.present |= META_LOGICAL_SECTOR_SIZE_PRESENT; continue; } if (guid_eq(md_entry.item_id, phys_sector_guid)) { if (s->metadata_entries.present & META_PHYS_SECTOR_SIZE_PRESENT) { ret = -EINVAL; goto exit; } s->metadata_entries.phys_sector_size_entry = md_entry; s->metadata_entries.present |= META_PHYS_SECTOR_SIZE_PRESENT; continue; } if (guid_eq(md_entry.item_id, parent_locator_guid)) { if (s->metadata_entries.present & META_PARENT_LOCATOR_PRESENT) { ret = -EINVAL; goto exit; } s->metadata_entries.parent_locator_entry = md_entry; s->metadata_entries.present |= META_PARENT_LOCATOR_PRESENT; continue; } if (md_entry.data_bits & VHDX_META_FLAGS_IS_REQUIRED) { /* cannot read vhdx file - required region table entry that * we do not understand. per spec, we must fail to open */ ret = -ENOTSUP; goto exit; } } if (s->metadata_entries.present != META_ALL_PRESENT) { ret = -ENOTSUP; goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.file_parameters_entry.offset + s->metadata_rt.file_offset, &s->params, sizeof(s->params)); if (ret < 0) { goto exit; } le32_to_cpus(&s->params.block_size); le32_to_cpus(&s->params.data_bits); /* We now have the file parameters, so we can tell if this is a * differencing file (i.e.. has_parent), is dynamic or fixed * sized (leave_blocks_allocated), and the block size */ /* The parent locator required iff the file parameters has_parent set */ if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) { if (s->metadata_entries.present & META_PARENT_LOCATOR_PRESENT) { /* TODO: parse parent locator fields */ ret = -ENOTSUP; /* temp, until differencing files are supported */ goto exit; } else { /* if has_parent is set, but there is not parent locator present, * then that is an invalid combination */ ret = -EINVAL; goto exit; } } /* determine virtual disk size, logical sector size, * and phys sector size */ ret = bdrv_pread(bs->file, s->metadata_entries.virtual_disk_size_entry.offset + s->metadata_rt.file_offset, &s->virtual_disk_size, sizeof(uint64_t)); if (ret < 0) { goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.logical_sector_size_entry.offset + s->metadata_rt.file_offset, &s->logical_sector_size, sizeof(uint32_t)); if (ret < 0) { goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.phys_sector_size_entry.offset + s->metadata_rt.file_offset, &s->physical_sector_size, sizeof(uint32_t)); if (ret < 0) { goto exit; } le64_to_cpus(&s->virtual_disk_size); le32_to_cpus(&s->logical_sector_size); le32_to_cpus(&s->physical_sector_size); if (s->logical_sector_size == 0 || s->params.block_size == 0) { ret = -EINVAL; goto exit; } /* both block_size and sector_size are guaranteed powers of 2 */ s->sectors_per_block = s->params.block_size / s->logical_sector_size; s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) * (uint64_t)s->logical_sector_size / (uint64_t)s->params.block_size; /* These values are ones we will want to use for division / multiplication * later on, and they are all guaranteed (per the spec) to be powers of 2, * so we can take advantage of that for shift operations during * reads/writes */ if (s->logical_sector_size & (s->logical_sector_size - 1)) { ret = -EINVAL; goto exit; } if (s->sectors_per_block & (s->sectors_per_block - 1)) { ret = -EINVAL; goto exit; } if (s->chunk_ratio & (s->chunk_ratio - 1)) { ret = -EINVAL; goto exit; } s->block_size = s->params.block_size; if (s->block_size & (s->block_size - 1)) { ret = -EINVAL; goto exit; } s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size); s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block); s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio); s->block_size_bits = 31 - clz32(s->block_size); ret = 0; exit: qemu_vfree(buffer); return ret; }
static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) { int ret = 0; uint8_t *buffer; int offset = 0; VHDXRegionTableEntry rt_entry; uint32_t i; bool bat_rt_found = false; bool metadata_rt_found = false; /* We have to read the whole 64KB block, because the crc32 is over the * whole block */ buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE); ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer, VHDX_HEADER_BLOCK_SIZE); if (ret < 0) { goto fail; } memcpy(&s->rt, buffer, sizeof(s->rt)); le32_to_cpus(&s->rt.signature); le32_to_cpus(&s->rt.checksum); le32_to_cpus(&s->rt.entry_count); le32_to_cpus(&s->rt.reserved); offset += sizeof(s->rt); if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4) || memcmp(&s->rt.signature, "regi", 4)) { ret = -EINVAL; goto fail; } /* Per spec, maximum region table entry count is 2047 */ if (s->rt.entry_count > 2047) { ret = -EINVAL; goto fail; } for (i = 0; i < s->rt.entry_count; i++) { memcpy(&rt_entry, buffer + offset, sizeof(rt_entry)); offset += sizeof(rt_entry); leguid_to_cpus(&rt_entry.guid); le64_to_cpus(&rt_entry.file_offset); le32_to_cpus(&rt_entry.length); le32_to_cpus(&rt_entry.data_bits); /* see if we recognize the entry */ if (guid_eq(rt_entry.guid, bat_guid)) { /* must be unique; if we have already found it this is invalid */ if (bat_rt_found) { ret = -EINVAL; goto fail; } bat_rt_found = true; s->bat_rt = rt_entry; continue; } if (guid_eq(rt_entry.guid, metadata_guid)) { /* must be unique; if we have already found it this is invalid */ if (metadata_rt_found) { ret = -EINVAL; goto fail; } metadata_rt_found = true; s->metadata_rt = rt_entry; continue; } if (rt_entry.data_bits & VHDX_REGION_ENTRY_REQUIRED) { /* cannot read vhdx file - required region table entry that * we do not understand. per spec, we must fail to open */ ret = -ENOTSUP; goto fail; } } ret = 0; fail: qemu_vfree(buffer); return ret; }