/* Copyright (c) 2013 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include "inode.h" #include "syncop.h" #include "qemu-block.h" #include "block/block_int.h" typedef struct BDRVGlusterState { inode_t *inode; } BDRVGlusterState; static QemuOptsList runtime_opts = { .name = "gluster", .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), .desc = { { .name = "filename", .type = QEMU_OPT_STRING, .help = "GFID of file", }, { /* end of list */ } }, }; inode_t * qb_inode_from_filename (const char *filename) { const char *iptr = NULL; inode_t *inode = NULL; iptr = filename + 17; sscanf (iptr, "%p", &inode); return inode; } int qb_inode_to_filename (inode_t *inode, char *filename, int size) { return snprintf (filename, size, "gluster://inodep:%p", inode); } static fd_t * fd_from_bs (BlockDriverState *bs) { BDRVGlusterState *s = bs->opaque; return fd_anonymous (s->inode); } static int qemu_gluster_open (BlockDriverState *bs, QDict *options, int bdrv_flags) { inode_t *inode = NULL; BDRVGlusterState *s = bs->opaque; QemuOpts *opts = NULL; Error *local_err = NULL; const char *filename = NULL; char gfid_str[128]; int ret; qb_conf_t *conf = THIS->private; opts = qemu_opts_create_nofail(&runtime_opts); qemu_opts_absorb_qdict(opts, options, &local_err); if (error_is_set(&local_err)) { qerror_report_err(local_err); error_free(local_err); return -EINVAL; } filename = qemu_opt_get(opts, "filename"); /* * gfid: format means we're opening a backing image. */ ret = sscanf(filename, "gluster://gfid:%s", gfid_str); if (ret) { loc_t loc = {0,}; struct iatt buf = {0,}; uuid_t gfid; gf_uuid_parse(gfid_str, gfid); loc.inode = inode_find(conf->root_inode->table, gfid); if (!loc.inode) { loc.inode = inode_new(conf->root_inode->table); gf_uuid_copy(loc.inode->gfid, gfid); } gf_uuid_copy(loc.gfid, loc.inode->gfid); ret = syncop_lookup(FIRST_CHILD(THIS), &loc, &buf, NULL, NULL, NULL); if (ret) { loc_wipe(&loc); return ret; } s->inode = inode_ref(loc.inode); loc_wipe(&loc); } else { inode = qb_inode_from_filename (filename); if (!inode) return -EINVAL; s->inode = inode_ref(inode); } return 0; } static int qemu_gluster_create (const char *filename, QEMUOptionParameter *options) { uint64_t total_size = 0; inode_t *inode = NULL; fd_t *fd = NULL; struct iatt stat = {0, }; int ret = 0; inode = qb_inode_from_filename (filename); if (!inode) return -EINVAL; while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { total_size = options->value.n / BDRV_SECTOR_SIZE; } options++; } fd = fd_anonymous (inode); if (!fd) return -ENOMEM; ret = syncop_fstat (FIRST_CHILD(THIS), fd, &stat, NULL, NULL); if (ret) { fd_unref (fd); return ret; } if (stat.ia_size) { /* format ONLY if the filesize is 0 bytes */ fd_unref (fd); return -EFBIG; } if (total_size) { ret = syncop_ftruncate (FIRST_CHILD(THIS), fd, total_size, NULL, NULL); if (ret) { fd_unref (fd); return ret; } } fd_unref (fd); return 0; } static int qemu_gluster_co_readv (BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { fd_t *fd = NULL; off_t offset = 0; size_t size = 0; struct iovec *iov = NULL; int count = 0; struct iobref *iobref = NULL; int ret = 0; fd = fd_from_bs (bs); if (!fd) return -EIO; offset = sector_num * BDRV_SECTOR_SIZE; size = nb_sectors * BDRV_SECTOR_SIZE; ret = syncop_readv (FIRST_CHILD(THIS), fd, size, offset, 0, &iov, &count, &iobref, NULL, NULL); if (ret < 0) goto out; iov_copy (qiov->iov, qiov->niov, iov, count); /* *choke!* */ out: GF_FREE (iov); if (iobref) iobref_unref (iobref); fd_unref (fd); return ret; } static int qemu_gluster_co_writev (BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { fd_t *fd = NULL; off_t offset = 0; size_t size = 0; struct iobref *iobref = NULL; struct iobuf *iobuf = NULL; struct iovec iov = {0, }; int ret = -ENOMEM; fd = fd_from_bs (bs); if (!fd) return -EIO; offset = sector_num * BDRV_SECTOR_SIZE; size = nb_sectors * BDRV_SECTOR_SIZE; iobuf = iobuf_get2 (THIS->ctx->iobuf_pool, size); if (!iobuf) goto out; iobref = iobref_new (); if (!iobref) { goto out; } iobref_add (iobref, iobuf); iov_unload (iobuf_ptr (iobuf), qiov->iov, qiov->niov); /* *choke!* */ iov.iov_base = iobuf_ptr (iobuf); iov.iov_len = size; ret = syncop_writev (FIRST_CHILD(THIS), fd, &iov, 1, offset, iobref, 0, NULL, NULL); out: if (iobuf) iobuf_unref (iobuf); if (iobref) iobref_unref (iobref); fd_unref (fd); return ret; } static int qemu_gluster_co_flush (BlockDriverState *bs) { fd_t *fd = NULL; int ret = 0; fd = fd_from_bs (bs); ret = syncop_flush (FIRST_CHILD(THIS), fd, NULL, NULL); fd_unref (fd); return ret; } static int qemu_gluster_co_fsync (BlockDriverState *bs) { fd_t *fd = NULL; int ret = 0; fd = fd_from_bs (bs); ret = syncop_fsync (FIRST_CHILD(THIS), fd, 0, NULL, NULL); fd_unref (fd); return ret; } static int qemu_gluster_truncate (BlockDriverState *bs, int64_t offset) { fd_t *fd = NULL; int ret = 0; fd = fd_from_bs (bs); ret = syncop_ftruncate (FIRST_CHILD(THIS), fd, offset, NULL, NULL); fd_unref (fd); return ret; } static int64_t qemu_gluster_getlength (BlockDriverState *bs) { fd_t *fd = NULL; int ret = 0; struct iatt iatt = {0, }; fd = fd_from_bs (bs); ret = syncop_fstat (FIRST_CHILD(THIS), fd, &iatt, NULL, NULL); if (ret < 0) return -1; return iatt.ia_size; } static int64_t qemu_gluster_allocated_file_size (BlockDriverState *bs) { fd_t *fd = NULL; int ret = 0; struct iatt iatt = {0, }; fd = fd_from_bs (bs); ret = syncop_fstat (FIRST_CHILD(THIS), fd, &iatt, NULL, NULL); if (ret < 0) return -1; return iatt.ia_blocks * 512; } static void qemu_gluster_close (BlockDriverState *bs) { BDRVGlusterState *s = NULL; s = bs->opaque; inode_unref (s->inode); return; } static QEMUOptionParameter qemu_gluster_create_options[] = { { .name = BLOCK_OPT_SIZE, .type = OPT_SIZE, .help = "Virtual disk size" }, { NULL } }; static BlockDriver bdrv_gluster = { .format_name = "gluster", .protocol_name = "gluster", .instance_size = sizeof(BDRVGlusterState), .bdrv_file_open = qemu_gluster_open, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_os = qemu_gluster_co_flush, .bdrv_co_flush_to_disk = qemu_gluster_co_fsync, .bdrv_truncate = qemu_gluster_truncate, .create_options = qemu_gluster_create_options, }; static void bdrv_gluster_init(void) { bdrv_register(&bdrv_gluster); } block_init(bdrv_gluster_init);