From patchwork Thu Aug 9 10:12:17 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wayne Xia X-Patchwork-Id: 176046 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 26FD52C00C8 for ; Thu, 9 Aug 2012 20:13:00 +1000 (EST) Received: from localhost ([::1]:52783 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SzPje-0003qp-4w for incoming@patchwork.ozlabs.org; Thu, 09 Aug 2012 06:12:58 -0400 Received: from eggs.gnu.org ([208.118.235.92]:55256) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SzPjH-0003XR-PP for qemu-devel@nongnu.org; Thu, 09 Aug 2012 06:12:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SzPjE-0007BJ-6A for qemu-devel@nongnu.org; Thu, 09 Aug 2012 06:12:35 -0400 Received: from e28smtp08.in.ibm.com ([122.248.162.8]:56251) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SzPjC-00079c-Vf for qemu-devel@nongnu.org; Thu, 09 Aug 2012 06:12:32 -0400 Received: from /spool/local by e28smtp08.in.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 9 Aug 2012 15:42:29 +0530 Received: from d28relay05.in.ibm.com (9.184.220.62) by e28smtp08.in.ibm.com (192.168.1.138) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 9 Aug 2012 15:42:26 +0530 Received: from d28av04.in.ibm.com (d28av04.in.ibm.com [9.184.220.66]) by d28relay05.in.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id q79ACPRR29294702 for ; Thu, 9 Aug 2012 15:42:26 +0530 Received: from d28av04.in.ibm.com (loopback [127.0.0.1]) by d28av04.in.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id q79ACPmY012679 for ; Thu, 9 Aug 2012 20:12:25 +1000 Received: from RedHat62GAWSWenchao (wenchaox.cn.ibm.com [9.115.122.25]) by d28av04.in.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id q79ACLmC012375; Thu, 9 Aug 2012 20:12:23 +1000 From: Wenchao Xia To: qemu-devel@nongnu.org Date: Thu, 9 Aug 2012 18:12:17 +0800 Message-Id: <1344507137-11173-1-git-send-email-xiawenc@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.1 x-cbid: 12080910-2000-0000-0000-000008A73A08 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 122.248.162.8 Cc: stefanha@gmail.com, aliguori@us.ibm.com, Wenchao Xia , pbonzini@redhat.com Subject: [Qemu-devel] [qemu-devel] [PATCH V2 2/3] [RFC] libqblock-API design 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 patch is the API design. Signed-off-by: Wenchao Xia --- libqblock.c | 670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ libqblock.h | 447 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1117 insertions(+), 0 deletions(-) create mode 100644 libqblock.c create mode 100644 libqblock.h diff --git a/libqblock.c b/libqblock.c new file mode 100644 index 0000000..52e46dc --- /dev/null +++ b/libqblock.c @@ -0,0 +1,670 @@ +/* + * Copyright IBM Corp. 2012 + * + * Authors: + * Wenchao Xia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "libqblock.h" + +#include +#include "block.h" +#include "block_int.h" + +#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; + char *filename; + const char *err_msg; + int err_no; +}; + +static void set_err_nomem(struct QBlockState *qbs) +{ + qbs->err_msg = NULL; + qbs->err_no = 0; +} + +void libqblock_init(void) +{ + bdrv_init(); + return; +} + +int qb_state_new(struct QBlockState **qbs) +{ + *qbs = FUNC_CALLOC(1, sizeof(struct QBlockState)); + if (*qbs == NULL) { + return QB_ERR_MEM_ERR; + } + (*qbs)->bdrvs = bdrv_new("hda"); + if ((*qbs)->bdrvs == NULL) { + CLEAN_FREE(*qbs); + return QB_ERR_INTERNAL_ERR; + } + return 0; +} + +void qb_state_free(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; +} + +/* copy information and take care the member difference in differect version. + Assuming all new member are added in the tail, struct size is the first + member, this is old to new version, src have its struct_size set. */ +static void qboo_adjust_o2n(struct QBlockOptionOpen *dest, + struct QBlockOptionOpen *src) +{ + /* for simple it does memcpy now, need to take care of embbed structure */ + memcpy(dest, src, src->struct_size); +} + +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op) +{ + int ret = 0, bd_ret; + BlockDriverState *bs; + struct QBlockOptionOpen qboo; + BlockDriver *bd; + const char *fmt; + + /* take care of user settings */ + qboo_adjust_o2n(&qboo, op); + + /* check parameters */ + if (qboo.o_loc.filename == NULL) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "filename was not set."; + qbs->err_no = 0; + goto out; + } + + if (path_has_protocol(qboo.o_loc.filename) > 0) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "filename have protocol."; + qbs->err_no = 0; + goto out; + } + + if (qboo.o_loc.protocol >= QB_PROTO_MAX) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "protocol was not supported."; + qbs->err_no = 0; + goto out; + } + + bd = NULL; + fmt = fmt2str(qboo.o_fmt_type); + if (fmt != NULL) { + bd = bdrv_find_format(fmt); + assert(bd != NULL); + } + + + /* do real openning */ + bs = qbs->bdrvs; + bd_ret = bdrv_open(bs, qboo.o_loc.filename, qboo.o_flag, bd); + if (bd_ret < 0) { + ret = QB_ERR_INTERNAL_ERR; + qbs->err_msg = "failed to open the file."; + qbs->err_no = -errno; + goto out; + } + + if (qbs->filename != NULL) { + FUNC_FREE(qbs->filename); + } + qbs->filename = strdup(qboo.o_loc.filename); + if (qbs->filename == NULL) { + ret = QB_ERR_INVALID_PARAM; + set_err_nomem(qbs); + goto out; + } + + out: + return ret; +} + +void qb_close(struct QBlockState *qbs) +{ + BlockDriverState *bs; + + bs = qbs->bdrvs; + bdrv_close(bs); + return; +} + +/* copy information and take care the member difference in differect version. + Assuming all new member are added in the tail, struct size is the first + member, this is old to new version, src have its struct_size set. */ +static void qboc_adjust_o2n(struct QBlockOptionCreate *dest, + struct QBlockOptionCreate *src) +{ + /* for simple it does memcpy now, need to take care of embbed structure */ + memcpy(dest, src, src->struct_size); +} + +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op) +{ + int ret = 0, bd_ret; + BlockDriverState *bs = NULL; + BlockDriver *drv = NULL, *backing_drv = NULL; + struct QBlockOptionCreate qbco; + const char *fmt = NULL, *fmt_backing_str = NULL, *tmp; + QEMUOptionParameter *param = NULL, *create_options = NULL; + QEMUOptionParameter *backing_fmt, *backing_file, *size; + struct QBlockOption_cow *o_cow = NULL; + struct QBlockOption_qed *o_qed = NULL; + struct QBlockOption_qcow *o_qcow = NULL; + struct QBlockOption_qcow2 *o_qcow2 = NULL; + struct QBlockOption_raw *o_raw = NULL; + struct QBlockOption_rbd *o_rbd = NULL; + struct QBlockOption_sheepdog *o_sd = NULL; + struct QBlockOption_vdi *o_vdi = NULL; + struct QBlockOption_vmdk *o_vmdk = NULL; + struct QBlockOption_vpc *o_vpc = NULL; + int backing_flag = 0; + + /* take care of user settings */ + qboc_adjust_o2n(&qbco, op); + + /* check parameters */ + if (qbco.o_loc.filename == NULL) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "filename was not set."; + qbs->err_no = 0; + goto out; + } + + if (path_has_protocol(qbco.o_loc.filename) > 0) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "filename have protocol."; + qbs->err_no = 0; + goto out; + } + + if (qbco.o_loc.protocol >= QB_PROTO_MAX) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "protocol was not supported."; + qbs->err_no = 0; + goto out; + } + + /* set parameters */ + fmt = fmt2str(qbco.o_fmt.fmt_type); + if (fmt == NULL) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "format is not valid."; + qbs->err_no = 0; + goto out; + } + drv = bdrv_find_format(fmt); + assert(drv != NULL); + + create_options = append_option_parameters(create_options, + drv->create_options); + param = parse_option_parameters("", create_options, param); + + switch (qbco.o_fmt.fmt_type) { + case QB_FMT_COW: + o_cow = &qbco.o_fmt.fmt_op.o_cow; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_cow->virt_size)); + if (o_cow->backing_file) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, o_cow->backing_file)); + } + backing_flag = o_cow->backing_flag; + break; + case QB_FMT_QED: + o_qed = &qbco.o_fmt.fmt_op.o_qed; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_qed->virt_size)); + if (o_qed->backing_file) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, o_qed->backing_file)); + } + fmt_backing_str = fmt2str(o_qed->backing_fmt); + if (fmt_backing_str) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FMT, fmt_backing_str)); + } + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_qed->cluster_size)); + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_TABLE_SIZE, o_qed->table_size)); + backing_flag = o_qed->backing_flag; + break; + case QB_FMT_QCOW: + o_qcow = &qbco.o_fmt.fmt_op.o_qcow; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_qcow->virt_size)); + if (o_qcow->backing_file) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, o_qcow->backing_file)); + } + tmp = o_qcow->encrypt ? "on" : "off"; + assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp)); + backing_flag = o_qcow->backing_flag; + break; + case QB_FMT_QCOW2: + o_qcow2 = &qbco.o_fmt.fmt_op.o_qcow2; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_qcow2->virt_size)); + if (o_qcow2->backing_file) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, o_qcow2->backing_file)); + } + fmt_backing_str = fmt2str(o_qcow2->backing_fmt); + if (fmt_backing_str) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FMT, fmt_backing_str)); + } + if (o_qcow2->compat_level) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_COMPAT_LEVEL, o_qcow2->compat_level)); + } + tmp = o_qcow2->encrypt ? "on" : "off"; + assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp)); + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_qcow2->cluster_size)); + if (o_qcow2->prealloc_mode) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_PREALLOC, o_qcow2->prealloc_mode)); + } + backing_flag = o_qcow2->backing_flag; + break; + case QB_FMT_RAW: + o_raw = &qbco.o_fmt.fmt_op.o_raw; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_raw->virt_size)); + break; + case QB_FMT_RBD: + o_rbd = &qbco.o_fmt.fmt_op.o_rbd; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_rbd->virt_size)); + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_rbd->cluster_size)); + break; + case QB_FMT_SHEEPDOG: + o_sd = &qbco.o_fmt.fmt_op.o_sheepdog; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_sd->virt_size)); + if (o_sd->backing_file) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, o_sd->backing_file)); + } + if (o_sd->prealloc_mode) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_PREALLOC, o_sd->prealloc_mode)); + } + backing_flag = o_sd->backing_flag; + break; + case QB_FMT_VDI: + o_vdi = &qbco.o_fmt.fmt_op.o_vdi; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_vdi->virt_size)); + /* following option is not always valid depends on configuration */ + set_option_parameter_int(param, + BLOCK_OPT_CLUSTER_SIZE, o_vdi->cluster_size); + set_option_parameter_int(param, "static", o_vdi->prealloc_mode); + break; + case QB_FMT_VMDK: + o_vmdk = &qbco.o_fmt.fmt_op.o_vmdk; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_vmdk->virt_size)); + if (o_vmdk->backing_file) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_BACKING_FILE, o_vmdk->backing_file)); + } + tmp = o_vmdk->compat_version6 ? "on" : "off"; + assert(0 == set_option_parameter(param, BLOCK_OPT_COMPAT6, tmp)); + if (o_vmdk->subfmt) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_SUBFMT, o_vmdk->subfmt)); + } + backing_flag = o_vmdk->backing_flag; + break; + case QB_FMT_VPC: + o_vpc = &qbco.o_fmt.fmt_op.o_vpc; + assert(0 == set_option_parameter_int(param, + BLOCK_OPT_SIZE, o_vpc->virt_size)); + if (o_vpc->subfmt) { + assert(0 == set_option_parameter(param, + BLOCK_OPT_SUBFMT, o_vpc->subfmt)); + } + break; + default: + break; + } + + backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE); + if (backing_file && backing_file->value.s) { + if (!strcmp(op->o_loc.filename, backing_file->value.s)) { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "backing file is the same with new file."; + qbs->err_no = 0; + 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 = + backing_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) { + ret = QB_ERR_INTERNAL_ERR; + qbs->err_msg = "failed to open the backing file."; + qbs->err_no = -errno; + goto out; + } + bdrv_get_geometry(bs, &size); + size *= 512; + + snprintf(buf, sizeof(buf), "%" PRId64, size); + set_option_parameter(param, BLOCK_OPT_SIZE, buf); + } else { + ret = QB_ERR_INVALID_PARAM; + qbs->err_msg = "neither size or backing file was not set."; + qbs->err_no = 0; + goto out; + } + } + + bd_ret = bdrv_create(drv, op->o_loc.filename, param); + + if (bd_ret < 0) { + ret = QB_ERR_INTERNAL_ERR; + qbs->err_no = bd_ret; + if (bd_ret == -ENOTSUP) { + qbs->err_msg = "formatting option not supported."; + } else if (bd_ret == -EFBIG) { + qbs->err_msg = "The image size is too large."; + } else { + qbs->err_msg = "Error in creating the image."; + } + goto out; + } + +out: + free_option_parameters(create_options); + free_option_parameters(param); + + if (bs) { + bdrv_delete(bs); + } + + return ret; +} + +/* ignore case that len is not alligned to 512 now. */ +int qb_read(struct QBlockState *qbs, const void *buf, size_t len, + off_t offset) +{ + int ret = 0, bd_ret; + BlockDriverState *bs; + + bs = qbs->bdrvs; + + assert((len & 0x01ff) == 0); + assert((offset & 0x01ff) == 0); + bd_ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, len >> 9); + if (bd_ret < 0) { + ret = QB_ERR_INTERNAL_ERR; + qbs->err_no = bd_ret; + if (bd_ret == -EIO) { + qbs->err_msg = "Generic I/O error."; + } else if (bd_ret == -ENOMEDIUM) { + qbs->err_msg = "No meida inserted."; + } else if (bd_ret == -EINVAL) { + qbs->err_msg = "Sector was not correct."; + } else { + qbs->err_msg = "Internal error."; + } + } + return ret; +} + +/* ignore case that len is not alligned to 512 now. */ +int qb_write(struct QBlockState *qbs, const void *buf, size_t len, + off_t offset) +{ + int ret = 0, bd_ret; + BlockDriverState *bs; + + bs = qbs->bdrvs; + + assert((len & 0x01ff) == 0); + assert((offset & 0x01ff) == 0); + bd_ret = bdrv_write(bs, offset >> 9, buf, len >> 9); + if (bd_ret < 0) { + ret = QB_ERR_INTERNAL_ERR; + qbs->err_no = bd_ret; + if (bd_ret == -EIO) { + qbs->err_msg = "Generic I/O error."; + } else if (bd_ret == -ENOMEDIUM) { + qbs->err_msg = "No meida inserted."; + } else if (bd_ret == -EINVAL) { + qbs->err_msg = "Sector was not correct."; + } else if (bd_ret == -EACCES) { + qbs->err_msg = "Ready only device."; + } else { + qbs->err_msg = "Internal error."; + } + } + return ret; +} + +int qb_flush(struct QBlockState *qbs) +{ + int ret = 0, bd_ret; + BlockDriverState *bs; + + bs = qbs->bdrvs; + bd_ret = bdrv_flush(bs); + if (bd_ret < 0) { + ret = QB_ERR_INTERNAL_ERR; + qbs->err_no = bd_ret; + qbs->err_msg = NULL; + } + return ret; +} + +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info) +{ + int ret = 0; + BlockDriverState *bs; + const char *tmp; + uint64_t total_sectors; + char backing_filename[1024]; + + struct QBlockInfoImage *ifimg = FUNC_CALLOC(1, + sizeof(struct QBlockInfoImage)); + if (ifimg == NULL) { + ret = QB_ERR_MEM_ERR; + set_err_nomem(qbs); + goto out; + } + + bs = qbs->bdrvs; + ifimg->filename = strdup(qbs->filename); + if (ifimg->filename == NULL) { + ret = QB_ERR_MEM_ERR; + set_err_nomem(qbs); + goto err; + } + tmp = bdrv_get_format_name(bs); + ifimg->format = str2fmt(tmp); + /* support only file now */ + ifimg->protocol = QB_PROTO_FILE; + + bdrv_get_geometry(bs, &total_sectors); + ifimg->virt_size = total_sectors * 512; + ifimg->allocated_size = bdrv_get_allocated_file_size(bs); + ifimg->encrypt = bdrv_is_encrypted(bs); + bdrv_get_full_backing_filename(bs, backing_filename, + sizeof(backing_filename)); + if (backing_filename[0] != '\0') { + ifimg->backing_filename = strdup(backing_filename); + if (ifimg->backing_filename == NULL) { + ret = QB_ERR_MEM_ERR; + set_err_nomem(qbs); + goto err; + } + } + + *info = ifimg; + out: + return ret; + err: + qb_infoimage_free(&ifimg); + return ret; +} + +void qb_infoimage_free(struct QBlockInfoImage **info) +{ + if (*info == NULL) { + return; + } + FUNC_FREE((*info)->filename); + FUNC_FREE((*info)->backing_filename); + FUNC_FREE(*info); + *info = NULL; + return; +} + +bool qb_supports_format(enum QBlockFormat fmt) +{ + if (fmt < QB_FMT_MAX) { + return true; + } + return false; +} + +bool qb_supports_protocol(enum QBlockProtocol proto) +{ + if (proto < QB_PROTO_MAX) { + return true; + } + return false; +} + +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num) +{ + *err_num = qbs->err_no; + return qbs->err_msg; +} diff --git a/libqblock.h b/libqblock.h new file mode 100644 index 0000000..d2e9502 --- /dev/null +++ b/libqblock.h @@ -0,0 +1,447 @@ +/* + * Copyright IBM Corp. 2012 + * + * Authors: + * Wenchao Xia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LIBQBLOCK_H +#define LIBQBLOCK_H + +#include +#include +#include + +#define bool _Bool + +#define QB_ERR_MEM_ERR (-1) +#define QB_ERR_INTERNAL_ERR (-2) +#define QB_ERR_INVALID_PARAM (-3) + +/* this library is designed around this core struct. */ +struct QBlockState; + +/* + libarary init + This function get the library ready to use. + */ +void libqblock_init(void); + +/* + create a new qbs object + params: + qbs: out, pointer that will receive created obj. + return: + 0 on succeed, negative on failure. + */ +int qb_state_new(struct QBlockState **qbs); + +/* + delete a qbs object + params: + qbs: in, pointer that will be freed. *qbs will be set to NULL. + return: + void. + */ +void qb_state_free(struct QBlockState **qbs); + + +/* flag used in open and create */ +#define LIBQBLOCK_O_RDWR 0x0002 +/* open the file read only and save writes in a snapshot */ +#define LIBQBLOCK_O_SNAPSHOT 0x0008 +/* do not use the host page cache */ +#define LIBQBLOCK_O_NOCACHE 0x0020 +/* use write-back caching */ +#define LIBQBLOCK_O_CACHE_WB 0x0040 +/* use native AIO instead of the thread pool */ +#define LIBQBLOCK_O_NATIVE_AIO 0x0080 +/* don't open the backing file */ +#define LIBQBLOCK_O_NO_BACKING 0x0100 +/* disable flushing on this disk */ +#define LIBQBLOCK_O_NO_FLUSH 0x0200 +/* copy read backing sectors into image */ +#define LIBQBLOCK_O_COPY_ON_READ 0x0400 +/* consistency hint for incoming migration */ +#define LIBQBLOCK_O_INCOMING 0x0800 + +#define LIBQBLOCK_O_CACHE_MASK \ + (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH) + +enum QBlockProtocol { + QB_PROTO_FILE = 0, + QB_PROTO_MAX +}; + +enum QBlockFormat { + QB_FMT_NONE = 0, + QB_FMT_COW, + QB_FMT_QED, + QB_FMT_QCOW, + QB_FMT_QCOW2, + QB_FMT_RAW, + QB_FMT_RBD, + QB_FMT_SHEEPDOG, + QB_FMT_VDI, + QB_FMT_VMDK, + QB_FMT_VPC, + QB_FMT_MAX +}; + +/* block target location info, it include all information about how to find + the image */ +struct QBlockLocInfo { + int struct_size; + const char *filename; + enum QBlockProtocol protocol; +}; + +/* how to open the image */ +struct QBlockOptionOpen { + int struct_size; + struct QBlockLocInfo o_loc; /* how to find */ + enum QBlockFormat o_fmt_type; /* how to extract */ + int o_flag; /* how to control */ +}; + +/* + create a new QBlockOptionOpen structure. + params: + op: out, pointer that will receive created structure. + return: + 0 on succeed, negative on failure. + */ +static inline int qb_oo_new(struct QBlockOptionOpen **op) +{ + *op = calloc(1, sizeof(struct QBlockOptionOpen)); + if (*op == NULL) { + return QB_ERR_MEM_ERR; + } + (*op)->struct_size = sizeof(struct QBlockOptionOpen); + (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo); + return 0; +} + +/* + free QBlockOptionOpen structure. + params: + op: in, *op will be set as NULL after called. + return: + void + */ +static inline void qb_oo_free(struct QBlockOptionOpen **op) +{ + free(*op); + *op = NULL; +} + +/* + open a block object. + params: + qbs: in, pointer to QBlockState. + op: in, options for open. + return: + 0 on success, negative on fail. + */ +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op); + +/* + close a block object. + params: + qbs: in, pointer to QBlockState. + return: + void. + */ +void qb_close(struct QBlockState *qbs); + +/* format related options, struct_size must be set. */ +struct QBlockOption_cow { + int struct_size; + size_t virt_size; + const char *backing_file; + int backing_flag; +}; + +struct QBlockOption_qed { + int struct_size; + size_t virt_size; + const char *backing_file; + enum QBlockFormat backing_fmt; + int backing_flag; + size_t cluster_size; /* unit is bytes */ + size_t table_size; /* unit is clusters */ +}; + +struct QBlockOption_qcow { + int struct_size; + size_t virt_size; + const char *backing_file; + int backing_flag; + bool encrypt; +}; + +struct QBlockOption_qcow2 { + int struct_size; + size_t virt_size; + const char *compat_level; /* "Compatibility level (0.10 or 1.1)" */ + const char *backing_file; + enum QBlockFormat backing_fmt; + int backing_flag; + bool encrypt; + size_t cluster_size; /* unit is bytes */ + const char *prealloc_mode; /* off or metadata */ +}; + +struct QBlockOption_raw { + int struct_size; + size_t virt_size; +}; + +struct QBlockOption_rbd { + int struct_size; + size_t virt_size; + size_t cluster_size; +}; + +struct QBlockOption_sheepdog { + int struct_size; + size_t virt_size; + const char *backing_file; + int backing_flag; + const char *prealloc_mode; /* off or full */ +}; + +struct QBlockOption_vdi { + int struct_size; + size_t virt_size; + size_t cluster_size; + bool prealloc_mode; +}; + +struct QBlockOption_vmdk { + int struct_size; + size_t virt_size; + const char *backing_file; + int backing_flag; + bool compat_version6; + const char *subfmt; + /* vmdk flat extent format, values: + "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | + twoGbMaxExtentFlat | streamOptimized} */ +}; + +struct QBlockOption_vpc { + int struct_size; + size_t virt_size; + const char *subfmt; /* "{dynamic (default) | fixed} " */ +}; + +union QBlockOption_fmt { + struct QBlockOption_cow o_cow; + struct QBlockOption_qed o_qed; + struct QBlockOption_qcow o_qcow; + struct QBlockOption_qcow2 o_qcow2; + struct QBlockOption_raw o_raw; + struct QBlockOption_rbd o_rbd; + struct QBlockOption_sheepdog o_sheepdog; + struct QBlockOption_vdi o_vdi; + struct QBlockOption_vmdk o_vmdk; + struct QBlockOption_vpc o_vpc; +}; + +struct QBlockOptionFormat { + int struct_size; + enum QBlockFormat fmt_type; + union QBlockOption_fmt fmt_op; +}; + +/* struct_size in o_loc and o_fmt must set. To make this structure extensible, + all new member must be added in the tail of each structure. */ +struct QBlockOptionCreate { + int struct_size; + struct QBlockLocInfo o_loc; + struct QBlockOptionFormat o_fmt; +}; + +/* + create a new QBlockOptionCreate structure. + params: + op: out, pointer that will receive created structure. + fmt: format want to use. + return: + 0 on succeed, negative on failure. + */ +static inline int qb_oc_new(struct QBlockOptionCreate **op, + enum QBlockFormat fmt) +{ + *op = calloc(1, sizeof(struct QBlockOptionCreate)); + if (*op == NULL) { + return QB_ERR_MEM_ERR; + } + (*op)->struct_size = sizeof(struct QBlockOptionCreate); + (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo); + (*op)->o_fmt.struct_size = sizeof(struct QBlockOptionFormat); + + switch (fmt) { + case QB_FMT_COW: + (*op)->o_fmt.fmt_op.o_cow.struct_size = + sizeof(struct QBlockOption_cow); + break; + case QB_FMT_QED: + (*op)->o_fmt.fmt_op.o_qed.struct_size = + sizeof(struct QBlockOption_qed); + break; + case QB_FMT_QCOW: + (*op)->o_fmt.fmt_op.o_qcow.struct_size = + sizeof(struct QBlockOption_qcow); + break; + case QB_FMT_QCOW2: + (*op)->o_fmt.fmt_op.o_qcow2.struct_size = + sizeof(struct QBlockOption_qcow2); + break; + case QB_FMT_RAW: + (*op)->o_fmt.fmt_op.o_raw.struct_size = + sizeof(struct QBlockOption_raw); + break; + case QB_FMT_RBD: + (*op)->o_fmt.fmt_op.o_rbd.struct_size = + sizeof(struct QBlockOption_rbd); + break; + case QB_FMT_SHEEPDOG: + (*op)->o_fmt.fmt_op.o_sheepdog.struct_size = + sizeof(struct QBlockOption_sheepdog); + break; + case QB_FMT_VDI: + (*op)->o_fmt.fmt_op.o_vdi.struct_size = + sizeof(struct QBlockOption_vdi); + break; + case QB_FMT_VMDK: + (*op)->o_fmt.fmt_op.o_vmdk.struct_size = + sizeof(struct QBlockOption_vmdk); + break; + case QB_FMT_VPC: + (*op)->o_fmt.fmt_op.o_vpc.struct_size = + sizeof(struct QBlockOption_vpc); + break; + default: + break; + } + (*op)->o_fmt.fmt_type = fmt; + return 0; +} + +/* + free QBlockOptionCreate structure. + params: + op: in, *op will be set as NULL after called. + return: + void + */ +static inline void qb_oc_free(struct QBlockOptionCreate **op) +{ + free(*op); + *op = NULL; +} +/* + create a block file. + params: + qbs: in, pointer to QBlockState. + op: in, create option. + return: + negative on fail, 0 on success. + */ +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op); + + +/* sync access */ +/* + block sync read. + params: + qbs: in, pointer to QBlockState. + buf: out, buffer that receive the content. + len: in, length to read. + offset: in, offset in the block data. + return: + negative on fail, 0 on success. + */ +int qb_read(struct QBlockState *qbs, const void *buf, size_t len, + off_t offset); + +/* + block sync read. + params: + qbs: in, pointer to QBlockState. + buf: in, buffer that would be wrote. + len: in, length to write. + offset: in, offset in the block data. + return: + negative on fail, 0 on success. + */ +int qb_write(struct QBlockState *qbs, const void *buf, size_t len, + off_t offset); + +/* + block sync flush. + params: + qbs: in, pointer to QBlockState. + return: + negative on fail, 0 on success. + */ +int qb_flush(struct QBlockState *qbs); + +/* information */ +/* image related info, static information, from user perspective. */ +/* now it is a plain structure, wonder if it could be foldered into embbed one + to reflect that format related information better. */ +struct QBlockInfoImage { + int struct_size; + char *filename; + enum QBlockProtocol protocol; + enum QBlockFormat format; + size_t virt_size; + /* advance info */ + size_t allocated_size; + bool encrypt; + char *backing_filename; +}; + +/* image info */ +/* + get image info. + params: + qbs: in, pointer to QBlockState. + info, out, pointer that would receive the information. + return: + negative on fail, 0 on success. + */ +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info); + +/* + free image info. + params: + info, in, pointer, *info would be set to NULL after function called. + return: + void. + */ +void qb_infoimage_free(struct QBlockInfoImage **info); + +/* misc */ +bool qb_supports_format(enum QBlockFormat fmt); +bool qb_supports_protocol(enum QBlockProtocol proto); + +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num); +#endif