On Thu, Aug 9, 2012 at 10:12 AM, Wenchao Xia <xiaw...@linux.vnet.ibm.com> wrote: > This patch is the API design. > > Signed-off-by: Wenchao Xia <xiaw...@linux.vnet.ibm.com> > --- > 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 <xiaw...@linux.vnet.ibm.com> > + * > + * 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
Current address of FSF is: 51 Franklin Street, Fifth Floor Boston, MA 02110-1301 USA However, please use the recommended web version like other files. > + */ > + > +#include "libqblock.h" > + > +#include <unistd.h> > +#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; \ > +} I don't think this is enough since block functions may use glib memory allocation. Probably the block files have to be compiled with wrappers for g_malloc and friends. > + > +/* 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; Useless 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; Remove. > +} > + > +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")) { Please use natural order: strcmp(fmt, "cow") == 0 > + 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); Since later there will be a field by field copy, I'd use: *dest = *src; > +} > + > +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."; had > + 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); > + } What should happen in the NULL fmt case, goto out? > + > + > + /* 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; Delete 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)); expr == 0, also below > + 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."; same as the 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; Where's the #define? > + > + 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."; s/f/F/ > + } 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. */ aligned > +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); Also here use the #defined constant. > + 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."; media > + } 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 <xiaw...@linux.vnet.ibm.com> > + * > + * 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 web version > + */ > + > +#ifndef LIBQBLOCK_H > +#define LIBQBLOCK_H > + > +#include <stdio.h> > +#include <stdint.h> > +#include <stdlib.h> > + > +#define bool _Bool #include <stdbool.h> > + > +#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 library > + This function get the library ready to use. > + */ Please use markup for the comments, see for example memory.h. > +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)); calloc() is not wrapped by your macros. > + 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); Also free() here. > + *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} " */ > +}; I'd rather have different open functions than variant structures. > + > +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); > + > A comment here stating that the string may not be free()d. +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num); > +#endif > -- > 1.7.1 > > >