static int dmg_open(BlockDriverState *bs, QDict *options, int flags,

                    Error **errp)

{

    BDRVDMGState *s = bs->opaque;

    DmgHeaderState ds;

    uint64_t rsrc_fork_offset, rsrc_fork_length;

    uint64_t plist_xml_offset, plist_xml_length;

    int64_t offset;

    int ret;



    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,

                               false, errp);

    if (!bs->file) {

        return -EINVAL;

    }



    block_module_load_one("dmg-bz2");

    bs->read_only = true;



    s->n_chunks = 0;

    s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;

    

    ds.data_fork_offset = 0;

    ds.max_compressed_size = 1;

    ds.max_sectors_per_chunk = 1;



    

    offset = dmg_find_koly_offset(bs->file, errp);

    if (offset < 0) {

        ret = offset;

        goto fail;

    }



    

    ret = read_uint64(bs, offset + 0x18, &ds.data_fork_offset);

    if (ret < 0) {

        goto fail;

    } else if (ds.data_fork_offset > offset) {

        ret = -EINVAL;

        goto fail;

    }



    

    ret = read_uint64(bs, offset + 0x28, &rsrc_fork_offset);

    if (ret < 0) {

        goto fail;

    }

    ret = read_uint64(bs, offset + 0x30, &rsrc_fork_length);

    if (ret < 0) {

        goto fail;

    }

    if (rsrc_fork_offset >= offset ||

        rsrc_fork_length > offset - rsrc_fork_offset) {

        ret = -EINVAL;

        goto fail;

    }

    

    ret = read_uint64(bs, offset + 0xd8, &plist_xml_offset);

    if (ret < 0) {

        goto fail;

    }

    ret = read_uint64(bs, offset + 0xe0, &plist_xml_length);

    if (ret < 0) {

        goto fail;

    }

    if (plist_xml_offset >= offset ||

        plist_xml_length > offset - plist_xml_offset) {

        ret = -EINVAL;

        goto fail;

    }

    ret = read_uint64(bs, offset + 0x1ec, (uint64_t *)&bs->total_sectors);

    if (ret < 0) {

        goto fail;

    }

    if (bs->total_sectors < 0) {

        ret = -EINVAL;

        goto fail;

    }

    if (rsrc_fork_length != 0) {

        ret = dmg_read_resource_fork(bs, &ds,

                                     rsrc_fork_offset, rsrc_fork_length);

        if (ret < 0) {

            goto fail;

        }

    } else if (plist_xml_length != 0) {

        ret = dmg_read_plist_xml(bs, &ds, plist_xml_offset, plist_xml_length);

        if (ret < 0) {

            goto fail;

        }

    } else {

        ret = -EINVAL;

        goto fail;

    }



    

    s->compressed_chunk = qemu_try_blockalign(bs->file->bs,

                                              ds.max_compressed_size + 1);

    s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs,

                                                512 * ds.max_sectors_per_chunk);

    if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) {

        ret = -ENOMEM;

        goto fail;

    }



    if (inflateInit(&s->zstream) != Z_OK) {

        ret = -EINVAL;

        goto fail;

    }



    s->current_chunk = s->n_chunks;



    qemu_co_mutex_init(&s->lock);

    return 0;



fail:

    g_free(s->types);

    g_free(s->offsets);

    g_free(s->lengths);

    g_free(s->sectors);

    g_free(s->sectorcounts);

    qemu_vfree(s->compressed_chunk);

    qemu_vfree(s->uncompressed_chunk);

    return ret;

}
