static int vmdk_open_vmdk4(BlockDriverState *bs,

                           BlockDriverState *file,

                           int flags, Error **errp)

{

    int ret;

    uint32_t magic;

    uint32_t l1_size, l1_entry_sectors;

    VMDK4Header header;

    VmdkExtent *extent;

    BDRVVmdkState *s = bs->opaque;

    int64_t l1_backup_offset = 0;



    ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));

    if (ret < 0) {

        error_setg_errno(errp, -ret,

                         "Could not read header from file '%s'",

                         file->filename);

        return -EINVAL;

    }

    if (header.capacity == 0) {

        uint64_t desc_offset = le64_to_cpu(header.desc_offset);

        if (desc_offset) {

            char *buf = vmdk_read_desc(file, desc_offset << 9, errp);

            if (!buf) {

                return -EINVAL;

            }

            ret = vmdk_open_desc_file(bs, flags, buf, errp);

            g_free(buf);

            return ret;

        }

    }



    if (!s->create_type) {

        s->create_type = g_strdup("monolithicSparse");

    }



    if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) {

        /*

         * The footer takes precedence over the header, so read it in. The

         * footer starts at offset -1024 from the end: One sector for the

         * footer, and another one for the end-of-stream marker.

         */

        struct {

            struct {

                uint64_t val;

                uint32_t size;

                uint32_t type;

                uint8_t pad[512 - 16];

            } QEMU_PACKED footer_marker;



            uint32_t magic;

            VMDK4Header header;

            uint8_t pad[512 - 4 - sizeof(VMDK4Header)];



            struct {

                uint64_t val;

                uint32_t size;

                uint32_t type;

                uint8_t pad[512 - 16];

            } QEMU_PACKED eos_marker;

        } QEMU_PACKED footer;



        ret = bdrv_pread(file,

            bs->file->total_sectors * 512 - 1536,

            &footer, sizeof(footer));

        if (ret < 0) {

            error_setg_errno(errp, -ret, "Failed to read footer");

            return ret;

        }



        /* Some sanity checks for the footer */

        if (be32_to_cpu(footer.magic) != VMDK4_MAGIC ||

            le32_to_cpu(footer.footer_marker.size) != 0  ||

            le32_to_cpu(footer.footer_marker.type) != MARKER_FOOTER ||

            le64_to_cpu(footer.eos_marker.val) != 0  ||

            le32_to_cpu(footer.eos_marker.size) != 0  ||

            le32_to_cpu(footer.eos_marker.type) != MARKER_END_OF_STREAM)

        {

            error_setg(errp, "Invalid footer");

            return -EINVAL;

        }



        header = footer.header;

    }



    if (le32_to_cpu(header.version) > 3) {

        char buf[64];

        snprintf(buf, sizeof(buf), "VMDK version %" PRId32,

                 le32_to_cpu(header.version));

        error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,

                  bdrv_get_device_or_node_name(bs), "vmdk", buf);

        return -ENOTSUP;

    } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {

        /* VMware KB 2064959 explains that version 3 added support for

         * persistent changed block tracking (CBT), and backup software can

         * read it as version=1 if it doesn't care about the changed area

         * information. So we are safe to enable read only. */

        error_setg(errp, "VMDK version 3 must be read only");

        return -EINVAL;

    }



    if (le32_to_cpu(header.num_gtes_per_gt) > 512) {

        error_setg(errp, "L2 table size too big");

        return -EINVAL;

    }



    l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gt)

                        * le64_to_cpu(header.granularity);

    if (l1_entry_sectors == 0) {

        error_setg(errp, "L1 entry size is invalid");

        return -EINVAL;

    }

    l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)

                / l1_entry_sectors;

    if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {

        l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;

    }

    if (bdrv_nb_sectors(file) < le64_to_cpu(header.grain_offset)) {

        error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes",

                   (int64_t)(le64_to_cpu(header.grain_offset)

                             * BDRV_SECTOR_SIZE));

        return -EINVAL;

    }



    ret = vmdk_add_extent(bs, file, false,

                          le64_to_cpu(header.capacity),

                          le64_to_cpu(header.gd_offset) << 9,

                          l1_backup_offset,

                          l1_size,

                          le32_to_cpu(header.num_gtes_per_gt),

                          le64_to_cpu(header.granularity),

                          &extent,

                          errp);

    if (ret < 0) {

        return ret;

    }

    extent->compressed =

        le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;

    if (extent->compressed) {

        g_free(s->create_type);

        s->create_type = g_strdup("streamOptimized");

    }

    extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;

    extent->version = le32_to_cpu(header.version);

    extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;

    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_vmdk4(BlockDriverState *bs,

                           BlockDriverState *file,

                           int flags, Error **errp)

