From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34687) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1c1GM9-0007gk-Ta for qemu-devel@nongnu.org; Mon, 31 Oct 2016 13:26:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1c1GM8-0000D6-6e for qemu-devel@nongnu.org; Mon, 31 Oct 2016 13:26:45 -0400 From: Kevin Wolf Date: Mon, 31 Oct 2016 18:25:54 +0100 Message-Id: <1477934758-29332-26-git-send-email-kwolf@redhat.com> In-Reply-To: <1477934758-29332-1-git-send-email-kwolf@redhat.com> References: <1477934758-29332-1-git-send-email-kwolf@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PULL 25/29] raw_bsd: add offset and size options List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org From: Tom=C3=A1=C5=A1 Golembiovsk=C3=BD Added two new options 'offset' and 'size'. This makes it possible to use only part of the file as a device. This can be used e.g. to limit the access only to single partition in a disk image or use a disk inside a tar archive (like OVA). When 'size' is specified we do our best to honour it. Signed-off-by: Tom=C3=A1=C5=A1 Golembiovsk=C3=BD Signed-off-by: Kevin Wolf --- block/raw_bsd.c | 217 +++++++++++++++++++++++++++++++++++++++++++++= +++++- qapi/block-core.json | 16 +++- 2 files changed, 229 insertions(+), 4 deletions(-) diff --git a/block/raw_bsd.c b/block/raw_bsd.c index fc16ec1..7c9bebb 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -31,6 +31,30 @@ #include "qapi/error.h" #include "qemu/option.h" =20 +typedef struct BDRVRawState { + uint64_t offset; + uint64_t size; + bool has_size; +} BDRVRawState; + +static QemuOptsList raw_runtime_opts =3D { + .name =3D "raw", + .head =3D QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), + .desc =3D { + { + .name =3D "offset", + .type =3D QEMU_OPT_SIZE, + .help =3D "offset in the disk where the image starts", + }, + { + .name =3D "size", + .type =3D QEMU_OPT_SIZE, + .help =3D "virtual disk size", + }, + { /* end of list */ } + }, +}; + static QemuOptsList raw_create_opts =3D { .name =3D "raw-create-opts", .head =3D QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), @@ -44,16 +68,108 @@ static QemuOptsList raw_create_opts =3D { } }; =20 +static int raw_read_options(QDict *options, BlockDriverState *bs, + BDRVRawState *s, Error **errp) +{ + Error *local_err =3D NULL; + QemuOpts *opts =3D NULL; + int64_t real_size =3D 0; + int ret; + + real_size =3D bdrv_getlength(bs->file->bs); + if (real_size < 0) { + error_setg_errno(errp, -real_size, "Could not get image size"); + return real_size; + } + + opts =3D qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret =3D -EINVAL; + goto end; + } + + s->offset =3D qemu_opt_get_size(opts, "offset", 0); + if (qemu_opt_find(opts, "size") !=3D NULL) { + s->size =3D qemu_opt_get_size(opts, "size", 0); + s->has_size =3D true; + } else { + s->has_size =3D false; + s->size =3D real_size - s->offset; + } + + /* Check size and offset */ + if (real_size < s->offset || (real_size - s->offset) < s->size) { + error_setg(errp, "The sum of offset (%" PRIu64 ") and size " + "(%" PRIu64 ") has to be smaller or equal to the " + " actual size of the containing file (%" PRId64 ")", + s->offset, s->size, real_size); + ret =3D -EINVAL; + goto end; + } + + /* Make sure size is multiple of BDRV_SECTOR_SIZE to prevent roundin= g + * up and leaking out of the specified area. */ + if (!QEMU_IS_ALIGNED(s->size, BDRV_SECTOR_SIZE)) { + error_setg(errp, "Specified size is not multiple of %llu", + BDRV_SECTOR_SIZE); + ret =3D -EINVAL; + goto end; + } + + ret =3D 0; + +end: + + qemu_opts_del(opts); + + return ret; +} + static int raw_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { - return 0; + assert(reopen_state !=3D NULL); + assert(reopen_state->bs !=3D NULL); + + reopen_state->opaque =3D g_new0(BDRVRawState, 1); + + return raw_read_options( + reopen_state->options, + reopen_state->bs, + reopen_state->opaque, + errp); +} + +static void raw_reopen_commit(BDRVReopenState *state) +{ + BDRVRawState *new_s =3D state->opaque; + BDRVRawState *s =3D state->bs->opaque; + + memcpy(s, new_s, sizeof(BDRVRawState)); + + g_free(state->opaque); + state->opaque =3D NULL; +} + +static void raw_reopen_abort(BDRVReopenState *state) +{ + g_free(state->opaque); + state->opaque =3D NULL; } =20 static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t off= set, uint64_t bytes, QEMUIOVector *qiov= , int flags) { + BDRVRawState *s =3D bs->opaque; + + if (offset > UINT64_MAX - s->offset) { + return -EINVAL; + } + offset +=3D s->offset; + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } @@ -62,11 +178,23 @@ static int coroutine_fn raw_co_pwritev(BlockDriverSt= ate *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qio= v, int flags) { + BDRVRawState *s =3D bs->opaque; void *buf =3D NULL; BlockDriver *drv; QEMUIOVector local_qiov; int ret; =20 + if (s->has_size && (offset > s->size || bytes > (s->size - offset)))= { + /* There's not enough space for the data. Don't write anything a= nd just + * fail to prevent leaking out of the size specified in options.= */ + return -ENOSPC; + } + + if (offset > UINT64_MAX - s->offset) { + ret =3D -EINVAL; + goto fail; + } + if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { /* Handling partial writes would be a pain - so we just * require that guests have 512-byte request alignment if @@ -101,6 +229,8 @@ static int coroutine_fn raw_co_pwritev(BlockDriverSta= te *bs, uint64_t offset, qiov =3D &local_qiov; } =20 + offset +=3D s->offset; + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret =3D bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); =20 @@ -117,8 +247,10 @@ static int64_t coroutine_fn raw_co_get_block_status(= BlockDriverState *bs, int nb_sectors, int *pnum, BlockDriverState **file) { + BDRVRawState *s =3D bs->opaque; *pnum =3D nb_sectors; *file =3D bs->file->bs; + sector_num +=3D s->offset / BDRV_SECTOR_SIZE; return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | (sector_num << BDRV_SECTOR_BITS); } @@ -127,18 +259,49 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockD= riverState *bs, int64_t offset, int count, BdrvRequestFlags flags) { + BDRVRawState *s =3D bs->opaque; + if (offset > UINT64_MAX - s->offset) { + return -EINVAL; + } + offset +=3D s->offset; return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags); } =20 static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) { + BDRVRawState *s =3D bs->opaque; + if (offset > UINT64_MAX - s->offset) { + return -EINVAL; + } + offset +=3D s->offset; return bdrv_co_pdiscard(bs->file->bs, offset, count); } =20 static int64_t raw_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + int64_t len; + BDRVRawState *s =3D bs->opaque; + + /* Update size. It should not change unless the file was externally + * modified. */ + len =3D bdrv_getlength(bs->file->bs); + if (len < 0) { + return len; + } + + if (len < s->offset) { + s->size =3D 0; + } else { + if (s->has_size) { + /* Try to honour the size */ + s->size =3D MIN(s->size, len - s->offset); + } else { + s->size =3D len - s->offset; + } + } + + return s->size; } =20 static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -158,6 +321,18 @@ static void raw_refresh_limits(BlockDriverState *bs,= Error **errp) =20 static int raw_truncate(BlockDriverState *bs, int64_t offset) { + BDRVRawState *s =3D bs->opaque; + + if (s->has_size) { + return -ENOTSUP; + } + + if (INT64_MAX - offset < s->offset) { + return -EINVAL; + } + + s->size =3D offset; + offset +=3D s->offset; return bdrv_truncate(bs->file->bs, offset); } =20 @@ -178,6 +353,10 @@ static void raw_lock_medium(BlockDriverState *bs, bo= ol locked) =20 static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, voi= d *buf) { + BDRVRawState *s =3D bs->opaque; + if (s->offset || s->has_size) { + return -ENOTSUP; + } return bdrv_co_ioctl(bs->file->bs, req, buf); } =20 @@ -194,6 +373,9 @@ static int raw_create(const char *filename, QemuOpts = *opts, Error **errp) static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + BDRVRawState *s =3D bs->opaque; + int ret; + bs->sg =3D bs->file->bs->sg; bs->supported_write_flags =3D BDRV_REQ_FUA & bs->file->bs->supported_write_flags; @@ -211,6 +393,16 @@ static int raw_open(BlockDriverState *bs, QDict *opt= ions, int flags, bs->file->bs->filename); } =20 + ret =3D raw_read_options(options, bs, s, errp); + if (ret < 0) { + return ret; + } + + if (bs->sg && (s->offset || s->has_size)) { + error_setg(errp, "Cannot use offset/size with SCSI generic devic= es"); + return -EINVAL; + } + return 0; } =20 @@ -228,18 +420,37 @@ static int raw_probe(const uint8_t *buf, int buf_si= ze, const char *filename) =20 static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) { - return bdrv_probe_blocksizes(bs->file->bs, bsz); + BDRVRawState *s =3D bs->opaque; + int ret; + + ret =3D bdrv_probe_blocksizes(bs->file->bs, bsz); + if (ret < 0) { + return ret; + } + + if (!QEMU_IS_ALIGNED(s->offset, MAX(bsz->log, bsz->phys))) { + return -ENOTSUP; + } + + return 0; } =20 static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { + BDRVRawState *s =3D bs->opaque; + if (s->offset || s->has_size) { + return -ENOTSUP; + } return bdrv_probe_geometry(bs->file->bs, geo); } =20 BlockDriver bdrv_raw =3D { .format_name =3D "raw", + .instance_size =3D sizeof(BDRVRawState), .bdrv_probe =3D &raw_probe, .bdrv_reopen_prepare =3D &raw_reopen_prepare, + .bdrv_reopen_commit =3D &raw_reopen_commit, + .bdrv_reopen_abort =3D &raw_reopen_abort, .bdrv_open =3D &raw_open, .bdrv_close =3D &raw_close, .bdrv_create =3D &raw_create, diff --git a/qapi/block-core.json b/qapi/block-core.json index 3592a9d..df3b987 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2270,6 +2270,20 @@ '*tls-creds': 'str' } } =20 ## +# @BlockdevOptionsRaw +# +# Driver specific block device options for the raw driver. +# +# @offset: #optional position where the block device starts +# @size: #optional the assumed size of the device +# +# Since: 2.8 +## +{ 'struct': 'BlockdevOptionsRaw', + 'base': 'BlockdevOptionsGenericFormat', + 'data': { '*offset': 'int', '*size': 'int' } } + +## # @BlockdevOptions # # Options for creating a block device. Many options are available for a= ll @@ -2323,7 +2337,7 @@ 'qcow': 'BlockdevOptionsGenericCOWFormat', 'qed': 'BlockdevOptionsGenericCOWFormat', 'quorum': 'BlockdevOptionsQuorum', - 'raw': 'BlockdevOptionsGenericFormat', + 'raw': 'BlockdevOptionsRaw', # TODO rbd: Wait for structured options 'replication':'BlockdevOptionsReplication', # TODO sheepdog: Wait for structured options --=20 1.8.3.1