qemu: remove obsolete package
Instead of using an ancient qemu version in-tree the building machine should just have qemu-utils installed. Signed-off-by: Paul Spooren <mail@aparcar.org>
This commit is contained in:
		| @@ -33,7 +33,6 @@ tools-$(CONFIG_TARGET_apm821xx)$(CONFIG_TARGET_gemini) += genext2fs | ||||
| tools-$(CONFIG_TARGET_ath79) += lzma-old squashfs | ||||
| tools-$(CONFIG_TARGET_mxs) += elftosb sdimage | ||||
| tools-$(CONFIG_TARGET_tegra) += cbootimage cbootimage-configs | ||||
| tools-$(CONFIG_TARGET_x86) += qemu | ||||
| tools-$(CONFIG_USES_MINOR) += kernel2minor | ||||
| tools-$(CONFIG_USE_SPARSE) += sparse | ||||
|  | ||||
| @@ -68,7 +67,6 @@ $(curdir)/mpfr/compile := $(curdir)/gmp/compile | ||||
| $(curdir)/mtd-utils/compile := $(curdir)/libtool/compile $(curdir)/e2fsprogs/compile $(curdir)/zlib/compile | ||||
| $(curdir)/padjffs2/compile := $(curdir)/findutils/compile | ||||
| $(curdir)/patchelf/compile := $(curdir)/libtool/compile | ||||
| $(curdir)/qemu/compile := $(curdir)/e2fsprogs/compile $(curdir)/zlib/compile | ||||
| $(curdir)/quilt/compile := $(curdir)/autoconf/compile $(curdir)/findutils/compile | ||||
| $(curdir)/sdcc/compile := $(curdir)/bison/compile | ||||
| $(curdir)/squashfs/compile := $(curdir)/lzma-old/compile | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2010-2012 OpenWrt.org | ||||
| # | ||||
| # This is free software, licensed under the GNU General Public License v2. | ||||
| # See /LICENSE for more information. | ||||
| # | ||||
| include $(TOPDIR)/rules.mk | ||||
|  | ||||
| PKG_NAME:=qemu | ||||
| PKG_CPE_ID:=cpe:/a:qemu:qemu | ||||
| PKG_VERSION:=0.14.1 | ||||
| PKG_RELEASE:=1 | ||||
|  | ||||
| PKG_SOURCE_URL:=@SAVANNAH/qemu | ||||
| PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz | ||||
| PKG_HASH:=33ceae3fbe516f2cbb151dc98d16c8ccfec74b1056674ad715e75a2f7fed45c3 | ||||
|  | ||||
| include $(INCLUDE_DIR)/host-build.mk | ||||
|  | ||||
| HOST_CFLAGS += \ | ||||
| 	-I$(STAGING_DIR_HOST)/include/e2fsprogs \ | ||||
| 	-DAES_cbc_encrypt=QEMU_AES_cbc_encrypt \ | ||||
| 	-DAES_decrypt=QEMU_AES_decrypt \ | ||||
| 	-DAES_encrypt=QEMU_AES_encrypt \ | ||||
| 	-DAES_set_decrypt_key=QEMU_AES_set_decrypt_key \ | ||||
| 	-DAES_set_encrypt_key=QEMU_AES_set_encrypt_key | ||||
|  | ||||
| HOST_CONFIGURE_VARS := \ | ||||
| 	CFLAGS="$(HOST_CFLAGS)" \ | ||||
| 	LDFLAGS="$(HOST_LDFLAGS)" | ||||
|  | ||||
| HOST_CONFIGURE_ARGS := \ | ||||
| 	--extra-cflags="$(HOST_CFLAGS)" \ | ||||
| 	--enable-uuid | ||||
|  | ||||
| define Host/Compile | ||||
| 	$(MAKE) -C $(HOST_BUILD_DIR) qemu-img | ||||
| endef | ||||
|  | ||||
| define Host/Install | ||||
| 	$(INSTALL_DIR) $(STAGING_DIR_HOST)/bin | ||||
| 	$(INSTALL_BIN) $(HOST_BUILD_DIR)/qemu-img $(STAGING_DIR_HOST)/bin | ||||
| endef | ||||
|  | ||||
| $(eval $(call HostBuild)) | ||||
| @@ -1,75 +0,0 @@ | ||||
| From 6afca0fc5430db0300fe53f2b9cd7d071a3925bb Mon Sep 17 00:00:00 2001 | ||||
| From: Alexander Graf <agraf@suse.de> | ||||
| Date: Wed, 25 May 2011 00:46:55 +0200 | ||||
| Subject: [PATCH 01/12] vmdk: fix endianness bugs | ||||
|  | ||||
| The vmdk code is sloppy when handling the header descriptor during | ||||
| creation of an image. Fix all header accesses in the create path to | ||||
| either store native endianness or convert it when appropriate. | ||||
|  | ||||
| Reported-by: Yury Tsarev <ytsarev@novell.com> | ||||
| Signed-off-by: Alexander Graf <agraf@suse.de> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 22 ++++++++++++++-------- | ||||
|  1 file changed, 14 insertions(+), 8 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -716,11 +716,11 @@ static int vmdk_create(const char *filen | ||||
|          return -errno; | ||||
|      magic = cpu_to_be32(VMDK4_MAGIC); | ||||
|      memset(&header, 0, sizeof(header)); | ||||
| -    header.version = cpu_to_le32(1); | ||||
| -    header.flags = cpu_to_le32(3); /* ?? */ | ||||
| -    header.capacity = cpu_to_le64(total_size); | ||||
| -    header.granularity = cpu_to_le64(128); | ||||
| -    header.num_gtes_per_gte = cpu_to_le32(512); | ||||
| +    header.version = 1; | ||||
| +    header.flags = 3; /* ?? */ | ||||
| +    header.capacity = total_size; | ||||
| +    header.granularity = 128; | ||||
| +    header.num_gtes_per_gte = 512; | ||||
|   | ||||
|      grains = (total_size + header.granularity - 1) / header.granularity; | ||||
|      gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; | ||||
| @@ -736,6 +736,12 @@ static int vmdk_create(const char *filen | ||||
|           header.granularity - 1) / header.granularity) * | ||||
|          header.granularity; | ||||
|   | ||||
| +    /* swap endianness for all header fields */ | ||||
| +    header.version = cpu_to_le32(header.version); | ||||
| +    header.flags = cpu_to_le32(header.flags); | ||||
| +    header.capacity = cpu_to_le64(header.capacity); | ||||
| +    header.granularity = cpu_to_le64(header.granularity); | ||||
| +    header.num_gtes_per_gte = cpu_to_le32(header.num_gtes_per_gte); | ||||
|      header.desc_offset = cpu_to_le64(header.desc_offset); | ||||
|      header.desc_size = cpu_to_le64(header.desc_size); | ||||
|      header.rgd_offset = cpu_to_le64(header.rgd_offset); | ||||
| @@ -759,7 +765,7 @@ static int vmdk_create(const char *filen | ||||
|          goto exit; | ||||
|      } | ||||
|   | ||||
| -    ret = ftruncate(fd, header.grain_offset << 9); | ||||
| +    ret = ftruncate(fd, le64_to_cpu(header.grain_offset) << 9); | ||||
|      if (ret < 0) { | ||||
|          ret = -errno; | ||||
|          goto exit; | ||||
| @@ -767,7 +773,7 @@ static int vmdk_create(const char *filen | ||||
|   | ||||
|      /* write grain directory */ | ||||
|      lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); | ||||
| -    for (i = 0, tmp = header.rgd_offset + gd_size; | ||||
| +    for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_size; | ||||
|           i < gt_count; i++, tmp += gt_size) { | ||||
|          ret = qemu_write_full(fd, &tmp, sizeof(tmp)); | ||||
|          if (ret != sizeof(tmp)) { | ||||
| @@ -778,7 +784,7 @@ static int vmdk_create(const char *filen | ||||
|   | ||||
|      /* write backup grain directory */ | ||||
|      lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); | ||||
| -    for (i = 0, tmp = header.gd_offset + gd_size; | ||||
| +    for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_size; | ||||
|           i < gt_count; i++, tmp += gt_size) { | ||||
|          ret = qemu_write_full(fd, &tmp, sizeof(tmp)); | ||||
|          if (ret != sizeof(tmp)) { | ||||
| @@ -1,557 +0,0 @@ | ||||
| From fcd9c52d160376184cbd25e04586aa6eef6abd61 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:28 +0800 | ||||
| Subject: [PATCH 02/12] VMDK: introduce VmdkExtent | ||||
|  | ||||
| Introduced VmdkExtent array into BDRVVmdkState, enable holding multiple | ||||
| image extents for multiple file image support. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 348 +++++++++++++++++++++++++++++++++++++++++------------------ | ||||
|  1 file changed, 246 insertions(+), 102 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -60,7 +60,11 @@ typedef struct { | ||||
|   | ||||
|  #define L2_CACHE_SIZE 16 | ||||
|   | ||||
| -typedef struct BDRVVmdkState { | ||||
| +typedef struct VmdkExtent { | ||||
| +    BlockDriverState *file; | ||||
| +    bool flat; | ||||
| +    int64_t sectors; | ||||
| +    int64_t end_sector; | ||||
|      int64_t l1_table_offset; | ||||
|      int64_t l1_backup_table_offset; | ||||
|      uint32_t *l1_table; | ||||
| @@ -74,7 +78,13 @@ typedef struct BDRVVmdkState { | ||||
|      uint32_t l2_cache_counts[L2_CACHE_SIZE]; | ||||
|   | ||||
|      unsigned int cluster_sectors; | ||||
| +} VmdkExtent; | ||||
| + | ||||
| +typedef struct BDRVVmdkState { | ||||
|      uint32_t parent_cid; | ||||
| +    int num_extents; | ||||
| +    /* Extent array with num_extents entries, ascend ordered by address */ | ||||
| +    VmdkExtent *extents; | ||||
|  } BDRVVmdkState; | ||||
|   | ||||
|  typedef struct VmdkMetaData { | ||||
| @@ -105,6 +115,19 @@ static int vmdk_probe(const uint8_t *buf | ||||
|  #define DESC_SIZE 20*SECTOR_SIZE	// 20 sectors of 512 bytes each | ||||
|  #define HEADER_SIZE 512   			// first sector of 512 bytes | ||||
|   | ||||
| +static void vmdk_free_extents(BlockDriverState *bs) | ||||
| +{ | ||||
| +    int i; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
| + | ||||
| +    for (i = 0; i < s->num_extents; i++) { | ||||
| +        qemu_free(s->extents[i].l1_table); | ||||
| +        qemu_free(s->extents[i].l2_cache); | ||||
| +        qemu_free(s->extents[i].l1_backup_table); | ||||
| +    } | ||||
| +    qemu_free(s->extents); | ||||
| +} | ||||
| + | ||||
|  static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) | ||||
|  { | ||||
|      char desc[DESC_SIZE]; | ||||
| @@ -358,11 +381,50 @@ static int vmdk_parent_open(BlockDriverS | ||||
|      return 0; | ||||
|  } | ||||
|   | ||||
| +/* Create and append extent to the extent array. Return the added VmdkExtent | ||||
| + * address. return NULL if allocation failed. */ | ||||
| +static VmdkExtent *vmdk_add_extent(BlockDriverState *bs, | ||||
| +                           BlockDriverState *file, bool flat, int64_t sectors, | ||||
| +                           int64_t l1_offset, int64_t l1_backup_offset, | ||||
| +                           uint32_t l1_size, | ||||
| +                           int l2_size, unsigned int cluster_sectors) | ||||
| +{ | ||||
| +    VmdkExtent *extent; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
| + | ||||
| +    s->extents = qemu_realloc(s->extents, | ||||
| +                              (s->num_extents + 1) * sizeof(VmdkExtent)); | ||||
| +    extent = &s->extents[s->num_extents]; | ||||
| +    s->num_extents++; | ||||
| + | ||||
| +    memset(extent, 0, sizeof(VmdkExtent)); | ||||
| +    extent->file = file; | ||||
| +    extent->flat = flat; | ||||
| +    extent->sectors = sectors; | ||||
| +    extent->l1_table_offset = l1_offset; | ||||
| +    extent->l1_backup_table_offset = l1_backup_offset; | ||||
| +    extent->l1_size = l1_size; | ||||
| +    extent->l1_entry_sectors = l2_size * cluster_sectors; | ||||
| +    extent->l2_size = l2_size; | ||||
| +    extent->cluster_sectors = cluster_sectors; | ||||
| + | ||||
| +    if (s->num_extents > 1) { | ||||
| +        extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; | ||||
| +    } else { | ||||
| +        extent->end_sector = extent->sectors; | ||||
| +    } | ||||
| +    bs->total_sectors = extent->end_sector; | ||||
| +    return extent; | ||||
| +} | ||||
| + | ||||
| + | ||||
|  static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|  { | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
|      uint32_t magic; | ||||
| -    int l1_size, i; | ||||
| +    int i; | ||||
| +    uint32_t l1_size, l1_entry_sectors; | ||||
| +    VmdkExtent *extent = NULL; | ||||
|   | ||||
|      if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) | ||||
|          goto fail; | ||||
| @@ -370,32 +432,34 @@ static int vmdk_open(BlockDriverState *b | ||||
|      magic = be32_to_cpu(magic); | ||||
|      if (magic == VMDK3_MAGIC) { | ||||
|          VMDK3Header header; | ||||
| - | ||||
| -        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||
| +        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) | ||||
| +                != sizeof(header)) { | ||||
|              goto fail; | ||||
| -        s->cluster_sectors = le32_to_cpu(header.granularity); | ||||
| -        s->l2_size = 1 << 9; | ||||
| -        s->l1_size = 1 << 6; | ||||
| -        bs->total_sectors = le32_to_cpu(header.disk_sectors); | ||||
| -        s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; | ||||
| -        s->l1_backup_table_offset = 0; | ||||
| -        s->l1_entry_sectors = s->l2_size * s->cluster_sectors; | ||||
| +        } | ||||
| +        extent = vmdk_add_extent(bs, bs->file, false, | ||||
| +                              le32_to_cpu(header.disk_sectors), | ||||
| +                              le32_to_cpu(header.l1dir_offset) << 9, 0, | ||||
| +                              1 << 6, 1 << 9, le32_to_cpu(header.granularity)); | ||||
|      } else if (magic == VMDK4_MAGIC) { | ||||
|          VMDK4Header header; | ||||
| - | ||||
| -        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||
| +        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) | ||||
| +                != sizeof(header)) { | ||||
|              goto fail; | ||||
| -        bs->total_sectors = le64_to_cpu(header.capacity); | ||||
| -        s->cluster_sectors = le64_to_cpu(header.granularity); | ||||
| -        s->l2_size = le32_to_cpu(header.num_gtes_per_gte); | ||||
| -        s->l1_entry_sectors = s->l2_size * s->cluster_sectors; | ||||
| -        if (s->l1_entry_sectors <= 0) | ||||
| +        } | ||||
| +        l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) | ||||
| +                            * le64_to_cpu(header.granularity); | ||||
| +        l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) | ||||
| +                    / l1_entry_sectors; | ||||
| +        extent = vmdk_add_extent(bs, bs->file, false, | ||||
| +                              le64_to_cpu(header.capacity), | ||||
| +                              le64_to_cpu(header.gd_offset) << 9, | ||||
| +                              le64_to_cpu(header.rgd_offset) << 9, | ||||
| +                              l1_size, | ||||
| +                              le32_to_cpu(header.num_gtes_per_gte), | ||||
| +                              le64_to_cpu(header.granularity)); | ||||
| +        if (extent->l1_entry_sectors <= 0) { | ||||
|              goto fail; | ||||
| -        s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) | ||||
| -            / s->l1_entry_sectors; | ||||
| -        s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; | ||||
| -        s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; | ||||
| - | ||||
| +        } | ||||
|          // try to open parent images, if exist | ||||
|          if (vmdk_parent_open(bs) != 0) | ||||
|              goto fail; | ||||
| @@ -406,40 +470,49 @@ static int vmdk_open(BlockDriverState *b | ||||
|      } | ||||
|   | ||||
|      /* read the L1 table */ | ||||
| -    l1_size = s->l1_size * sizeof(uint32_t); | ||||
| -    s->l1_table = qemu_malloc(l1_size); | ||||
| -    if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size) | ||||
| -        goto fail; | ||||
| -    for(i = 0; i < s->l1_size; i++) { | ||||
| -        le32_to_cpus(&s->l1_table[i]); | ||||
| -    } | ||||
| - | ||||
| -    if (s->l1_backup_table_offset) { | ||||
| -        s->l1_backup_table = qemu_malloc(l1_size); | ||||
| -        if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) | ||||
| +    l1_size = extent->l1_size * sizeof(uint32_t); | ||||
| +    extent->l1_table = qemu_malloc(l1_size); | ||||
| +    if (bdrv_pread(bs->file, | ||||
| +            extent->l1_table_offset, | ||||
| +            extent->l1_table, | ||||
| +            l1_size) | ||||
| +        != l1_size) { | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    for (i = 0; i < extent->l1_size; i++) { | ||||
| +        le32_to_cpus(&extent->l1_table[i]); | ||||
| +    } | ||||
| + | ||||
| +    if (extent->l1_backup_table_offset) { | ||||
| +        extent->l1_backup_table = qemu_malloc(l1_size); | ||||
| +        if (bdrv_pread(bs->file, | ||||
| +                    extent->l1_backup_table_offset, | ||||
| +                    extent->l1_backup_table, | ||||
| +                    l1_size) | ||||
| +                != l1_size) { | ||||
|              goto fail; | ||||
| -        for(i = 0; i < s->l1_size; i++) { | ||||
| -            le32_to_cpus(&s->l1_backup_table[i]); | ||||
| +        } | ||||
| +        for (i = 0; i < extent->l1_size; i++) { | ||||
| +            le32_to_cpus(&extent->l1_backup_table[i]); | ||||
|          } | ||||
|      } | ||||
|   | ||||
| -    s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); | ||||
| +    extent->l2_cache = | ||||
| +        qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); | ||||
|      return 0; | ||||
|   fail: | ||||
| -    qemu_free(s->l1_backup_table); | ||||
| -    qemu_free(s->l1_table); | ||||
| -    qemu_free(s->l2_cache); | ||||
| +    vmdk_free_extents(bs); | ||||
|      return -1; | ||||
|  } | ||||
|   | ||||
| -static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, | ||||
| -                                   uint64_t offset, int allocate); | ||||
| - | ||||
| -static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, | ||||
| -                             uint64_t offset, int allocate) | ||||
| +static int get_whole_cluster(BlockDriverState *bs, | ||||
| +                VmdkExtent *extent, | ||||
| +                uint64_t cluster_offset, | ||||
| +                uint64_t offset, | ||||
| +                bool allocate) | ||||
|  { | ||||
| -    BDRVVmdkState *s = bs->opaque; | ||||
| -    uint8_t  whole_grain[s->cluster_sectors*512];        // 128 sectors * 512 bytes each = grain size 64KB | ||||
| +    /* 128 sectors * 512 bytes each = grain size 64KB */ | ||||
| +    uint8_t  whole_grain[extent->cluster_sectors * 512]; | ||||
|   | ||||
|      // we will be here if it's first write on non-exist grain(cluster). | ||||
|      // try to read from parent image, if exist | ||||
| @@ -450,14 +523,14 @@ static int get_whole_cluster(BlockDriver | ||||
|              return -1; | ||||
|   | ||||
|          ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain, | ||||
| -            s->cluster_sectors); | ||||
| +                extent->cluster_sectors); | ||||
|          if (ret < 0) { | ||||
|              return -1; | ||||
|          } | ||||
|   | ||||
|          //Write grain only into the active image | ||||
| -        ret = bdrv_write(bs->file, cluster_offset, whole_grain, | ||||
| -            s->cluster_sectors); | ||||
| +        ret = bdrv_write(extent->file, cluster_offset, whole_grain, | ||||
| +                extent->cluster_sectors); | ||||
|          if (ret < 0) { | ||||
|              return -1; | ||||
|          } | ||||
| @@ -465,29 +538,39 @@ static int get_whole_cluster(BlockDriver | ||||
|      return 0; | ||||
|  } | ||||
|   | ||||
| -static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data) | ||||
| +static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data) | ||||
|  { | ||||
| -    BDRVVmdkState *s = bs->opaque; | ||||
| - | ||||
|      /* update L2 table */ | ||||
| -    if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||
| -                    &(m_data->offset), sizeof(m_data->offset)) < 0) | ||||
| +    if (bdrv_pwrite_sync( | ||||
| +                extent->file, | ||||
| +                ((int64_t)m_data->l2_offset * 512) | ||||
| +                    + (m_data->l2_index * sizeof(m_data->offset)), | ||||
| +                &(m_data->offset), | ||||
| +                sizeof(m_data->offset) | ||||
| +            ) < 0) { | ||||
|          return -1; | ||||
| +    } | ||||
|      /* update backup L2 table */ | ||||
| -    if (s->l1_backup_table_offset != 0) { | ||||
| -        m_data->l2_offset = s->l1_backup_table[m_data->l1_index]; | ||||
| -        if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||
| -                        &(m_data->offset), sizeof(m_data->offset)) < 0) | ||||
| +    if (extent->l1_backup_table_offset != 0) { | ||||
| +        m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; | ||||
| +        if (bdrv_pwrite_sync( | ||||
| +                    extent->file, | ||||
| +                    ((int64_t)m_data->l2_offset * 512) | ||||
| +                        + (m_data->l2_index * sizeof(m_data->offset)), | ||||
| +                    &(m_data->offset), sizeof(m_data->offset) | ||||
| +                ) < 0) { | ||||
|              return -1; | ||||
| +        } | ||||
|      } | ||||
|   | ||||
|      return 0; | ||||
|  } | ||||
|   | ||||
| -static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, | ||||
| -                                   uint64_t offset, int allocate) | ||||
| +static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
| +                                    VmdkExtent *extent, | ||||
| +                                    VmdkMetaData *m_data, | ||||
| +                                    uint64_t offset, int allocate) | ||||
|  { | ||||
| -    BDRVVmdkState *s = bs->opaque; | ||||
|      unsigned int l1_index, l2_offset, l2_index; | ||||
|      int min_index, i, j; | ||||
|      uint32_t min_count, *l2_table, tmp = 0; | ||||
| @@ -496,21 +579,23 @@ static uint64_t get_cluster_offset(Block | ||||
|      if (m_data) | ||||
|          m_data->valid = 0; | ||||
|   | ||||
| -    l1_index = (offset >> 9) / s->l1_entry_sectors; | ||||
| -    if (l1_index >= s->l1_size) | ||||
| +    l1_index = (offset >> 9) / extent->l1_entry_sectors; | ||||
| +    if (l1_index >= extent->l1_size) { | ||||
|          return 0; | ||||
| -    l2_offset = s->l1_table[l1_index]; | ||||
| -    if (!l2_offset) | ||||
| +    } | ||||
| +    l2_offset = extent->l1_table[l1_index]; | ||||
| +    if (!l2_offset) { | ||||
|          return 0; | ||||
| +    } | ||||
|      for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||
| -        if (l2_offset == s->l2_cache_offsets[i]) { | ||||
| +        if (l2_offset == extent->l2_cache_offsets[i]) { | ||||
|              /* increment the hit count */ | ||||
| -            if (++s->l2_cache_counts[i] == 0xffffffff) { | ||||
| +            if (++extent->l2_cache_counts[i] == 0xffffffff) { | ||||
|                  for(j = 0; j < L2_CACHE_SIZE; j++) { | ||||
| -                    s->l2_cache_counts[j] >>= 1; | ||||
| +                    extent->l2_cache_counts[j] >>= 1; | ||||
|                  } | ||||
|              } | ||||
| -            l2_table = s->l2_cache + (i * s->l2_size); | ||||
| +            l2_table = extent->l2_cache + (i * extent->l2_size); | ||||
|              goto found; | ||||
|          } | ||||
|      } | ||||
| @@ -518,20 +603,25 @@ static uint64_t get_cluster_offset(Block | ||||
|      min_index = 0; | ||||
|      min_count = 0xffffffff; | ||||
|      for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||
| -        if (s->l2_cache_counts[i] < min_count) { | ||||
| -            min_count = s->l2_cache_counts[i]; | ||||
| +        if (extent->l2_cache_counts[i] < min_count) { | ||||
| +            min_count = extent->l2_cache_counts[i]; | ||||
|              min_index = i; | ||||
|          } | ||||
|      } | ||||
| -    l2_table = s->l2_cache + (min_index * s->l2_size); | ||||
| -    if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != | ||||
| -                                                                        s->l2_size * sizeof(uint32_t)) | ||||
| +    l2_table = extent->l2_cache + (min_index * extent->l2_size); | ||||
| +    if (bdrv_pread( | ||||
| +                extent->file, | ||||
| +                (int64_t)l2_offset * 512, | ||||
| +                l2_table, | ||||
| +                extent->l2_size * sizeof(uint32_t) | ||||
| +            ) != extent->l2_size * sizeof(uint32_t)) { | ||||
|          return 0; | ||||
| +    } | ||||
|   | ||||
| -    s->l2_cache_offsets[min_index] = l2_offset; | ||||
| -    s->l2_cache_counts[min_index] = 1; | ||||
| +    extent->l2_cache_offsets[min_index] = l2_offset; | ||||
| +    extent->l2_cache_counts[min_index] = 1; | ||||
|   found: | ||||
| -    l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; | ||||
| +    l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; | ||||
|      cluster_offset = le32_to_cpu(l2_table[l2_index]); | ||||
|   | ||||
|      if (!cluster_offset) { | ||||
| @@ -539,8 +629,11 @@ static uint64_t get_cluster_offset(Block | ||||
|              return 0; | ||||
|   | ||||
|          // Avoid the L2 tables update for the images that have snapshots. | ||||
| -        cluster_offset = bdrv_getlength(bs->file); | ||||
| -        bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9)); | ||||
| +        cluster_offset = bdrv_getlength(extent->file); | ||||
| +        bdrv_truncate( | ||||
| +            extent->file, | ||||
| +            cluster_offset + (extent->cluster_sectors << 9) | ||||
| +        ); | ||||
|   | ||||
|          cluster_offset >>= 9; | ||||
|          tmp = cpu_to_le32(cluster_offset); | ||||
| @@ -551,7 +644,8 @@ static uint64_t get_cluster_offset(Block | ||||
|           * This problem may occur because of insufficient space on host disk | ||||
|           * or inappropriate VM shutdown. | ||||
|           */ | ||||
| -        if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1) | ||||
| +        if (get_whole_cluster( | ||||
| +                bs, extent, cluster_offset, offset, allocate) == -1) | ||||
|              return 0; | ||||
|   | ||||
|          if (m_data) { | ||||
| @@ -566,33 +660,69 @@ static uint64_t get_cluster_offset(Block | ||||
|      return cluster_offset; | ||||
|  } | ||||
|   | ||||
| +static VmdkExtent *find_extent(BDRVVmdkState *s, | ||||
| +                                int64_t sector_num, VmdkExtent *start_hint) | ||||
| +{ | ||||
| +    VmdkExtent *extent = start_hint; | ||||
| + | ||||
| +    if (!extent) { | ||||
| +        extent = &s->extents[0]; | ||||
| +    } | ||||
| +    while (extent < &s->extents[s->num_extents]) { | ||||
| +        if (sector_num < extent->end_sector) { | ||||
| +            return extent; | ||||
| +        } | ||||
| +        extent++; | ||||
| +    } | ||||
| +    return NULL; | ||||
| +} | ||||
| + | ||||
|  static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||
|                               int nb_sectors, int *pnum) | ||||
|  { | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
| -    int index_in_cluster, n; | ||||
| -    uint64_t cluster_offset; | ||||
|   | ||||
| -    cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0); | ||||
| -    index_in_cluster = sector_num % s->cluster_sectors; | ||||
| -    n = s->cluster_sectors - index_in_cluster; | ||||
| +    int64_t index_in_cluster, n, ret; | ||||
| +    uint64_t offset; | ||||
| +    VmdkExtent *extent; | ||||
| + | ||||
| +    extent = find_extent(s, sector_num, NULL); | ||||
| +    if (!extent) { | ||||
| +        return 0; | ||||
| +    } | ||||
| +    if (extent->flat) { | ||||
| +        n = extent->end_sector - sector_num; | ||||
| +        ret = 1; | ||||
| +    } else { | ||||
| +        offset = get_cluster_offset(bs, extent, NULL, sector_num * 512, 0); | ||||
| +        index_in_cluster = sector_num % extent->cluster_sectors; | ||||
| +        n = extent->cluster_sectors - index_in_cluster; | ||||
| +        ret = offset ? 1 : 0; | ||||
| +    } | ||||
|      if (n > nb_sectors) | ||||
|          n = nb_sectors; | ||||
|      *pnum = n; | ||||
| -    return (cluster_offset != 0); | ||||
| +    return ret; | ||||
|  } | ||||
|   | ||||
|  static int vmdk_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                      uint8_t *buf, int nb_sectors) | ||||
|  { | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
| -    int index_in_cluster, n, ret; | ||||
| +    int ret; | ||||
| +    uint64_t n, index_in_cluster; | ||||
| +    VmdkExtent *extent = NULL; | ||||
|      uint64_t cluster_offset; | ||||
|   | ||||
|      while (nb_sectors > 0) { | ||||
| -        cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0); | ||||
| -        index_in_cluster = sector_num % s->cluster_sectors; | ||||
| -        n = s->cluster_sectors - index_in_cluster; | ||||
| +        extent = find_extent(s, sector_num, extent); | ||||
| +        if (!extent) { | ||||
| +            return -EIO; | ||||
| +        } | ||||
| +        cluster_offset = get_cluster_offset( | ||||
| +                            bs, extent, NULL, sector_num << 9, 0); | ||||
| +        index_in_cluster = sector_num % extent->cluster_sectors; | ||||
| +        n = extent->cluster_sectors - index_in_cluster; | ||||
|          if (n > nb_sectors) | ||||
|              n = nb_sectors; | ||||
|          if (!cluster_offset) { | ||||
| @@ -621,10 +751,12 @@ static int vmdk_write(BlockDriverState * | ||||
|                       const uint8_t *buf, int nb_sectors) | ||||
|  { | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
| -    VmdkMetaData m_data; | ||||
| -    int index_in_cluster, n; | ||||
| +    VmdkExtent *extent = NULL; | ||||
| +    int n; | ||||
| +    int64_t index_in_cluster; | ||||
|      uint64_t cluster_offset; | ||||
|      static int cid_update = 0; | ||||
| +    VmdkMetaData m_data; | ||||
|   | ||||
|      if (sector_num > bs->total_sectors) { | ||||
|          fprintf(stderr, | ||||
| @@ -635,20 +767,35 @@ static int vmdk_write(BlockDriverState * | ||||
|      } | ||||
|   | ||||
|      while (nb_sectors > 0) { | ||||
| -        index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
| -        n = s->cluster_sectors - index_in_cluster; | ||||
| -        if (n > nb_sectors) | ||||
| -            n = nb_sectors; | ||||
| -        cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1); | ||||
| -        if (!cluster_offset) | ||||
| +        extent = find_extent(s, sector_num, extent); | ||||
| +        if (!extent) { | ||||
| +            return -EIO; | ||||
| +        } | ||||
| +        cluster_offset = get_cluster_offset( | ||||
| +                                bs, | ||||
| +                                extent, | ||||
| +                                &m_data, | ||||
| +                                sector_num << 9, 1); | ||||
| +        if (!cluster_offset) { | ||||
|              return -1; | ||||
| +        } | ||||
| +        index_in_cluster = sector_num % extent->cluster_sectors; | ||||
| +        n = extent->cluster_sectors - index_in_cluster; | ||||
| +        if (n > nb_sectors) { | ||||
| +            n = nb_sectors; | ||||
| +        } | ||||
|   | ||||
| -        if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||
| +        if (bdrv_pwrite(bs->file, | ||||
| +                        cluster_offset + index_in_cluster * 512, | ||||
| +                        buf, n * 512) | ||||
| +                != n * 512) { | ||||
|              return -1; | ||||
| +        } | ||||
|          if (m_data.valid) { | ||||
|              /* update L2 tables */ | ||||
| -            if (vmdk_L2update(bs, &m_data) == -1) | ||||
| +            if (vmdk_L2update(extent, &m_data) == -1) { | ||||
|                  return -1; | ||||
| +            } | ||||
|          } | ||||
|          nb_sectors -= n; | ||||
|          sector_num += n; | ||||
| @@ -822,10 +969,7 @@ exit: | ||||
|   | ||||
|  static void vmdk_close(BlockDriverState *bs) | ||||
|  { | ||||
| -    BDRVVmdkState *s = bs->opaque; | ||||
| - | ||||
| -    qemu_free(s->l1_table); | ||||
| -    qemu_free(s->l2_cache); | ||||
| +    vmdk_free_extents(bs); | ||||
|  } | ||||
|   | ||||
|  static int vmdk_flush(BlockDriverState *bs) | ||||
| @@ -1,46 +0,0 @@ | ||||
| From 66922efc83a670178b208dec2f5123ec85fb6ba3 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:29 +0800 | ||||
| Subject: [PATCH 03/12] VMDK: bugfix, align offset to cluster in | ||||
|  get_whole_cluster | ||||
|  | ||||
| In get_whole_cluster, the offset is not aligned to cluster when reading | ||||
| from backing_hd. When the first write to child is not at the cluster | ||||
| boundary, wrong address data from parent is copied to child. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 8 +++++--- | ||||
|  1 file changed, 5 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -514,21 +514,23 @@ static int get_whole_cluster(BlockDriver | ||||
|      /* 128 sectors * 512 bytes each = grain size 64KB */ | ||||
|      uint8_t  whole_grain[extent->cluster_sectors * 512]; | ||||
|   | ||||
| -    // we will be here if it's first write on non-exist grain(cluster). | ||||
| -    // try to read from parent image, if exist | ||||
| +    /* we will be here if it's first write on non-exist grain(cluster). | ||||
| +     * try to read from parent image, if exist */ | ||||
|      if (bs->backing_hd) { | ||||
|          int ret; | ||||
|   | ||||
|          if (!vmdk_is_cid_valid(bs)) | ||||
|              return -1; | ||||
|   | ||||
| +        /* floor offset to cluster */ | ||||
| +        offset -= offset % (extent->cluster_sectors * 512); | ||||
|          ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain, | ||||
|                  extent->cluster_sectors); | ||||
|          if (ret < 0) { | ||||
|              return -1; | ||||
|          } | ||||
|   | ||||
| -        //Write grain only into the active image | ||||
| +        /* Write grain only into the active image */ | ||||
|          ret = bdrv_write(extent->file, cluster_offset, whole_grain, | ||||
|                  extent->cluster_sectors); | ||||
|          if (ret < 0) { | ||||
| @@ -1,74 +0,0 @@ | ||||
| From 930e57ecb64bbab75c2b71d0d1ba07451fec4567 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:30 +0800 | ||||
| Subject: [PATCH 04/12] VMDK: probe for monolithicFlat images | ||||
|  | ||||
| Probe as the same behavior as VMware does. | ||||
| Recognize image as monolithicFlat descriptor file when the file is text | ||||
| and the first effective line (not '#' leaded comment or space line) is | ||||
| either 'version=1' or 'version=2'. No space or upper case charactors | ||||
| accepted. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- | ||||
|  1 file changed, 43 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -103,10 +103,51 @@ static int vmdk_probe(const uint8_t *buf | ||||
|          return 0; | ||||
|      magic = be32_to_cpu(*(uint32_t *)buf); | ||||
|      if (magic == VMDK3_MAGIC || | ||||
| -        magic == VMDK4_MAGIC) | ||||
| +        magic == VMDK4_MAGIC) { | ||||
|          return 100; | ||||
| -    else | ||||
| +    } else { | ||||
| +        const char *p = (const char *)buf; | ||||
| +        const char *end = p + buf_size; | ||||
| +        while (p < end) { | ||||
| +            if (*p == '#') { | ||||
| +                /* skip comment line */ | ||||
| +                while (p < end && *p != '\n') { | ||||
| +                    p++; | ||||
| +                } | ||||
| +                p++; | ||||
| +                continue; | ||||
| +            } | ||||
| +            if (*p == ' ') { | ||||
| +                while (p < end && *p == ' ') { | ||||
| +                    p++; | ||||
| +                } | ||||
| +                /* skip '\r' if windows line endings used. */ | ||||
| +                if (p < end && *p == '\r') { | ||||
| +                    p++; | ||||
| +                } | ||||
| +                /* only accept blank lines before 'version=' line */ | ||||
| +                if (p == end || *p != '\n') { | ||||
| +                    return 0; | ||||
| +                } | ||||
| +                p++; | ||||
| +                continue; | ||||
| +            } | ||||
| +            if (end - p >= strlen("version=X\n")) { | ||||
| +                if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || | ||||
| +                    strncmp("version=2\n", p, strlen("version=2\n")) == 0) { | ||||
| +                    return 100; | ||||
| +                } | ||||
| +            } | ||||
| +            if (end - p >= strlen("version=X\r\n")) { | ||||
| +                if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || | ||||
| +                    strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { | ||||
| +                    return 100; | ||||
| +                } | ||||
| +            } | ||||
| +            return 0; | ||||
| +        } | ||||
|          return 0; | ||||
| +    } | ||||
|  } | ||||
|   | ||||
|  #define CHECK_CID 1 | ||||
| @@ -1,241 +0,0 @@ | ||||
| From 97cf5df76657bab81d6b8669607f6f13215201c1 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:31 +0800 | ||||
| Subject: [PATCH 05/12] VMDK: separate vmdk_open by format version | ||||
|  | ||||
| Separate vmdk_open by subformats to: | ||||
| * vmdk_open_vmdk3 | ||||
| * vmdk_open_vmdk4 | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 178 +++++++++++++++++++++++++++++++++++++---------------------- | ||||
|  1 file changed, 112 insertions(+), 66 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -458,67 +458,20 @@ static VmdkExtent *vmdk_add_extent(Block | ||||
|      return extent; | ||||
|  } | ||||
|   | ||||
| - | ||||
| -static int vmdk_open(BlockDriverState *bs, int flags) | ||||
| +static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent) | ||||
|  { | ||||
| -    BDRVVmdkState *s = bs->opaque; | ||||
| -    uint32_t magic; | ||||
| -    int i; | ||||
| -    uint32_t l1_size, l1_entry_sectors; | ||||
| -    VmdkExtent *extent = NULL; | ||||
| - | ||||
| -    if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) | ||||
| -        goto fail; | ||||
| - | ||||
| -    magic = be32_to_cpu(magic); | ||||
| -    if (magic == VMDK3_MAGIC) { | ||||
| -        VMDK3Header header; | ||||
| -        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) | ||||
| -                != sizeof(header)) { | ||||
| -            goto fail; | ||||
| -        } | ||||
| -        extent = vmdk_add_extent(bs, bs->file, false, | ||||
| -                              le32_to_cpu(header.disk_sectors), | ||||
| -                              le32_to_cpu(header.l1dir_offset) << 9, 0, | ||||
| -                              1 << 6, 1 << 9, le32_to_cpu(header.granularity)); | ||||
| -    } else if (magic == VMDK4_MAGIC) { | ||||
| -        VMDK4Header header; | ||||
| -        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) | ||||
| -                != sizeof(header)) { | ||||
| -            goto fail; | ||||
| -        } | ||||
| -        l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) | ||||
| -                            * le64_to_cpu(header.granularity); | ||||
| -        l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) | ||||
| -                    / l1_entry_sectors; | ||||
| -        extent = vmdk_add_extent(bs, bs->file, false, | ||||
| -                              le64_to_cpu(header.capacity), | ||||
| -                              le64_to_cpu(header.gd_offset) << 9, | ||||
| -                              le64_to_cpu(header.rgd_offset) << 9, | ||||
| -                              l1_size, | ||||
| -                              le32_to_cpu(header.num_gtes_per_gte), | ||||
| -                              le64_to_cpu(header.granularity)); | ||||
| -        if (extent->l1_entry_sectors <= 0) { | ||||
| -            goto fail; | ||||
| -        } | ||||
| -        // try to open parent images, if exist | ||||
| -        if (vmdk_parent_open(bs) != 0) | ||||
| -            goto fail; | ||||
| -        // write the CID once after the image creation | ||||
| -        s->parent_cid = vmdk_read_cid(bs,1); | ||||
| -    } else { | ||||
| -        goto fail; | ||||
| -    } | ||||
| +    int ret; | ||||
| +    int l1_size, i; | ||||
|   | ||||
|      /* read the L1 table */ | ||||
|      l1_size = extent->l1_size * sizeof(uint32_t); | ||||
|      extent->l1_table = qemu_malloc(l1_size); | ||||
| -    if (bdrv_pread(bs->file, | ||||
| -            extent->l1_table_offset, | ||||
| -            extent->l1_table, | ||||
| -            l1_size) | ||||
| -        != l1_size) { | ||||
| -        goto fail; | ||||
| +    ret = bdrv_pread(extent->file, | ||||
| +                    extent->l1_table_offset, | ||||
| +                    extent->l1_table, | ||||
| +                    l1_size); | ||||
| +    if (ret < 0) { | ||||
| +        goto fail_l1; | ||||
|      } | ||||
|      for (i = 0; i < extent->l1_size; i++) { | ||||
|          le32_to_cpus(&extent->l1_table[i]); | ||||
| @@ -526,12 +479,12 @@ static int vmdk_open(BlockDriverState *b | ||||
|   | ||||
|      if (extent->l1_backup_table_offset) { | ||||
|          extent->l1_backup_table = qemu_malloc(l1_size); | ||||
| -        if (bdrv_pread(bs->file, | ||||
| -                    extent->l1_backup_table_offset, | ||||
| -                    extent->l1_backup_table, | ||||
| -                    l1_size) | ||||
| -                != l1_size) { | ||||
| -            goto fail; | ||||
| +        ret = bdrv_pread(extent->file, | ||||
| +                        extent->l1_backup_table_offset, | ||||
| +                        extent->l1_backup_table, | ||||
| +                        l1_size); | ||||
| +        if (ret < 0) { | ||||
| +            goto fail_l1b; | ||||
|          } | ||||
|          for (i = 0; i < extent->l1_size; i++) { | ||||
|              le32_to_cpus(&extent->l1_backup_table[i]); | ||||
| @@ -541,9 +494,102 @@ static int vmdk_open(BlockDriverState *b | ||||
|      extent->l2_cache = | ||||
|          qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); | ||||
|      return 0; | ||||
| + fail_l1b: | ||||
| +    qemu_free(extent->l1_backup_table); | ||||
| + fail_l1: | ||||
| +    qemu_free(extent->l1_table); | ||||
| +    return ret; | ||||
| +} | ||||
| + | ||||
| +static int vmdk_open_vmdk3(BlockDriverState *bs, int flags) | ||||
| +{ | ||||
| +    int ret; | ||||
| +    uint32_t magic; | ||||
| +    VMDK3Header header; | ||||
| +    VmdkExtent *extent; | ||||
| + | ||||
| +    ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); | ||||
| +    if (ret < 0) { | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    extent = vmdk_add_extent(bs, | ||||
| +                             bs->file, false, | ||||
| +                             le32_to_cpu(header.disk_sectors), | ||||
| +                             le32_to_cpu(header.l1dir_offset) << 9, | ||||
| +                             0, 1 << 6, 1 << 9, | ||||
| +                             le32_to_cpu(header.granularity)); | ||||
| +    ret = vmdk_init_tables(bs, extent); | ||||
| +    if (ret) { | ||||
| +        /* vmdk_init_tables cleans up on fail, so only free allocation of | ||||
| +         * vmdk_add_extent here. */ | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    return 0; | ||||
|   fail: | ||||
|      vmdk_free_extents(bs); | ||||
| -    return -1; | ||||
| +    return ret; | ||||
| +} | ||||
| + | ||||
| +static int vmdk_open_vmdk4(BlockDriverState *bs, int flags) | ||||
| +{ | ||||
| +    int ret; | ||||
| +    uint32_t magic; | ||||
| +    uint32_t l1_size, l1_entry_sectors; | ||||
| +    VMDK4Header header; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
| +    VmdkExtent *extent; | ||||
| + | ||||
| +    ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); | ||||
| +    if (ret < 0) { | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) | ||||
| +                        * le64_to_cpu(header.granularity); | ||||
| +    l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) | ||||
| +                / l1_entry_sectors; | ||||
| +    extent = vmdk_add_extent(bs, bs->file, false, | ||||
| +                          le64_to_cpu(header.capacity), | ||||
| +                          le64_to_cpu(header.gd_offset) << 9, | ||||
| +                          le64_to_cpu(header.rgd_offset) << 9, | ||||
| +                          l1_size, | ||||
| +                          le32_to_cpu(header.num_gtes_per_gte), | ||||
| +                          le64_to_cpu(header.granularity)); | ||||
| +    if (extent->l1_entry_sectors <= 0) { | ||||
| +        ret = -EINVAL; | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    /* try to open parent images, if exist */ | ||||
| +    ret = vmdk_parent_open(bs); | ||||
| +    if (ret) { | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    s->parent_cid = vmdk_read_cid(bs, 1); | ||||
| +    ret = vmdk_init_tables(bs, extent); | ||||
| +    if (ret) { | ||||
| +        goto fail; | ||||
| +    } | ||||
| +    return 0; | ||||
| + fail: | ||||
| +    vmdk_free_extents(bs); | ||||
| +    return ret; | ||||
| +} | ||||
| + | ||||
| +static int vmdk_open(BlockDriverState *bs, int flags) | ||||
| +{ | ||||
| +    uint32_t magic; | ||||
| + | ||||
| +    if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) { | ||||
| +        return -EIO; | ||||
| +    } | ||||
| + | ||||
| +    magic = be32_to_cpu(magic); | ||||
| +    if (magic == VMDK3_MAGIC) { | ||||
| +        return vmdk_open_vmdk3(bs, flags); | ||||
| +    } else if (magic == VMDK4_MAGIC) { | ||||
| +        return vmdk_open_vmdk4(bs, flags); | ||||
| +    } else { | ||||
| +        return -EINVAL; | ||||
| +    } | ||||
|  } | ||||
|   | ||||
|  static int get_whole_cluster(BlockDriverState *bs, | ||||
| @@ -630,11 +676,11 @@ static uint64_t get_cluster_offset(Block | ||||
|      if (!l2_offset) { | ||||
|          return 0; | ||||
|      } | ||||
| -    for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||
| +    for (i = 0; i < L2_CACHE_SIZE; i++) { | ||||
|          if (l2_offset == extent->l2_cache_offsets[i]) { | ||||
|              /* increment the hit count */ | ||||
|              if (++extent->l2_cache_counts[i] == 0xffffffff) { | ||||
| -                for(j = 0; j < L2_CACHE_SIZE; j++) { | ||||
| +                for (j = 0; j < L2_CACHE_SIZE; j++) { | ||||
|                      extent->l2_cache_counts[j] >>= 1; | ||||
|                  } | ||||
|              } | ||||
| @@ -645,7 +691,7 @@ static uint64_t get_cluster_offset(Block | ||||
|      /* not found: load a new entry in the least used one */ | ||||
|      min_index = 0; | ||||
|      min_count = 0xffffffff; | ||||
| -    for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||
| +    for (i = 0; i < L2_CACHE_SIZE; i++) { | ||||
|          if (extent->l2_cache_counts[i] < min_count) { | ||||
|              min_count = extent->l2_cache_counts[i]; | ||||
|              min_index = i; | ||||
| @@ -1,102 +0,0 @@ | ||||
| From 1c1781fa1c45a7c012f7b2c4be1be372f19e3cc6 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:32 +0800 | ||||
| Subject: [PATCH 06/12] VMDK: add field BDRVVmdkState.desc_offset | ||||
|  | ||||
| There are several occurrence of magic number 0x200 as the descriptor | ||||
| offset within mono sparse image file. This is not the case for images | ||||
| with separate descriptor file. So a field is added to BDRVVmdkState to | ||||
| hold the correct value. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 27 ++++++++++++++++++--------- | ||||
|  1 file changed, 18 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -81,6 +81,7 @@ typedef struct VmdkExtent { | ||||
|  } VmdkExtent; | ||||
|   | ||||
|  typedef struct BDRVVmdkState { | ||||
| +    int desc_offset; | ||||
|      uint32_t parent_cid; | ||||
|      int num_extents; | ||||
|      /* Extent array with num_extents entries, ascend ordered by address */ | ||||
| @@ -175,10 +176,11 @@ static uint32_t vmdk_read_cid(BlockDrive | ||||
|      uint32_t cid; | ||||
|      const char *p_name, *cid_str; | ||||
|      size_t cid_str_size; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
|   | ||||
| -    /* the descriptor offset = 0x200 */ | ||||
| -    if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
| +    if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { | ||||
|          return 0; | ||||
| +    } | ||||
|   | ||||
|      if (parent) { | ||||
|          cid_str = "parentCID"; | ||||
| @@ -200,10 +202,12 @@ static int vmdk_write_cid(BlockDriverSta | ||||
|  { | ||||
|      char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; | ||||
|      char *p_name, *tmp_str; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
|   | ||||
| -    /* the descriptor offset = 0x200 */ | ||||
| -    if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
| -        return -1; | ||||
| +    memset(desc, 0, sizeof(desc)); | ||||
| +    if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { | ||||
| +        return -EIO; | ||||
| +    } | ||||
|   | ||||
|      tmp_str = strstr(desc,"parentCID"); | ||||
|      pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); | ||||
| @@ -213,8 +217,9 @@ static int vmdk_write_cid(BlockDriverSta | ||||
|          pstrcat(desc, sizeof(desc), tmp_desc); | ||||
|      } | ||||
|   | ||||
| -    if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0) | ||||
| -        return -1; | ||||
| +    if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) { | ||||
| +        return -EIO; | ||||
| +    } | ||||
|      return 0; | ||||
|  } | ||||
|   | ||||
| @@ -402,10 +407,11 @@ static int vmdk_parent_open(BlockDriverS | ||||
|  { | ||||
|      char *p_name; | ||||
|      char desc[DESC_SIZE]; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
|   | ||||
| -    /* the descriptor offset = 0x200 */ | ||||
| -    if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
| +    if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { | ||||
|          return -1; | ||||
| +    } | ||||
|   | ||||
|      if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { | ||||
|          char *end_name; | ||||
| @@ -506,8 +512,10 @@ static int vmdk_open_vmdk3(BlockDriverSt | ||||
|      int ret; | ||||
|      uint32_t magic; | ||||
|      VMDK3Header header; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
|      VmdkExtent *extent; | ||||
|   | ||||
| +    s->desc_offset = 0x200; | ||||
|      ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); | ||||
|      if (ret < 0) { | ||||
|          goto fail; | ||||
| @@ -539,6 +547,7 @@ static int vmdk_open_vmdk4(BlockDriverSt | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
|      VmdkExtent *extent; | ||||
|   | ||||
| +    s->desc_offset = 0x200; | ||||
|      ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); | ||||
|      if (ret < 0) { | ||||
|          goto fail; | ||||
| @@ -1,35 +0,0 @@ | ||||
| From 49885608e66c1e76a2b715fb36fd2f27f73e5202 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:33 +0800 | ||||
| Subject: [PATCH 07/12] VMDK: flush multiple extents | ||||
|  | ||||
| Flush all the file that referenced by the image. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 12 +++++++++++- | ||||
|  1 file changed, 11 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -1072,7 +1072,17 @@ static void vmdk_close(BlockDriverState | ||||
|   | ||||
|  static int vmdk_flush(BlockDriverState *bs) | ||||
|  { | ||||
| -    return bdrv_flush(bs->file); | ||||
| +    int i, ret, err; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
| + | ||||
| +    ret = bdrv_flush(bs->file); | ||||
| +    for (i = 0; i < s->num_extents; i++) { | ||||
| +        err = bdrv_flush(s->extents[i].file); | ||||
| +        if (err < 0) { | ||||
| +            ret = err; | ||||
| +        } | ||||
| +    } | ||||
| +    return ret; | ||||
|  } | ||||
|   | ||||
|   | ||||
| @@ -1,46 +0,0 @@ | ||||
| From 9d8117f00364c12cbd658de903b1ed26a30584e0 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:34 +0800 | ||||
| Subject: [PATCH 08/12] VMDK: move 'static' cid_update flag to bs field | ||||
|  | ||||
| Cid_update is the flag for updating CID on first write after opening the | ||||
| image. This should be per image open rather than per program life cycle, | ||||
| so change it from static var of vmdk_write to a field in BDRVVmdkState. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 6 +++--- | ||||
|  1 file changed, 3 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -82,6 +82,7 @@ typedef struct VmdkExtent { | ||||
|   | ||||
|  typedef struct BDRVVmdkState { | ||||
|      int desc_offset; | ||||
| +    bool cid_updated; | ||||
|      uint32_t parent_cid; | ||||
|      int num_extents; | ||||
|      /* Extent array with num_extents entries, ascend ordered by address */ | ||||
| @@ -853,7 +854,6 @@ static int vmdk_write(BlockDriverState * | ||||
|      int n; | ||||
|      int64_t index_in_cluster; | ||||
|      uint64_t cluster_offset; | ||||
| -    static int cid_update = 0; | ||||
|      VmdkMetaData m_data; | ||||
|   | ||||
|      if (sector_num > bs->total_sectors) { | ||||
| @@ -900,9 +900,9 @@ static int vmdk_write(BlockDriverState * | ||||
|          buf += n * 512; | ||||
|   | ||||
|          // update CID on the first write every time the virtual disk is opened | ||||
| -        if (!cid_update) { | ||||
| +        if (!s->cid_updated) { | ||||
|              vmdk_write_cid(bs, time(NULL)); | ||||
| -            cid_update++; | ||||
| +            s->cid_updated = true; | ||||
|          } | ||||
|      } | ||||
|      return 0; | ||||
| @@ -1,198 +0,0 @@ | ||||
| From 9e1ddc6967e8739f4fa47fa4f6a767ebe319f6ff Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 12 Jul 2011 19:56:35 +0800 | ||||
| Subject: [PATCH 09/12] VMDK: change get_cluster_offset return type | ||||
|  | ||||
| The return type of get_cluster_offset was an offset that use 0 to denote | ||||
| 'not allocated', this will be no longer true for flat extents, as we see | ||||
| flat extent file as a single huge cluster whose offset is 0 and length | ||||
| is the whole file length. | ||||
| So now we use int return value, 0 means success and otherwise offset | ||||
| invalid. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 79 ++++++++++++++++++++++++++++++++---------------------------- | ||||
|  1 file changed, 42 insertions(+), 37 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -665,26 +665,31 @@ static int vmdk_L2update(VmdkExtent *ext | ||||
|      return 0; | ||||
|  } | ||||
|   | ||||
| -static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
| +static int get_cluster_offset(BlockDriverState *bs, | ||||
|                                      VmdkExtent *extent, | ||||
|                                      VmdkMetaData *m_data, | ||||
| -                                    uint64_t offset, int allocate) | ||||
| +                                    uint64_t offset, | ||||
| +                                    int allocate, | ||||
| +                                    uint64_t *cluster_offset) | ||||
|  { | ||||
|      unsigned int l1_index, l2_offset, l2_index; | ||||
|      int min_index, i, j; | ||||
|      uint32_t min_count, *l2_table, tmp = 0; | ||||
| -    uint64_t cluster_offset; | ||||
|   | ||||
|      if (m_data) | ||||
|          m_data->valid = 0; | ||||
| +    if (extent->flat) { | ||||
| +        *cluster_offset = 0; | ||||
| +        return 0; | ||||
| +    } | ||||
|   | ||||
|      l1_index = (offset >> 9) / extent->l1_entry_sectors; | ||||
|      if (l1_index >= extent->l1_size) { | ||||
| -        return 0; | ||||
| +        return -1; | ||||
|      } | ||||
|      l2_offset = extent->l1_table[l1_index]; | ||||
|      if (!l2_offset) { | ||||
| -        return 0; | ||||
| +        return -1; | ||||
|      } | ||||
|      for (i = 0; i < L2_CACHE_SIZE; i++) { | ||||
|          if (l2_offset == extent->l2_cache_offsets[i]) { | ||||
| @@ -714,28 +719,29 @@ static uint64_t get_cluster_offset(Block | ||||
|                  l2_table, | ||||
|                  extent->l2_size * sizeof(uint32_t) | ||||
|              ) != extent->l2_size * sizeof(uint32_t)) { | ||||
| -        return 0; | ||||
| +        return -1; | ||||
|      } | ||||
|   | ||||
|      extent->l2_cache_offsets[min_index] = l2_offset; | ||||
|      extent->l2_cache_counts[min_index] = 1; | ||||
|   found: | ||||
|      l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; | ||||
| -    cluster_offset = le32_to_cpu(l2_table[l2_index]); | ||||
| +    *cluster_offset = le32_to_cpu(l2_table[l2_index]); | ||||
|   | ||||
| -    if (!cluster_offset) { | ||||
| -        if (!allocate) | ||||
| -            return 0; | ||||
| +    if (!*cluster_offset) { | ||||
| +        if (!allocate) { | ||||
| +            return -1; | ||||
| +        } | ||||
|   | ||||
|          // Avoid the L2 tables update for the images that have snapshots. | ||||
| -        cluster_offset = bdrv_getlength(extent->file); | ||||
| +        *cluster_offset = bdrv_getlength(extent->file); | ||||
|          bdrv_truncate( | ||||
|              extent->file, | ||||
| -            cluster_offset + (extent->cluster_sectors << 9) | ||||
| +            *cluster_offset + (extent->cluster_sectors << 9) | ||||
|          ); | ||||
|   | ||||
| -        cluster_offset >>= 9; | ||||
| -        tmp = cpu_to_le32(cluster_offset); | ||||
| +        *cluster_offset >>= 9; | ||||
| +        tmp = cpu_to_le32(*cluster_offset); | ||||
|          l2_table[l2_index] = tmp; | ||||
|   | ||||
|          /* First of all we write grain itself, to avoid race condition | ||||
| @@ -744,8 +750,8 @@ static uint64_t get_cluster_offset(Block | ||||
|           * or inappropriate VM shutdown. | ||||
|           */ | ||||
|          if (get_whole_cluster( | ||||
| -                bs, extent, cluster_offset, offset, allocate) == -1) | ||||
| -            return 0; | ||||
| +                bs, extent, *cluster_offset, offset, allocate) == -1) | ||||
| +            return -1; | ||||
|   | ||||
|          if (m_data) { | ||||
|              m_data->offset = tmp; | ||||
| @@ -755,8 +761,8 @@ static uint64_t get_cluster_offset(Block | ||||
|              m_data->valid = 1; | ||||
|          } | ||||
|      } | ||||
| -    cluster_offset <<= 9; | ||||
| -    return cluster_offset; | ||||
| +    *cluster_offset <<= 9; | ||||
| +    return 0; | ||||
|  } | ||||
|   | ||||
|  static VmdkExtent *find_extent(BDRVVmdkState *s, | ||||
| @@ -780,7 +786,6 @@ static int vmdk_is_allocated(BlockDriver | ||||
|                               int nb_sectors, int *pnum) | ||||
|  { | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
| - | ||||
|      int64_t index_in_cluster, n, ret; | ||||
|      uint64_t offset; | ||||
|      VmdkExtent *extent; | ||||
| @@ -789,15 +794,13 @@ static int vmdk_is_allocated(BlockDriver | ||||
|      if (!extent) { | ||||
|          return 0; | ||||
|      } | ||||
| -    if (extent->flat) { | ||||
| -        n = extent->end_sector - sector_num; | ||||
| -        ret = 1; | ||||
| -    } else { | ||||
| -        offset = get_cluster_offset(bs, extent, NULL, sector_num * 512, 0); | ||||
| -        index_in_cluster = sector_num % extent->cluster_sectors; | ||||
| -        n = extent->cluster_sectors - index_in_cluster; | ||||
| -        ret = offset ? 1 : 0; | ||||
| -    } | ||||
| +    ret = get_cluster_offset(bs, extent, NULL, | ||||
| +                            sector_num * 512, 0, &offset); | ||||
| +    /* get_cluster_offset returning 0 means success */ | ||||
| +    ret = !ret; | ||||
| + | ||||
| +    index_in_cluster = sector_num % extent->cluster_sectors; | ||||
| +    n = extent->cluster_sectors - index_in_cluster; | ||||
|      if (n > nb_sectors) | ||||
|          n = nb_sectors; | ||||
|      *pnum = n; | ||||
| @@ -818,14 +821,15 @@ static int vmdk_read(BlockDriverState *b | ||||
|          if (!extent) { | ||||
|              return -EIO; | ||||
|          } | ||||
| -        cluster_offset = get_cluster_offset( | ||||
| -                            bs, extent, NULL, sector_num << 9, 0); | ||||
| +        ret = get_cluster_offset( | ||||
| +                            bs, extent, NULL, | ||||
| +                            sector_num << 9, 0, &cluster_offset); | ||||
|          index_in_cluster = sector_num % extent->cluster_sectors; | ||||
|          n = extent->cluster_sectors - index_in_cluster; | ||||
|          if (n > nb_sectors) | ||||
|              n = nb_sectors; | ||||
| -        if (!cluster_offset) { | ||||
| -            // try to read from parent image, if exist | ||||
| +        if (ret) { | ||||
| +            /* if not allocated, try to read from parent image, if exist */ | ||||
|              if (bs->backing_hd) { | ||||
|                  if (!vmdk_is_cid_valid(bs)) | ||||
|                      return -1; | ||||
| @@ -851,7 +855,7 @@ static int vmdk_write(BlockDriverState * | ||||
|  { | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
|      VmdkExtent *extent = NULL; | ||||
| -    int n; | ||||
| +    int n, ret; | ||||
|      int64_t index_in_cluster; | ||||
|      uint64_t cluster_offset; | ||||
|      VmdkMetaData m_data; | ||||
| @@ -869,13 +873,14 @@ static int vmdk_write(BlockDriverState * | ||||
|          if (!extent) { | ||||
|              return -EIO; | ||||
|          } | ||||
| -        cluster_offset = get_cluster_offset( | ||||
| +        ret = get_cluster_offset( | ||||
|                                  bs, | ||||
|                                  extent, | ||||
|                                  &m_data, | ||||
| -                                sector_num << 9, 1); | ||||
| -        if (!cluster_offset) { | ||||
| -            return -1; | ||||
| +                                sector_num << 9, 1, | ||||
| +                                &cluster_offset); | ||||
| +        if (ret) { | ||||
| +            return -EINVAL; | ||||
|          } | ||||
|          index_in_cluster = sector_num % extent->cluster_sectors; | ||||
|          n = extent->cluster_sectors - index_in_cluster; | ||||
| @@ -1,257 +0,0 @@ | ||||
| From e6b783a12f7ff491a1a2147d9fe55b4535aa046e Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 19 Jul 2011 08:38:22 +0800 | ||||
| Subject: [PATCH 10/12] VMDK: open/read/write for monolithicFlat image | ||||
|  | ||||
| Parse vmdk decriptor file and open mono flat image. | ||||
| Read/write the flat extent. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- | ||||
|  1 file changed, 158 insertions(+), 13 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -65,6 +65,7 @@ typedef struct VmdkExtent { | ||||
|      bool flat; | ||||
|      int64_t sectors; | ||||
|      int64_t end_sector; | ||||
| +    int64_t flat_start_offset; | ||||
|      int64_t l1_table_offset; | ||||
|      int64_t l1_backup_table_offset; | ||||
|      uint32_t *l1_table; | ||||
| @@ -407,9 +408,10 @@ fail: | ||||
|  static int vmdk_parent_open(BlockDriverState *bs) | ||||
|  { | ||||
|      char *p_name; | ||||
| -    char desc[DESC_SIZE]; | ||||
| +    char desc[DESC_SIZE + 1]; | ||||
|      BDRVVmdkState *s = bs->opaque; | ||||
|   | ||||
| +    desc[DESC_SIZE] = '\0'; | ||||
|      if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { | ||||
|          return -1; | ||||
|      } | ||||
| @@ -584,6 +586,144 @@ static int vmdk_open_vmdk4(BlockDriverSt | ||||
|      return ret; | ||||
|  } | ||||
|   | ||||
| +/* find an option value out of descriptor file */ | ||||
| +static int vmdk_parse_description(const char *desc, const char *opt_name, | ||||
| +        char *buf, int buf_size) | ||||
| +{ | ||||
| +    char *opt_pos, *opt_end; | ||||
| +    const char *end = desc + strlen(desc); | ||||
| + | ||||
| +    opt_pos = strstr(desc, opt_name); | ||||
| +    if (!opt_pos) { | ||||
| +        return -1; | ||||
| +    } | ||||
| +    /* Skip "=\"" following opt_name */ | ||||
| +    opt_pos += strlen(opt_name) + 2; | ||||
| +    if (opt_pos >= end) { | ||||
| +        return -1; | ||||
| +    } | ||||
| +    opt_end = opt_pos; | ||||
| +    while (opt_end < end && *opt_end != '"') { | ||||
| +        opt_end++; | ||||
| +    } | ||||
| +    if (opt_end == end || buf_size < opt_end - opt_pos + 1) { | ||||
| +        return -1; | ||||
| +    } | ||||
| +    pstrcpy(buf, opt_end - opt_pos + 1, opt_pos); | ||||
| +    return 0; | ||||
| +} | ||||
| + | ||||
| +static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, | ||||
| +        const char *desc_file_path) | ||||
| +{ | ||||
| +    int ret; | ||||
| +    char access[11]; | ||||
| +    char type[11]; | ||||
| +    char fname[512]; | ||||
| +    const char *p = desc; | ||||
| +    int64_t sectors = 0; | ||||
| +    int64_t flat_offset; | ||||
| + | ||||
| +    while (*p) { | ||||
| +        /* parse extent line: | ||||
| +         * RW [size in sectors] FLAT "file-name.vmdk" OFFSET | ||||
| +         * or | ||||
| +         * RW [size in sectors] SPARSE "file-name.vmdk" | ||||
| +         */ | ||||
| +        flat_offset = -1; | ||||
| +        ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, | ||||
| +                access, §ors, type, fname, &flat_offset); | ||||
| +        if (ret < 4 || strcmp(access, "RW")) { | ||||
| +            goto next_line; | ||||
| +        } else if (!strcmp(type, "FLAT")) { | ||||
| +            if (ret != 5 || flat_offset < 0) { | ||||
| +                return -EINVAL; | ||||
| +            } | ||||
| +        } else if (ret != 4) { | ||||
| +            return -EINVAL; | ||||
| +        } | ||||
| + | ||||
| +        /* trim the quotation marks around */ | ||||
| +        if (fname[0] == '"') { | ||||
| +            memmove(fname, fname + 1, strlen(fname)); | ||||
| +            if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { | ||||
| +                return -EINVAL; | ||||
| +            } | ||||
| +            fname[strlen(fname) - 1] = '\0'; | ||||
| +        } | ||||
| +        if (sectors <= 0 || | ||||
| +            (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || | ||||
| +            (strcmp(access, "RW"))) { | ||||
| +            goto next_line; | ||||
| +        } | ||||
| + | ||||
| +        /* save to extents array */ | ||||
| +        if (!strcmp(type, "FLAT")) { | ||||
| +            /* FLAT extent */ | ||||
| +            char extent_path[PATH_MAX]; | ||||
| +            BlockDriverState *extent_file; | ||||
| +            VmdkExtent *extent; | ||||
| + | ||||
| +            path_combine(extent_path, sizeof(extent_path), | ||||
| +                    desc_file_path, fname); | ||||
| +            ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags); | ||||
| +            if (ret) { | ||||
| +                return ret; | ||||
| +            } | ||||
| +            extent = vmdk_add_extent(bs, extent_file, true, sectors, | ||||
| +                            0, 0, 0, 0, sectors); | ||||
| +            extent->flat_start_offset = flat_offset; | ||||
| +        } else { | ||||
| +            /* SPARSE extent, not supported for now */ | ||||
| +            fprintf(stderr, | ||||
| +                "VMDK: Not supported extent type \"%s\""".\n", type); | ||||
| +            return -ENOTSUP; | ||||
| +        } | ||||
| +next_line: | ||||
| +        /* move to next line */ | ||||
| +        while (*p && *p != '\n') { | ||||
| +            p++; | ||||
| +        } | ||||
| +        p++; | ||||
| +    } | ||||
| +    return 0; | ||||
| +} | ||||
| + | ||||
| +static int vmdk_open_desc_file(BlockDriverState *bs, int flags) | ||||
| +{ | ||||
| +    int ret; | ||||
| +    char buf[2048]; | ||||
| +    char ct[128]; | ||||
| +    BDRVVmdkState *s = bs->opaque; | ||||
| + | ||||
| +    ret = bdrv_pread(bs->file, 0, buf, sizeof(buf)); | ||||
| +    if (ret < 0) { | ||||
| +        return ret; | ||||
| +    } | ||||
| +    buf[2047] = '\0'; | ||||
| +    if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { | ||||
| +        return -EINVAL; | ||||
| +    } | ||||
| +    if (strcmp(ct, "monolithicFlat")) { | ||||
| +        fprintf(stderr, | ||||
| +                "VMDK: Not supported image type \"%s\""".\n", ct); | ||||
| +        return -ENOTSUP; | ||||
| +    } | ||||
| +    s->desc_offset = 0; | ||||
| +    ret = vmdk_parse_extents(buf, bs, bs->file->filename); | ||||
| +    if (ret) { | ||||
| +        return ret; | ||||
| +    } | ||||
| + | ||||
| +    /* try to open parent images, if exist */ | ||||
| +    if (vmdk_parent_open(bs)) { | ||||
| +        qemu_free(s->extents); | ||||
| +        return -EINVAL; | ||||
| +    } | ||||
| +    s->parent_cid = vmdk_read_cid(bs, 1); | ||||
| +    return 0; | ||||
| +} | ||||
| + | ||||
|  static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|  { | ||||
|      uint32_t magic; | ||||
| @@ -598,7 +738,7 @@ static int vmdk_open(BlockDriverState *b | ||||
|      } else if (magic == VMDK4_MAGIC) { | ||||
|          return vmdk_open_vmdk4(bs, flags); | ||||
|      } else { | ||||
| -        return -EINVAL; | ||||
| +        return vmdk_open_desc_file(bs, flags); | ||||
|      } | ||||
|  } | ||||
|   | ||||
| @@ -679,7 +819,7 @@ static int get_cluster_offset(BlockDrive | ||||
|      if (m_data) | ||||
|          m_data->valid = 0; | ||||
|      if (extent->flat) { | ||||
| -        *cluster_offset = 0; | ||||
| +        *cluster_offset = extent->flat_start_offset; | ||||
|          return 0; | ||||
|      } | ||||
|   | ||||
| @@ -832,16 +972,20 @@ static int vmdk_read(BlockDriverState *b | ||||
|              /* if not allocated, try to read from parent image, if exist */ | ||||
|              if (bs->backing_hd) { | ||||
|                  if (!vmdk_is_cid_valid(bs)) | ||||
| -                    return -1; | ||||
| +                    return -EINVAL; | ||||
|                  ret = bdrv_read(bs->backing_hd, sector_num, buf, n); | ||||
|                  if (ret < 0) | ||||
| -                    return -1; | ||||
| +                    return ret; | ||||
|              } else { | ||||
|                  memset(buf, 0, 512 * n); | ||||
|              } | ||||
|          } else { | ||||
| -            if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||
| -                return -1; | ||||
| +            ret = bdrv_pread(extent->file, | ||||
| +                            cluster_offset + index_in_cluster * 512, | ||||
| +                            buf, n * 512); | ||||
| +            if (ret < 0) { | ||||
| +                return ret; | ||||
| +            } | ||||
|          } | ||||
|          nb_sectors -= n; | ||||
|          sector_num += n; | ||||
| @@ -865,7 +1009,7 @@ static int vmdk_write(BlockDriverState * | ||||
|                  "(VMDK) Wrong offset: sector_num=0x%" PRIx64 | ||||
|                  " total_sectors=0x%" PRIx64 "\n", | ||||
|                  sector_num, bs->total_sectors); | ||||
| -        return -1; | ||||
| +        return -EIO; | ||||
|      } | ||||
|   | ||||
|      while (nb_sectors > 0) { | ||||
| @@ -888,16 +1032,17 @@ static int vmdk_write(BlockDriverState * | ||||
|              n = nb_sectors; | ||||
|          } | ||||
|   | ||||
| -        if (bdrv_pwrite(bs->file, | ||||
| +        ret = bdrv_pwrite(extent->file, | ||||
|                          cluster_offset + index_in_cluster * 512, | ||||
| -                        buf, n * 512) | ||||
| -                != n * 512) { | ||||
| -            return -1; | ||||
| +                        buf, | ||||
| +                        n * 512); | ||||
| +        if (ret < 0) { | ||||
| +            return ret; | ||||
|          } | ||||
|          if (m_data.valid) { | ||||
|              /* update L2 tables */ | ||||
|              if (vmdk_L2update(extent, &m_data) == -1) { | ||||
| -                return -1; | ||||
| +                return -EIO; | ||||
|              } | ||||
|          } | ||||
|          nb_sectors -= n; | ||||
| @@ -1,595 +0,0 @@ | ||||
| From 0d0f2ba577bd05491b5954751787f8b969ca1ec3 Mon Sep 17 00:00:00 2001 | ||||
| From: Fam Zheng <famcool@gmail.com> | ||||
| Date: Tue, 19 Jul 2011 08:45:23 +0800 | ||||
| Subject: [PATCH 11/12] VMDK: create different subformats | ||||
|  | ||||
| Add create option 'format', with enums: | ||||
|     monolithicSparse | ||||
|     monolithicFlat | ||||
|     twoGbMaxExtentSparse | ||||
|     twoGbMaxExtentFlat | ||||
| Each creates a subformat image file. The default is monolithicSparse. | ||||
|  | ||||
| Signed-off-by: Fam Zheng <famcool@gmail.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> | ||||
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 503 ++++++++++++++++++++++++++++++++--------------------------- | ||||
|  block_int.h  |   1 + | ||||
|  2 files changed, 275 insertions(+), 229 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -156,8 +156,9 @@ static int vmdk_probe(const uint8_t *buf | ||||
|  #define CHECK_CID 1 | ||||
|   | ||||
|  #define SECTOR_SIZE 512 | ||||
| -#define DESC_SIZE 20*SECTOR_SIZE	// 20 sectors of 512 bytes each | ||||
| -#define HEADER_SIZE 512   			// first sector of 512 bytes | ||||
| +#define DESC_SIZE (20 * SECTOR_SIZE)    /* 20 sectors of 512 bytes each */ | ||||
| +#define BUF_SIZE 4096 | ||||
| +#define HEADER_SIZE 512                 /* first sector of 512 bytes */ | ||||
|   | ||||
|  static void vmdk_free_extents(BlockDriverState *bs) | ||||
|  { | ||||
| @@ -243,168 +244,6 @@ static int vmdk_is_cid_valid(BlockDriver | ||||
|      return 1; | ||||
|  } | ||||
|   | ||||
| -static int vmdk_snapshot_create(const char *filename, const char *backing_file) | ||||
| -{ | ||||
| -    int snp_fd, p_fd; | ||||
| -    int ret; | ||||
| -    uint32_t p_cid; | ||||
| -    char *p_name, *gd_buf, *rgd_buf; | ||||
| -    const char *real_filename, *temp_str; | ||||
| -    VMDK4Header header; | ||||
| -    uint32_t gde_entries, gd_size; | ||||
| -    int64_t gd_offset, rgd_offset, capacity, gt_size; | ||||
| -    char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE]; | ||||
| -    static const char desc_template[] = | ||||
| -    "# Disk DescriptorFile\n" | ||||
| -    "version=1\n" | ||||
| -    "CID=%x\n" | ||||
| -    "parentCID=%x\n" | ||||
| -    "createType=\"monolithicSparse\"\n" | ||||
| -    "parentFileNameHint=\"%s\"\n" | ||||
| -    "\n" | ||||
| -    "# Extent description\n" | ||||
| -    "RW %u SPARSE \"%s\"\n" | ||||
| -    "\n" | ||||
| -    "# The Disk Data Base \n" | ||||
| -    "#DDB\n" | ||||
| -    "\n"; | ||||
| - | ||||
| -    snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); | ||||
| -    if (snp_fd < 0) | ||||
| -        return -errno; | ||||
| -    p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||
| -    if (p_fd < 0) { | ||||
| -        close(snp_fd); | ||||
| -        return -errno; | ||||
| -    } | ||||
| - | ||||
| -    /* read the header */ | ||||
| -    if (lseek(p_fd, 0x0, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| -    if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| - | ||||
| -    /* write the header */ | ||||
| -    if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| -    if (write(snp_fd, hdr, HEADER_SIZE) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| - | ||||
| -    memset(&header, 0, sizeof(header)); | ||||
| -    memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC | ||||
| - | ||||
| -    if (ftruncate(snp_fd, header.grain_offset << 9)) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| -    /* the descriptor offset = 0x200 */ | ||||
| -    if (lseek(p_fd, 0x200, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| -    if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| - | ||||
| -    if ((p_name = strstr(p_desc,"CID")) != NULL) { | ||||
| -        p_name += sizeof("CID"); | ||||
| -        sscanf(p_name,"%x",&p_cid); | ||||
| -    } | ||||
| - | ||||
| -    real_filename = filename; | ||||
| -    if ((temp_str = strrchr(real_filename, '\\')) != NULL) | ||||
| -        real_filename = temp_str + 1; | ||||
| -    if ((temp_str = strrchr(real_filename, '/')) != NULL) | ||||
| -        real_filename = temp_str + 1; | ||||
| -    if ((temp_str = strrchr(real_filename, ':')) != NULL) | ||||
| -        real_filename = temp_str + 1; | ||||
| - | ||||
| -    snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file, | ||||
| -             (uint32_t)header.capacity, real_filename); | ||||
| - | ||||
| -    /* write the descriptor */ | ||||
| -    if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| -    if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail; | ||||
| -    } | ||||
| - | ||||
| -    gd_offset = header.gd_offset * SECTOR_SIZE;     // offset of GD table | ||||
| -    rgd_offset = header.rgd_offset * SECTOR_SIZE;   // offset of RGD table | ||||
| -    capacity = header.capacity * SECTOR_SIZE;       // Extent size | ||||
| -    /* | ||||
| -     * Each GDE span 32M disk, means: | ||||
| -     * 512 GTE per GT, each GTE points to grain | ||||
| -     */ | ||||
| -    gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; | ||||
| -    if (!gt_size) { | ||||
| -        ret = -EINVAL; | ||||
| -        goto fail; | ||||
| -    } | ||||
| -    gde_entries = (uint32_t)(capacity / gt_size);  // number of gde/rgde | ||||
| -    gd_size = gde_entries * sizeof(uint32_t); | ||||
| - | ||||
| -    /* write RGD */ | ||||
| -    rgd_buf = qemu_malloc(gd_size); | ||||
| -    if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_rgd; | ||||
| -    } | ||||
| -    if (read(p_fd, rgd_buf, gd_size) != gd_size) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_rgd; | ||||
| -    } | ||||
| -    if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_rgd; | ||||
| -    } | ||||
| -    if (write(snp_fd, rgd_buf, gd_size) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_rgd; | ||||
| -    } | ||||
| - | ||||
| -    /* write GD */ | ||||
| -    gd_buf = qemu_malloc(gd_size); | ||||
| -    if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_gd; | ||||
| -    } | ||||
| -    if (read(p_fd, gd_buf, gd_size) != gd_size) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_gd; | ||||
| -    } | ||||
| -    if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_gd; | ||||
| -    } | ||||
| -    if (write(snp_fd, gd_buf, gd_size) == -1) { | ||||
| -        ret = -errno; | ||||
| -        goto fail_gd; | ||||
| -    } | ||||
| -    ret = 0; | ||||
| - | ||||
| -fail_gd: | ||||
| -    qemu_free(gd_buf); | ||||
| -fail_rgd: | ||||
| -    qemu_free(rgd_buf); | ||||
| -fail: | ||||
| -    close(p_fd); | ||||
| -    close(snp_fd); | ||||
| -    return ret; | ||||
| -} | ||||
| - | ||||
|  static int vmdk_parent_open(BlockDriverState *bs) | ||||
|  { | ||||
|      char *p_name; | ||||
| @@ -1058,68 +897,40 @@ static int vmdk_write(BlockDriverState * | ||||
|      return 0; | ||||
|  } | ||||
|   | ||||
| -static int vmdk_create(const char *filename, QEMUOptionParameter *options) | ||||
| + | ||||
| +static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat) | ||||
|  { | ||||
| -    int fd, i; | ||||
| +    int ret, i; | ||||
| +    int fd = 0; | ||||
|      VMDK4Header header; | ||||
|      uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; | ||||
| -    static const char desc_template[] = | ||||
| -        "# Disk DescriptorFile\n" | ||||
| -        "version=1\n" | ||||
| -        "CID=%x\n" | ||||
| -        "parentCID=ffffffff\n" | ||||
| -        "createType=\"monolithicSparse\"\n" | ||||
| -        "\n" | ||||
| -        "# Extent description\n" | ||||
| -        "RW %" PRId64 " SPARSE \"%s\"\n" | ||||
| -        "\n" | ||||
| -        "# The Disk Data Base \n" | ||||
| -        "#DDB\n" | ||||
| -        "\n" | ||||
| -        "ddb.virtualHWVersion = \"%d\"\n" | ||||
| -        "ddb.geometry.cylinders = \"%" PRId64 "\"\n" | ||||
| -        "ddb.geometry.heads = \"16\"\n" | ||||
| -        "ddb.geometry.sectors = \"63\"\n" | ||||
| -        "ddb.adapterType = \"ide\"\n"; | ||||
| -    char desc[1024]; | ||||
| -    const char *real_filename, *temp_str; | ||||
| -    int64_t total_size = 0; | ||||
| -    const char *backing_file = NULL; | ||||
| -    int flags = 0; | ||||
| -    int ret; | ||||
|   | ||||
| -    // Read out options | ||||
| -    while (options && options->name) { | ||||
| -        if (!strcmp(options->name, BLOCK_OPT_SIZE)) { | ||||
| -            total_size = options->value.n / 512; | ||||
| -        } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { | ||||
| -            backing_file = options->value.s; | ||||
| -        } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { | ||||
| -            flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0; | ||||
| -        } | ||||
| -        options++; | ||||
| +    fd = open( | ||||
| +        filename, | ||||
| +        O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | ||||
| +        0644); | ||||
| +    if (fd < 0) { | ||||
| +        return -errno; | ||||
|      } | ||||
| - | ||||
| -    /* XXX: add support for backing file */ | ||||
| -    if (backing_file) { | ||||
| -        return vmdk_snapshot_create(filename, backing_file); | ||||
| +    if (flat) { | ||||
| +        ret = ftruncate(fd, filesize); | ||||
| +        if (ret < 0) { | ||||
| +            ret = -errno; | ||||
| +        } | ||||
| +        goto exit; | ||||
|      } | ||||
| - | ||||
| -    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | ||||
| -              0644); | ||||
| -    if (fd < 0) | ||||
| -        return -errno; | ||||
|      magic = cpu_to_be32(VMDK4_MAGIC); | ||||
|      memset(&header, 0, sizeof(header)); | ||||
|      header.version = 1; | ||||
|      header.flags = 3; /* ?? */ | ||||
| -    header.capacity = total_size; | ||||
| +    header.capacity = filesize / 512; | ||||
|      header.granularity = 128; | ||||
|      header.num_gtes_per_gte = 512; | ||||
|   | ||||
| -    grains = (total_size + header.granularity - 1) / header.granularity; | ||||
| +    grains = (filesize / 512 + header.granularity - 1) / header.granularity; | ||||
|      gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; | ||||
| -    gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; | ||||
| +    gt_count = | ||||
| +        (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; | ||||
|      gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; | ||||
|   | ||||
|      header.desc_offset = 1; | ||||
| @@ -1130,7 +941,6 @@ static int vmdk_create(const char *filen | ||||
|         ((header.gd_offset + gd_size + (gt_size * gt_count) + | ||||
|           header.granularity - 1) / header.granularity) * | ||||
|          header.granularity; | ||||
| - | ||||
|      /* swap endianness for all header fields */ | ||||
|      header.version = cpu_to_le32(header.version); | ||||
|      header.flags = cpu_to_le32(header.flags); | ||||
| @@ -1188,27 +998,255 @@ static int vmdk_create(const char *filen | ||||
|          } | ||||
|      } | ||||
|   | ||||
| -    /* compose the descriptor */ | ||||
| -    real_filename = filename; | ||||
| -    if ((temp_str = strrchr(real_filename, '\\')) != NULL) | ||||
| -        real_filename = temp_str + 1; | ||||
| -    if ((temp_str = strrchr(real_filename, '/')) != NULL) | ||||
| -        real_filename = temp_str + 1; | ||||
| -    if ((temp_str = strrchr(real_filename, ':')) != NULL) | ||||
| -        real_filename = temp_str + 1; | ||||
| -    snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL), | ||||
| -             total_size, real_filename, | ||||
| -             (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), | ||||
| -             total_size / (int64_t)(63 * 16)); | ||||
| +    ret = 0; | ||||
| + exit: | ||||
| +    close(fd); | ||||
| +    return ret; | ||||
| +} | ||||
|   | ||||
| -    /* write the descriptor */ | ||||
| -    lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); | ||||
| +static int filename_decompose(const char *filename, char *path, char *prefix, | ||||
| +        char *postfix, size_t buf_len) | ||||
| +{ | ||||
| +    const char *p, *q; | ||||
| + | ||||
| +    if (filename == NULL || !strlen(filename)) { | ||||
| +        fprintf(stderr, "Vmdk: no filename provided.\n"); | ||||
| +        return -1; | ||||
| +    } | ||||
| +    p = strrchr(filename, '/'); | ||||
| +    if (p == NULL) { | ||||
| +        p = strrchr(filename, '\\'); | ||||
| +    } | ||||
| +    if (p == NULL) { | ||||
| +        p = strrchr(filename, ':'); | ||||
| +    } | ||||
| +    if (p != NULL) { | ||||
| +        p++; | ||||
| +        if (p - filename >= buf_len) { | ||||
| +            return -1; | ||||
| +        } | ||||
| +        pstrcpy(path, p - filename + 1, filename); | ||||
| +    } else { | ||||
| +        p = filename; | ||||
| +        path[0] = '\0'; | ||||
| +    } | ||||
| +    q = strrchr(p, '.'); | ||||
| +    if (q == NULL) { | ||||
| +        pstrcpy(prefix, buf_len, p); | ||||
| +        postfix[0] = '\0'; | ||||
| +    } else { | ||||
| +        if (q - p >= buf_len) { | ||||
| +            return -1; | ||||
| +        } | ||||
| +        pstrcpy(prefix, q - p + 1, p); | ||||
| +        pstrcpy(postfix, buf_len, q); | ||||
| +    } | ||||
| +    return 0; | ||||
| +} | ||||
| + | ||||
| +static int relative_path(char *dest, int dest_size, | ||||
| +        const char *base, const char *target) | ||||
| +{ | ||||
| +    int i = 0; | ||||
| +    int n = 0; | ||||
| +    const char *p, *q; | ||||
| +#ifdef _WIN32 | ||||
| +    const char *sep = "\\"; | ||||
| +#else | ||||
| +    const char *sep = "/"; | ||||
| +#endif | ||||
| + | ||||
| +    if (!(dest && base && target)) { | ||||
| +        return -1; | ||||
| +    } | ||||
| +    if (path_is_absolute(target)) { | ||||
| +        dest[dest_size - 1] = '\0'; | ||||
| +        strncpy(dest, target, dest_size - 1); | ||||
| +        return 0; | ||||
| +    } | ||||
| +    while (base[i] == target[i]) { | ||||
| +        i++; | ||||
| +    } | ||||
| +    p = &base[i]; | ||||
| +    q = &target[i]; | ||||
| +    while (*p) { | ||||
| +        if (*p == *sep) { | ||||
| +            n++; | ||||
| +        } | ||||
| +        p++; | ||||
| +    } | ||||
| +    dest[0] = '\0'; | ||||
| +    for (; n; n--) { | ||||
| +        pstrcat(dest, dest_size, ".."); | ||||
| +        pstrcat(dest, dest_size, sep); | ||||
| +    } | ||||
| +    pstrcat(dest, dest_size, q); | ||||
| +    return 0; | ||||
| +} | ||||
| + | ||||
| +static int vmdk_create(const char *filename, QEMUOptionParameter *options) | ||||
| +{ | ||||
| +    int fd, idx = 0; | ||||
| +    char desc[BUF_SIZE]; | ||||
| +    int64_t total_size = 0, filesize; | ||||
| +    const char *backing_file = NULL; | ||||
| +    const char *fmt = NULL; | ||||
| +    int flags = 0; | ||||
| +    int ret = 0; | ||||
| +    bool flat, split; | ||||
| +    char ext_desc_lines[BUF_SIZE] = ""; | ||||
| +    char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; | ||||
| +    const int64_t split_size = 0x80000000;  /* VMDK has constant split size */ | ||||
| +    const char *desc_extent_line; | ||||
| +    char parent_desc_line[BUF_SIZE] = ""; | ||||
| +    uint32_t parent_cid = 0xffffffff; | ||||
| +    const char desc_template[] = | ||||
| +        "# Disk DescriptorFile\n" | ||||
| +        "version=1\n" | ||||
| +        "CID=%x\n" | ||||
| +        "parentCID=%x\n" | ||||
| +        "createType=\"%s\"\n" | ||||
| +        "%s" | ||||
| +        "\n" | ||||
| +        "# Extent description\n" | ||||
| +        "%s" | ||||
| +        "\n" | ||||
| +        "# The Disk Data Base\n" | ||||
| +        "#DDB\n" | ||||
| +        "\n" | ||||
| +        "ddb.virtualHWVersion = \"%d\"\n" | ||||
| +        "ddb.geometry.cylinders = \"%" PRId64 "\"\n" | ||||
| +        "ddb.geometry.heads = \"16\"\n" | ||||
| +        "ddb.geometry.sectors = \"63\"\n" | ||||
| +        "ddb.adapterType = \"ide\"\n"; | ||||
| + | ||||
| +    if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { | ||||
| +        return -EINVAL; | ||||
| +    } | ||||
| +    /* Read out options */ | ||||
| +    while (options && options->name) { | ||||
| +        if (!strcmp(options->name, BLOCK_OPT_SIZE)) { | ||||
| +            total_size = options->value.n; | ||||
| +        } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { | ||||
| +            backing_file = options->value.s; | ||||
| +        } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { | ||||
| +            flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0; | ||||
| +        } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) { | ||||
| +            fmt = options->value.s; | ||||
| +        } | ||||
| +        options++; | ||||
| +    } | ||||
| +    if (!fmt) { | ||||
| +        /* Default format to monolithicSparse */ | ||||
| +        fmt = "monolithicSparse"; | ||||
| +    } else if (strcmp(fmt, "monolithicFlat") && | ||||
| +               strcmp(fmt, "monolithicSparse") && | ||||
| +               strcmp(fmt, "twoGbMaxExtentSparse") && | ||||
| +               strcmp(fmt, "twoGbMaxExtentFlat")) { | ||||
| +        fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt); | ||||
| +        return -EINVAL; | ||||
| +    } | ||||
| +    split = !(strcmp(fmt, "twoGbMaxExtentFlat") && | ||||
| +              strcmp(fmt, "twoGbMaxExtentSparse")); | ||||
| +    flat = !(strcmp(fmt, "monolithicFlat") && | ||||
| +             strcmp(fmt, "twoGbMaxExtentFlat")); | ||||
| +    if (flat) { | ||||
| +        desc_extent_line = "RW %lld FLAT \"%s\" 0\n"; | ||||
| +    } else { | ||||
| +        desc_extent_line = "RW %lld SPARSE \"%s\"\n"; | ||||
| +    } | ||||
| +    if (flat && backing_file) { | ||||
| +        /* not supporting backing file for flat image */ | ||||
| +        return -ENOTSUP; | ||||
| +    } | ||||
| +    if (backing_file) { | ||||
| +        char parent_filename[PATH_MAX]; | ||||
| +        BlockDriverState *bs = bdrv_new(""); | ||||
| +        ret = bdrv_open(bs, backing_file, 0, NULL); | ||||
| +        if (ret != 0) { | ||||
| +            bdrv_delete(bs); | ||||
| +            return ret; | ||||
| +        } | ||||
| +        if (strcmp(bs->drv->format_name, "vmdk")) { | ||||
| +            bdrv_delete(bs); | ||||
| +            return -EINVAL; | ||||
| +        } | ||||
| +        filesize = bdrv_getlength(bs); | ||||
| +        parent_cid = vmdk_read_cid(bs, 0); | ||||
| +        bdrv_delete(bs); | ||||
| +        relative_path(parent_filename, sizeof(parent_filename), | ||||
| +                      filename, backing_file); | ||||
| +        snprintf(parent_desc_line, sizeof(parent_desc_line), | ||||
| +                "parentFileNameHint=\"%s\"", parent_filename); | ||||
| +    } | ||||
| + | ||||
| +    /* Create extents */ | ||||
| +    filesize = total_size; | ||||
| +    while (filesize > 0) { | ||||
| +        char desc_line[BUF_SIZE]; | ||||
| +        char ext_filename[PATH_MAX]; | ||||
| +        char desc_filename[PATH_MAX]; | ||||
| +        int64_t size = filesize; | ||||
| + | ||||
| +        if (split && size > split_size) { | ||||
| +            size = split_size; | ||||
| +        } | ||||
| +        if (split) { | ||||
| +            snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s", | ||||
| +                    prefix, flat ? 'f' : 's', ++idx, postfix); | ||||
| +        } else if (flat) { | ||||
| +            snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s", | ||||
| +                    prefix, postfix); | ||||
| +        } else { | ||||
| +            snprintf(desc_filename, sizeof(desc_filename), "%s%s", | ||||
| +                    prefix, postfix); | ||||
| +        } | ||||
| +        snprintf(ext_filename, sizeof(ext_filename), "%s%s", | ||||
| +                path, desc_filename); | ||||
| + | ||||
| +        if (vmdk_create_extent(ext_filename, size, flat)) { | ||||
| +            return -EINVAL; | ||||
| +        } | ||||
| +        filesize -= size; | ||||
| + | ||||
| +        /* Format description line */ | ||||
| +        snprintf(desc_line, sizeof(desc_line), | ||||
| +                    desc_extent_line, size / 512, desc_filename); | ||||
| +        pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line); | ||||
| +    } | ||||
| +    /* generate descriptor file */ | ||||
| +    snprintf(desc, sizeof(desc), desc_template, | ||||
| +            (unsigned int)time(NULL), | ||||
| +            parent_cid, | ||||
| +            fmt, | ||||
| +            parent_desc_line, | ||||
| +            ext_desc_lines, | ||||
| +            (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), | ||||
| +            total_size / (int64_t)(63 * 16 * 512)); | ||||
| +    if (split || flat) { | ||||
| +        fd = open( | ||||
| +                filename, | ||||
| +                O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | ||||
| +                0644); | ||||
| +    } else { | ||||
| +        fd = open( | ||||
| +                filename, | ||||
| +                O_WRONLY | O_BINARY | O_LARGEFILE, | ||||
| +                0644); | ||||
| +    } | ||||
| +    if (fd < 0) { | ||||
| +        return -errno; | ||||
| +    } | ||||
| +    /* the descriptor offset = 0x200 */ | ||||
| +    if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) { | ||||
| +        ret = -errno; | ||||
| +        goto exit; | ||||
| +    } | ||||
|      ret = qemu_write_full(fd, desc, strlen(desc)); | ||||
|      if (ret != strlen(desc)) { | ||||
|          ret = -errno; | ||||
|          goto exit; | ||||
|      } | ||||
| - | ||||
|      ret = 0; | ||||
|  exit: | ||||
|      close(fd); | ||||
| @@ -1252,6 +1290,13 @@ static QEMUOptionParameter vmdk_create_o | ||||
|          .type = OPT_FLAG, | ||||
|          .help = "VMDK version 6 image" | ||||
|      }, | ||||
| +    { | ||||
| +        .name = BLOCK_OPT_SUBFMT, | ||||
| +        .type = OPT_STRING, | ||||
| +        .help = | ||||
| +            "VMDK flat extent format, can be one of " | ||||
| +            "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} " | ||||
| +    }, | ||||
|      { NULL } | ||||
|  }; | ||||
|   | ||||
| --- a/block_int.h | ||||
| +++ b/block_int.h | ||||
| @@ -39,6 +39,7 @@ | ||||
|  #define BLOCK_OPT_CLUSTER_SIZE  "cluster_size" | ||||
|  #define BLOCK_OPT_TABLE_SIZE    "table_size" | ||||
|  #define BLOCK_OPT_PREALLOC      "preallocation" | ||||
| +#define BLOCK_OPT_SUBFMT        "subformat" | ||||
|   | ||||
|  typedef struct AIOPool { | ||||
|      void (*cancel)(BlockDriverAIOCB *acb); | ||||
| @@ -1,114 +0,0 @@ | ||||
| From 5483df4df2729a5d1e4888a48039b1cd90438480 Mon Sep 17 00:00:00 2001 | ||||
| From: Othmar Pasteka <pasteka@kabsi.at> | ||||
| Date: Wed, 30 Jan 2013 00:26:52 +0100 | ||||
| Subject: [PATCH 12/12] vmdk: Allow selecting SCSI adapter in image creation | ||||
|  | ||||
| Introduce a new option "adapter_type" when converting to vmdk images. | ||||
| It can be one of the following: ide (default), buslogic, lsilogic | ||||
| or legacyESX (according to the vmdk spec from vmware). | ||||
|  | ||||
| In case of a non-ide adapter, heads is set to 255 instead of the 16. | ||||
| The latter is used for "ide". | ||||
|  | ||||
| Also see LP#545089 | ||||
|  | ||||
| Signed-off-by: Othmar Pasteka <pasteka@kabsi.at> | ||||
| Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| --- | ||||
|  block/vmdk.c | 31 ++++++++++++++++++++++++++++--- | ||||
|  block_int.h  |  1 + | ||||
|  2 files changed, 29 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/block/vmdk.c | ||||
| +++ b/block/vmdk.c | ||||
| @@ -1089,6 +1089,7 @@ static int vmdk_create(const char *filen | ||||
|      int fd, idx = 0; | ||||
|      char desc[BUF_SIZE]; | ||||
|      int64_t total_size = 0, filesize; | ||||
| +    const char *adapter_type = NULL; | ||||
|      const char *backing_file = NULL; | ||||
|      const char *fmt = NULL; | ||||
|      int flags = 0; | ||||
| @@ -1100,6 +1101,7 @@ static int vmdk_create(const char *filen | ||||
|      const char *desc_extent_line; | ||||
|      char parent_desc_line[BUF_SIZE] = ""; | ||||
|      uint32_t parent_cid = 0xffffffff; | ||||
| +    uint32_t number_heads = 16; | ||||
|      const char desc_template[] = | ||||
|          "# Disk DescriptorFile\n" | ||||
|          "version=1\n" | ||||
| @@ -1116,9 +1118,9 @@ static int vmdk_create(const char *filen | ||||
|          "\n" | ||||
|          "ddb.virtualHWVersion = \"%d\"\n" | ||||
|          "ddb.geometry.cylinders = \"%" PRId64 "\"\n" | ||||
| -        "ddb.geometry.heads = \"16\"\n" | ||||
| +        "ddb.geometry.heads = \"%d\"\n" | ||||
|          "ddb.geometry.sectors = \"63\"\n" | ||||
| -        "ddb.adapterType = \"ide\"\n"; | ||||
| +        "ddb.adapterType = \"%s\"\n"; | ||||
|   | ||||
|      if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { | ||||
|          return -EINVAL; | ||||
| @@ -1127,6 +1129,8 @@ static int vmdk_create(const char *filen | ||||
|      while (options && options->name) { | ||||
|          if (!strcmp(options->name, BLOCK_OPT_SIZE)) { | ||||
|              total_size = options->value.n; | ||||
| +        } else if (!strcmp(options->name, BLOCK_OPT_ADAPTER_TYPE)) { | ||||
| +            adapter_type = options->value.s; | ||||
|          } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { | ||||
|              backing_file = options->value.s; | ||||
|          } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { | ||||
| @@ -1136,6 +1140,20 @@ static int vmdk_create(const char *filen | ||||
|          } | ||||
|          options++; | ||||
|      } | ||||
| +    if (!adapter_type) { | ||||
| +        adapter_type = "ide"; | ||||
| +    } else if (strcmp(adapter_type, "ide") && | ||||
| +               strcmp(adapter_type, "buslogic") && | ||||
| +               strcmp(adapter_type, "lsilogic") && | ||||
| +               strcmp(adapter_type, "legacyESX")) { | ||||
| +        fprintf(stderr, "VMDK: Unknown adapter type: '%s'.\n", adapter_type); | ||||
| +        return -EINVAL; | ||||
| +    } | ||||
| +    if (strcmp(adapter_type, "ide") != 0) { | ||||
| +        /* that's the number of heads with which vmware operates when | ||||
| +           creating, exporting, etc. vmdk files with a non-ide adapter type */ | ||||
| +        number_heads = 255; | ||||
| +    } | ||||
|      if (!fmt) { | ||||
|          /* Default format to monolithicSparse */ | ||||
|          fmt = "monolithicSparse"; | ||||
| @@ -1222,7 +1240,8 @@ static int vmdk_create(const char *filen | ||||
|              parent_desc_line, | ||||
|              ext_desc_lines, | ||||
|              (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), | ||||
| -            total_size / (int64_t)(63 * 16 * 512)); | ||||
| +            total_size / (int64_t)(63 * number_heads * 512), number_heads, | ||||
| +                adapter_type); | ||||
|      if (split || flat) { | ||||
|          fd = open( | ||||
|                  filename, | ||||
| @@ -1281,6 +1300,12 @@ static QEMUOptionParameter vmdk_create_o | ||||
|          .help = "Virtual disk size" | ||||
|      }, | ||||
|      { | ||||
| +        .name = BLOCK_OPT_ADAPTER_TYPE, | ||||
| +        .type = OPT_STRING, | ||||
| +        .help = "Virtual adapter type, can be one of " | ||||
| +                "ide (default), lsilogic, buslogic or legacyESX" | ||||
| +    }, | ||||
| +    { | ||||
|          .name = BLOCK_OPT_BACKING_FILE, | ||||
|          .type = OPT_STRING, | ||||
|          .help = "File name of a base image" | ||||
| --- a/block_int.h | ||||
| +++ b/block_int.h | ||||
| @@ -40,6 +40,7 @@ | ||||
|  #define BLOCK_OPT_TABLE_SIZE    "table_size" | ||||
|  #define BLOCK_OPT_PREALLOC      "preallocation" | ||||
|  #define BLOCK_OPT_SUBFMT        "subformat" | ||||
| +#define BLOCK_OPT_ADAPTER_TYPE      "adapter_type" | ||||
|   | ||||
|  typedef struct AIOPool { | ||||
|      void (*cancel)(BlockDriverAIOCB *acb); | ||||
		Reference in New Issue
	
	Block a user
	 Paul Spooren
					Paul Spooren