{

    int ret;

    uint32_t magic;

    uint32_t l1_size, l1_entry_sectors;

    VMDK4Header header;

    VmdkExtent *extent;

    BDRVVmdkState *s = bs->opaque;

    int64_t l1_backup_offset = 0;



    ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));

    if (ret < 0) {

        error_setg_errno(errp, -ret,

                         "Could not read header from file '%s'",

                         file->filename);

        return -EINVAL;

    }

    if (header.capacity == 0) {

        uint64_t desc_offset = le64_to_cpu(header.desc_offset);

        if (desc_offset) {

            char *buf = vmdk_read_desc(file, desc_offset << 9, errp);

            if (!buf) {

                return -EINVAL;

            }

            ret = vmdk_open_desc_file(bs, flags, buf, errp);

            g_free(buf);

            return ret;

        }

    }



    if (!s->create_type) {

        s->create_type = g_strdup("monolithicSparse");

    }



    if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) {

        









        struct {

            struct {

                uint64_t val;

                uint32_t size;

                uint32_t type;

                uint8_t pad[512 - 16];

            } QEMU_PACKED footer_marker;



            uint32_t magic;

            VMDK4Header header;

            uint8_t pad[512 - 4 - sizeof(VMDK4Header)];



            struct {

                uint64_t val;

                uint32_t size;

                uint32_t type;

                uint8_t pad[512 - 16];

            } QEMU_PACKED eos_marker;

        } QEMU_PACKED footer;



        ret = bdrv_pread(file,

            bs->file->total_sectors * 512 - 1536,

            &footer, sizeof(footer));

        if (ret < 0) {

            error_setg_errno(errp, -ret, "Failed to read footer");

            return ret;

        }



        

        if (be32_to_cpu(footer.magic) != VMDK4_MAGIC ||

            le32_to_cpu(footer.footer_marker.size) != 0  ||

            le32_to_cpu(footer.footer_marker.type) != MARKER_FOOTER ||

            le64_to_cpu(footer.eos_marker.val) != 0  ||

            le32_to_cpu(footer.eos_marker.size) != 0  ||

            le32_to_cpu(footer.eos_marker.type) != MARKER_END_OF_STREAM)

        {

            error_setg(errp, "Invalid footer");

            return -EINVAL;

        }



        header = footer.header;

    }



    if (le32_to_cpu(header.version) > 3) {

        char buf[64];

        snprintf(buf, sizeof(buf), "VMDK version %" PRId32,

                 le32_to_cpu(header.version));

        error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,

                  bdrv_get_device_or_node_name(bs), "vmdk", buf);

        return -ENOTSUP;

    } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {

        







        error_setg(errp, "VMDK version 3 must be read only");

        return -EINVAL;

    }



    if (le32_to_cpu(header.num_gtes_per_gt) > 512) {

        error_setg(errp, "L2 table size too big");

        return -EINVAL;

    }



    l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gt)

                        * le64_to_cpu(header.granularity);

    if (l1_entry_sectors == 0) {

        error_setg(errp, "L1 entry size is invalid");

        return -EINVAL;

    }

    l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)

                / l1_entry_sectors;

    if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {

        l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;

    }

    if (bdrv_nb_sectors(file) < le64_to_cpu(header.grain_offset)) {

        error_setg(errp, "File truncated, expecting at least %" PRId64 " bytes",

                   (int64_t)(le64_to_cpu(header.grain_offset)

                             * BDRV_SECTOR_SIZE));

        return -EINVAL;

    }



    ret = vmdk_add_extent(bs, file, false,

                          le64_to_cpu(header.capacity),

                          le64_to_cpu(header.gd_offset) << 9,

                          l1_backup_offset,

                          l1_size,

                          le32_to_cpu(header.num_gtes_per_gt),

                          le64_to_cpu(header.granularity),

                          &extent,

                          errp);

    if (ret < 0) {

        return ret;

    }

    extent->compressed =

        le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;

    if (extent->compressed) {

        g_free(s->create_type);

        s->create_type = g_strdup("streamOptimized");

    }

    extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;

    extent->version = le32_to_cpu(header.version);

    extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;

    ret = vmdk_init_tables(bs, extent, errp);

    if (ret) {

        

        vmdk_free_last_extent(bs);

    }

    return ret;

}

static int FUNC_0(BlockDriverState *VAR_0,

                           BlockDriverState *VAR_1,

                           int VAR_2, Error **VAR_3)

