Commit 7fec76a0 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-06-24' into staging



Block patches:
- The SSH block driver now uses libssh instead of libssh2
- The VMDK block driver gets read-only support for the seSparse
  subformat
- Various fixes

# gpg: Signature made Mon 24 Jun 2019 15:42:56 BST
# gpg:                using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg:                issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1  1829 F407 DB00 61D5 CF40

* remotes/maxreitz/tags/pull-block-2019-06-24:
  iotests: Fix 205 for concurrent runs
  ssh: switch from libssh2 to libssh
  vmdk: Add read-only support for seSparse snapshots
  vmdk: Reduce the max bound for L1 table size
  vmdk: Fix comment regarding max l1_size coverage
  iotest 134: test cluster-misaligned encrypted write
  blockdev: enable non-root nodes for transaction drive-backup source
  nvme: do not advertise support for unsupported arbitration mechanism

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 474f3938 ab5d4a30
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ addons:
      - libseccomp-dev
      - libspice-protocol-dev
      - libspice-server-dev
      - libssh2-1-dev
      - libssh-dev
      - liburcu-dev
      - libusb-1.0-0-dev
      - libvte-2.91-dev
@@ -270,7 +270,7 @@ matrix:
            - libseccomp-dev
            - libspice-protocol-dev
            - libspice-server-dev
            - libssh2-1-dev
            - libssh-dev
            - liburcu-dev
            - libusb-1.0-0-dev
            - libvte-2.91-dev
+3 −3
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
block-obj-$(CONFIG_VXHS) += vxhs.o
block-obj-$(CONFIG_LIBSSH2) += ssh.o
block-obj-$(CONFIG_LIBSSH) += ssh.o
block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o
block-obj-y += backup.o
@@ -52,8 +52,8 @@ rbd.o-libs := $(RBD_LIBS)
gluster.o-cflags   := $(GLUSTERFS_CFLAGS)
gluster.o-libs     := $(GLUSTERFS_LIBS)
vxhs.o-libs        := $(VXHS_LIBS)
ssh.o-cflags       := $(LIBSSH2_CFLAGS)
ssh.o-libs         := $(LIBSSH2_LIBS)
ssh.o-cflags       := $(LIBSSH_CFLAGS)
ssh.o-libs         := $(LIBSSH_LIBS)
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
dmg-bz2.o-libs     := $(BZIP2_LIBS)
+360 −292

File changed.

Preview size limit exceeded, changes collapsed.

+8 −6
Original line number Diff line number Diff line
@@ -171,19 +171,21 @@ nbd_client_connect_success(const char *export_name) "export '%s'"
# ssh.c
ssh_restart_coroutine(void *co) "co=%p"
ssh_flush(void) "fsync"
ssh_check_host_key_knownhosts(const char *key) "host key OK: %s"
ssh_check_host_key_knownhosts(void) "host key OK"
ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o"
ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p"
ssh_co_yield_back(int sock) "s->sock=%d - back"
ssh_getlength(int64_t length) "length=%" PRIi64
ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64
ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu"
ssh_read_return(ssize_t ret) "sftp_read returned %zd"
ssh_read_buf(void *buf, size_t size, size_t actual_size) "sftp_read buf=%p size=%zu (actual size=%zu)"
ssh_read_return(ssize_t ret, int sftp_err) "sftp_read returned %zd (sftp error=%d)"
ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu"
ssh_write_return(ssize_t ret) "sftp_write returned %zd"
ssh_write_buf(void *buf, size_t size, size_t actual_size) "sftp_write buf=%p size=%zu (actual size=%zu)"
ssh_write_return(ssize_t ret, int sftp_err) "sftp_write returned %zd (sftp error=%d)"
ssh_seek(int64_t offset) "seeking to offset=%" PRIi64
ssh_auth_methods(int methods) "auth methods=0x%x"
ssh_server_status(int status) "server status=%d"

# curl.c
curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld"
@@ -216,4 +218,4 @@ sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s"
sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32

