From patchwork Wed Aug 29 11:05:24 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wayne Xia X-Patchwork-Id: 180683 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E67A62C0336 for ; Wed, 29 Aug 2012 21:05:53 +1000 (EST) Received: from localhost ([::1]:57614 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T6g5n-0006UQ-Et for incoming@patchwork.ozlabs.org; Wed, 29 Aug 2012 07:05:51 -0400 Received: from eggs.gnu.org ([208.118.235.92]:37198) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T6g5Y-0006HX-IP for qemu-devel@nongnu.org; Wed, 29 Aug 2012 07:05:39 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1T6g5R-0007jT-Ue for qemu-devel@nongnu.org; Wed, 29 Aug 2012 07:05:36 -0400 Received: from e28smtp09.in.ibm.com ([122.248.162.9]:47899) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T6g5Q-0007hP-NL for qemu-devel@nongnu.org; Wed, 29 Aug 2012 07:05:29 -0400 Received: from /spool/local by e28smtp09.in.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 29 Aug 2012 16:35:26 +0530 Received: from d28relay05.in.ibm.com (9.184.220.62) by e28smtp09.in.ibm.com (192.168.1.139) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 29 Aug 2012 16:35:25 +0530 Received: from d28av01.in.ibm.com (d28av01.in.ibm.com [9.184.220.63]) by d28relay05.in.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id q7TB5OhR34996236 for ; Wed, 29 Aug 2012 16:35:24 +0530 Received: from d28av01.in.ibm.com (loopback [127.0.0.1]) by d28av01.in.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id q7TGYxb0008018 for ; Wed, 29 Aug 2012 22:04:59 +0530 Received: from RedHat62GAWSWenchao (wenchaox.cn.ibm.com [9.115.122.69]) by d28av01.in.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id q7TGYtAc007710; Wed, 29 Aug 2012 22:04:57 +0530 From: Wenchao Xia To: qemu-devel@nongnu.org Date: Wed, 29 Aug 2012 19:05:24 +0800 Message-Id: <1346238324-24557-1-git-send-email-xiawenc@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.1 x-cbid: 12082911-2674-0000-0000-000005C66ECD X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 122.248.162.9 Cc: kwolf@redhat.com, aliguori@us.ibm.com, stefanha@gmail.com, Wenchao Xia , blauwirbel@gmail.com, pbonzini@redhat.com, eblake@redhat.com, afaerber@suse.de Subject: [Qemu-devel] [PATCH v3 3/5] [RFC] libqblock, implemention major X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This is the major part of the implemetion. Many codes are spent on handling different formats, translating it from enum types to qemu internal strings. From user's perspective, he will always spent codes on handling format types, with enum type instead of string, it will be easier for him. Signed-off-by: Wenchao Xia --- libqblock/libqblock.c | 1031 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1031 insertions(+), 0 deletions(-) create mode 100644 libqblock/libqblock.c diff --git a/libqblock/libqblock.c b/libqblock/libqblock.c new file mode 100644 index 0000000..f4e106f --- /dev/null +++ b/libqblock/libqblock.c @@ -0,0 +1,1031 @@ +/* + * QEMU block layer library + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Wenchao Xia + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include +#include + +#include "libqblock.h" +#include "block.h" +#include "block_int.h" +#include "qemu-aio.h" + +#define SECTOR_SIZE (512) +#define SECTOR_SIZE_MASK (0x01ff) +#define SECTOR_SIZE_BITS_NUM (9) +#define FUNC_FREE free +#define FUNC_MALLOC malloc +#define FUNC_CALLOC calloc + +#define CLEAN_FREE(p) { \ + FUNC_FREE(p); \ + (p) = NULL; \ +} + +/* details should be hidden to user */ +struct QBlockState { + BlockDriverState *bdrvs; + /* internal used file name now, if it is not NULL, it means + image was opened. + */ + char *filename; +}; + +#define QB_ERR_STRING_SIZE (1024) +struct QBroker { + /* last error */ + char err_msg[QB_ERR_STRING_SIZE]; + int err_ret; /* last error return of libqblock. */ + int err_no; /* 2nd level of error, errno what below reports */ +}; + +struct QBAioReq { + BlockDriverAIOCB *bd_aio_int; +}; + +static void set_err_nomem(struct QBroker *broker) +{ + broker->err_ret = QB_ERR_MEM_ERR; + broker->err_no = 0; + snprintf(broker->err_msg, sizeof(broker->err_msg), + "Not enough of memory."); +} + +static void set_err_internal(struct QBroker *broker, const char *fmt, ...) +{ + va_list ap; + + broker->err_ret = QB_ERR_INTERNAL_ERR; + broker->err_no = -errno; + + va_start(ap, fmt); + vsnprintf(broker->err_msg, sizeof(broker->err_msg), fmt, ap); + va_end(ap); +} + +static void set_err_inv_param(struct QBroker *broker, const char *fmt, ...) +{ + va_list ap; + + broker->err_ret = QB_ERR_INVALID_PARAM; + broker->err_no = 0; + + va_start(ap, fmt); + vsnprintf(broker->err_msg, sizeof(broker->err_msg), fmt, ap); + va_end(ap); +} + +void libqblock_init(void) +{ + bdrv_init(); + qemu_init_main_loop(); +} + +int qb_broker_new(struct QBroker **broker) +{ + *broker = FUNC_CALLOC(1, sizeof(struct QBroker)); + if (*broker == NULL) { + return QB_ERR_MEM_ERR; + } + return 0; +} + +void qb_broker_delete(struct QBroker **broker) +{ + CLEAN_FREE(*broker); + return; +} + +int qb_state_new(struct QBroker *broker, + struct QBlockState **qbs) +{ + *qbs = FUNC_CALLOC(1, sizeof(struct QBlockState)); + if (*qbs == NULL) { + set_err_nomem(broker); + return broker->err_ret; + } + (*qbs)->bdrvs = bdrv_new("hda"); + if ((*qbs)->bdrvs == NULL) { + CLEAN_FREE(*qbs); + set_err_internal(broker, "failed to create the driver."); + return broker->err_ret; + } + return 0; +} + +void qb_state_delete(struct QBroker *broker, + struct QBlockState **qbs) +{ + CLEAN_FREE((*qbs)->filename); + if ((*qbs)->bdrvs != NULL) { + bdrv_delete((*qbs)->bdrvs); + (*qbs)->bdrvs = NULL; + } + CLEAN_FREE(*qbs); + return; +} + +static const char *fmt2str(enum QBlockFormat fmt) +{ + const char *ret = NULL; + switch (fmt) { + case QB_FMT_COW: + ret = "cow"; + break; + case QB_FMT_QED: + ret = "qed"; + break; + case QB_FMT_QCOW: + ret = "qcow"; + break; + case QB_FMT_QCOW2: + ret = "qcow2"; + break; + case QB_FMT_RAW: + ret = "raw"; + break; + case QB_FMT_RBD: + ret = "rbd"; + break; + case QB_FMT_SHEEPDOG: + ret = "sheepdog"; + break; + case QB_FMT_VDI: + ret = "vdi"; + break; + case QB_FMT_VMDK: + ret = "vmdk"; + break; + case QB_FMT_VPC: + ret = "vpc"; + break; + default: + break; + } + return ret; +} + +static enum QBlockFormat str2fmt(const char *fmt) +{ + enum QBlockFormat ret = QB_FMT_NONE; + if (0 == strcmp(fmt, "cow")) { + ret = QB_FMT_COW; + } else if (0 == strcmp(fmt, "qed")) { + ret = QB_FMT_QED; + } else if (0 == strcmp(fmt, "qcow")) { + ret = QB_FMT_QCOW; + } else if (0 == strcmp(fmt, "qcow2")) { + ret = QB_FMT_QCOW2; + } else if (0 == strcmp(fmt, "raw")) { + ret = QB_FMT_RAW; + } else if (0 == strcmp(fmt, "rbd")) { + ret = QB_FMT_RBD; + } else if (0 == strcmp(fmt, "sheepdog")) { + ret = QB_FMT_SHEEPDOG; + } else if (0 == strcmp(fmt, "vdi")) { + ret = QB_FMT_VDI; + } else if (0 == strcmp(fmt, "vmdk")) { + ret = QB_FMT_VMDK; + } else if (0 == strcmp(fmt, "vpc")) { + ret = QB_FMT_VPC; + } + return ret; +} + +int qb_ol_new(struct QBroker *broker, + struct QBlockOptionLoc **op) +{ + *op = FUNC_CALLOC(1, sizeof(struct QBlockOptionLoc)); + if (*op == NULL) { + set_err_nomem(broker); + return broker->err_ret; + } + return 0; +} + +void qb_ol_delete(struct QBroker *broker, + struct QBlockOptionLoc **op) +{ + CLEAN_FREE(*op); +} + +int qb_of_new(struct QBroker *broker, + struct QBlockOptionFormat **op) +{ + *op = FUNC_CALLOC(1, sizeof(struct QBlockOptionFormat)); + if (*op == NULL) { + set_err_nomem(broker); + return broker->err_ret; + } + return 0; +} + +void qb_of_delete(struct QBroker *broker, + struct QBlockOptionFormat **op) +{ + CLEAN_FREE(*op); +} + +/* return 0 if every thing is fine */ +static int loc_check_params(struct QBroker *broker, + struct QBlockOptionLoc *loc) +{ + broker->err_ret = 0; + + switch (loc->prot_type) { + case QB_PROTO_FILE: + if (loc->prot_op.o_file.filename == NULL) { + set_err_inv_param(broker, "Filename was not set."); + goto out; + } + if (path_has_protocol(loc->prot_op.o_file.filename) > 0) { + set_err_inv_param(broker, + "filename [%s] had protocol.", + loc->prot_op.o_file.filename); + goto out; + } + break; + default: + set_err_inv_param(broker, "Protocol type [%d] was not valid.", + loc->prot_type); + break; + } + + out: + return broker->err_ret; +} + +/* translate loc structure to internal filename, returned char* need free, + * assuming filename is not NULL. *filename would be set to NULL if no valid + * filename found. *filename must be freed later. + * return 0 if no error with *filename set. + */ +static int loc2filename(struct QBroker *broker, + struct QBlockOptionLoc *loc, + char **filename) +{ + broker->err_ret = 0; + + if (*filename != NULL) { + CLEAN_FREE(*filename); + } + switch (loc->prot_type) { + case QB_PROTO_FILE: + *filename = strdup(loc->prot_op.o_file.filename); + if (*filename == NULL) { + set_err_nomem(broker); + } + break; + default: + set_err_inv_param(broker, + "protocol type [%d] is not supported.", + loc->prot_type); + break; + } + + return broker->err_ret; +} + +/* translate filename to location, loc->prot_type = NONE if fail, filename + must be valid. loc internal char pointer must be freed later. + * return 0 if no error. + */ +static int filename2loc(struct QBroker *broker, + struct QBlockOptionLoc *loc, + const char *filename) +{ + broker->err_ret = 0; + + if (path_has_protocol(filename) > 0) { + set_err_inv_param(broker, + "Filename [%s] had protocol, not supportted now.", + filename); + goto out; + } + + loc->prot_type = QB_PROTO_FILE; + switch (loc->prot_type) { + case QB_PROTO_FILE: + loc->prot_op.o_file.filename = strdup(filename); + if (loc->prot_op.o_file.filename == NULL) { + set_err_nomem(broker); + goto out; + } + break; + default: + break; + } + + out: + return broker->err_ret; +} + +/* return 0 if OK, or qblock error number */ +static int set_backing_file_options(struct QBroker *broker, + QEMUOptionParameter *param, + struct QBlockOptionLoc *loc, + enum QBlockFormat *fmt) +{ + char *backing_filename = NULL; + const char *fmtstr_backing = NULL; + int ret = 0; + + if (loc == NULL) { + goto out; + } + + ret = loc2filename(broker, loc, &backing_filename); + /* ret can < 0 if loc have not been set, mean user did not specify backing + file */ + if (ret == QB_ERR_MEM_ERR) { + goto out; + } + ret = 0; + + if (backing_filename) { + ret = set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, backing_filename); + assert(ret == 0); + if (fmt == NULL) { + goto out; + } + fmtstr_backing = fmt2str(*fmt); + if (fmtstr_backing) { + ret = set_option_parameter(param, + BLOCK_OPT_BACKING_FMT, fmtstr_backing); + assert(ret == 0); + } + } + + out: + FUNC_FREE(backing_filename); + return ret; +} + +int qb_create(struct QBroker *broker, + struct QBlockState *qbs, + struct QBlockOptionLoc *loc, + struct QBlockOptionFormat *fmt, + int flag) +{ + int ret = 0, bd_ret; + char *filename = NULL; + BlockDriverState *bs = NULL; + BlockDriver *drv = NULL, *backing_drv = NULL; + bool tmp_bool; + + const char *fmtstr = NULL, *tmp = NULL; + QEMUOptionParameter *param = NULL, *create_options = NULL; + QEMUOptionParameter *backing_fmt, *backing_file, *size; + struct QBlockOption_fmt_cow *o_cow = NULL; + struct QBlockOption_fmt_qed *o_qed = NULL; + struct QBlockOption_fmt_qcow *o_qcow = NULL; + struct QBlockOption_fmt_qcow2 *o_qcow2 = NULL; + struct QBlockOption_fmt_raw *o_raw = NULL; + struct QBlockOption_fmt_rbd *o_rbd = NULL; + struct QBlockOption_fmt_sheepdog *o_sd = NULL; + struct QBlockOption_fmt_vdi *o_vdi = NULL; + struct QBlockOption_fmt_vmdk *o_vmdk = NULL; + struct QBlockOption_fmt_vpc *o_vpc = NULL; + + + /* check parameters */ + if (flag & (~LIBQBLOCK_O_VALID_MASK)) { + set_err_inv_param(broker, "invalid flag was set."); + ret = broker->err_ret; + goto out; + } + + if ((loc == NULL) || (qbs == NULL) || (fmt == NULL)) { + set_err_inv_param(broker, + "Got unexpected NULL pointer in parameters."); + ret = broker->err_ret; + goto out; + } + + ret = loc_check_params(broker, loc); + if (ret != 0) { + goto out; + } + + /* internal translate */ + ret = loc2filename(broker, loc, &filename); + if (ret != 0) { + goto out; + } + + fmtstr = fmt2str(fmt->fmt_type); + if (fmtstr == NULL) { + set_err_inv_param(broker, + "Got unexpected NULL pointer in parameters."); + ret = broker->err_ret; + goto out; + } + + drv = bdrv_find_format(fmtstr); + assert(drv != NULL); + + create_options = append_option_parameters(create_options, + drv->create_options); + param = parse_option_parameters("", create_options, param); + + switch (fmt->fmt_type) { + case QB_FMT_COW: + o_cow = &(fmt->fmt_op.o_cow); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_cow->virt_size); + assert(bd_ret == 0); + /* do not need to check loc, it may be not set */ + ret = set_backing_file_options(broker, param, + &o_cow->backing_loc, NULL); + if (ret != 0) { + goto out; + } + break; + case QB_FMT_QED: + o_qed = &(fmt->fmt_op.o_qed); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_qed->virt_size); + assert(bd_ret == 0); + ret = set_backing_file_options(broker, param, + &o_qed->backing_loc, &o_qed->backing_fmt); + if (ret != 0) { + goto out; + } + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_qed->cluster_size); + assert(bd_ret == 0); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_TABLE_SIZE, o_qed->table_size); + assert(bd_ret == 0); + break; + case QB_FMT_QCOW: + o_qcow = &(fmt->fmt_op.o_qcow); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_qcow->virt_size); + assert(bd_ret == 0); + ret = set_backing_file_options(broker, param, + &o_qcow->backing_loc, NULL); + if (ret != 0) { + goto out; + } + tmp = o_qcow->encrypt ? "on" : "off"; + bd_ret = set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp); + assert(bd_ret == 0); + break; + case QB_FMT_QCOW2: + o_qcow2 = &(fmt->fmt_op.o_qcow2); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_qcow2->virt_size); + assert(bd_ret == 0); + ret = set_backing_file_options(broker, param, + &o_qcow2->backing_loc, &o_qcow2->backing_fmt); + if (ret != 0) { + goto out; + } + tmp = o_qcow2->encrypt ? "on" : "off"; + bd_ret = set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp); + assert(bd_ret == 0); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_qcow2->cluster_size); + assert(bd_ret == 0); + + if (o_qcow2->cpt_lv != QBO_FMT_QCOW2_CPT_NONE) { + tmp = o_qcow2->cpt_lv == QBO_FMT_QCOW2_CPT_V010 ? "0.10" : "1.1"; + bd_ret = set_option_parameter(param, + BLOCK_OPT_COMPAT_LEVEL, tmp); + assert(bd_ret == 0); + } + + if (o_qcow2->pre_mode != QBO_FMT_QCOW2_PREALLOC_NONE) { + tmp = o_qcow2->pre_mode == QBO_FMT_QCOW2_PREALLOC_OFF ? + "off" : "metadata"; + bd_ret = set_option_parameter(param, + BLOCK_OPT_PREALLOC, tmp); + assert(bd_ret == 0); + } + break; + + case QB_FMT_RAW: + o_raw = &(fmt->fmt_op.o_raw); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_raw->virt_size); + assert(bd_ret == 0); + break; + case QB_FMT_RBD: + o_rbd = &(fmt->fmt_op.o_rbd); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_rbd->virt_size); + assert(bd_ret == 0); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_rbd->cluster_size); + assert(bd_ret == 0); + break; + case QB_FMT_SHEEPDOG: + o_sd = &(fmt->fmt_op.o_sheepdog); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_sd->virt_size); + assert(bd_ret == 0); + ret = set_backing_file_options(broker, param, + &o_sd->backing_loc, NULL); + if (ret != 0) { + goto out; + } + if (o_sd->pre_mode != QBO_FMT_SD_PREALLOC_NONE) { + tmp = o_sd->pre_mode == QBO_FMT_SD_PREALLOC_OFF ? "off" : "full"; + bd_ret = set_option_parameter(param, + BLOCK_OPT_PREALLOC, tmp); + assert(bd_ret == 0); + } + break; + case QB_FMT_VDI: + o_vdi = &(fmt->fmt_op.o_vdi); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_vdi->virt_size); + assert(bd_ret == 0); + /* following option is not always valid depends on configuration */ + set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_vdi->cluster_size); + if (o_vdi->pre_mode != QBO_FMT_VDI_PREALLOC_NONE) { + tmp_bool = o_sd->pre_mode == QBO_FMT_VDI_PREALLOC_TRUE ? + true : false; + set_option_parameter_int(param, "static", tmp_bool); + } + break; + case QB_FMT_VMDK: + o_vmdk = &(fmt->fmt_op.o_vmdk); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_vmdk->virt_size); + assert(bd_ret == 0); + ret = set_backing_file_options(broker, param, + &o_vmdk->backing_loc, NULL); + if (ret != 0) { + goto out; + } + + if (o_vmdk->cpt_lv != QBO_FMT_VMDK_CPT_NONE) { + tmp_bool = o_vmdk->cpt_lv == QBO_FMT_VMDK_CPT_VMDKV6_TRUE ? + true : false; + bd_ret = set_option_parameter_int(param, BLOCK_OPT_COMPAT6, + tmp_bool); + assert(bd_ret == 0); + } + if (o_vmdk->subfmt != QBO_FMT_VMDK_SUBFMT_MONOLITHIC_NONE) { + switch (o_vmdk->subfmt) { + case QBO_FMT_VMDK_SUBFMT_MONOLITHIC_SPARSE: + tmp = "monolithicSparse"; + break; + case QBO_FMT_VMDK_SUBFMT_MONOLITHIC_FLAT: + tmp = "monolithicFlat"; + break; + case QBO_FMT_VMDK_SUBFMT_TWOGBMAX_EXTENT_SPARSE: + tmp = "twoGbMaxExtentSparse"; + break; + case QBO_FMT_VMDK_SUBFMT_TWOGBMAX_EXTENT_FLAT: + tmp = "twoGbMaxExtentFlat"; + break; + case QBO_FMT_VMDK_SUBFMT_STREAM_OPTIMIZED: + tmp = "streamOptimized"; + break; + default: + assert(false); + break; + } + bd_ret = set_option_parameter(param, + BLOCK_OPT_SUBFMT, tmp); + assert(bd_ret == 0); + } + break; + case QB_FMT_VPC: + o_vpc = &(fmt->fmt_op.o_vpc); + bd_ret = set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_vpc->virt_size); + assert(bd_ret == 0); + if (o_vpc->subfmt != QBO_FMT_VPC_SUBFMT_NONE) { + tmp = o_vpc->subfmt == QBO_FMT_VPC_SUBFMT_DYNAMIC ? + "dynamic" : "fixed"; + bd_ret = set_option_parameter(param, + BLOCK_OPT_SUBFMT, tmp); + assert(bd_ret == 0); + } + break; + default: + assert(false); + break; + } + + backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE); + if (backing_file && backing_file->value.s) { + if (!strcmp(filename, backing_file->value.s)) { + set_err_inv_param(broker, + "Backing file is the same with new file."); + ret = broker->err_ret; + goto out; + } + } + + backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT); + if (backing_fmt && backing_fmt->value.s) { + backing_drv = bdrv_find_format(backing_fmt->value.s); + assert(backing_drv != NULL); + } + + size = get_option_parameter(param, BLOCK_OPT_SIZE); + if (size && size->value.n <= 0) { + if (backing_file && backing_file->value.s) { + uint64_t size; + char buf[32]; + int back_flags; + + /* backing files always opened read-only */ + back_flags = + flag & + ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + + bs = bdrv_new(""); + + ret = bdrv_open(bs, backing_file->value.s, + back_flags, backing_drv); + if (ret < 0) { + set_err_internal(broker, "Failed to open the backing file."); + ret = broker->err_ret; + goto out; + } + bdrv_get_geometry(bs, &size); + size *= SECTOR_SIZE; + + snprintf(buf, sizeof(buf), "%" PRId64, size); + set_option_parameter(param, BLOCK_OPT_SIZE, buf); + } else { + set_err_internal(broker, + "Neither size or backing file was not set."); + ret = broker->err_ret; + goto out; + } + } + + bd_ret = bdrv_create(drv, filename, param); + + + if (bd_ret < 0) { + const char *errstr; + if (bd_ret == -ENOTSUP) { + errstr = "formatting option not supported."; + } else if (bd_ret == -EFBIG) { + errstr = "The image size is too large."; + } else { + errstr = "Error in creating the image."; + } + set_err_internal(broker, errstr); + ret = broker->err_ret; + } + +out: + free_option_parameters(create_options); + free_option_parameters(param); + FUNC_FREE(filename); + if (bs) { + bdrv_delete(bs); + } + + return ret; +} + +int qb_open(struct QBroker *broker, + struct QBlockState *qbs, + struct QBlockOptionLoc *loc, + struct QBlockOptionFormat *fmt, + int flag) +{ + int ret = 0, bd_ret; + BlockDriverState *bs; + BlockDriver *bd; + const char *fmtstr; + char *filename = NULL; + + /* take care of user settings */ + /* do nothing now */ + + /* check parameters */ + if (flag & (~LIBQBLOCK_O_VALID_MASK)) { + set_err_inv_param(broker, + "Invalid flag was set."); + ret = broker->err_ret; + goto out; + } + + if ((loc == NULL) || (qbs == NULL)) { + set_err_inv_param(broker, + "Got unexpected NULL pointer in parameters."); + ret = broker->err_ret; + goto out; + } + + ret = loc_check_params(broker, loc); + if (ret != 0) { + goto out; + } + + /* internal translate */ + ret = loc2filename(broker, loc, &filename); + if (ret != 0) { + goto out; + } + + fmtstr = NULL; + bd = NULL; + if (fmt != NULL) { + fmtstr = fmt2str(fmt->fmt_type); + } + + if (fmtstr != NULL) { + bd = bdrv_find_format(fmtstr); + assert(bd != NULL); + } + + /* do real openning */ + bs = qbs->bdrvs; + bd_ret = bdrv_open(bs, filename, flag, bd); + if (bd_ret < 0) { + set_err_internal(broker, + "Failed in opening with driver, bd_ret is %d.", bd_ret); + ret = broker->err_ret; + goto out; + } + + if (qbs->filename != NULL) { + FUNC_FREE(qbs->filename); + } + qbs->filename = strdup(filename); + if (qbs->filename == NULL) { + set_err_nomem(broker); + ret = broker->err_ret; + bdrv_close(bs); + goto out; + } + + out: + FUNC_FREE(filename); + return ret; +} + +void qb_close(struct QBroker *broker, + struct QBlockState *qbs) +{ + BlockDriverState *bs; + + bs = qbs->bdrvs; + + if (qbs->filename != NULL) { + CLEAN_FREE(qbs->filename); + bdrv_close(bs); + } + return; +} + +struct qb_aio_inter_struct { + QBAioCompFunc *cb; + void *cb_opaque; + QEMUIOVector io_vec; +}; + +/* internall used call back */ +static void qb_aio_inter_cb(void *opaque, int ret) +{ + struct qb_aio_inter_struct *qb_struct = opaque; + if (qb_struct->cb) { + qb_struct->cb(qb_struct->cb_opaque, ret); + } + qemu_iovec_destroy(&(qb_struct->io_vec)); + CLEAN_FREE(qb_struct); +} + +int qb_aio_write(struct QBroker *broker, + struct QBlockState *qbs, + void *buf, + size_t len, + off_t offset, + QBAioCompFunc *cb, + void *cb_opaque, + struct QBAioReq **req) +{ + BlockDriverAIOCB *aio_req; + BlockDriverState *bs; + int ret = 0; + QEMUIOVector *io_vec; + struct qb_aio_inter_struct *qb_struct; + + bs = qbs->bdrvs; + assert((len & SECTOR_SIZE_MASK) == 0); + assert((offset & SECTOR_SIZE_MASK) == 0); + + if (buf == NULL) { + set_err_inv_param(broker, "Buf is NULL."); + ret = broker->err_ret; + goto out; + } + + qb_struct = FUNC_MALLOC(sizeof(struct qb_aio_inter_struct)); + if (qb_struct == NULL) { + set_err_nomem(broker); + ret = broker->err_ret; + goto out; + } + + io_vec = &(qb_struct->io_vec); + qemu_iovec_init(io_vec, 1); + qemu_iovec_add(io_vec, buf, len); + qb_struct->cb = cb; + qb_struct->cb_opaque = cb_opaque; + + aio_req = bdrv_aio_writev(bs, offset >> SECTOR_SIZE_BITS_NUM, io_vec, + len >> SECTOR_SIZE_BITS_NUM, qb_aio_inter_cb, qb_struct); + + if (req != NULL) { + *req = (struct QBAioReq *) aio_req; + } + + out: + return ret; +} + +int qb_aio_read(struct QBroker *broker, + struct QBlockState *qbs, + void *buf, + size_t len, + off_t offset, + QBAioCompFunc *cb, + void *cb_opaque, + struct QBAioReq **req) +{ + BlockDriverAIOCB *aio_req; + BlockDriverState *bs; + int ret = 0; + QEMUIOVector *io_vec; + struct qb_aio_inter_struct *qb_struct; + + bs = qbs->bdrvs; + + assert((len & SECTOR_SIZE_MASK) == 0); + assert((offset & SECTOR_SIZE_MASK) == 0); + + if (buf == NULL) { + set_err_inv_param(broker, "Buf is NULL."); + ret = broker->err_ret; + goto out; + } + + qb_struct = FUNC_MALLOC(sizeof(struct qb_aio_inter_struct)); + if (qb_struct == NULL) { + set_err_nomem(broker); + ret = broker->err_ret; + goto out; + } + + io_vec = &(qb_struct->io_vec); + qemu_iovec_init(io_vec, 1); + qemu_iovec_add(io_vec, buf, len); + qb_struct->cb = cb; + qb_struct->cb_opaque = cb_opaque; + + aio_req = bdrv_aio_readv(bs, offset >> SECTOR_SIZE_BITS_NUM, io_vec, + len >> SECTOR_SIZE_BITS_NUM, qb_aio_inter_cb, qb_struct); + + if (req != NULL) { + *req = (struct QBAioReq *) aio_req; + } + + out: + return ret; +} + +bool qb_aio_check(struct QBroker *broker) +{ + return qemu_aio_check(); +} + +int qb_info_image_static_get(struct QBroker *broker, + struct QBlockState *qbs, + struct QBlockInfoImageStatic **info) +{ + int ret = 0; + BlockDriverState *bs; + struct QBlockInfoImageStatic *info_tmp; + const char *fmt_str; + size_t total_sectors; + char backing_filename[1024]; + + if (qbs->filename == NULL) { + set_err_inv_param(broker, "Block Image was not openned."); + ret = broker->err_ret; + goto out; + } + + info_tmp = FUNC_CALLOC(1, sizeof(struct QBlockInfoImageStatic)); + if (info_tmp == NULL) { + set_err_nomem(broker); + ret = broker->err_ret; + goto out; + } + + bs = qbs->bdrvs; + + ret = filename2loc(broker, + &(info_tmp->loc), + qbs->filename); + if (ret < 0) { + goto free; + } + + fmt_str = bdrv_get_format_name(bs); + info_tmp->fmt_type = str2fmt(fmt_str); + + bdrv_get_geometry(bs, &total_sectors); + info_tmp->virt_size = total_sectors * SECTOR_SIZE; + + info_tmp->allocated_size = bdrv_get_allocated_file_size(bs); + info_tmp->encrypt = bdrv_is_encrypted(bs); + + bdrv_get_full_backing_filename(bs, backing_filename, + sizeof(backing_filename)); + if (backing_filename[0] != '\0') { + ret = filename2loc(broker, + &(info_tmp->backing_loc), + backing_filename); + if (ret < 0) { + goto free; + } + } + + *info = info_tmp; + + out: + return ret; + free: + qb_info_image_static_delete(broker, &info_tmp); + return ret; +} + +/* free locations if it has string allocated on heap. */ +static void loc_free(struct QBlockOptionLoc *loc) +{ + switch (loc->prot_type) { + case QB_PROTO_FILE: + CLEAN_FREE(loc->prot_op.o_file.filename); + break; + default: + break; + } +} + +void qb_info_image_static_delete(struct QBroker *broker, + struct QBlockInfoImageStatic **info) +{ + loc_free(&(*info)->loc); + loc_free(&(*info)->backing_loc); + + CLEAN_FREE(*info); +} + +void qb_error_get_human_str(struct QBroker *broker, + char *buf, int buf_size) +{ + const char *err_ret_str; + switch (broker->err_ret) { + case QB_ERR_MEM_ERR: + err_ret_str = "Not enough memory."; + break; + case QB_ERR_INTERNAL_ERR: + err_ret_str = "Internal error."; + break; + case QB_ERR_INVALID_PARAM: + err_ret_str = "Invalid param."; + break; + default: + err_ret_str = "Unknow error."; + break; + } + if (broker == NULL) { + snprintf(buf, buf_size, "%s", err_ret_str); + return; + } + + if (broker->err_ret == QB_ERR_INTERNAL_ERR) { + snprintf(buf, buf_size, "%s %s errno [%d]. strerror [%s].", + err_ret_str, broker->err_msg, + broker->err_no, strerror(-broker->err_no)); + } else { + snprintf(buf, buf_size, "%s %s", + err_ret_str, broker->err_msg); + } + return; +}