{

    int VAR_4;

    uint32_t magic;

    uint32_t l1_size, l1_entry_sectors;

    VMDK4Header header;

    VmdkExtent *extent;

    BDRVVmdkState *s = VAR_0->opaque;

    int64_t l1_backup_offset = 0;



    VAR_4 = bdrv_pread(VAR_1, sizeof(magic), &header, sizeof(header));

    if (VAR_4 < 0) {

        error_setg_errno(VAR_3, -VAR_4,

                         "Could not read header from VAR_1 '%s'",

                         VAR_1->filename);

        return -EINVAL;

    }

    if (header.capacity == 0) {

        uint64_t desc_offset = le64_to_cpu(header.desc_offset);

        if (desc_offset) {

            char *VAR_7 = vmdk_read_desc(VAR_1, desc_offset << 9, VAR_3);

            if (!VAR_7) {

                return -EINVAL;

            }

            VAR_4 = vmdk_open_desc_file(VAR_0, VAR_2, VAR_7, VAR_3);

            g_free(VAR_7);

            return VAR_4;

        }

    }



    if (!s->create_type) {

        s->create_type = g_strdup("monolithicSparse");

    }



    if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) {

        









        struct {

            struct {

                uint64_t val;

                uint32_t size;

                uint32_t type;

                uint8_t pad[512 - 16];

            } QEMU_PACKED footer_marker;



            uint32_t magic;

            VMDK4Header header;

            uint8_t pad[512 - 4 - sizeof(VMDK4Header)];



            struct {

                uint64_t val;

                uint32_t size;

                uint32_t type;

                uint8_t pad[512 - 16];

            } QEMU_PACKED eos_marker;

        } QEMU_PACKED VAR_6;



        VAR_4 = bdrv_pread(VAR_1,

            VAR_0->VAR_1->total_sectors * 512 - 1536,

            &VAR_6, sizeof(VAR_6));

        if (VAR_4 < 0) {

            error_setg_errno(VAR_3, -VAR_4, "Failed to read VAR_6");

            return VAR_4;

        }



        

        if (be32_to_cpu(VAR_6.magic) != VMDK4_MAGIC ||

            le32_to_cpu(VAR_6.footer_marker.size) != 0  ||

            le32_to_cpu(VAR_6.footer_marker.type) != MARKER_FOOTER ||

            le64_to_cpu(VAR_6.eos_marker.val) != 0  ||

            le32_to_cpu(VAR_6.eos_marker.size) != 0  ||

            le32_to_cpu(VAR_6.eos_marker.type) != MARKER_END_OF_STREAM)

        {

            error_setg(VAR_3, "Invalid VAR_6");

            return -EINVAL;

        }



        header = VAR_6.header;

    }



    if (le32_to_cpu(header.version) > 3) {

        char VAR_7[64];

        snprintf(VAR_7, sizeof(VAR_7), "VMDK version %" PRId32,

                 le32_to_cpu(header.version));

        error_set(VAR_3, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,

                  bdrv_get_device_or_node_name(VAR_0), "vmdk", VAR_7);

        return -ENOTSUP;

    } else if (le32_to_cpu(header.version) == 3 && (VAR_2 & BDRV_O_RDWR)) {

        







        error_setg(VAR_3, "VMDK version 3 must be read only");

        return -EINVAL;

    }



    if (le32_to_cpu(header.num_gtes_per_gt) > 512) {

        error_setg(VAR_3, "L2 table size too big");

        return -EINVAL;

    }



    l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gt)

                        * le64_to_cpu(header.granularity);

    if (l1_entry_sectors == 0) {

        error_setg(VAR_3, "L1 entry size is invalid");

        return -EINVAL;

    }

    l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)

                / l1_entry_sectors;

    if (le32_to_cpu(header.VAR_2) & VMDK4_FLAG_RGD) {

        l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;

    }

    if (bdrv_nb_sectors(VAR_1) < le64_to_cpu(header.grain_offset)) {

        error_setg(VAR_3, "File truncated, expecting at least %" PRId64 " bytes",

                   (int64_t)(le64_to_cpu(header.grain_offset)

                             * BDRV_SECTOR_SIZE));

        return -EINVAL;

    }



    VAR_4 = vmdk_add_extent(VAR_0, VAR_1, false,

                          le64_to_cpu(header.capacity),

                          le64_to_cpu(header.gd_offset) << 9,

                          l1_backup_offset,

                          l1_size,

                          le32_to_cpu(header.num_gtes_per_gt),

                          le64_to_cpu(header.granularity),

                          &extent,

                          VAR_3);

    if (VAR_4 < 0) {

        return VAR_4;

    }

    extent->compressed =

        le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;

    if (extent->compressed) {

        g_free(s->create_type);

        s->create_type = g_strdup("streamOptimized");

    }

    extent->has_marker = le32_to_cpu(header.VAR_2) & VMDK4_FLAG_MARKER;

    extent->version = le32_to_cpu(header.version);

    extent->has_zero_grain = le32_to_cpu(header.VAR_2) & VMDK4_FLAG_ZERO_GRAIN;

    VAR_4 = vmdk_init_tables(VAR_0, extent, VAR_3);

    if (VAR_4) {

        

        vmdk_free_last_extent(VAR_0);

    }

    return VAR_4;

}