# ssh.c
sftp_error(const char *op, const char *ssh_err, int ssh_err_code, unsigned long sftp_err_code) "%s failed: %s (libssh2 error code: %d, sftp error code: %lu)"
sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)"
+352 −20
Original line number Diff line number Diff line
@@ -91,6 +91,44 @@ typedef struct {
    uint16_t compressAlgorithm;
} QEMU_PACKED VMDK4Header;

typedef struct VMDKSESparseConstHeader {
    uint64_t magic;
    uint64_t version;
    uint64_t capacity;
    uint64_t grain_size;
    uint64_t grain_table_size;
    uint64_t flags;
    uint64_t reserved1;
    uint64_t reserved2;
    uint64_t reserved3;
    uint64_t reserved4;
    uint64_t volatile_header_offset;
    uint64_t volatile_header_size;
    uint64_t journal_header_offset;
    uint64_t journal_header_size;
    uint64_t journal_offset;
    uint64_t journal_size;
    uint64_t grain_dir_offset;
    uint64_t grain_dir_size;
    uint64_t grain_tables_offset;
    uint64_t grain_tables_size;
    uint64_t free_bitmap_offset;
    uint64_t free_bitmap_size;
    uint64_t backmap_offset;
    uint64_t backmap_size;
    uint64_t grains_offset;
    uint64_t grains_size;
    uint8_t pad[304];
} QEMU_PACKED VMDKSESparseConstHeader;

typedef struct VMDKSESparseVolatileHeader {
    uint64_t magic;
    uint64_t free_gt_number;
    uint64_t next_txn_seq_number;
    uint64_t replay_journal;
    uint8_t pad[480];
} QEMU_PACKED VMDKSESparseVolatileHeader;

#define L2_CACHE_SIZE 16

