From nobody Tue Feb 10 06:26:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1493178664311568.0787328304338; Tue, 25 Apr 2017 20:51:04 -0700 (PDT) Received: from localhost ([::1]:52498 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d3Dyp-0007JN-4m for importer@patchew.org; Tue, 25 Apr 2017 23:51:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43777) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d3Djh-0003Fh-0W for qemu-devel@nongnu.org; Tue, 25 Apr 2017 23:35:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d3Djf-0001t8-3X for qemu-devel@nongnu.org; Tue, 25 Apr 2017 23:35:25 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37422) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d3Djb-0001rf-JL; Tue, 25 Apr 2017 23:35:19 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9119361B92; Wed, 26 Apr 2017 03:35:18 +0000 (UTC) Received: from lemon.redhat.com (ovpn-8-31.pek2.redhat.com [10.72.8.31]) by smtp.corp.redhat.com (Postfix) with ESMTP id 624861714E; Wed, 26 Apr 2017 03:35:16 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 9119361B92 Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=famz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 9119361B92 From: Fam Zheng To: qemu-devel@nongnu.org Date: Wed, 26 Apr 2017 11:34:12 +0800 Message-Id: <20170426033413.17192-21-famz@redhat.com> In-Reply-To: <20170426033413.17192-1-famz@redhat.com> References: <20170426033413.17192-1-famz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Wed, 26 Apr 2017 03:35:18 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v15 20/21] file-posix: Add image locking to perm operations X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , qemu-block@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This extends the permission bits of op blocker API to external using Linux OFD locks. Each permission in @perm and @shared_perm is represented by a locked byte in the image file. Requesting a permission in @perm is translated to a shared lock of the corresponding byte; rejecting to share the same permission is translated to a shared lock of a separate byte. With that, we use 2x number of bytes of distinct permission types. virtlockd in libvirt locks the first byte, so we do locking from a higher offset. Suggested-by: Kevin Wolf Signed-off-by: Fam Zheng --- block/file-posix.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 264 insertions(+), 3 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 2114720..b92fdc3 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -129,12 +129,28 @@ do { \ =20 #define MAX_BLOCKSIZE 4096 =20 +/* Posix file locking bytes. Libvirt takes byte 0, we start from higher by= tes, + * leaving a few more bytes for its future use. */ +#define RAW_LOCK_PERM_BASE 100 +#define RAW_LOCK_SHARED_BASE 200 +#ifdef F_OFD_SETLK +#define RAW_LOCK_SUPPORTED 1 +#else +#define RAW_LOCK_SUPPORTED 0 +#endif + typedef struct BDRVRawState { int fd; + int lock_fd; + bool use_lock; int type; int open_flags; size_t buf_align; =20 + /* The current permissions. */ + uint64_t perm; + uint64_t shared_perm; + #ifdef CONFIG_XFS bool is_xfs:1; #endif @@ -440,6 +456,8 @@ static int raw_open_common(BlockDriverState *bs, QDict = *options, } s->use_linux_aio =3D (aio =3D=3D BLOCKDEV_AIO_OPTIONS_NATIVE); =20 + s->use_lock =3D qemu_opt_get_bool(opts, "locking", true); + s->open_flags =3D open_flags; raw_parse_flags(bdrv_flags, &s->open_flags); =20 @@ -455,6 +473,21 @@ static int raw_open_common(BlockDriverState *bs, QDict= *options, } s->fd =3D fd; =20 + s->lock_fd =3D -1; + fd =3D qemu_open(filename, O_RDONLY); + if (fd < 0) { + if (RAW_LOCK_SUPPORTED) { + ret =3D -errno; + error_setg_errno(errp, errno, "Could not open '%s' for locking= ", + filename); + qemu_close(s->fd); + goto fail; + } + } + s->lock_fd =3D fd; + s->perm =3D 0; + s->shared_perm =3D BLK_PERM_ALL; + #ifdef CONFIG_LINUX_AIO /* Currently Linux does AIO only for files opened with O_DIRECT */ if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { @@ -542,6 +575,156 @@ static int raw_open(BlockDriverState *bs, QDict *opti= ons, int flags, return raw_open_common(bs, options, flags, 0, errp); } =20 +typedef enum { + RAW_PL_PREPARE, + RAW_PL_COMMIT, + RAW_PL_ABORT, +} RawPermLockOp; + +/* Lock wanted bytes by @perm and ~@shared_perm in the file; if @unlock = =3D=3D + * true, also unlock the unneeded bytes. */ +static int raw_apply_lock_bytes(BDRVRawState *s, + uint64_t perm_lock_bits, + uint64_t shared_perm_lock_bits, + bool unlock, Error **errp) +{ + int ret; + int i; + + for (i =3D 0; i < BLK_PERM_MAX; ++i) { + int off =3D RAW_LOCK_PERM_BASE + i; + if (perm_lock_bits & (1ULL << i)) { + ret =3D qemu_lock_fd(s->lock_fd, off, 1, false); + if (ret) { + error_setg(errp, "Failed to lock byte %d", off); + return ret; + } + } else if (unlock) { + ret =3D qemu_unlock_fd(s->lock_fd, off, 1); + if (ret) { + error_setg(errp, "Failed to unlock byte %d", off); + return ret; + } + } + } + for (i =3D 0; i < BLK_PERM_MAX; ++i) { + int off =3D RAW_LOCK_SHARED_BASE + i; + if (shared_perm_lock_bits & (1ULL << i)) { + ret =3D qemu_lock_fd(s->lock_fd, off, 1, false); + if (ret) { + error_setg(errp, "Failed to lock byte %d", off); + return ret; + } + } else if (unlock) { + ret =3D qemu_unlock_fd(s->lock_fd, off, 1); + if (ret) { + error_setg(errp, "Failed to unlock byte %d", off); + return ret; + } + } + } + return 0; +} + +/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. = */ +static int raw_check_lock_bytes(BDRVRawState *s, + uint64_t perm, uint64_t shared_perm, + Error **errp) +{ + int ret; + int i; + + for (i =3D 0; i < BLK_PERM_MAX; ++i) { + int off =3D RAW_LOCK_SHARED_BASE + i; + uint64_t p =3D 1ULL << i; + if (perm & p) { + ret =3D qemu_lock_fd_test(s->lock_fd, off, 1, true); + if (ret) { + error_setg(errp, + "Failed to check byte %d for \"%s\" permission", + off, bdrv_perm_names(p)); + error_append_hint(errp, + "Is another process using the image?\n"); + return ret; + } + } + } + for (i =3D 0; i < BLK_PERM_MAX; ++i) { + int off =3D RAW_LOCK_PERM_BASE + i; + uint64_t p =3D 1ULL << i; + if (!(shared_perm & p)) { + ret =3D qemu_lock_fd_test(s->lock_fd, off, 1, true); + if (ret) { + error_setg(errp, + "Failed to check byte %d for shared \"%s\" perm= ission", + off, bdrv_perm_names(p)); + error_append_hint(errp, + "Is another process using the image?\n"); + return ret; + } + } + } + return 0; +} + +static int raw_handle_perm_lock(BlockDriverState *bs, + RawPermLockOp op, + uint64_t new_perm, uint64_t new_shared, + Error **errp) +{ + BDRVRawState *s =3D bs->opaque; + int ret =3D 0; + Error *local_err =3D NULL; + + if (!RAW_LOCK_SUPPORTED) { + return 0; + } + + if (!s->use_lock) { + return 0; + } + + if (bdrv_get_flags(bs) & BDRV_O_INACTIVE) { + return 0; + } + + assert(s->lock_fd > 0); + + switch (op) { + case RAW_PL_PREPARE: + ret =3D raw_apply_lock_bytes(s, s->perm | new_perm, + ~s->shared_perm | ~new_shared, + false, errp); + if (!ret) { + ret =3D raw_check_lock_bytes(s, new_perm, new_shared, errp); + if (!ret) { + return 0; + } + } + op =3D RAW_PL_ABORT; + /* fall through to unlock bytes. */ + case RAW_PL_ABORT: + raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err= ); + if (local_err) { + /* Theoretically the above call only unlocks bytes and it cann= ot + * fail. Something weird happened, report it. + */ + error_report_err(local_err); + } + break; + case RAW_PL_COMMIT: + raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err); + if (local_err) { + /* Theoretically the above call only unlocks bytes and it cann= ot + * fail. Something weird happened, report it. + */ + error_report_err(local_err); + } + break; + } + return ret; +} + static int raw_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { @@ -549,6 +732,8 @@ static int raw_reopen_prepare(BDRVReopenState *state, BDRVRawReopenState *rs; int ret =3D 0; Error *local_err =3D NULL; + uint64_t clear_perms =3D state->flags & BDRV_O_RDWR ? 0 : + BLK_PERM_WRITE | BLK_PERM_RESIZE | BLK_PERM_WRITE_UNCHANGED; =20 assert(state !=3D NULL); assert(state->bs !=3D NULL); @@ -613,13 +798,22 @@ static int raw_reopen_prepare(BDRVReopenState *state, if (rs->fd !=3D -1) { raw_probe_alignment(state->bs, rs->fd, &local_err); if (local_err) { - qemu_close(rs->fd); - rs->fd =3D -1; error_propagate(errp, local_err); ret =3D -EINVAL; + goto fail; } } =20 + ret =3D raw_handle_perm_lock(state->bs, RAW_PL_PREPARE, + s->perm & ~clear_perms, + s->shared_perm, errp); + if (ret) { + goto fail; + } + return 0; +fail: + qemu_close(rs->fd); + rs->fd =3D -1; return ret; } =20 @@ -627,6 +821,8 @@ static void raw_reopen_commit(BDRVReopenState *state) { BDRVRawReopenState *rs =3D state->opaque; BDRVRawState *s =3D state->bs->opaque; + uint64_t clear_perms =3D state->flags & BDRV_O_RDWR ? 0 : + BLK_PERM_WRITE | BLK_PERM_RESIZE | BLK_PERM_WRITE_UNCHANGED; =20 s->open_flags =3D rs->open_flags; =20 @@ -635,12 +831,17 @@ static void raw_reopen_commit(BDRVReopenState *state) =20 g_free(state->opaque); state->opaque =3D NULL; + raw_handle_perm_lock(state->bs, RAW_PL_COMMIT, s->perm & ~clear_perms, + s->shared_perm, NULL); } =20 =20 static void raw_reopen_abort(BDRVReopenState *state) { + BDRVRawState *s =3D state->bs->opaque; BDRVRawReopenState *rs =3D state->opaque; + uint64_t clear_perms =3D state->flags & BDRV_O_RDWR ? 0 : + BLK_PERM_WRITE | BLK_PERM_RESIZE | BLK_PERM_WRITE_UNCHANGED; =20 /* nothing to do if NULL, we didn't get far enough */ if (rs =3D=3D NULL) { @@ -653,6 +854,8 @@ static void raw_reopen_abort(BDRVReopenState *state) } g_free(state->opaque); state->opaque =3D NULL; + raw_handle_perm_lock(state->bs, RAW_PL_ABORT, s->perm & ~clear_perms, + s->shared_perm, NULL); } =20 static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd) @@ -1410,6 +1613,10 @@ static void raw_close(BlockDriverState *bs) qemu_close(s->fd); s->fd =3D -1; } + if (s->lock_fd >=3D 0) { + qemu_close(s->lock_fd); + s->lock_fd =3D -1; + } } =20 static int raw_truncate(BlockDriverState *bs, int64_t offset) @@ -1947,6 +2154,56 @@ static QemuOptsList raw_create_opts =3D { } }; =20 +static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t sh= ared, + Error **errp) +{ + return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp); +} + +static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t sha= red) +{ + BDRVRawState *s =3D bs->opaque; + raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL); + s->perm =3D perm; + s->shared_perm =3D shared; +} + +static void raw_abort_perm_update(BlockDriverState *bs) +{ + BDRVRawState *s =3D bs->opaque; + + raw_handle_perm_lock(bs, RAW_PL_ABORT, s->perm, s->shared_perm, NULL); +} + +static int raw_inactivate(BlockDriverState *bs) +{ + int ret; + uint64_t perm =3D 0; + uint64_t shared =3D BLK_PERM_ALL; + + ret =3D raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, NULL); + if (ret) { + return ret; + } + raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL); + return 0; +} + + +static void raw_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + BDRVRawState *s =3D bs->opaque; + int ret; + + assert(!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)); + ret =3D raw_handle_perm_lock(bs, RAW_PL_PREPARE, s->perm, s->shared_pe= rm, + errp); + if (ret) { + return; + } + raw_handle_perm_lock(bs, RAW_PL_COMMIT, s->perm, s->shared_perm, NULL); +} + BlockDriver bdrv_file =3D { .format_name =3D "file", .protocol_name =3D "file", @@ -1977,7 +2234,11 @@ BlockDriver bdrv_file =3D { .bdrv_get_info =3D raw_get_info, .bdrv_get_allocated_file_size =3D raw_get_allocated_file_size, - + .bdrv_inactivate =3D raw_inactivate, + .bdrv_invalidate_cache =3D raw_invalidate_cache, + .bdrv_check_perm =3D raw_check_perm, + .bdrv_set_perm =3D raw_set_perm, + .bdrv_abort_perm_update =3D raw_abort_perm_update, .create_opts =3D &raw_create_opts, }; =20 --=20 2.9.3