static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)

{

    IscsiLun *iscsilun = bs->opaque;

    struct iscsi_context *iscsi = NULL;

    struct iscsi_url *iscsi_url = NULL;

    struct IscsiTask task;

    char *initiator_name = NULL;

    int ret;



    if ((BDRV_SECTOR_SIZE % 512) != 0) {

        error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "

                     "BDRV_SECTOR_SIZE(%lld) is not a multiple "

                     "of 512", BDRV_SECTOR_SIZE);

        return -EINVAL;

    }



    iscsi_url = iscsi_parse_full_url(iscsi, filename);

    if (iscsi_url == NULL) {

        error_report("Failed to parse URL : %s %s", filename,

                     iscsi_get_error(iscsi));

        ret = -EINVAL;

        goto out;

    }



    memset(iscsilun, 0, sizeof(IscsiLun));



    initiator_name = parse_initiator_name(iscsi_url->target);



    iscsi = iscsi_create_context(initiator_name);

    if (iscsi == NULL) {

        error_report("iSCSI: Failed to create iSCSI context.");

        ret = -ENOMEM;

        goto out;

    }



    if (iscsi_set_targetname(iscsi, iscsi_url->target)) {

        error_report("iSCSI: Failed to set target name.");

        ret = -EINVAL;

        goto out;

    }



    if (iscsi_url->user != NULL) {

        ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,

                                              iscsi_url->passwd);

        if (ret != 0) {

            error_report("Failed to set initiator username and password");

            ret = -EINVAL;

            goto out;

        }

    }



    /* check if we got CHAP username/password via the options */

    if (parse_chap(iscsi, iscsi_url->target) != 0) {

        error_report("iSCSI: Failed to set CHAP user/password");

        ret = -EINVAL;

        goto out;

    }



    if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {

        error_report("iSCSI: Failed to set session type to normal.");

        ret = -EINVAL;

        goto out;

    }



    iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);



    /* check if we got HEADER_DIGEST via the options */

    parse_header_digest(iscsi, iscsi_url->target);



    task.iscsilun = iscsilun;

    task.status = 0;

    task.complete = 0;

    task.bs = bs;



    iscsilun->iscsi = iscsi;

    iscsilun->lun   = iscsi_url->lun;



    if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,

                                 iscsi_connect_cb, &task)

        != 0) {

        error_report("iSCSI: Failed to start async connect.");

        ret = -EINVAL;

        goto out;

    }



    while (!task.complete) {

        iscsi_set_events(iscsilun);

        qemu_aio_wait();

    }

    if (task.status != 0) {

        error_report("iSCSI: Failed to connect to LUN : %s",

                     iscsi_get_error(iscsi));

        ret = -EINVAL;

        goto out;

    }



    /* Medium changer or tape. We dont have any emulation for this so this must

     * be sg ioctl compatible. We force it to be sg, otherwise qemu will try

     * to read from the device to guess the image format.

     */

    if (iscsilun->type == TYPE_MEDIUM_CHANGER ||

        iscsilun->type == TYPE_TAPE) {

        bs->sg = 1;

    }



    ret = 0;



out:

    if (initiator_name != NULL) {

        g_free(initiator_name);

    }

    if (iscsi_url != NULL) {

        iscsi_destroy_url(iscsi_url);

    }



    if (ret) {

        if (iscsi != NULL) {

            iscsi_destroy_context(iscsi);

        }

        memset(iscsilun, 0, sizeof(IscsiLun));

    }

    return ret;

}