typedef struct VmdkExtent {
@@ -99,19 +137,23 @@ typedef struct VmdkExtent {
    bool compressed;
    bool has_marker;
    bool has_zero_grain;
    bool sesparse;
    uint64_t sesparse_l2_tables_offset;
    uint64_t sesparse_clusters_offset;
    int32_t entry_size;
    int version;
    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;
    void *l1_table;
    uint32_t *l1_backup_table;
    unsigned int l1_size;
    uint32_t l1_entry_sectors;

    unsigned int l2_size;
    uint32_t *l2_cache;
    void *l2_cache;
    uint32_t l2_cache_offsets[L2_CACHE_SIZE];
    uint32_t l2_cache_counts[L2_CACHE_SIZE];

@@ -425,11 +467,22 @@ static int vmdk_add_extent(BlockDriverState *bs,
        error_setg(errp, "Invalid granularity, image may be corrupt");
        return -EFBIG;
    }
    if (l1_size > 512 * 1024 * 1024) {
        /* Although with big capacity and small l1_entry_sectors, we can get a
    if (l1_size > 32 * 1024 * 1024) {
        /*
         * Although with big capacity and small l1_entry_sectors, we can get a
         * big l1_size, we don't want unbounded value to allocate the table.
         * Limit it to 512M, which is 16PB for default cluster and L2 table
         * size */
         * Limit it to 32M, which is enough to store:
         *     8TB  - for both VMDK3 & VMDK4 with
         *            minimal cluster size: 512B
         *            minimal L2 table size: 512 entries
         *            8 TB is still more than the maximal value supported for
         *            VMDK3 & VMDK4 which is 2TB.
         *     64TB - for "ESXi seSparse Extent"
         *            minimal cluster size: 512B (default is 4KB)
         *            L2 table size: 4096 entries (const).
         *            64TB is more than the maximal value supported for
         *            seSparse VMDKs (which is slightly less than 64TB)
         */
        error_setg(errp, "L1 size too big");
        return -EFBIG;
    }
@@ -454,6 +507,7 @@ static int vmdk_add_extent(BlockDriverState *bs,
    extent->l2_size = l2_size;
    extent->cluster_sectors = flat ? sectors : cluster_sectors;
    extent->next_cluster_sector = ROUND_UP(nb_sectors, cluster_sectors);
    extent->entry_size = sizeof(uint32_t);

    if (s->num_extents > 1) {
        extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
@@ -475,7 +529,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
    int i;

    /* read the L1 table */
    l1_size = extent->l1_size * sizeof(uint32_t);
    l1_size = extent->l1_size * extent->entry_size;
    extent->l1_table = g_try_malloc(l1_size);
    if (l1_size && extent->l1_table == NULL) {
        return -ENOMEM;
@@ -493,10 +547,16 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
        goto fail_l1;
    }
    for (i = 0; i < extent->l1_size; i++) {
        le32_to_cpus(&extent->l1_table[i]);
        if (extent->entry_size == sizeof(uint64_t)) {
            le64_to_cpus((uint64_t *)extent->l1_table + i);
        } else {
            assert(extent->entry_size == sizeof(uint32_t));
            le32_to_cpus((uint32_t *)extent->l1_table + i);
        }
    }

    if (extent->l1_backup_table_offset) {
        assert(!extent->sesparse);
        extent->l1_backup_table = g_try_malloc(l1_size);
        if (l1_size && extent->l1_backup_table == NULL) {
            ret = -ENOMEM;
@@ -519,7 +579,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
    }

    extent->l2_cache =
        g_new(uint32_t, extent->l2_size * L2_CACHE_SIZE);
        g_malloc(extent->entry_size * extent->l2_size * L2_CACHE_SIZE);
    return 0;
 fail_l1b:
    g_free(extent->l1_backup_table);
@@ -565,6 +625,205 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
    return ret;
}

#define SESPARSE_CONST_HEADER_MAGIC UINT64_C(0x00000000cafebabe)
#define SESPARSE_VOLATILE_HEADER_MAGIC UINT64_C(0x00000000cafecafe)

/* Strict checks - format not officially documented */
static int check_se_sparse_const_header(VMDKSESparseConstHeader *header,
                                        Error **errp)
{
    header->magic = le64_to_cpu(header->magic);
    header->version = le64_to_cpu(header->version);
    header->grain_size = le64_to_cpu(header->grain_size);
    header->grain_table_size = le64_to_cpu(header->grain_table_size);
    header->flags = le64_to_cpu(header->flags);
    header->reserved1 = le64_to_cpu(header->reserved1);
    header->reserved2 = le64_to_cpu(header->reserved2);
    header->reserved3 = le64_to_cpu(header->reserved3);
    header->reserved4 = le64_to_cpu(header->reserved4);

    header->volatile_header_offset =
        le64_to_cpu(header->volatile_header_offset);
    header->volatile_header_size = le64_to_cpu(header->volatile_header_size);

    header->journal_header_offset = le64_to_cpu(header->journal_header_offset);
    header->journal_header_size = le64_to_cpu(header->journal_header_size);

    header->journal_offset = le64_to_cpu(header->journal_offset);
    header->journal_size = le64_to_cpu(header->journal_size);

    header->grain_dir_offset = le64_to_cpu(header->grain_dir_offset);
    header->grain_dir_size = le64_to_cpu(header->grain_dir_size);

    header->grain_tables_offset = le64_to_cpu(header->grain_tables_offset);
    header->grain_tables_size = le64_to_cpu(header->grain_tables_size);

    header->free_bitmap_offset = le64_to_cpu(header->free_bitmap_offset);
    header->free_bitmap_size = le64_to_cpu(header->free_bitmap_size);

    header->backmap_offset = le64_to_cpu(header->backmap_offset);
    header->backmap_size = le64_to_cpu(header->backmap_size);

    header->grains_offset = le64_to_cpu(header->grains_offset);
    header->grains_size = le64_to_cpu(header->grains_size);

    if (header->magic != SESPARSE_CONST_HEADER_MAGIC) {
        error_setg(errp, "Bad const header magic: 0x%016" PRIx64,
                   header->magic);
        return -EINVAL;
    }

    if (header->version != 0x0000000200000001) {
        error_setg(errp, "Unsupported version: 0x%016" PRIx64,
                   header->version);
        return -ENOTSUP;
    }

    if (header->grain_size != 8) {
        error_setg(errp, "Unsupported grain size: %" PRIu64,
                   header->grain_size);
        return -ENOTSUP;
    }

    if (header->grain_table_size != 64) {
        error_setg(errp, "Unsupported grain table size: %" PRIu64,
                   header->grain_table_size);
        return -ENOTSUP;
    }

    if (header->flags != 0) {
        error_setg(errp, "Unsupported flags: 0x%016" PRIx64,
                   header->flags);
        return -ENOTSUP;
    }

    if (header->reserved1 != 0 || header->reserved2 != 0 ||
        header->reserved3 != 0 || header->reserved4 != 0) {
        error_setg(errp, "Unsupported reserved bits:"
                   " 0x%016" PRIx64 " 0x%016" PRIx64
                   " 0x%016" PRIx64 " 0x%016" PRIx64,
                   header->reserved1, header->reserved2,
                   header->reserved3, header->reserved4);
        return -ENOTSUP;
    }

    /* check that padding is 0 */
    if (!buffer_is_zero(header->pad, sizeof(header->pad))) {
        error_setg(errp, "Unsupported non-zero const header padding");
        return -ENOTSUP;
    }

    return 0;
}

static int check_se_sparse_volatile_header(VMDKSESparseVolatileHeader *header,
                                           Error **errp)
{
    header->magic = le64_to_cpu(header->magic);
    header->free_gt_number = le64_to_cpu(header->free_gt_number);
    header->next_txn_seq_number = le64_to_cpu(header->next_txn_seq_number);
    header->replay_journal = le64_to_cpu(header->replay_journal);

    if (header->magic != SESPARSE_VOLATILE_HEADER_MAGIC) {
        error_setg(errp, "Bad volatile header magic: 0x%016" PRIx64,
                   header->magic);
        return -EINVAL;
    }

    if (header->replay_journal) {
        error_setg(errp, "Image is dirty, Replaying journal not supported");
        return -ENOTSUP;
    }

    /* check that padding is 0 */
    if (!buffer_is_zero(header->pad, sizeof(header->pad))) {
        error_setg(errp, "Unsupported non-zero volatile header padding");
        return -ENOTSUP;
    }

    return 0;
}

static int vmdk_open_se_sparse(BlockDriverState *bs,
                               BdrvChild *file,
                               int flags, Error **errp)
{
    int ret;
    VMDKSESparseConstHeader const_header;
    VMDKSESparseVolatileHeader volatile_header;
    VmdkExtent *extent;

    ret = bdrv_apply_auto_read_only(bs,
            "No write support for seSparse images available", errp);
    if (ret < 0) {
        return ret;
    }

    assert(sizeof(const_header) == SECTOR_SIZE);

    ret = bdrv_pread(file, 0, &const_header, sizeof(const_header));
    if (ret < 0) {
        bdrv_refresh_filename(file->bs);
        error_setg_errno(errp, -ret,
                         "Could not read const header from file '%s'",
                         file->bs->filename);
        return ret;
    }

    /* check const header */
    ret = check_se_sparse_const_header(&const_header, errp);
    if (ret < 0) {
        return ret;
    }

    assert(sizeof(volatile_header) == SECTOR_SIZE);

    ret = bdrv_pread(file,
                     const_header.volatile_header_offset * SECTOR_SIZE,
                     &volatile_header, sizeof(volatile_header));
    if (ret < 0) {
        bdrv_refresh_filename(file->bs);
        error_setg_errno(errp, -ret,
                         "Could not read volatile header from file '%s'",
                         file->bs->filename);
        return ret;
    }

    /* check volatile header */
    ret = check_se_sparse_volatile_header(&volatile_header, errp);
    if (ret < 0) {
        return ret;
    }

    ret = vmdk_add_extent(bs, file, false,
                          const_header.capacity,
                          const_header.grain_dir_offset * SECTOR_SIZE,
                          0,
                          const_header.grain_dir_size *
                          SECTOR_SIZE / sizeof(uint64_t),
                          const_header.grain_table_size *
                          SECTOR_SIZE / sizeof(uint64_t),
                          const_header.grain_size,
                          &extent,
                          errp);
    if (ret < 0) {
        return ret;
    }

    extent->sesparse = true;
    extent->sesparse_l2_tables_offset = const_header.grain_tables_offset;
    extent->sesparse_clusters_offset = const_header.grains_offset;
    extent->entry_size = sizeof(uint64_t);

    ret = vmdk_init_tables(bs, extent, errp);
    if (ret) {
        /* free extent allocated by vmdk_add_extent */
        vmdk_free_last_extent(bs);
    }

    return ret;
}

static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
                               QDict *options, Error **errp);

@@ -842,6 +1101,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
         * RW [size in sectors] SPARSE "file-name.vmdk"
         * RW [size in sectors] VMFS "file-name.vmdk"
         * RW [size in sectors] VMFSSPARSE "file-name.vmdk"
         * RW [size in sectors] SESPARSE "file-name.vmdk"
         */
        flat_offset = -1;
        matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
@@ -864,7 +1124,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,

        if (sectors <= 0 ||
            (strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
             strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) ||
             strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE") &&
             strcmp(type, "SESPARSE")) ||
            (strcmp(access, "RW"))) {
            continue;
        }
@@ -917,6 +1178,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
                return ret;
            }
            extent = &s->extents[s->num_extents - 1];
        } else if (!strcmp(type, "SESPARSE")) {
            ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
            if (ret) {
                bdrv_unref_child(bs, extent_file);
                return ret;
            }
            extent = &s->extents[s->num_extents - 1];
        } else {
            error_setg(errp, "Unsupported extent type '%s'", type);
            bdrv_unref_child(bs, extent_file);
@@ -951,6 +1219,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
    if (strcmp(ct, "monolithicFlat") &&
        strcmp(ct, "vmfs") &&
        strcmp(ct, "vmfsSparse") &&
        strcmp(ct, "seSparse") &&
        strcmp(ct, "twoGbMaxExtentSparse") &&
        strcmp(ct, "twoGbMaxExtentFlat")) {
        error_setg(errp, "Unsupported image type '%s'", ct);
@@ -1201,10 +1470,12 @@ static int get_cluster_offset(BlockDriverState *bs,
{
    unsigned int l1_index, l2_offset, l2_index;
    int min_index, i, j;
    uint32_t min_count, *l2_table;
    uint32_t min_count;
    void *l2_table;
    bool zeroed = false;
    int64_t ret;
    int64_t cluster_sector;
    unsigned int l2_size_bytes = extent->l2_size * extent->entry_size;

    if (m_data) {
        m_data->valid = 0;
@@ -1219,7 +1490,36 @@ static int get_cluster_offset(BlockDriverState *bs,
    if (l1_index >= extent->l1_size) {
        return VMDK_ERROR;
    }
    l2_offset = extent->l1_table[l1_index];
    if (extent->sesparse) {
        uint64_t l2_offset_u64;

        assert(extent->entry_size == sizeof(uint64_t));

        l2_offset_u64 = ((uint64_t *)extent->l1_table)[l1_index];
        if (l2_offset_u64 == 0) {
            l2_offset = 0;
        } else if ((l2_offset_u64 & 0xffffffff00000000) != 0x1000000000000000) {
            /*
             * Top most nibble is 0x1 if grain table is allocated.
             * strict check - top most 4 bytes must be 0x10000000 since max
             * supported size is 64TB for disk - so no more than 64TB / 16MB
             * grain directories which is smaller than uint32,
             * where 16MB is the only supported default grain table coverage.
             */
            return VMDK_ERROR;
        } else {
            l2_offset_u64 = l2_offset_u64 & 0x00000000ffffffff;
            l2_offset_u64 = extent->sesparse_l2_tables_offset +
                l2_offset_u64 * l2_size_bytes / SECTOR_SIZE;
            if (l2_offset_u64 > 0x00000000ffffffff) {
                return VMDK_ERROR;
            }
            l2_offset = (unsigned int)(l2_offset_u64);
        }
    } else {
        assert(extent->entry_size == sizeof(uint32_t));
        l2_offset = ((uint32_t *)extent->l1_table)[l1_index];
    }
    if (!l2_offset) {
        return VMDK_UNALLOC;
    }
@@ -1231,7 +1531,7 @@ static int get_cluster_offset(BlockDriverState *bs,
                    extent->l2_cache_counts[j] >>= 1;
                }
            }
            l2_table = extent->l2_cache + (i * extent->l2_size);
            l2_table = (char *)extent->l2_cache + (i * l2_size_bytes);
            goto found;
        }
    }
@@ -1244,13 +1544,13 @@ static int get_cluster_offset(BlockDriverState *bs,
            min_index = i;
        }
    }
    l2_table = extent->l2_cache + (min_index * extent->l2_size);
    l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes);
    BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD);
    if (bdrv_pread(extent->file,
                (int64_t)l2_offset * 512,
                l2_table,
                extent->l2_size * sizeof(uint32_t)
            ) != extent->l2_size * sizeof(uint32_t)) {
                l2_size_bytes
            ) != l2_size_bytes) {
        return VMDK_ERROR;
    }