static int FUNC_0(BlockDriverState *VAR_0,
BlockDriverState *VAR_1,
int VAR_2, Error **VAR_3)
{ int VAR_4; uint32_t magic; uint32_t l1_size, l1_entry_sectors; VMDK4Header header; VmdkExtent *extent; BDRVVmdkState *s = VAR_0->opaque; int64_t l1_backup_offset = 0; VAR_4 = bdrv_pread(VAR_1, sizeof(magic), &header, sizeof(header)); if (VAR_4 < 0) { error_setg_errno(VAR_3, -VAR_4,
"Could not read header from VAR_1 '%s'",
VAR_1->filename); return -EINVAL; } if (header.capacity == 0) { uint64_t desc_offset = le64_to_cpu(header.desc_offset); if (desc_offset) { char *VAR_7 = vmdk_read_desc(VAR_1, desc_offset << 9, VAR_3); if (!VAR_7) { return -EINVAL; } VAR_4 = vmdk_open_desc_file(VAR_0, VAR_2, VAR_7, VAR_3); g_free(VAR_7); return VAR_4; } } if (!s->create_type) { s->create_type = g_strdup("monolithicSparse"); } if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) { struct { struct { uint64_t val; uint32_t size; uint32_t type; uint8_t pad[512 - 16]; } QEMU_PACKED footer_marker; uint32_t magic; VMDK4Header header; uint8_t pad[512 - 4 - sizeof(VMDK4Header)]; struct { uint64_t val; uint32_t size; uint32_t type; uint8_t pad[512 - 16]; } QEMU_PACKED eos_marker; } QEMU_PACKED VAR_6; VAR_4 = bdrv_pread(VAR_1,
VAR_0->VAR_1->total_sectors * 512 - 1536,
&VAR_6, sizeof(VAR_6)); if (VAR_4 < 0) { error_setg_errno(VAR_3, -VAR_4, "Failed to read VAR_6"); return VAR_4; } if (be32_to_cpu(VAR_6.magic) != VMDK4_MAGIC ||
le32_to_cpu(VAR_6.footer_marker.size) != 0  ||
le32_to_cpu(VAR_6.footer_marker.type) != MARKER_FOOTER ||
le64_to_cpu(VAR_6.eos_marker.val) != 0  ||
le32_to_cpu(VAR_6.eos_marker.size) != 0  ||
le32_to_cpu(VAR_6.eos_marker.type) != MARKER_END_OF_STREAM)
{ error_setg(VAR_3, "Invalid VAR_6"); return -EINVAL; } header = VAR_6.header; } if (le32_to_cpu(header.version) > 3) { char VAR_7[64]; snprintf(VAR_7, sizeof(VAR_7), "VMDK version %" PRId32,
le32_to_cpu(header.version)); error_set(VAR_3, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bdrv_get_device_or_node_name(VAR_0), "vmdk", VAR_7); return -ENOTSUP; } else if (le32_to_cpu(header.version) == 3 && (VAR_2 & BDRV_O_RDWR)) { error_setg(VAR_3, "VMDK version 3 must be read only"); return -EINVAL; } if (le32_to_cpu(header.num_gtes_per_gt) > 512) { error_setg(VAR_3, "L2 table size too big"); return -EINVAL; } l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gt)
* le64_to_cpu(header.granularity); if (l1_entry_sectors == 0) { error_setg(VAR_3, "L1 entry size is invalid"); return -EINVAL; } l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
/ l1_entry_sectors; if (le32_to_cpu(header.VAR_2) & VMDK4_FLAG_RGD) { l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9; } if (bdrv_nb_sectors(VAR_1) < le64_to_cpu(header.grain_offset)) { error_setg(VAR_3, "File truncated, expecting at least %" PRId64 " bytes",
(int64_t)(le64_to_cpu(header.grain_offset)
* BDRV_SECTOR_SIZE)); return -EINVAL; } VAR_4 = vmdk_add_extent(VAR_0, VAR_1, false,
le64_to_cpu(header.capacity),
le64_to_cpu(header.gd_offset) << 9,
l1_backup_offset,
l1_size,
le32_to_cpu(header.num_gtes_per_gt),
le64_to_cpu(header.granularity),
&extent,
VAR_3); if (VAR_4 < 0) { return VAR_4; } extent->compressed =
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE; if (extent->compressed) { g_free(s->create_type); s->create_type = g_strdup("streamOptimized"); } extent->has_marker = le32_to_cpu(header.VAR_2) & VMDK4_FLAG_MARKER; extent->version = le32_to_cpu(header.version); extent->has_zero_grain = le32_to_cpu(header.VAR_2) & VMDK4_FLAG_ZERO_GRAIN; VAR_4 = vmdk_init_tables(VAR_0, extent, VAR_3); if (VAR_4) { vmdk_free_last_extent(VAR_0); } return VAR_4; }