@@ -1258,16 +1558,45 @@ static int get_cluster_offset(BlockDriverState *bs,
    extent->l2_cache_counts[min_index] = 1;
 found:
    l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
    cluster_sector = le32_to_cpu(l2_table[l2_index]);

    if (extent->sesparse) {
        cluster_sector = le64_to_cpu(((uint64_t *)l2_table)[l2_index]);
        switch (cluster_sector & 0xf000000000000000) {
        case 0x0000000000000000:
            /* unallocated grain */
            if (cluster_sector != 0) {
                return VMDK_ERROR;
            }
            break;
        case 0x1000000000000000:
            /* scsi-unmapped grain - fallthrough */
        case 0x2000000000000000:
            /* zero grain */
            zeroed = true;
            break;
        case 0x3000000000000000:
            /* allocated grain */
            cluster_sector = (((cluster_sector & 0x0fff000000000000) >> 48) |
                              ((cluster_sector & 0x0000ffffffffffff) << 12));
            cluster_sector = extent->sesparse_clusters_offset +
                cluster_sector * extent->cluster_sectors;
            break;
        default:
            return VMDK_ERROR;
        }
    } else {
        cluster_sector = le32_to_cpu(((uint32_t *)l2_table)[l2_index]);

        if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
            zeroed = true;
        }
    }

    if (!cluster_sector || zeroed) {
        if (!allocate) {
            return zeroed ? VMDK_ZEROED : VMDK_UNALLOC;
        }
        assert(!extent->sesparse);

        if (extent->next_cluster_sector >= VMDK_EXTENT_MAX_SECTORS) {
            return VMDK_ERROR;
@@ -1291,7 +1620,7 @@ static int get_cluster_offset(BlockDriverState *bs,
            m_data->l1_index = l1_index;
            m_data->l2_index = l2_index;
            m_data->l2_offset = l2_offset;
            m_data->l2_cache_entry = &l2_table[l2_index];
            m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index;
        }
    }
    *cluster_offset = cluster_sector << BDRV_SECTOR_BITS;
@@ -1617,6 +1946,9 @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset,
        if (!extent) {
            return -EIO;
        }
        if (extent->sesparse) {
            return -ENOTSUP;
        }
        offset_in_cluster = vmdk_find_offset_in_cluster(extent, offset);
        n_bytes = MIN(bytes, extent->cluster_sectors * BDRV_SECTOR_SIZE
                             - offset_in_cluster);
Loading