All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC PATCH 00/41] New op blocker system
@ 2017-02-13 17:22 Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open() Kevin Wolf
                   ` (41 more replies)
  0 siblings, 42 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This series is a first rough attempt at implementing the new op blocker system
whose design was agreed on quite a while ago, but proved a bit tricky to
implement in places. There is still work left to do, but if we want to get this
(or the greatest part of it) into 2.9, it's probably time to start review and
discussion.

The basic idea is that every user of a block node (including things outside the
block layer that go through a BlockBackend, and also other block nodes that
hold references to it) has to declare which low-level operations/permissions it
needs and which operation it allows other users to perform on the same node.
Depending on these declarations, conflicts are avoided by returning an error
for attempts to attach a conflicting user to the same node.

After this series, all users request permissions, and hopefully all of the
permissions they need. For a subset of them, getting the permission first is
actually enforced with assertions. We can probably add more assertions to this.

The series doesn't remove the old op blockers yet, though in theory the new op
blockers should block everything they used to block. (In practice it's not
completely true yet, some monitor commands don't get the right permissions yet,
in particular related to resize/graph modification.)

Kevin Wolf (41):
  block: Attach bs->file only during .bdrv_open()
  block: Add op blocker permission constants
  block: Add Error argument to bdrv_attach_child()
  block: Let callers request permissions when attaching a child node
  tests: Use opened block node for block job tests
  block: Involve block drivers in permission granting
  block: Default .bdrv_child_perm() for filter drivers
  block: Request child permissions in filter drivers
  block: Default .bdrv_child_perm() for format drivers
  block: Request child permissions in format drivers
  vvfat: Implement .bdrv_child_perm()
  block: Require .bdrv_child_perm() with child nodes
  block: Request real permissions in bdrv_attach_child()
  block: Add permissions to BlockBackend
  block: Add permissions to blk_new()
  block: Add error parameter to blk_insert_bs()
  block: Request real permissions in blk_new_open()
  block: Allow error return in BlockDevOps.change_media_cb()
  hw/block: Request permissions
  hw/block: Introduce share-rw qdev property
  blockjob: Add permissions to block_job_create()
  block: Add BdrvChildRole.get_link_name()
  block: Include details on permission errors in message
  block: Add BdrvChildRole.stay_at_node
  blockjob: Add permissions to block_job_add_bdrv()
  block: Factor out bdrv_open_driver()
  block: Add bdrv_new_open_driver()
  commit: Use real permissions in commit block job
  commit: Use real permissions for HMP 'commit'
  backup: Use real permissions in backup block job
  block: Fix pending requests check in bdrv_append()
  block: BdrvChildRole.attach/detach() callbacks
  block: Allow backing file links in change_parent_backing_link()
  mirror: Use real permissions in mirror/active commit block job
  stream: Use real permissions in streaming block job
  hmp: Request permissions in qemu-io
  migration/block: Use real permissions
  nbd/server: Use real permissions for NBD exports
  tests: Remove FIXME comments
  block: Pass BdrvChild to bdrv_aligned_preadv/pwritev
  block: Assertions for write permissions

 block.c                          | 615 ++++++++++++++++++++++++++++++++-------
 block/backup.c                   |  21 +-
 block/blkdebug.c                 |   2 +
 block/blkreplay.c                |   1 +
 block/blkverify.c                |   1 +
 block/block-backend.c            |  95 +++++-
 block/bochs.c                    |   7 +
 block/cloop.c                    |   7 +
 block/commit.c                   | 164 +++++++++--
 block/crypto.c                   |   7 +
 block/dmg.c                      |   7 +
 block/io.c                       |  40 ++-
 block/mirror.c                   | 160 ++++++++--
 block/parallels.c                |   7 +
 block/qcow.c                     |   7 +
 block/qcow2.c                    |  19 +-
 block/qed.c                      |  19 +-
 block/quorum.c                   |  10 +-
 block/raw-format.c               |   7 +
 block/replication.c              |   7 +
 block/stream.c                   |  36 ++-
 block/vdi.c                      |   7 +
 block/vhdx.c                     |   7 +
 block/vmdk.c                     |   7 +
 block/vpc.c                      |   7 +
 block/vvfat.c                    |  13 +
 blockdev.c                       |  27 +-
 blockjob.c                       |  39 ++-
 hmp.c                            |  33 ++-
 hw/block/block.c                 |  21 +-
 hw/block/fdc.c                   |  28 +-
 hw/block/m25p80.c                |   8 +
 hw/block/nand.c                  |   7 +
 hw/block/nvme.c                  |   8 +-
 hw/block/onenand.c               |   7 +
 hw/block/pflash_cfi01.c          |  18 +-
 hw/block/pflash_cfi02.c          |  19 +-
 hw/block/virtio-blk.c            |   8 +-
 hw/core/qdev-properties-system.c |   9 +-
 hw/ide/core.c                    |   2 +-
 hw/ide/qdev.c                    |   8 +-
 hw/nvram/spapr_nvram.c           |   8 +
 hw/scsi/scsi-disk.c              |  11 +-
 hw/sd/sd.c                       |   8 +-
 hw/usb/dev-storage.c             |   6 +-
 include/block/block.h            |   5 +-
 include/block/block_int.h        | 124 +++++++-
 include/block/blockjob.h         |   4 +-
 include/block/blockjob_int.h     |   4 +-
 include/hw/block/block.h         |   7 +-
 include/qemu-io.h                |   1 +
 include/sysemu/block-backend.h   |   9 +-
 migration/block.c                |  20 +-
 nbd/server.c                     |  16 +-
 qemu-img.c                       |   6 +-
 qemu-io-cmds.c                   |  28 ++
 tests/qemu-iotests/051.out       |   4 +-
 tests/qemu-iotests/051.pc.out    |  10 +-
 tests/qemu-iotests/055           |  11 +-
 tests/qemu-iotests/141           |   2 +-
 tests/qemu-iotests/141.out       |   4 +-
 tests/qemu-iotests/172.out       |  53 ++++
 tests/test-blockjob-txn.c        |  12 +-
 tests/test-blockjob.c            |  16 +-
 tests/test-throttle.c            |   7 +-
 65 files changed, 1616 insertions(+), 282 deletions(-)

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 14:34   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 02/41] block: Add op blocker permission constants Kevin Wolf
                   ` (40 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

The way that attaching bs->file worked was a bit unusual in that it was
the only child that would be attached to a node which is not opened yet.
Because of this, the block layer couldn't know yet which permissions the
driver would eventually need.

This patch moves the point where bs->file is attached to the beginning
of the individual .bdrv_open() implementations, so drivers already know
what they are going to do with the child. This is also more consistent
with how driver-specific children work.

bdrv_open() still needs its own BdrvChild to perform image probing, but
instead of directly assigning this BdrvChild to the BDS, it becomes a
temporary one and the node name is passed as an option to the drivers,
so that they can simply use bdrv_open_child() to create another
reference for their own use.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                       | 34 +++++++++++++++++++++++-----------
 block/bochs.c                 |  6 ++++++
 block/cloop.c                 |  6 ++++++
 block/crypto.c                |  6 ++++++
 block/dmg.c                   |  6 ++++++
 block/parallels.c             |  6 ++++++
 block/qcow.c                  |  6 ++++++
 block/qcow2.c                 | 18 +++++++++++++++---
 block/qed.c                   | 18 +++++++++++++++---
 block/raw-format.c            |  6 ++++++
 block/replication.c           |  6 ++++++
 block/vdi.c                   |  6 ++++++
 block/vhdx.c                  |  6 ++++++
 block/vmdk.c                  |  6 ++++++
 block/vpc.c                   |  6 ++++++
 tests/qemu-iotests/051.out    |  4 ++--
 tests/qemu-iotests/051.pc.out |  4 ++--
 17 files changed, 129 insertions(+), 21 deletions(-)

diff --git a/block.c b/block.c
index 743c349..0618f4b 100644
--- a/block.c
+++ b/block.c
@@ -1103,13 +1103,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
         assert(!drv->bdrv_needs_filename || filename != NULL);
         ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
     } else {
-        if (file == NULL) {
-            error_setg(errp, "Can't use '%s' as a block driver for the "
-                       "protocol level", drv->format_name);
-            ret = -EINVAL;
-            goto free_and_fail;
-        }
-        bs->file = file;
         ret = drv->bdrv_open(bs, options, open_flags, &local_err);
     }
 
@@ -1145,7 +1138,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
     return 0;
 
 free_and_fail:
-    bs->file = NULL;
     g_free(bs->opaque);
     bs->opaque = NULL;
     bs->drv = NULL;
@@ -1368,7 +1360,18 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
     }
 
     if (child->bs->inherits_from == parent) {
-        child->bs->inherits_from = NULL;
+        BdrvChild *c;
+
+        /* Remove inherits_from only when the last reference between parent and
+         * child->bs goes away. */
+        QLIST_FOREACH(c, &parent->children, next) {
+            if (c != child && c->bs == child->bs) {
+                break;
+            }
+        }
+        if (c == NULL) {
+            child->bs->inherits_from = NULL;
+        }
     }
 
     bdrv_root_unref_child(child);
@@ -1789,13 +1792,19 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
         qdict_del(options, "backing");
     }
 
-    /* Open image file without format layer */
+    /* Open image file without format layer. This BdrvChild is only used for
+     * probing, the block drivers will do their own bdrv_open_child() for the
+     * same BDS, which is why we put the node name back into options. */
     if ((flags & BDRV_O_PROTOCOL) == 0) {
         file = bdrv_open_child(filename, options, "file", bs,
                                &child_file, true, &local_err);
         if (local_err) {
             goto fail;
         }
+        if (file != NULL) {
+            qdict_put(options, "file",
+                      qstring_from_str(bdrv_get_node_name(file->bs)));
+        }
     }
 
     /* Image format probing */
@@ -1835,7 +1844,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
         goto fail;
     }
 
-    if (file && (bs->file != file)) {
+    if (file) {
         bdrv_unref_child(bs, file);
         file = NULL;
     }
@@ -1901,6 +1910,9 @@ fail:
     if (file != NULL) {
         bdrv_unref_child(bs, file);
     }
+    if (bs->file != NULL) {
+        bdrv_unref_child(bs, bs->file);
+    }
     QDECREF(snapshot_options);
     QDECREF(bs->explicit_options);
     QDECREF(bs->options);
diff --git a/block/bochs.c b/block/bochs.c
index 8c9652e..7dd2ac4 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -104,6 +104,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
     struct bochs_header bochs;
     int ret;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     bs->read_only = true; /* no write support yet */
 
     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
diff --git a/block/cloop.c b/block/cloop.c
index 7b75f7e..877c9b0 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -66,6 +66,12 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
     uint32_t offsets_size, max_compressed_block_size = 1, i;
     int ret;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     bs->read_only = true;
 
     /* read header */
diff --git a/block/crypto.c b/block/crypto.c
index 7aa7eb5..200fd0b 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -300,6 +300,12 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
     QCryptoBlockOpenOptions *open_opts = NULL;
     unsigned int cflags = 0;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
diff --git a/block/dmg.c b/block/dmg.c
index 58a3ae8..8e387cd 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -413,6 +413,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
     int64_t offset;
     int ret;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     block_module_load_one("dmg-bz2");
     bs->read_only = true;
 
diff --git a/block/parallels.c b/block/parallels.c
index 2ccefa7..d3970e1 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -581,6 +581,12 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
     Error *local_err = NULL;
     char *buf;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
     if (ret < 0) {
         goto fail;
diff --git a/block/qcow.c b/block/qcow.c
index fb738fc..a6dfe1a 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -106,6 +106,12 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
     QCowHeader header;
     Error *local_err = NULL;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
     if (ret < 0) {
         goto fail;
diff --git a/block/qcow2.c b/block/qcow2.c
index 3e274bd..4684554 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -814,8 +814,8 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
     return ret;
 }
 
-static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
-                      Error **errp)
+static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
+                         Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
     unsigned int len, i;
@@ -1205,6 +1205,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
     return ret;
 }
 
+static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
+{
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
+    return qcow2_do_open(bs, options, flags, errp);
+}
+
 static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
@@ -1785,7 +1797,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
     options = qdict_clone_shallow(bs->options);
 
     flags &= ~BDRV_O_INACTIVE;
-    ret = qcow2_open(bs, options, flags, &local_err);
+    ret = qcow2_do_open(bs, options, flags, &local_err);
     QDECREF(options);
     if (local_err) {
         error_propagate(errp, local_err);
diff --git a/block/qed.c b/block/qed.c
index 1a7ef0a..1ea5114 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -391,8 +391,8 @@ static void bdrv_qed_drain(BlockDriverState *bs)
     }
 }
 
-static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
-                         Error **errp)
+static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags,
+                            Error **errp)
 {
     BDRVQEDState *s = bs->opaque;
     QEDHeader le_header;
@@ -526,6 +526,18 @@ out:
     return ret;
 }
 
+static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
+                         Error **errp)
+{
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
+    return bdrv_qed_do_open(bs, options, flags, errp);
+}
+
 static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
 {
     BDRVQEDState *s = bs->opaque;
@@ -1603,7 +1615,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
     bdrv_qed_close(bs);
 
     memset(s, 0, sizeof(BDRVQEDState));
-    ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err);
+    ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         error_prepend(errp, "Could not reopen qed layer: ");
diff --git a/block/raw-format.c b/block/raw-format.c
index 8404a82..30caaa5 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -384,6 +384,12 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
     BDRVRawState *s = bs->opaque;
     int ret;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     bs->sg = bs->file->bs->sg;
     bs->supported_write_flags = BDRV_REQ_FUA &
         bs->file->bs->supported_write_flags;
diff --git a/block/replication.c b/block/replication.c
index 729dd12..eff85c7 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -86,6 +86,12 @@ static int replication_open(BlockDriverState *bs, QDict *options,
     const char *mode;
     const char *top_id;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     ret = -EINVAL;
     opts = qemu_opts_create(&replication_runtime_opts, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
diff --git a/block/vdi.c b/block/vdi.c
index 0aeb940..18b4773 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -363,6 +363,12 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
     int ret;
     Error *local_err = NULL;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     logout("\n");
 
     ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1);
diff --git a/block/vhdx.c b/block/vhdx.c
index 68db9e0..4dc2743 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -898,6 +898,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
     uint64_t signature;
     Error *local_err = NULL;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     s->bat = NULL;
     s->first_visible_write = true;
 
diff --git a/block/vmdk.c b/block/vmdk.c
index 393c84d..9d68ec5 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -943,6 +943,12 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
     uint32_t magic;
     Error *local_err = NULL;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     buf = vmdk_read_desc(bs->file, 0, errp);
     if (!buf) {
         return -EINVAL;
diff --git a/block/vpc.c b/block/vpc.c
index ed6353d..d0df2a1 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -220,6 +220,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
     int disk_type = VHD_DYNAMIC;
     int ret;
 
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
     opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 42bf416..7524c62 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -225,7 +225,7 @@ Testing: -drive driver=nbd
 QEMU_PROG: -drive driver=nbd: NBD server address missing
 
 Testing: -drive driver=raw
-QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level
+QEMU_PROG: -drive driver=raw: A block device must be specified for "file"
 
 Testing: -drive file.driver=file
 QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name
@@ -234,7 +234,7 @@ Testing: -drive file.driver=nbd
 QEMU_PROG: -drive file.driver=nbd: NBD server address missing
 
 Testing: -drive file.driver=raw
-QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level
+QEMU_PROG: -drive file.driver=raw: A block device must be specified for "file"
 
 Testing: -drive foo=bar
 QEMU_PROG: -drive foo=bar: Must specify either driver or file
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index 603bb76..f1669c1 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -319,7 +319,7 @@ Testing: -drive driver=nbd
 QEMU_PROG: -drive driver=nbd: NBD server address missing
 
 Testing: -drive driver=raw
-QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level
+QEMU_PROG: -drive driver=raw: A block device must be specified for "file"
 
 Testing: -drive file.driver=file
 QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name
@@ -328,7 +328,7 @@ Testing: -drive file.driver=nbd
 QEMU_PROG: -drive file.driver=nbd: NBD server address missing
 
 Testing: -drive file.driver=raw
-QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level
+QEMU_PROG: -drive file.driver=raw: A block device must be specified for "file"
 
 Testing: -drive foo=bar
 QEMU_PROG: -drive foo=bar: Must specify either driver or file
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 02/41] block: Add op blocker permission constants
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 03/41] block: Add Error argument to bdrv_attach_child() Kevin Wolf
                   ` (39 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This patch defines the permission categories that will be used by the
new op blocker system.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 include/block/block_int.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/include/block/block_int.h b/include/block/block_int.h
index 2d92d7e..6a37b06 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -414,6 +414,40 @@ struct BdrvChildRole {
 extern const BdrvChildRole child_file;
 extern const BdrvChildRole child_format;
 
+enum {
+    /**
+     * A user that has the "permission" of consistent reads is guaranteed that
+     * their view of the contents of the block device is complete and
+     * self-consistent, repesenting the contents of a disk at a specific point.
+     *
+     * For most block devices (including their backing files) this is true, but
+     * the property cannot be maintained in a few situations like for
+     * intermediate nodes of a commit block job.
+     */
+    BLK_PERM_CONSISTENT_READ    = 0x01,
+
+    /** This permission is required to change the visible disk contents. */
+    BLK_PERM_WRITE              = 0x02,
+
+    /**
+     * This permission (which is weaker than BLK_PERM_WRITE) is both enough and
+     * required for writes to the block node when the caller promises that
+     * this visible disk contents doesn't change.
+     */
+    BLK_PERM_WRITE_UNCHANGED    = 0x04,
+
+    /** This permission is required to change the size of a block node. */
+    BLK_PERM_RESIZE             = 0x08,
+
+    /**
+     * This permission is required to change the node that this BdrvChild
+     * points to.
+     */
+    BLK_PERM_GRAPH_MOD          = 0x10,
+
+    BLK_PERM_ALL                = 0x1f,
+};
+
 struct BdrvChild {
     BlockDriverState *bs;
     char *name;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 03/41] block: Add Error argument to bdrv_attach_child()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open() Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 02/41] block: Add op blocker permission constants Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 14:48   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 04/41] block: Let callers request permissions when attaching a child node Kevin Wolf
                   ` (38 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

It will have to return an error soon, so prepare the callers for it.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c               | 13 ++++++++++---
 block/quorum.c        |  8 +++++++-
 include/block/block.h |  3 ++-
 3 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/block.c b/block.c
index 0618f4b..9895aaf 100644
--- a/block.c
+++ b/block.c
@@ -1323,7 +1323,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
 BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
                              BlockDriverState *child_bs,
                              const char *child_name,
-                             const BdrvChildRole *child_role)
+                             const BdrvChildRole *child_role,
+                             Error **errp)
 {
     BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role,
                                               parent_bs);
@@ -1424,7 +1425,9 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
         bs->backing = NULL;
         goto out;
     }
-    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
+    /* FIXME Error handling */
+    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
+                                    &error_abort);
     bs->open_flags &= ~BDRV_O_NO_BACKING;
     pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
     pstrcpy(bs->backing_format, sizeof(bs->backing_format),
@@ -1594,7 +1597,11 @@ BdrvChild *bdrv_open_child(const char *filename,
         goto done;
     }
 
-    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
+    c = bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
+    if (!c) {
+        bdrv_unref(bs);
+        goto done;
+    }
 
 done:
     qdict_del(options, bdref_key);
diff --git a/block/quorum.c b/block/quorum.c
index 86e2072..45bf010 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -1032,10 +1032,16 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
 
     /* We can safely add the child now */
     bdrv_ref(child_bs);
-    child = bdrv_attach_child(bs, child_bs, indexstr, &child_format);
+
+    child = bdrv_attach_child(bs, child_bs, indexstr, &child_format, errp);
+    if (child == NULL) {
+        bdrv_unref(child_bs);
+        goto out;
+    }
     s->children = g_renew(BdrvChild *, s->children, s->num_children + 1);
     s->children[s->num_children++] = child;
 
+out:
     bdrv_drained_end(bs);
 }
 
diff --git a/include/block/block.h b/include/block/block.h
index 4e81f20..93812df 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -482,7 +482,8 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child);
 BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
                              BlockDriverState *child_bs,
                              const char *child_name,
-                             const BdrvChildRole *child_role);
+                             const BdrvChildRole *child_role,
+                             Error **errp);
 
 bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp);
 void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 04/41] block: Let callers request permissions when attaching a child node
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (2 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 03/41] block: Add Error argument to bdrv_attach_child() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 05/41] tests: Use opened block node for block job tests Kevin Wolf
                   ` (37 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

When attaching a node as a child to a new parent, the required and
shared permissions for this parent are checked against all other parents
of the node now, and an error is returned if there is a conflict.

This allows error returns to a function that previously always
succeeded, and the same is true for quite a few callers and their
callers. Converting all of them within the same patch would be too much,
so for now everyone tells that they don't need any permissions and allow
everyone else to do anything. This way we can use &error_abort initially
and convert caller by caller to pass actual permission requirements and
implement error handling.

All these places are marked with FIXME comments and it will be the job
of the next patches to clean them up again.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 66 +++++++++++++++++++++++++++++++++++++++++------
 block/block-backend.c     |  8 ++++--
 include/block/block_int.h | 15 ++++++++++-
 3 files changed, 78 insertions(+), 11 deletions(-)

diff --git a/block.c b/block.c
index 9895aaf..5977492 100644
--- a/block.c
+++ b/block.c
@@ -1281,6 +1281,38 @@ static int bdrv_fill_options(QDict **options, const char *filename,
     return 0;
 }
 
+static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
+                                  uint64_t new_shared_perm,
+                                  BdrvChild *ignore_child, Error **errp)
+{
+    BdrvChild *c;
+
+    /* There is no reason why anyone couldn't tolerate write_unchanged */
+    assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
+
+    QLIST_FOREACH(c, &bs->parents, next_parent) {
+        if (c == ignore_child) {
+            continue;
+        }
+
+        if ((new_used_perm & c->shared_perm) != new_used_perm ||
+            (c->perm & new_shared_perm) != c->perm)
+        {
+            const char *user = NULL;
+            if (c->role->get_name) {
+                user = c->role->get_name(c);
+                if (user && !*user) {
+                    user = NULL;
+                }
+            }
+            error_setg(errp, "Conflicts with %s", user ?: "another operation");
+            return -EPERM;
+        }
+    }
+
+    return 0;
+}
+
 static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
 {
     BlockDriverState *old_bs = child->bs;
@@ -1305,14 +1337,25 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
 BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
                                   const char *child_name,
                                   const BdrvChildRole *child_role,
-                                  void *opaque)
+                                  uint64_t perm, uint64_t shared_perm,
+                                  void *opaque, Error **errp)
 {
-    BdrvChild *child = g_new(BdrvChild, 1);
+    BdrvChild *child;
+    int ret;
+
+    ret = bdrv_check_update_perm(child_bs, perm, shared_perm, NULL, errp);
+    if (ret < 0) {
+        return NULL;
+    }
+
+    child = g_new(BdrvChild, 1);
     *child = (BdrvChild) {
-        .bs     = NULL,
-        .name   = g_strdup(child_name),
-        .role   = child_role,
-        .opaque = opaque,
+        .bs             = NULL,
+        .name           = g_strdup(child_name),
+        .role           = child_role,
+        .perm           = perm,
+        .shared_perm    = shared_perm,
+        .opaque         = opaque,
     };
 
     bdrv_replace_child(child, child_bs);
@@ -1326,8 +1369,15 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
                              const BdrvChildRole *child_role,
                              Error **errp)
 {
-    BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role,
-                                              parent_bs);
+    BdrvChild *child;
+
+    /* FIXME Use real permissions */
+    child = bdrv_root_attach_child(child_bs, child_name, child_role,
+                                   0, BLK_PERM_ALL, parent_bs, errp);
+    if (child == NULL) {
+        return NULL;
+    }
+
     QLIST_INSERT_HEAD(&parent_bs->children, child, next);
     return child;
 }
diff --git a/block/block-backend.c b/block/block-backend.c
index efbf398..d080f01 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -163,7 +163,9 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
         return NULL;
     }
 
-    blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk);
+    /* FIXME Use real permissions */
+    blk->root = bdrv_root_attach_child(bs, "root", &child_root,
+                                       0, BLK_PERM_ALL, blk, &error_abort);
 
     return blk;
 }
@@ -498,7 +500,9 @@ void blk_remove_bs(BlockBackend *blk)
 void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
 {
     bdrv_ref(bs);
-    blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk);
+    /* FIXME Use real permissions */
+    blk->root = bdrv_root_attach_child(bs, "root", &child_root,
+                                       0, BLK_PERM_ALL, blk, &error_abort);
 
     notifier_list_notify(&blk->insert_bs_notifiers, blk);
     if (blk->public.throttle_state) {
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 6a37b06..f36b064 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -453,6 +453,18 @@ struct BdrvChild {
     char *name;
     const BdrvChildRole *role;
     void *opaque;
+
+    /**
+     * Granted permissions for operating on this BdrvChild (BLK_PERM_* bitmask)
+     */
+    uint64_t perm;
+
+    /**
+     * Permissions that can still be granted to other users of @bs while this
+     * BdrvChild is still attached to it. (BLK_PERM_* bitmask)
+     */
+    uint64_t shared_perm;
+
     QLIST_ENTRY(BdrvChild) next;
     QLIST_ENTRY(BdrvChild) next_parent;
 };
@@ -816,7 +828,8 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr);
 BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
                                   const char *child_name,
                                   const BdrvChildRole *child_role,
-                                  void *opaque);
+                                  uint64_t perm, uint64_t shared_perm,
+                                  void *opaque, Error **errp);
 void bdrv_root_unref_child(BdrvChild *child);
 
 const char *bdrv_get_parent_name(const BlockDriverState *bs);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 05/41] tests: Use opened block node for block job tests
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (3 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 04/41] block: Let callers request permissions when attaching a child node Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting Kevin Wolf
                   ` (36 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

blk_insert_bs() and block job related functions will soon require an
opened block node (permission calculations will involve the block
driver), so let our tests be consistent with the real users in this
respect.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/test-blockjob-txn.c | 6 +++++-
 tests/test-blockjob.c     | 6 +++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
index b132e39..f6dfd08 100644
--- a/tests/test-blockjob-txn.c
+++ b/tests/test-blockjob-txn.c
@@ -96,7 +96,10 @@ static BlockJob *test_block_job_start(unsigned int iterations,
     char job_id[24];
 
     data = g_new0(TestBlockJobCBData, 1);
-    bs = bdrv_new();
+
+    bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
+    g_assert_nonnull(bs);
+
     snprintf(job_id, sizeof(job_id), "job%u", counter++);
     s = block_job_create(job_id, &test_block_job_driver, bs, 0,
                          BLOCK_JOB_DEFAULT, test_block_job_cb,
@@ -242,6 +245,7 @@ static void test_pair_jobs_fail_cancel_race(void)
 int main(int argc, char **argv)
 {
     qemu_init_main_loop(&error_abort);
+    bdrv_init();
 
     g_test_init(&argc, &argv, NULL);
     g_test_add_func("/single/success", test_single_job_success);
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 60b78a3..068c9e4 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -54,7 +54,10 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
 static BlockBackend *create_blk(const char *name)
 {
     BlockBackend *blk = blk_new();
-    BlockDriverState *bs = bdrv_new();
+    BlockDriverState *bs;
+
+    bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
+    g_assert_nonnull(bs);
 
     blk_insert_bs(blk, bs);
     bdrv_unref(bs);
@@ -140,6 +143,7 @@ static void test_job_ids(void)
 int main(int argc, char **argv)
 {
     qemu_init_main_loop(&error_abort);
+    bdrv_init();
 
     g_test_init(&argc, &argv, NULL);
     g_test_add_func("/blockjob/ids", test_job_ids);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (4 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 05/41] tests: Use opened block node for block job tests Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-14  5:51   ` Fam Zheng
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 07/41] block: Default .bdrv_child_perm() for filter drivers Kevin Wolf
                   ` (35 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

In many cases, the required permissions of one node on its children
depends on what its parents require from it. For example, the raw format
or most filter drivers only need to request consistent reads if that's
something that one of their parents wants.

In order to achieve this, this patch introduces two new BlockDriver
callbacks. The first one lets drivers first check (recursively) whether
the requested permissions can be set; the second one actually sets the
new permission bitmask.

Also add helper functions that drivers can use in their implementation
of the callbacks to update their permissions on a specific child.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 125 ++++++++++++++++++++++++++++++++++++++++++++++
 include/block/block_int.h |  46 +++++++++++++++++
 2 files changed, 171 insertions(+)

diff --git a/block.c b/block.c
index 5977492..c27cdce 100644
--- a/block.c
+++ b/block.c
@@ -1281,11 +1281,103 @@ static int bdrv_fill_options(QDict **options, const char *filename,
     return 0;
 }
 
+static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
+                           uint64_t cumulative_shared_perms, Error **errp)
+{
+    BlockDriver *drv = bs->drv;
+    BdrvChild *c;
+    int ret;
+
+    if (!drv) {
+        error_setg(errp, "Block node is not opened");
+        return -EINVAL;
+    }
+
+    /* Write permissions never work with read-only images */
+    if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
+        bdrv_is_read_only(bs))
+    {
+        error_setg(errp, "Block node is read-only");
+        return -EPERM;
+    }
+
+    /* Check this node */
+    if (drv->bdrv_check_perm) {
+        return drv->bdrv_check_perm(bs, cumulative_perms,
+                                    cumulative_shared_perms, errp);
+    }
+
+    /* Drivers may not have .bdrv_child_perm() */
+    if (!drv->bdrv_child_perm) {
+        return 0;
+    }
+
+    /* Check all children */
+    QLIST_FOREACH(c, &bs->children, next) {
+        uint64_t cur_perm, cur_shared;
+        drv->bdrv_child_perm(bs, c, c->role,
+                             cumulative_perms, cumulative_shared_perms,
+                             &cur_perm, &cur_shared);
+        ret = bdrv_child_check_perm(c, cur_perm, cur_shared, errp);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
+                          uint64_t cumulative_shared_perms)
+{
+    BlockDriver *drv = bs->drv;
+    BdrvChild *c;
+
+    if (!drv) {
+        return;
+    }
+
+    /* Update this node */
+    if (drv->bdrv_set_perm) {
+        drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
+    }
+
+    /* Drivers may not have .bdrv_child_perm() */
+    if (!drv->bdrv_child_perm) {
+        return;
+    }
+
+    /* Update all children */
+    QLIST_FOREACH(c, &bs->children, next) {
+        uint64_t cur_perm, cur_shared;
+        drv->bdrv_child_perm(bs, c, c->role,
+                             cumulative_perms, cumulative_shared_perms,
+                             &cur_perm, &cur_shared);
+        bdrv_child_set_perm(c, cur_perm, cur_shared);
+    }
+}
+
+static void bdrv_update_perm(BlockDriverState *bs)
+{
+    BdrvChild *c;
+    uint64_t cumulative_perms = 0;
+    uint64_t cumulative_shared_perms = BLK_PERM_ALL;
+
+    QLIST_FOREACH(c, &bs->parents, next_parent) {
+        cumulative_perms |= c->perm;
+        cumulative_shared_perms &= c->shared_perm;
+    }
+
+    bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
+}
+
 static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
                                   uint64_t new_shared_perm,
                                   BdrvChild *ignore_child, Error **errp)
 {
     BdrvChild *c;
+    uint64_t cumulative_perms = new_used_perm;
+    uint64_t cumulative_shared_perms = new_shared_perm;
 
     /* There is no reason why anyone couldn't tolerate write_unchanged */
     assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
@@ -1308,8 +1400,39 @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
             error_setg(errp, "Conflicts with %s", user ?: "another operation");
             return -EPERM;
         }
+
+        cumulative_perms |= c->perm;
+        cumulative_shared_perms &= c->shared_perm;
     }
 
+    return bdrv_check_perm(bs, cumulative_perms, cumulative_shared_perms, errp);
+}
+
+int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
+                          Error **errp)
+{
+    return bdrv_check_update_perm(c->bs, perm, shared, c, errp);
+}
+
+void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
+{
+    c->perm = perm;
+    c->shared_perm = shared;
+    bdrv_update_perm(c->bs);
+}
+
+int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
+                            Error **errp)
+{
+    int ret;
+
+    ret = bdrv_child_check_perm(c, perm, shared, errp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    bdrv_child_set_perm(c, perm, shared);
+
     return 0;
 }
 
@@ -1322,6 +1445,7 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
             child->role->drained_end(child);
         }
         QLIST_REMOVE(child, next_parent);
+        bdrv_update_perm(old_bs);
     }
 
     child->bs = new_bs;
@@ -1331,6 +1455,7 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
         if (new_bs->quiesce_counter && child->role->drained_begin) {
             child->role->drained_begin(child);
         }
+        bdrv_update_perm(new_bs);
     }
 }
 
diff --git a/include/block/block_int.h b/include/block/block_int.h
index f36b064..8578e17 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -320,6 +320,45 @@ struct BlockDriver {
     void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child,
                            Error **errp);
 
+    /**
+     * Checks whether the requested set of cumulative permissions in @perm
+     * can be granted for accessing @bs and whether no other users are using
+     * permissions other than those given in @shared (both arguments take
+     * BLK_PERM_* bitmasks).
+     *
+     * If both conditions are met, 0 is returned. Otherwise, -errno is returned
+     * and errp is set to an error describing the conflict.
+     */
+    int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm,
+                           uint64_t shared, Error **errp);
+
+    /**
+     * Called to inform the driver that the set of cumulative set of used
+     * permissions for @bs has changed to @perm, and the set of sharable
+     * permission to @shared. The driver can use this to propagate changes to
+     * its children (i.e. request permissions only if a parent actually needs
+     * them).
+     *
+     * If permissions are added to @perm or dropped from @shared, callers must
+     * use bdrv_check_perm() first to ensure that this operation is valid.
+     * Dropping from @perm or adding to @shared is always allowed without a
+     * previous check.
+     */
+    void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared);
+
+    /**
+     * Returns in @nperm and @nshared the permissions that the driver for @bs
+     * needs on its child @c, based on the cumulative permissions requested by
+     * the parents in @parent_perm and @parent_shared.
+     *
+     * If @c is NULL, return the permissions for attaching a new child for the
+     * given @role.
+     */
+     void (*bdrv_child_perm)(BlockDriverState* bs, BdrvChild *c,
+                             const BdrvChildRole *role,
+                             uint64_t parent_perm, uint64_t parent_shared,
+                             uint64_t *nperm, uint64_t *nshared);
+
     QLIST_ENTRY(BlockDriver) list;
 };
 
@@ -832,6 +871,13 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
                                   void *opaque, Error **errp);
 void bdrv_root_unref_child(BdrvChild *child);
 
+int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
+                          Error **errp);
+void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
+int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
+                            Error **errp);
+
+
 const char *bdrv_get_parent_name(const BlockDriverState *bs);
 void blk_dev_change_media_cb(BlockBackend *blk, bool load);
 bool blk_dev_has_removable_media(BlockBackend *blk);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 07/41] block: Default .bdrv_child_perm() for filter drivers
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (5 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 17:00   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 08/41] block: Request child permissions in " Kevin Wolf
                   ` (34 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Most filters need permissions related to read and write for their
children, but only if the node has a parent that wants to use the same
operation on the filter. The same is true for resize.

This adds a default implementation that simply forwards all necessary
permissions to all children of the node and leaves the other permissions
unchanged.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 24 ++++++++++++++++++++++++
 include/block/block_int.h |  8 ++++++++
 2 files changed, 32 insertions(+)

diff --git a/block.c b/block.c
index c27cdce..290768d 100644
--- a/block.c
+++ b/block.c
@@ -1436,6 +1436,30 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
     return 0;
 }
 
+#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
+                                 | BLK_PERM_WRITE \
+                                 | BLK_PERM_WRITE_UNCHANGED \
+                                 | BLK_PERM_RESIZE)
+#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
+
+void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
+                               const BdrvChildRole *role,
+                               uint64_t perm, uint64_t shared,
+                               uint64_t *nperm, uint64_t *nshared)
+{
+    if (c == NULL) {
+        *nperm = 0;
+        *nshared = BLK_PERM_ALL;
+        return;
+    }
+
+    *nperm   = (perm & DEFAULT_PERM_PASSTHROUGH) |
+               (c->perm & DEFAULT_PERM_UNCHANGED);
+    *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) |
+               (c->shared_perm & DEFAULT_PERM_UNCHANGED);
+}
+
+
 static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
 {
     BlockDriverState *old_bs = child->bs;
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 8578e17..2d74f92 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -877,6 +877,14 @@ void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
 int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
                             Error **errp);
 
+/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
+ * block filters: Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED and RESIZE to
+ * all children */
+void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
+                               const BdrvChildRole *role,
+                               uint64_t perm, uint64_t shared,
+                               uint64_t *nperm, uint64_t *nshared);
+
 
 const char *bdrv_get_parent_name(const BlockDriverState *bs);
 void blk_dev_change_media_cb(BlockBackend *blk, bool load);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 08/41] block: Request child permissions in filter drivers
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (6 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 07/41] block: Default .bdrv_child_perm() for filter drivers Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers Kevin Wolf
                   ` (33 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

All callers will have to request permissions for all of their child
nodes. Block drivers that act as simply filters can use the default
implementation of .bdrv_child_perm().

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/blkdebug.c    | 2 ++
 block/blkreplay.c   | 1 +
 block/blkverify.c   | 1 +
 block/quorum.c      | 2 ++
 block/raw-format.c  | 1 +
 block/replication.c | 1 +
 6 files changed, 8 insertions(+)

diff --git a/block/blkdebug.c b/block/blkdebug.c
index acccf85..39301d8 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -741,6 +741,8 @@ static BlockDriver bdrv_blkdebug = {
     .bdrv_file_open         = blkdebug_open,
     .bdrv_close             = blkdebug_close,
     .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
+    .bdrv_child_perm        = bdrv_filter_default_perms,
+
     .bdrv_getlength         = blkdebug_getlength,
     .bdrv_truncate          = blkdebug_truncate,
     .bdrv_refresh_filename  = blkdebug_refresh_filename,
diff --git a/block/blkreplay.c b/block/blkreplay.c
index a741654..c4e07f0 100755
--- a/block/blkreplay.c
+++ b/block/blkreplay.c
@@ -137,6 +137,7 @@ static BlockDriver bdrv_blkreplay = {
 
     .bdrv_file_open         = blkreplay_open,
     .bdrv_close             = blkreplay_close,
+    .bdrv_child_perm        = bdrv_filter_default_perms,
     .bdrv_getlength         = blkreplay_getlength,
 
     .bdrv_co_preadv         = blkreplay_co_preadv,
diff --git a/block/blkverify.c b/block/blkverify.c
index 43a940c..9a1e21c 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -320,6 +320,7 @@ static BlockDriver bdrv_blkverify = {
     .bdrv_parse_filename              = blkverify_parse_filename,
     .bdrv_file_open                   = blkverify_open,
     .bdrv_close                       = blkverify_close,
+    .bdrv_child_perm                  = bdrv_filter_default_perms,
     .bdrv_getlength                   = blkverify_getlength,
     .bdrv_refresh_filename            = blkverify_refresh_filename,
 
diff --git a/block/quorum.c b/block/quorum.c
index 45bf010..b6d8215 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -1132,6 +1132,8 @@ static BlockDriver bdrv_quorum = {
     .bdrv_add_child                     = quorum_add_child,
     .bdrv_del_child                     = quorum_del_child,
 
+    .bdrv_child_perm                    = bdrv_filter_default_perms,
+
     .is_filter                          = true,
     .bdrv_recurse_is_first_non_filter   = quorum_recurse_is_first_non_filter,
 };
diff --git a/block/raw-format.c b/block/raw-format.c
index 30caaa5..bbcfcbf 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -467,6 +467,7 @@ BlockDriver bdrv_raw = {
     .bdrv_reopen_abort    = &raw_reopen_abort,
     .bdrv_open            = &raw_open,
     .bdrv_close           = &raw_close,
+    .bdrv_child_perm      = bdrv_filter_default_perms,
     .bdrv_create          = &raw_create,
     .bdrv_co_preadv       = &raw_co_preadv,
     .bdrv_co_pwritev      = &raw_co_pwritev,
diff --git a/block/replication.c b/block/replication.c
index eff85c7..91465cb 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -660,6 +660,7 @@ BlockDriver bdrv_replication = {
 
     .bdrv_open                  = replication_open,
     .bdrv_close                 = replication_close,
+    .bdrv_child_perm            = bdrv_filter_default_perms,
 
     .bdrv_getlength             = replication_getlength,
     .bdrv_co_readv              = replication_co_readv,
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (7 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 08/41] block: Request child permissions in " Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-14  6:01   ` Fam Zheng
  2017-02-15 17:11   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 10/41] block: Request child permissions in " Kevin Wolf
                   ` (32 subsequent siblings)
  41 siblings, 2 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Almost all format drivers have the same characteristics as far as
permissions are concerned: They have one or more children for storing
their own data and, more importantly, metadata (can be written to and
grow even without external write requests, must be protected against
other writers and present consistent data) and optionally a backing file
(this is just data, so like for a filter, it only depends on what the
parent nodes need).

This provides a default implementation that can be shared by most of
our format drivers.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 37 +++++++++++++++++++++++++++++++++++++
 include/block/block_int.h |  8 ++++++++
 2 files changed, 45 insertions(+)

diff --git a/block.c b/block.c
index 290768d..8e99bb5 100644
--- a/block.c
+++ b/block.c
@@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
                (c->shared_perm & DEFAULT_PERM_UNCHANGED);
 }
 
+void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
+                               const BdrvChildRole *role,
+                               uint64_t perm, uint64_t shared,
+                               uint64_t *nperm, uint64_t *nshared)
+{
+    bool backing = (role == &child_backing);
+    assert(role == &child_backing || role == &child_file);
+
+    if (!backing) {
+        /* Apart from the modifications below, the same permissions are
+         * forwarded and left alone as for filters */
+        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
+
+        /* Format drivers may touch metadata even if the guest doesn't write */
+        if (!bdrv_is_read_only(bs)) {
+            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
+        }
+
+        /* bs->file always needs to be consistent because of the metadata. We
+         * can never allow other users to resize or write to it. */
+        perm |= BLK_PERM_CONSISTENT_READ;
+        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
+    } else {
+        /* We want consistent read from backing files if the parent needs it.
+         * No other operations are performed on backing files. */
+        perm &= BLK_PERM_CONSISTENT_READ;
+
+        /* If the parent can deal with changing data, we're okay with a
+         * writable backing file. */
+        shared &= BLK_PERM_WRITE;
+        shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
+                  BLK_PERM_WRITE_UNCHANGED;
+    }
+
+    *nperm = perm;
+    *nshared = shared;
+}
 
 static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
 {
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 2d74f92..46f51a6 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -885,6 +885,14 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
                                uint64_t perm, uint64_t shared,
                                uint64_t *nperm, uint64_t *nshared);
 
+/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
+ * (non-raw) image formats: Like above for bs->backing, but for bs->file it
+ * requires WRITE | RESIZE for read-write images, always requires
+ * CONSISTENT_READ and doesn't share WRITE. */
+void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
+                               const BdrvChildRole *role,
+                               uint64_t perm, uint64_t shared,
+                               uint64_t *nperm, uint64_t *nshared);
 
 const char *bdrv_get_parent_name(const BlockDriverState *bs);
 void blk_dev_change_media_cb(BlockBackend *blk, bool load);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 10/41] block: Request child permissions in format drivers
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (8 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 17:26   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm() Kevin Wolf
                   ` (31 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This makes use of the .bdrv_child_perm() implementation for formats that
we just added. All format drivers expose the permissions they actually
need nows, so that they can be set accordingly and updated when parents
are attached or detached.

The only format not included here is raw, which was already converted
with the other filter drivers.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/bochs.c     | 1 +
 block/cloop.c     | 1 +
 block/crypto.c    | 1 +
 block/dmg.c       | 1 +
 block/parallels.c | 1 +
 block/qcow.c      | 1 +
 block/qcow2.c     | 1 +
 block/qed.c       | 1 +
 block/vdi.c       | 1 +
 block/vhdx.c      | 1 +
 block/vmdk.c      | 1 +
 block/vpc.c       | 1 +
 12 files changed, 12 insertions(+)

diff --git a/block/bochs.c b/block/bochs.c
index 7dd2ac4..516da56 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -293,6 +293,7 @@ static BlockDriver bdrv_bochs = {
     .instance_size	= sizeof(BDRVBochsState),
     .bdrv_probe		= bochs_probe,
     .bdrv_open		= bochs_open,
+    .bdrv_child_perm     = bdrv_format_default_perms,
     .bdrv_refresh_limits = bochs_refresh_limits,
     .bdrv_co_preadv = bochs_co_preadv,
     .bdrv_close		= bochs_close,
diff --git a/block/cloop.c b/block/cloop.c
index 877c9b0..a6c7b9d 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -290,6 +290,7 @@ static BlockDriver bdrv_cloop = {
     .instance_size  = sizeof(BDRVCloopState),
     .bdrv_probe     = cloop_probe,
     .bdrv_open      = cloop_open,
+    .bdrv_child_perm     = bdrv_format_default_perms,
     .bdrv_refresh_limits = cloop_refresh_limits,
     .bdrv_co_preadv = cloop_co_preadv,
     .bdrv_close     = cloop_close,
diff --git a/block/crypto.c b/block/crypto.c
index 200fd0b..ca46883 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -628,6 +628,7 @@ BlockDriver bdrv_crypto_luks = {
     .bdrv_probe         = block_crypto_probe_luks,
     .bdrv_open          = block_crypto_open_luks,
     .bdrv_close         = block_crypto_close,
+    .bdrv_child_perm    = bdrv_format_default_perms,
     .bdrv_create        = block_crypto_create_luks,
     .bdrv_truncate      = block_crypto_truncate,
     .create_opts        = &block_crypto_create_opts_luks,
diff --git a/block/dmg.c b/block/dmg.c
index 8e387cd..a7d25fc 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -697,6 +697,7 @@ static BlockDriver bdrv_dmg = {
     .bdrv_probe     = dmg_probe,
     .bdrv_open      = dmg_open,
     .bdrv_refresh_limits = dmg_refresh_limits,
+    .bdrv_child_perm     = bdrv_format_default_perms,
     .bdrv_co_preadv = dmg_co_preadv,
     .bdrv_close     = dmg_close,
 };
diff --git a/block/parallels.c b/block/parallels.c
index d3970e1..b79e7df 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -762,6 +762,7 @@ static BlockDriver bdrv_parallels = {
     .bdrv_probe		= parallels_probe,
     .bdrv_open		= parallels_open,
     .bdrv_close		= parallels_close,
+    .bdrv_child_perm          = bdrv_format_default_perms,
     .bdrv_co_get_block_status = parallels_co_get_block_status,
     .bdrv_has_zero_init       = bdrv_has_zero_init_1,
     .bdrv_co_flush_to_os      = parallels_co_flush_to_os,
diff --git a/block/qcow.c b/block/qcow.c
index a6dfe1a..3a95d4f 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -1052,6 +1052,7 @@ static BlockDriver bdrv_qcow = {
     .bdrv_probe		= qcow_probe,
     .bdrv_open		= qcow_open,
     .bdrv_close		= qcow_close,
+    .bdrv_child_perm        = bdrv_format_default_perms,
     .bdrv_reopen_prepare    = qcow_reopen_prepare,
     .bdrv_create            = qcow_create,
     .bdrv_has_zero_init     = bdrv_has_zero_init_1,
diff --git a/block/qcow2.c b/block/qcow2.c
index 4684554..dac3fb8 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3399,6 +3399,7 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_reopen_commit   = qcow2_reopen_commit,
     .bdrv_reopen_abort    = qcow2_reopen_abort,
     .bdrv_join_options    = qcow2_join_options,
+    .bdrv_child_perm      = bdrv_format_default_perms,
     .bdrv_create        = qcow2_create,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
     .bdrv_co_get_block_status = qcow2_co_get_block_status,
diff --git a/block/qed.c b/block/qed.c
index 1ea5114..eda9402 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -1678,6 +1678,7 @@ static BlockDriver bdrv_qed = {
     .bdrv_open                = bdrv_qed_open,
     .bdrv_close               = bdrv_qed_close,
     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare,
+    .bdrv_child_perm          = bdrv_format_default_perms,
     .bdrv_create              = bdrv_qed_create,
     .bdrv_has_zero_init       = bdrv_has_zero_init_1,
     .bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
diff --git a/block/vdi.c b/block/vdi.c
index 18b4773..fd6e26d 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -891,6 +891,7 @@ static BlockDriver bdrv_vdi = {
     .bdrv_open = vdi_open,
     .bdrv_close = vdi_close,
     .bdrv_reopen_prepare = vdi_reopen_prepare,
+    .bdrv_child_perm          = bdrv_format_default_perms,
     .bdrv_create = vdi_create,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
     .bdrv_co_get_block_status = vdi_co_get_block_status,
diff --git a/block/vhdx.c b/block/vhdx.c
index 4dc2743..8c5ade2 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1983,6 +1983,7 @@ static BlockDriver bdrv_vhdx = {
     .bdrv_open              = vhdx_open,
     .bdrv_close             = vhdx_close,
     .bdrv_reopen_prepare    = vhdx_reopen_prepare,
+    .bdrv_child_perm        = bdrv_format_default_perms,
     .bdrv_co_readv          = vhdx_co_readv,
     .bdrv_co_writev         = vhdx_co_writev,
     .bdrv_create            = vhdx_create,
diff --git a/block/vmdk.c b/block/vmdk.c
index 9d68ec5..f5e2fb5 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2359,6 +2359,7 @@ static BlockDriver bdrv_vmdk = {
     .bdrv_open                    = vmdk_open,
     .bdrv_check                   = vmdk_check,
     .bdrv_reopen_prepare          = vmdk_reopen_prepare,
+    .bdrv_child_perm              = bdrv_format_default_perms,
     .bdrv_co_preadv               = vmdk_co_preadv,
     .bdrv_co_pwritev              = vmdk_co_pwritev,
     .bdrv_co_pwritev_compressed   = vmdk_co_pwritev_compressed,
diff --git a/block/vpc.c b/block/vpc.c
index d0df2a1..b9c9832 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -1067,6 +1067,7 @@ static BlockDriver bdrv_vpc = {
     .bdrv_open              = vpc_open,
     .bdrv_close             = vpc_close,
     .bdrv_reopen_prepare    = vpc_reopen_prepare,
+    .bdrv_child_perm        = bdrv_format_default_perms,
     .bdrv_create            = vpc_create,
 
     .bdrv_co_preadv             = vpc_co_preadv,
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (9 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 10/41] block: Request child permissions in " Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 17:30   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 12/41] block: Require .bdrv_child_perm() with child nodes Kevin Wolf
                   ` (30 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

vvfat is the last remaining driver that can have children, but doesn't
implement .bdrv_child_perm() yet. The default handlers aren't suitable
here, so let's implement a very simple driver-specific one that protects
the internal child from being used by other users as good as our
permissions permit.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/vvfat.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/block/vvfat.c b/block/vvfat.c
index c6bf67e..7246432 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -3052,6 +3052,18 @@ err:
     return ret;
 }
 
+static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
+                             const BdrvChildRole *role,
+                             uint64_t perm, uint64_t shared,
+                             uint64_t *nperm, uint64_t *nshared)
+{
+    assert(role == &child_vvfat_qcow);
+
+    /* This is a private node, nobody should try to attach to it */
+    *nperm = BLK_PERM_WRITE;
+    *nshared = 0;
+}
+
 static void vvfat_close(BlockDriverState *bs)
 {
     BDRVVVFATState *s = bs->opaque;
@@ -3077,6 +3089,7 @@ static BlockDriver bdrv_vvfat = {
     .bdrv_file_open         = vvfat_open,
     .bdrv_refresh_limits    = vvfat_refresh_limits,
     .bdrv_close             = vvfat_close,
+    .bdrv_child_perm        = vvfat_child_perm,
 
     .bdrv_co_preadv         = vvfat_co_preadv,
     .bdrv_co_pwritev        = vvfat_co_pwritev,
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 12/41] block: Require .bdrv_child_perm() with child nodes
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (10 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 13/41] block: Request real permissions in bdrv_attach_child() Kevin Wolf
                   ` (29 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

All block drivers that can have child nodes implement .bdrv_child_perm()
now. Make this officially a requirement by asserting that only drivers
without children can omit .bdrv_child_perm().

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/block.c b/block.c
index 8e99bb5..682548a 100644
--- a/block.c
+++ b/block.c
@@ -1307,8 +1307,9 @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
                                     cumulative_shared_perms, errp);
     }
 
-    /* Drivers may not have .bdrv_child_perm() */
+    /* Drivers that never have children can omit .bdrv_child_perm() */
     if (!drv->bdrv_child_perm) {
+        assert(QLIST_EMPTY(&bs->children));
         return 0;
     }
 
@@ -1342,8 +1343,9 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
         drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
     }
 
-    /* Drivers may not have .bdrv_child_perm() */
+    /* Drivers that never have children can omit .bdrv_child_perm() */
     if (!drv->bdrv_child_perm) {
+        assert(QLIST_EMPTY(&bs->children));
         return;
     }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 13/41] block: Request real permissions in bdrv_attach_child()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (11 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 12/41] block: Require .bdrv_child_perm() with child nodes Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 19:23   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 14/41] block: Add permissions to BlockBackend Kevin Wolf
                   ` (28 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Now that all block drivers with children tell us what permissions they
need from each of their children, bdrv_attach_child() can use this
information and make the right requirements while trying to attach new
children.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/block.c b/block.c
index 682548a..fd06549 100644
--- a/block.c
+++ b/block.c
@@ -1558,10 +1558,21 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
                              Error **errp)
 {
     BdrvChild *child;
+    uint64_t perm, shared_perm;
+
+    /* XXX This check is a workaround for bdrv_open_inherit(), which attaches
+     * bs->file before bs is opened for format probing. The 'else' branch
+     * really shouldn't exist. */
+    if (parent_bs->drv) {
+        parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
+                                        0, BLK_PERM_ALL, &perm, &shared_perm);
+    } else {
+        perm = 0;
+        shared_perm = BLK_PERM_ALL;
+    }
 
-    /* FIXME Use real permissions */
     child = bdrv_root_attach_child(child_bs, child_name, child_role,
-                                   0, BLK_PERM_ALL, parent_bs, errp);
+                                   perm, shared_perm, parent_bs, errp);
     if (child == NULL) {
         return NULL;
     }
@@ -2041,6 +2052,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
      * probing, the block drivers will do their own bdrv_open_child() for the
      * same BDS, which is why we put the node name back into options. */
     if ((flags & BDRV_O_PROTOCOL) == 0) {
+        /* FIXME Really shouldn't try to open a child for a still closed bs,
+         * see bdrv_attach_child() */
         file = bdrv_open_child(filename, options, "file", bs,
                                &child_file, true, &local_err);
         if (local_err) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 14/41] block: Add permissions to BlockBackend
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (12 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 13/41] block: Request real permissions in bdrv_attach_child() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-15 19:26   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 15/41] block: Add permissions to blk_new() Kevin Wolf
                   ` (27 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

The BlockBackend can now store the permissions that its user requires.
This is necessary because nodes can be ejected from or inserted into a
BlockBackend and all of these operations must make sure that the user
still gets what it requested initially.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/block-backend.c          | 27 +++++++++++++++++++++++++++
 include/sysemu/block-backend.h |  2 ++
 2 files changed, 29 insertions(+)

diff --git a/block/block-backend.c b/block/block-backend.c
index d080f01..8f0348d 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -59,6 +59,9 @@ struct BlockBackend {
     bool iostatus_enabled;
     BlockDeviceIoStatus iostatus;
 
+    uint64_t perm;
+    uint64_t shared_perm;
+
     bool allow_write_beyond_eof;
 
     NotifierList remove_bs_notifiers, insert_bs_notifiers;
@@ -126,6 +129,8 @@ BlockBackend *blk_new(void)
 
     blk = g_new0(BlockBackend, 1);
     blk->refcnt = 1;
+    blk->perm = 0;
+    blk->shared_perm = BLK_PERM_ALL;
     blk_set_enable_write_cache(blk, true);
 
     qemu_co_queue_init(&blk->public.throttled_reqs[0]);
@@ -511,6 +516,27 @@ void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
     }
 }
 
+/*
+ * Sets the permission bitmasks that the user of the BlockBackend needs.
+ */
+int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
+                 Error **errp)
+{
+    int ret;
+
+    if (blk->root) {
+        ret = bdrv_child_try_set_perm(blk->root, perm, shared_perm, errp);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    blk->perm = perm;
+    blk->shared_perm = shared_perm;
+
+    return 0;
+}
+
 static int blk_do_attach_dev(BlockBackend *blk, void *dev)
 {
     if (blk->dev) {
@@ -557,6 +583,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev)
     blk->dev_ops = NULL;
     blk->dev_opaque = NULL;
     blk->guest_block_size = 512;
+    blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
     blk_unref(blk);
 }
 
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 6444e41..4a5783a 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -99,6 +99,8 @@ void blk_remove_bs(BlockBackend *blk);
 void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
 bool bdrv_has_blk(BlockDriverState *bs);
 bool bdrv_is_root_node(BlockDriverState *bs);
+int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
+                 Error **errp);
 
 void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow);
 void blk_iostatus_enable(BlockBackend *blk);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 15/41] block: Add permissions to blk_new()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (13 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 14/41] block: Add permissions to BlockBackend Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 10:29   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs() Kevin Wolf
                   ` (26 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

We want every user to be specific about the permissions it needs, so
we'll pass the initial permissions as parameters to blk_new(). A user
only needs to call blk_set_perm() if it wants to change the permissions
after the fact.

The permissions are stored in the BlockBackend and applied whenever a
BlockDriverState should be attached in blk_insert_bs().

This does not include actually choosing the right set of permissions
yet. Instead, the usual FIXME comment is added to each place and will be
addressed in individual patches.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/backup.c                   |  3 ++-
 block/block-backend.c            | 12 ++++++------
 block/commit.c                   | 12 ++++++++----
 block/mirror.c                   |  3 ++-
 blockdev.c                       |  2 +-
 blockjob.c                       |  3 ++-
 hmp.c                            |  3 ++-
 hw/block/fdc.c                   |  3 ++-
 hw/core/qdev-properties-system.c |  3 ++-
 hw/ide/qdev.c                    |  3 ++-
 hw/scsi/scsi-disk.c              |  3 ++-
 include/sysemu/block-backend.h   |  2 +-
 migration/block.c                |  3 ++-
 nbd/server.c                     |  3 ++-
 tests/test-blockjob.c            |  3 ++-
 tests/test-throttle.c            |  7 ++++---
 16 files changed, 42 insertions(+), 26 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index ea38733..bb76c69 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -624,7 +624,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         goto error;
     }
 
-    job->target = blk_new();
+    /* FIXME Use real permissions */
+    job->target = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(job->target, target);
 
     job->on_source_error = on_source_error;
diff --git a/block/block-backend.c b/block/block-backend.c
index 8f0348d..a219f0b 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -123,14 +123,14 @@ static const BdrvChildRole child_root = {
  * Store an error through @errp on failure, unless it's null.
  * Return the new BlockBackend on success, null on failure.
  */
-BlockBackend *blk_new(void)
+BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
 {
     BlockBackend *blk;
 
     blk = g_new0(BlockBackend, 1);
     blk->refcnt = 1;
-    blk->perm = 0;
-    blk->shared_perm = BLK_PERM_ALL;
+    blk->perm = perm;
+    blk->shared_perm = shared_perm;
     blk_set_enable_write_cache(blk, true);
 
     qemu_co_queue_init(&blk->public.throttled_reqs[0]);
@@ -161,7 +161,7 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
     BlockBackend *blk;
     BlockDriverState *bs;
 
-    blk = blk_new();
+    blk = blk_new(0, BLK_PERM_ALL);
     bs = bdrv_open(filename, reference, options, flags, errp);
     if (!bs) {
         blk_unref(blk);
@@ -505,9 +505,9 @@ void blk_remove_bs(BlockBackend *blk)
 void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
 {
     bdrv_ref(bs);
-    /* FIXME Use real permissions */
     blk->root = bdrv_root_attach_child(bs, "root", &child_root,
-                                       0, BLK_PERM_ALL, blk, &error_abort);
+                                       blk->perm, blk->shared_perm, blk,
+                                       &error_abort);
 
     notifier_list_notify(&blk->insert_bs_notifiers, blk);
     if (blk->public.throttle_state) {
diff --git a/block/commit.c b/block/commit.c
index c284e85..1897e98 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -275,10 +275,12 @@ void commit_start(const char *job_id, BlockDriverState *bs,
         block_job_add_bdrv(&s->common, overlay_bs);
     }
 
-    s->base = blk_new();
+    /* FIXME Use real permissions */
+    s->base = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(s->base, base);
 
-    s->top = blk_new();
+    /* FIXME Use real permissions */
+    s->top = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(s->top, top);
 
     s->active = bs;
@@ -328,10 +330,12 @@ int bdrv_commit(BlockDriverState *bs)
         }
     }
 
-    src = blk_new();
+    /* FIXME Use real permissions */
+    src = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(src, bs);
 
-    backing = blk_new();
+    /* FIXME Use real permissions */
+    backing = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(backing, bs->backing->bs);
 
     length = blk_getlength(src);
diff --git a/block/mirror.c b/block/mirror.c
index 301ba92..810933e 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -985,7 +985,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
         return;
     }
 
-    s->target = blk_new();
+    /* FIXME Use real permissions */
+    s->target = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(s->target, target);
 
     s->replaces = g_strdup(replaces);
diff --git a/blockdev.c b/blockdev.c
index db82ac9..2e195f1 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -554,7 +554,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
     if ((!file || !*file) && !qdict_size(bs_opts)) {
         BlockBackendRootState *blk_rs;
 
-        blk = blk_new();
+        blk = blk_new(0, BLK_PERM_ALL);
         blk_rs = blk_get_root_state(blk);
         blk_rs->open_flags    = bdrv_flags;
         blk_rs->read_only     = read_only;
diff --git a/blockjob.c b/blockjob.c
index abee11b..508e0e5 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -159,7 +159,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
         }
     }
 
-    blk = blk_new();
+    /* FIXME Use real permissions */
+    blk = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(blk, bs);
 
     job = g_malloc0(driver->instance_size);
diff --git a/hmp.c b/hmp.c
index 2bc4f06..15fd3f7 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2044,7 +2044,8 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
     if (!blk) {
         BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
         if (bs) {
-            blk = local_blk = blk_new();
+            /* FIXME Use real permissions */
+            blk = local_blk = blk_new(0, BLK_PERM_ALL);
             blk_insert_bs(blk, bs);
         } else {
             goto fail;
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 17d29e7..74f3634 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -533,7 +533,8 @@ static int floppy_drive_init(DeviceState *qdev)
 
     if (!dev->conf.blk) {
         /* Anonymous BlockBackend for an empty drive */
-        dev->conf.blk = blk_new();
+        /* FIXME Use real permissions */
+        dev->conf.blk = blk_new(0, BLK_PERM_ALL);
         ret = blk_attach_dev(dev->conf.blk, qdev);
         assert(ret == 0);
     }
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 94f4d8b..cca4775 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -78,7 +78,8 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
     if (!blk) {
         BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
         if (bs) {
-            blk = blk_new();
+            /* FIXME Use real permissions */
+            blk = blk_new(0, BLK_PERM_ALL);
             blk_insert_bs(blk, bs);
             blk_created = true;
         }
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index dbaa75c..bb3c377 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -170,7 +170,8 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
             return -1;
         } else {
             /* Anonymous BlockBackend for an empty drive */
-            dev->conf.blk = blk_new();
+            /* FIXME Use real permissions */
+            dev->conf.blk = blk_new(0, BLK_PERM_ALL);
         }
     }
 
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index cc06fe5..61c44a9 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2365,7 +2365,8 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
 
     if (!dev->conf.blk) {
-        dev->conf.blk = blk_new();
+        /* FIXME Use real permissions */
+        dev->conf.blk = blk_new(0, BLK_PERM_ALL);
     }
 
     s->qdev.blocksize = 2048;
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 4a5783a..1bda6d7 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -78,7 +78,7 @@ typedef struct BlockBackendPublic {
     QLIST_ENTRY(BlockBackendPublic) round_robin;
 } BlockBackendPublic;
 
-BlockBackend *blk_new(void);
+BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);
 BlockBackend *blk_new_open(const char *filename, const char *reference,
                            QDict *options, int flags, Error **errp);
 int blk_get_refcnt(BlockBackend *blk);
diff --git a/migration/block.c b/migration/block.c
index ebc10e6..6b7ffd4 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -415,7 +415,8 @@ static void init_blk_migration(QEMUFile *f)
         }
 
         bmds = g_new0(BlkMigDevState, 1);
-        bmds->blk = blk_new();
+        /* FIXME Use real permissions */
+        bmds->blk = blk_new(0, BLK_PERM_ALL);
         bmds->blk_name = g_strdup(bdrv_get_device_name(bs));
         bmds->bulk_completed = 0;
         bmds->total_sectors = sectors;
diff --git a/nbd/server.c b/nbd/server.c
index efe5cb8..871111c 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -890,7 +890,8 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
     BlockBackend *blk;
     NBDExport *exp = g_malloc0(sizeof(NBDExport));
 
-    blk = blk_new();
+    /* FIXME Use real permissions */
+    blk = blk_new(0, BLK_PERM_ALL);
     blk_insert_bs(blk, bs);
     blk_set_enable_write_cache(blk, !writethrough);
 
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 068c9e4..1dd1cfa 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -53,7 +53,8 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
  * BlockDriverState inserted. */
 static BlockBackend *create_blk(const char *name)
 {
-    BlockBackend *blk = blk_new();
+    /* FIXME Use real permissions */
+    BlockBackend *blk = blk_new(0, BLK_PERM_ALL);
     BlockDriverState *bs;
 
     bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 363b59a..5846433 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -593,9 +593,10 @@ static void test_groups(void)
     BlockBackend *blk1, *blk2, *blk3;
     BlockBackendPublic *blkp1, *blkp2, *blkp3;
 
-    blk1 = blk_new();
-    blk2 = blk_new();
-    blk3 = blk_new();
+    /* FIXME Use real permissions */
+    blk1 = blk_new(0, BLK_PERM_ALL);
+    blk2 = blk_new(0, BLK_PERM_ALL);
+    blk3 = blk_new(0, BLK_PERM_ALL);
 
     blkp1 = blk_get_public(blk1);
     blkp2 = blk_get_public(blk2);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (14 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 15/41] block: Add permissions to blk_new() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-14  6:58   ` Fam Zheng
  2017-02-20 11:04   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 17/41] block: Request real permissions in blk_new_open() Kevin Wolf
                   ` (25 subsequent siblings)
  41 siblings, 2 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Mow that blk_insert_bs() requests the BlockBackend permissions for the
node it attaches to, it can fail. Instead of aborting, pass the errors
to the callers.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/backup.c                   |  5 ++++-
 block/block-backend.c            | 12 ++++++++----
 block/commit.c                   | 38 ++++++++++++++++++++++++++++++--------
 block/mirror.c                   | 15 ++++++++++++---
 blockdev.c                       |  6 +++++-
 blockjob.c                       |  7 ++++++-
 hmp.c                            |  6 +++++-
 hw/core/qdev-properties-system.c |  7 ++++++-
 include/sysemu/block-backend.h   |  2 +-
 migration/block.c                |  2 +-
 nbd/server.c                     |  6 +++++-
 tests/test-blockjob.c            |  2 +-
 12 files changed, 84 insertions(+), 24 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index bb76c69..42e8e99 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -626,7 +626,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 
     /* FIXME Use real permissions */
     job->target = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(job->target, target);
+    ret = blk_insert_bs(job->target, target, errp);
+    if (ret < 0) {
+        goto error;
+    }
 
     job->on_source_error = on_source_error;
     job->on_target_error = on_target_error;
diff --git a/block/block-backend.c b/block/block-backend.c
index a219f0b..1f80854 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -502,18 +502,22 @@ void blk_remove_bs(BlockBackend *blk)
 /*
  * Associates a new BlockDriverState with @blk.
  */
-void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
+int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
 {
-    bdrv_ref(bs);
     blk->root = bdrv_root_attach_child(bs, "root", &child_root,
-                                       blk->perm, blk->shared_perm, blk,
-                                       &error_abort);
+                                       blk->perm, blk->shared_perm, blk, errp);
+    if (blk->root == NULL) {
+        return -EPERM;
+    }
+    bdrv_ref(bs);
 
     notifier_list_notify(&blk->insert_bs_notifiers, blk);
     if (blk->public.throttle_state) {
         throttle_timers_attach_aio_context(
             &blk->public.throttle_timers, bdrv_get_aio_context(bs));
     }
+
+    return 0;
 }
 
 /*
diff --git a/block/commit.c b/block/commit.c
index 1897e98..2ad8138 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -220,6 +220,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
     BlockDriverState *iter;
     BlockDriverState *overlay_bs;
     Error *local_err = NULL;
+    int ret;
 
     assert(top != bs);
     if (top == base) {
@@ -256,8 +257,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
         bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
         if (local_err != NULL) {
             error_propagate(errp, local_err);
-            block_job_unref(&s->common);
-            return;
+            goto fail;
         }
     }
 
@@ -277,11 +277,17 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 
     /* FIXME Use real permissions */
     s->base = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(s->base, base);
+    ret = blk_insert_bs(s->base, base, errp);
+    if (ret < 0) {
+        goto fail;
+    }
 
     /* FIXME Use real permissions */
     s->top = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(s->top, top);
+    ret = blk_insert_bs(s->top, top, errp);
+    if (ret < 0) {
+        goto fail;
+    }
 
     s->active = bs;
 
@@ -294,6 +300,16 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 
     trace_commit_start(bs, base, top, s);
     block_job_start(&s->common);
+    return;
+
+fail:
+    if (s->base) {
+        blk_unref(s->base);
+    }
+    if (s->top) {
+        blk_unref(s->top);
+    }
+    block_job_unref(&s->common);
 }
 
 
@@ -332,11 +348,17 @@ int bdrv_commit(BlockDriverState *bs)
 
     /* FIXME Use real permissions */
     src = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(src, bs);
-
-    /* FIXME Use real permissions */
     backing = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(backing, bs->backing->bs);
+
+    ret = blk_insert_bs(src, bs, NULL);
+    if (ret < 0) {
+        goto ro_cleanup;
+    }
+
+    ret = blk_insert_bs(backing, bs->backing->bs, NULL);
+    if (ret < 0) {
+        goto ro_cleanup;
+    }
 
     length = blk_getlength(src);
     if (length < 0) {
diff --git a/block/mirror.c b/block/mirror.c
index 810933e..630e5ef 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -517,9 +517,12 @@ static void mirror_exit(BlockJob *job, void *opaque)
         bdrv_replace_in_backing_chain(to_replace, target_bs);
         bdrv_drained_end(target_bs);
 
-        /* We just changed the BDS the job BB refers to */
+        /* We just changed the BDS the job BB refers to, so switch the BB back
+         * so the cleanup does the right thing. We don't need any permissions
+         * any more now. */
         blk_remove_bs(job->blk);
-        blk_insert_bs(job->blk, src);
+        blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
+        blk_insert_bs(job->blk, src, &error_abort);
     }
     if (s->to_replace) {
         bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
@@ -963,6 +966,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
                              bool auto_complete)
 {
     MirrorBlockJob *s;
+    int ret;
 
     if (granularity == 0) {
         granularity = bdrv_get_default_bitmap_granularity(target);
@@ -987,7 +991,12 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
 
     /* FIXME Use real permissions */
     s->target = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(s->target, target);
+    ret = blk_insert_bs(s->target, target, errp);
+    if (ret < 0) {
+        blk_unref(s->target);
+        block_job_unref(&s->common);
+        return;
+    }
 
     s->replaces = g_strdup(replaces);
     s->on_source_error = on_source_error;
diff --git a/blockdev.c b/blockdev.c
index 2e195f1..c590e67 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2432,6 +2432,7 @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
                                             BlockDriverState *bs, Error **errp)
 {
     bool has_device;
+    int ret;
 
     /* For BBs without a device, we can exchange the BDS tree at will */
     has_device = blk_get_attached_dev(blk);
@@ -2451,7 +2452,10 @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
         return;
     }
 
-    blk_insert_bs(blk, bs);
+    ret = blk_insert_bs(blk, bs, errp);
+    if (ret < 0) {
+        return;
+    }
 
     if (!blk_dev_has_tray(blk)) {
         /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
diff --git a/blockjob.c b/blockjob.c
index 508e0e5..72b7d4c 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -128,6 +128,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
 {
     BlockBackend *blk;
     BlockJob *job;
+    int ret;
 
     if (bs->job) {
         error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
@@ -161,7 +162,11 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
 
     /* FIXME Use real permissions */
     blk = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(blk, bs);
+    ret = blk_insert_bs(blk, bs, errp);
+    if (ret < 0) {
+        blk_unref(blk);
+        return NULL;
+    }
 
     job = g_malloc0(driver->instance_size);
     error_setg(&job->blocker, "block device is in use by block job: %s",
diff --git a/hmp.c b/hmp.c
index 15fd3f7..801fddb 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2039,6 +2039,7 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
     const char* device = qdict_get_str(qdict, "device");
     const char* command = qdict_get_str(qdict, "command");
     Error *err = NULL;
+    int ret;
 
     blk = blk_by_name(device);
     if (!blk) {
@@ -2046,7 +2047,10 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
         if (bs) {
             /* FIXME Use real permissions */
             blk = local_blk = blk_new(0, BLK_PERM_ALL);
-            blk_insert_bs(blk, bs);
+            ret = blk_insert_bs(blk, bs, &err);
+            if (ret < 0) {
+                goto fail;
+            }
         } else {
             goto fail;
         }
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index cca4775..66ba367 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -73,6 +73,7 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
 {
     BlockBackend *blk;
     bool blk_created = false;
+    int ret;
 
     blk = blk_by_name(str);
     if (!blk) {
@@ -80,8 +81,12 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
         if (bs) {
             /* FIXME Use real permissions */
             blk = blk_new(0, BLK_PERM_ALL);
-            blk_insert_bs(blk, bs);
             blk_created = true;
+
+            ret = blk_insert_bs(blk, bs, errp);
+            if (ret < 0) {
+                goto fail;
+            }
         }
     }
     if (!blk) {
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 1bda6d7..6f21508 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -96,7 +96,7 @@ BlockBackend *blk_by_public(BlockBackendPublic *public);
 
 BlockDriverState *blk_bs(BlockBackend *blk);
 void blk_remove_bs(BlockBackend *blk);
-void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
+int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp);
 bool bdrv_has_blk(BlockDriverState *bs);
 bool bdrv_is_root_node(BlockDriverState *bs);
 int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
diff --git a/migration/block.c b/migration/block.c
index 6b7ffd4..d259936 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -446,7 +446,7 @@ static void init_blk_migration(QEMUFile *f)
         BlockDriverState *bs = bmds_bs[i].bs;
 
         if (bmds) {
-            blk_insert_bs(bmds->blk, bs);
+            blk_insert_bs(bmds->blk, bs, &error_abort);
 
             alloc_aio_bitmap(bmds);
             error_setg(&bmds->blocker, "block device is in use by migration");
diff --git a/nbd/server.c b/nbd/server.c
index 871111c..deb4358 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -889,10 +889,14 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
 {
     BlockBackend *blk;
     NBDExport *exp = g_malloc0(sizeof(NBDExport));
+    int ret;
 
     /* FIXME Use real permissions */
     blk = blk_new(0, BLK_PERM_ALL);
-    blk_insert_bs(blk, bs);
+    ret = blk_insert_bs(blk, bs, errp);
+    if (ret < 0) {
+        goto fail;
+    }
     blk_set_enable_write_cache(blk, !writethrough);
 
     exp->refcount = 1;
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 1dd1cfa..143ce96 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -60,7 +60,7 @@ static BlockBackend *create_blk(const char *name)
     bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
     g_assert_nonnull(bs);
 
-    blk_insert_bs(blk, bs);
+    blk_insert_bs(blk, bs, &error_abort);
     bdrv_unref(bs);
 
     if (name) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 17/41] block: Request real permissions in blk_new_open()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (15 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 11:16   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 18/41] block: Allow error return in BlockDevOps.change_media_cb() Kevin Wolf
                   ` (24 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

We can figure out the necessary permissions from the flags that the
caller passed.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/block-backend.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index 1f80854..e10a278 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -160,6 +160,7 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
 {
     BlockBackend *blk;
     BlockDriverState *bs;
+    uint64_t perm;
 
     blk = blk_new(0, BLK_PERM_ALL);
     bs = bdrv_open(filename, reference, options, flags, errp);
@@ -168,9 +169,20 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
         return NULL;
     }
 
-    /* FIXME Use real permissions */
+    /* blk_new_open() is mainly used in .bdrv_create implementations and the
+     * tools where sharing isn't a concern because the BDS stays private, so we
+     * just request permission according to the flags.
+     *
+     * The exceptions are xen_disk and blockdev_init(); in these cases, the
+     * caller of blk_new_open() doesn't make use of the permissions, but they
+     * shouldn't hurt either. We can still share everything here because the
+     * guest devices will add their own blockers if they can't share. */
+    perm = BLK_PERM_CONSISTENT_READ;
+    if (flags & BDRV_O_RDWR) {
+        perm |= BLK_PERM_WRITE;
+    }
     blk->root = bdrv_root_attach_child(bs, "root", &child_root,
-                                       0, BLK_PERM_ALL, blk, &error_abort);
+                                       perm, BLK_PERM_ALL, blk, &error_abort);
 
     return blk;
 }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 18/41] block: Allow error return in BlockDevOps.change_media_cb()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (16 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 17/41] block: Request real permissions in blk_new_open() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 11:31   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions Kevin Wolf
                   ` (23 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Some devices allow a media change between read-only and read-write
media. They need to adapt the permissions in their .change_media_cb()
implementation, which can fail. So add an Error parameter to the
function.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/block-backend.c          | 12 +++++++++---
 blockdev.c                     | 19 +++++++++++++++----
 hw/block/fdc.c                 |  2 +-
 hw/ide/core.c                  |  2 +-
 hw/scsi/scsi-disk.c            |  2 +-
 hw/sd/sd.c                     |  2 +-
 include/block/block_int.h      |  2 +-
 include/sysemu/block-backend.h |  2 +-
 8 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index e10a278..0c23add 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -671,15 +671,21 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
  * Else, notify of media eject.
  * Also send DEVICE_TRAY_MOVED events as appropriate.
  */
-void blk_dev_change_media_cb(BlockBackend *blk, bool load)
+void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
 {
     if (blk->dev_ops && blk->dev_ops->change_media_cb) {
         bool tray_was_open, tray_is_open;
+        Error *local_err = NULL;
 
         assert(!blk->legacy_dev);
 
         tray_was_open = blk_dev_is_tray_open(blk);
-        blk->dev_ops->change_media_cb(blk->dev_opaque, load);
+        blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
+        if (local_err) {
+            assert(load == true);
+            error_propagate(errp, local_err);
+            return;
+        }
         tray_is_open = blk_dev_is_tray_open(blk);
 
         if (tray_was_open != tray_is_open) {
@@ -693,7 +699,7 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load)
 
 static void blk_root_change_media(BdrvChild *child, bool load)
 {
-    blk_dev_change_media_cb(child->opaque, load);
+    blk_dev_change_media_cb(child->opaque, load, NULL);
 }
 
 /*
diff --git a/blockdev.c b/blockdev.c
index c590e67..8c30084 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2307,7 +2307,7 @@ static int do_open_tray(const char *blk_name, const char *qdev_id,
     }
 
     if (!locked || force) {
-        blk_dev_change_media_cb(blk, false);
+        blk_dev_change_media_cb(blk, false, &error_abort);
     }
 
     if (locked && !force) {
@@ -2345,6 +2345,7 @@ void qmp_blockdev_close_tray(bool has_device, const char *device,
                              Error **errp)
 {
     BlockBackend *blk;
+    Error *local_err = NULL;
 
     device = has_device ? device : NULL;
     id = has_id ? id : NULL;
@@ -2368,7 +2369,11 @@ void qmp_blockdev_close_tray(bool has_device, const char *device,
         return;
     }
 
-    blk_dev_change_media_cb(blk, true);
+    blk_dev_change_media_cb(blk, true, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
 }
 
 void qmp_x_blockdev_remove_medium(bool has_device, const char *device,
@@ -2421,7 +2426,7 @@ void qmp_x_blockdev_remove_medium(bool has_device, const char *device,
          * called at all); therefore, the medium needs to be ejected here.
          * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
          * value passed here (i.e. false). */
-        blk_dev_change_media_cb(blk, false);
+        blk_dev_change_media_cb(blk, false, &error_abort);
     }
 
 out:
@@ -2431,6 +2436,7 @@ out:
 static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
                                             BlockDriverState *bs, Error **errp)
 {
+    Error *local_err = NULL;
     bool has_device;
     int ret;
 
@@ -2463,7 +2469,12 @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
          * slot here.
          * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
          * value passed here (i.e. true). */
-        blk_dev_change_media_cb(blk, true);
+        blk_dev_change_media_cb(blk, true, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            blk_remove_bs(blk);
+            return;
+        }
     }
 }
 
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 74f3634..5f6c496 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -469,7 +469,7 @@ static void fd_revalidate(FDrive *drv)
     }
 }
 
-static void fd_change_cb(void *opaque, bool load)
+static void fd_change_cb(void *opaque, bool load, Error **errp)
 {
     FDrive *drive = opaque;
 
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 43709e5..f88b1e5 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1120,7 +1120,7 @@ static void ide_cfata_metadata_write(IDEState *s)
 }
 
 /* called when the inserted state of the media has changed */
-static void ide_cd_change_cb(void *opaque, bool load)
+static void ide_cd_change_cb(void *opaque, bool load, Error **errp)
 {
     IDEState *s = opaque;
     uint64_t nb_sectors;
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 61c44a9..f3f20da 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2225,7 +2225,7 @@ static void scsi_disk_resize_cb(void *opaque)
     }
 }
 
-static void scsi_cd_change_media_cb(void *opaque, bool load)
+static void scsi_cd_change_media_cb(void *opaque, bool load, Error **errp)
 {
     SCSIDiskState *s = opaque;
 
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 8e88e83..8e31491 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -458,7 +458,7 @@ static bool sd_get_readonly(SDState *sd)
     return sd->wp_switch;
 }
 
-static void sd_cardchange(void *opaque, bool load)
+static void sd_cardchange(void *opaque, bool load, Error **errp)
 {
     SDState *sd = opaque;
     DeviceState *dev = DEVICE(sd);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 46f51a6..7558f99 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -895,7 +895,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
                                uint64_t *nperm, uint64_t *nshared);
 
 const char *bdrv_get_parent_name(const BlockDriverState *bs);
-void blk_dev_change_media_cb(BlockBackend *blk, bool load);
+void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
 bool blk_dev_has_removable_media(BlockBackend *blk);
 bool blk_dev_has_tray(BlockBackend *blk);
 void blk_dev_eject_request(BlockBackend *blk, bool force);
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 6f21508..65bd081 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -34,7 +34,7 @@ typedef struct BlockDevOps {
      * changes.  Sure would be useful if it did.
      * Device models with removable media must implement this callback.
      */
-    void (*change_media_cb)(void *opaque, bool load);
+    void (*change_media_cb)(void *opaque, bool load, Error **errp);
     /*
      * Runs when an eject request is issued from the monitor, the tray
      * is closed, and the medium is locked.
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (17 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 18/41] block: Allow error return in BlockDevOps.change_media_cb() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 12:25   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property Kevin Wolf
                   ` (22 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This makes all device emulations with a qdev drive property request
permissions on their BlockBackend. We don't block anything yet.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 hw/block/block.c                 | 19 ++++++++++++++++++-
 hw/block/fdc.c                   | 25 +++++++++++++++++++++++--
 hw/block/m25p80.c                |  8 ++++++++
 hw/block/nand.c                  |  7 +++++++
 hw/block/nvme.c                  |  8 +++++++-
 hw/block/onenand.c               |  7 +++++++
 hw/block/pflash_cfi01.c          | 18 ++++++++++++------
 hw/block/pflash_cfi02.c          | 19 +++++++++++++------
 hw/block/virtio-blk.c            |  8 +++++++-
 hw/core/qdev-properties-system.c |  1 -
 hw/ide/qdev.c                    |  7 +++++--
 hw/nvram/spapr_nvram.c           |  8 ++++++++
 hw/scsi/scsi-disk.c              |  8 ++++++--
 hw/sd/sd.c                       |  6 ++++++
 hw/usb/dev-storage.c             |  6 +++++-
 include/hw/block/block.h         |  3 ++-
 tests/qemu-iotests/051.pc.out    |  6 +++---
 17 files changed, 137 insertions(+), 27 deletions(-)

diff --git a/hw/block/block.c b/hw/block/block.c
index 8dc9d84..c3d3901 100644
--- a/hw/block/block.c
+++ b/hw/block/block.c
@@ -51,11 +51,28 @@ void blkconf_blocksizes(BlockConf *conf)
     }
 }
 
-void blkconf_apply_backend_options(BlockConf *conf)
+void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
+                                   Error **errp)
 {
     BlockBackend *blk = conf->blk;
     BlockdevOnError rerror, werror;
+    uint64_t perm;
     bool wce;
+    int ret;
+
+    perm = BLK_PERM_CONSISTENT_READ;
+    if (!readonly) {
+        perm |= BLK_PERM_WRITE;
+    }
+
+    /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
+    ret = blk_set_perm(blk, perm,
+                       BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
+                       BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE | BLK_PERM_WRITE,
+                       errp);
+    if (ret < 0) {
+        return;
+    }
 
     switch (conf->wce) {
     case ON_OFF_AUTO_ON:    wce = true; break;
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 5f6c496..b3fa4c7 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -186,6 +186,7 @@ typedef enum FDiskFlags {
 struct FDrive {
     FDCtrl *fdctrl;
     BlockBackend *blk;
+    BlockConf *conf;
     /* Drive status */
     FloppyDriveType drive;    /* CMOS drive type        */
     uint8_t perpendicular;    /* 2.88 MB access mode    */
@@ -472,6 +473,19 @@ static void fd_revalidate(FDrive *drv)
 static void fd_change_cb(void *opaque, bool load, Error **errp)
 {
     FDrive *drive = opaque;
+    Error *local_err = NULL;
+
+    if (!load) {
+        blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
+    } else {
+        blkconf_apply_backend_options(drive->conf,
+                                      blk_is_read_only(drive->blk),
+                                      &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return;
+        }
+    }
 
     drive->media_changed = 1;
     drive->media_validated = false;
@@ -508,6 +522,7 @@ static int floppy_drive_init(DeviceState *qdev)
     FloppyDrive *dev = FLOPPY_DRIVE(qdev);
     FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
     FDrive *drive;
+    Error *local_err = NULL;
     int ret;
 
     if (dev->unit == -1) {
@@ -533,7 +548,6 @@ static int floppy_drive_init(DeviceState *qdev)
 
     if (!dev->conf.blk) {
         /* Anonymous BlockBackend for an empty drive */
-        /* FIXME Use real permissions */
         dev->conf.blk = blk_new(0, BLK_PERM_ALL);
         ret = blk_attach_dev(dev->conf.blk, qdev);
         assert(ret == 0);
@@ -552,7 +566,13 @@ static int floppy_drive_init(DeviceState *qdev)
      * blkconf_apply_backend_options(). */
     dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
     dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
-    blkconf_apply_backend_options(&dev->conf);
+
+    blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk),
+                                  &local_err);
+    if (local_err) {
+        error_report_err(local_err);
+        return -1;
+    }
 
     /* 'enospc' is the default for -drive, 'report' is what blk_new() gives us
      * for empty drives. */
@@ -566,6 +586,7 @@ static int floppy_drive_init(DeviceState *qdev)
         return -1;
     }
 
+    drive->conf = &dev->conf;
     drive->blk = dev->conf.blk;
     drive->fdctrl = bus->fdc;
 
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 2d6eb46..190573c 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -1215,6 +1215,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp)
 {
     Flash *s = M25P80(ss);
     M25P80Class *mc = M25P80_GET_CLASS(s);
+    int ret;
 
     s->pi = mc->pi;
 
@@ -1222,6 +1223,13 @@ static void m25p80_realize(SSISlave *ss, Error **errp)
     s->dirty_page = -1;
 
     if (s->blk) {
+        uint64_t perm = BLK_PERM_CONSISTENT_READ |
+                        (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
+        ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
+        if (ret < 0) {
+            return;
+        }
+
         DB_PRINT_L(0, "Binding to IF_MTD drive\n");
         s->storage = blk_blockalign(s->blk, s->size);
 
diff --git a/hw/block/nand.c b/hw/block/nand.c
index c69e675..0d33ac2 100644
--- a/hw/block/nand.c
+++ b/hw/block/nand.c
@@ -373,6 +373,8 @@ static void nand_realize(DeviceState *dev, Error **errp)
 {
     int pagesize;
     NANDFlashState *s = NAND(dev);
+    int ret;
+
 
     s->buswidth = nand_flash_ids[s->chip_id].width >> 3;
     s->size = nand_flash_ids[s->chip_id].size << 20;
@@ -407,6 +409,11 @@ static void nand_realize(DeviceState *dev, Error **errp)
             error_setg(errp, "Can't use a read-only drive");
             return;
         }
+        ret = blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
+                           BLK_PERM_ALL, errp);
+        if (ret < 0) {
+            return;
+        }
         if (blk_getlength(s->blk) >=
                 (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
             pagesize = 0;
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index ae91a18..4d423af 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -835,6 +835,7 @@ static int nvme_init(PCIDevice *pci_dev)
     int i;
     int64_t bs_size;
     uint8_t *pci_conf;
+    Error *local_err = NULL;
 
     if (!n->conf.blk) {
         return -1;
@@ -850,7 +851,12 @@ static int nvme_init(PCIDevice *pci_dev)
         return -1;
     }
     blkconf_blocksizes(&n->conf);
-    blkconf_apply_backend_options(&n->conf);
+    blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
+                                  &local_err);
+    if (local_err) {
+        error_report_err(local_err);
+        return -1;
+    }
 
     pci_conf = pci_dev->config;
     pci_conf[PCI_INTERRUPT_PIN] = 1;
diff --git a/hw/block/onenand.c b/hw/block/onenand.c
index 8d84227..ddf5492 100644
--- a/hw/block/onenand.c
+++ b/hw/block/onenand.c
@@ -778,6 +778,7 @@ static int onenand_initfn(SysBusDevice *sbd)
     OneNANDState *s = ONE_NAND(dev);
     uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7));
     void *ram;
+    Error *local_err = NULL;
 
     s->base = (hwaddr)-1;
     s->rdy = NULL;
@@ -796,6 +797,12 @@ static int onenand_initfn(SysBusDevice *sbd)
             error_report("Can't use a read-only drive");
             return -1;
         }
+        blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
+                     BLK_PERM_ALL, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            return -1;
+        }
         s->blk_cur = s->blk;
     }
     s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT),
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 71b98a3..594d4cf 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -758,6 +758,18 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
 
     if (pfl->blk) {
+        uint64_t perm;
+        pfl->ro = blk_is_read_only(pfl->blk);
+        perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
+        ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
+        if (ret < 0) {
+            return;
+        }
+    } else {
+        pfl->ro = 0;
+    }
+
+    if (pfl->blk) {
         /* read the initial flash content */
         ret = blk_pread(pfl->blk, 0, pfl->storage, total_len);
 
@@ -768,12 +780,6 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
         }
     }
 
-    if (pfl->blk) {
-        pfl->ro = blk_is_read_only(pfl->blk);
-    } else {
-        pfl->ro = 0;
-    }
-
     /* Default to devices being used at their maximum device width. This was
      * assumed before the device_width support was added.
      */
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
index ef71322..e6c5c6c 100644
--- a/hw/block/pflash_cfi02.c
+++ b/hw/block/pflash_cfi02.c
@@ -632,6 +632,19 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
     vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl));
     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
     pfl->chip_len = chip_len;
+
+    if (pfl->blk) {
+        uint64_t perm;
+        pfl->ro = blk_is_read_only(pfl->blk);
+        perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
+        ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
+        if (ret < 0) {
+            return;
+        }
+    } else {
+        pfl->ro = 0;
+    }
+
     if (pfl->blk) {
         /* read the initial flash content */
         ret = blk_pread(pfl->blk, 0, pfl->storage, chip_len);
@@ -646,12 +659,6 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
     pfl->rom_mode = 1;
     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
 
-    if (pfl->blk) {
-        pfl->ro = blk_is_read_only(pfl->blk);
-    } else {
-        pfl->ro = 0;
-    }
-
     pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
     pfl->wcycle = 0;
     pfl->cmd = 0;
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 702eda8..18fcaa0 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -905,7 +905,13 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
     }
 
     blkconf_serial(&conf->conf, &conf->serial);
-    blkconf_apply_backend_options(&conf->conf);
+    blkconf_apply_backend_options(&conf->conf,
+                                  blk_is_read_only(conf->conf.blk),
+                                  &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
     s->original_wce = blk_enable_write_cache(conf->conf.blk);
     blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err);
     if (err) {
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 66ba367..c34be1c 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -79,7 +79,6 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
     if (!blk) {
         BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
         if (bs) {
-            /* FIXME Use real permissions */
             blk = blk_new(0, BLK_PERM_ALL);
             blk_created = true;
 
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index bb3c377..db36843 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -170,7 +170,6 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
             return -1;
         } else {
             /* Anonymous BlockBackend for an empty drive */
-            /* FIXME Use real permissions */
             dev->conf.blk = blk_new(0, BLK_PERM_ALL);
         }
     }
@@ -197,7 +196,11 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
             return -1;
         }
     }
-    blkconf_apply_backend_options(&dev->conf);
+    blkconf_apply_backend_options(&dev->conf, kind == IDE_CD, &err);
+    if (err) {
+        error_report_err(err);
+        return -1;
+    }
 
     if (ide_init_drive(s, dev->conf.blk, kind,
                        dev->version, dev->serial, dev->model, dev->wwn,
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
index eb42ea3..a7cc203 100644
--- a/hw/nvram/spapr_nvram.c
+++ b/hw/nvram/spapr_nvram.c
@@ -141,9 +141,17 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp)
 {
     sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev);
+    int ret;
 
     if (nvram->blk) {
         nvram->size = blk_getlength(nvram->blk);
+
+        ret = blk_set_perm(nvram->blk,
+                           BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
+                           BLK_PERM_ALL, errp);
+        if (ret < 0) {
+            return;
+        }
     } else {
         nvram->size = DEFAULT_NVRAM_SIZE;
     }
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index f3f20da..5683af6 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2313,7 +2313,12 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
             return;
         }
     }
-    blkconf_apply_backend_options(&dev->conf);
+    blkconf_apply_backend_options(&dev->conf,
+                                  blk_is_read_only(s->qdev.conf.blk), &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
 
     if (s->qdev.conf.discard_granularity == -1) {
         s->qdev.conf.discard_granularity =
@@ -2365,7 +2370,6 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
 
     if (!dev->conf.blk) {
-        /* FIXME Use real permissions */
         dev->conf.blk = blk_new(0, BLK_PERM_ALL);
     }
 
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 8e31491..ba47bff 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -1887,6 +1887,7 @@ static void sd_instance_finalize(Object *obj)
 static void sd_realize(DeviceState *dev, Error **errp)
 {
     SDState *sd = SD_CARD(dev);
+    int ret;
 
     if (sd->blk && blk_is_read_only(sd->blk)) {
         error_setg(errp, "Cannot use read-only drive as SD card");
@@ -1894,6 +1895,11 @@ static void sd_realize(DeviceState *dev, Error **errp)
     }
 
     if (sd->blk) {
+        ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
+                           BLK_PERM_ALL, errp);
+        if (ret < 0) {
+            return;
+        }
         blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
     }
 }
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index c607f76..46def96 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -603,7 +603,11 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
 
     blkconf_serial(&s->conf, &dev->serial);
     blkconf_blocksizes(&s->conf);
-    blkconf_apply_backend_options(&s->conf);
+    blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
 
     /*
      * Hack alert: this pretends to be a block device, but it's really
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
index df9d207..3daccf1 100644
--- a/include/hw/block/block.h
+++ b/include/hw/block/block.h
@@ -73,7 +73,8 @@ void blkconf_geometry(BlockConf *conf, int *trans,
                       unsigned cyls_max, unsigned heads_max, unsigned secs_max,
                       Error **errp);
 void blkconf_blocksizes(BlockConf *conf);
-void blkconf_apply_backend_options(BlockConf *conf);
+void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
+                                   Error **errp);
 
 /* Hard disk geometry */
 
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index f1669c1..eb4b3b5 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -176,7 +176,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
 
 Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
 QEMU X.Y.Z monitor - type 'help' for more information
-(qemu) QEMU_PROG: Can't use a read-only drive
+(qemu) QEMU_PROG: Block node is read-only
 QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed.
 
 Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on
@@ -197,12 +197,12 @@ QEMU X.Y.Z monitor - type 'help' for more information
 
 Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk
 QEMU X.Y.Z monitor - type 'help' for more information
-(qemu) QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive
+(qemu) QEMU_PROG: -device ide-drive,drive=disk: Block node is read-only
 QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed.
 
 Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk
 QEMU X.Y.Z monitor - type 'help' for more information
-(qemu) QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive
+(qemu) QEMU_PROG: -device ide-hd,drive=disk: Block node is read-only
 QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed.
 
 Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (18 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 12:28   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 21/41] blockjob: Add permissions to block_job_create() Kevin Wolf
                   ` (21 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

By default, don't allow another writer for block devices that are
attached to a guest device. For the cases where this setup is intended
(e.g. using a cluster filesystem on the disk), the new option can be
used to allow it.

This change affects only devices using DEFINE_BLOCK_PROPERTIES().
Devices directly using DEFINE_PROP_DRIVE() still accept writers
unconditionally.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 hw/block/block.c           | 14 ++++++------
 include/hw/block/block.h   |  4 +++-
 tests/qemu-iotests/172.out | 53 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 64 insertions(+), 7 deletions(-)

diff --git a/hw/block/block.c b/hw/block/block.c
index c3d3901..3c218eb 100644
--- a/hw/block/block.c
+++ b/hw/block/block.c
@@ -56,7 +56,7 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
 {
     BlockBackend *blk = conf->blk;
     BlockdevOnError rerror, werror;
-    uint64_t perm;
+    uint64_t perm, shared_perm;
     bool wce;
     int ret;
 
@@ -65,11 +65,13 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
         perm |= BLK_PERM_WRITE;
     }
 
-    /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
-    ret = blk_set_perm(blk, perm,
-                       BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
-                       BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE | BLK_PERM_WRITE,
-                       errp);
+    shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
+                  BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE;
+    if (conf->share_rw) {
+        shared_perm |= BLK_PERM_WRITE;
+    }
+
+    ret = blk_set_perm(blk, perm, shared_perm, errp);
     if (ret < 0) {
         return;
     }
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
index 3daccf1..ff78f12 100644
--- a/include/hw/block/block.h
+++ b/include/hw/block/block.h
@@ -26,6 +26,7 @@ typedef struct BlockConf {
     /* geometry, not all devices use this */
     uint32_t cyls, heads, secs;
     OnOffAuto wce;
+    bool share_rw;
     BlockdevOnError rerror;
     BlockdevOnError werror;
 } BlockConf;
@@ -53,7 +54,8 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
     DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0),    \
     DEFINE_PROP_UINT32("discard_granularity", _state, \
                        _conf.discard_granularity, -1), \
-    DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, ON_OFF_AUTO_AUTO)
+    DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, ON_OFF_AUTO_AUTO), \
+    DEFINE_PROP_BOOL("share-rw", _state, _conf.share_rw, false)
 
 #define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf)      \
     DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0),  \
diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out
index 6b7edaf..54b5329 100644
--- a/tests/qemu-iotests/172.out
+++ b/tests/qemu-iotests/172.out
@@ -28,6 +28,7 @@ Testing:
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "288"
 
 
@@ -57,6 +58,7 @@ Testing: -fda TEST_DIR/t.qcow2
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fdb TEST_DIR/t.qcow2
@@ -83,6 +85,7 @@ Testing: -fdb TEST_DIR/t.qcow2
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -93,6 +96,7 @@ Testing: -fdb TEST_DIR/t.qcow2
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "288"
 
 Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
@@ -119,6 +123,7 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -129,6 +134,7 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 
@@ -158,6 +164,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
@@ -184,6 +191,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -194,6 +202,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "288"
 
 Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
@@ -220,6 +229,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -230,6 +240,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 
@@ -259,6 +270,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
@@ -285,6 +297,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
@@ -311,6 +324,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -321,6 +335,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 
@@ -350,6 +365,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
@@ -376,6 +392,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1
@@ -402,6 +419,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -412,6 +430,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 
@@ -441,6 +460,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -451,6 +471,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
@@ -477,6 +498,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -487,6 +509,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
@@ -513,6 +536,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
@@ -539,6 +563,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 
@@ -568,6 +593,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -578,6 +604,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
@@ -604,6 +631,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -614,6 +642,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
@@ -640,6 +669,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 1 (0x1)
@@ -650,6 +680,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
@@ -676,6 +707,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 1 (0x1)
@@ -686,6 +718,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
@@ -723,6 +756,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -733,6 +767,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
@@ -759,6 +794,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -769,6 +805,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
@@ -802,6 +839,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -812,6 +850,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
@@ -838,6 +877,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 0 (0x0)
@@ -848,6 +888,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
@@ -874,6 +915,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 1 (0x1)
@@ -884,6 +926,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
@@ -910,6 +953,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
               dev: floppy, id ""
                 unit = 1 (0x1)
@@ -920,6 +964,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
@@ -964,6 +1009,7 @@ Testing: -device floppy
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "288"
 
 Testing: -device floppy,drive-type=120
@@ -990,6 +1036,7 @@ Testing: -device floppy,drive-type=120
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "120"
 
 Testing: -device floppy,drive-type=144
@@ -1016,6 +1063,7 @@ Testing: -device floppy,drive-type=144
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -device floppy,drive-type=288
@@ -1042,6 +1090,7 @@ Testing: -device floppy,drive-type=288
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "288"
 
 
@@ -1071,6 +1120,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "120"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-type=288
@@ -1097,6 +1147,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "288"
 
 
@@ -1126,6 +1177,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physical_block_size=512
@@ -1152,6 +1204,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica
                 opt_io_size = 0 (0x0)
                 discard_granularity = 4294967295 (0xffffffff)
                 write-cache = "auto"
+                share-rw = false
                 drive-type = "144"
 
 Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical_block_size=4096
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 21/41] blockjob: Add permissions to block_job_create()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (19 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 22/41] block: Add BdrvChildRole.get_link_name() Kevin Wolf
                   ` (20 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This functions creates a BlockBackend internally, so the block jobs need
to tell it what they want to do with the BB.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/backup.c               | 5 +++--
 block/commit.c               | 5 +++--
 block/mirror.c               | 5 +++--
 block/stream.c               | 5 +++--
 blockjob.c                   | 6 +++---
 include/block/blockjob_int.h | 4 +++-
 tests/test-blockjob-txn.c    | 6 +++---
 tests/test-blockjob.c        | 5 +++--
 8 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 42e8e99..12ab25c 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -618,8 +618,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         goto error;
     }
 
-    job = block_job_create(job_id, &backup_job_driver, bs, speed,
-                           creation_flags, cb, opaque, errp);
+    /* FIXME Use real permissions */
+    job = block_job_create(job_id, &backup_job_driver, bs, 0, BLK_PERM_ALL,
+                           speed, creation_flags, cb, opaque, errp);
     if (!job) {
         goto error;
     }
diff --git a/block/commit.c b/block/commit.c
index 2ad8138..60d29a9 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -235,8 +235,9 @@ void commit_start(const char *job_id, BlockDriverState *bs,
         return;
     }
 
-    s = block_job_create(job_id, &commit_job_driver, bs, speed,
-                         BLOCK_JOB_DEFAULT, NULL, NULL, errp);
+    /* FIXME Use real permissions */
+    s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
+                         speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
     if (!s) {
         return;
     }
diff --git a/block/mirror.c b/block/mirror.c
index 630e5ef..22680d7 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -983,8 +983,9 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
         buf_size = DEFAULT_MIRROR_BUF_SIZE;
     }
 
-    s = block_job_create(job_id, driver, bs, speed, creation_flags,
-                         cb, opaque, errp);
+    /* FIXME Use real permissions */
+    s = block_job_create(job_id, driver, bs, 0, BLK_PERM_ALL, speed,
+                         creation_flags, cb, opaque, errp);
     if (!s) {
         return;
     }
diff --git a/block/stream.c b/block/stream.c
index 1523ba7..7f49279 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -229,8 +229,9 @@ void stream_start(const char *job_id, BlockDriverState *bs,
     BlockDriverState *iter;
     int orig_bs_flags;
 
-    s = block_job_create(job_id, &stream_job_driver, bs, speed,
-                         BLOCK_JOB_DEFAULT, NULL, NULL, errp);
+    /* FIXME Use real permissions */
+    s = block_job_create(job_id, &stream_job_driver, bs, 0, BLK_PERM_ALL,
+                         speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
     if (!s) {
         return;
     }
diff --git a/blockjob.c b/blockjob.c
index 72b7d4c..27833c7 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -123,7 +123,8 @@ void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
 }
 
 void *block_job_create(const char *job_id, const BlockJobDriver *driver,
-                       BlockDriverState *bs, int64_t speed, int flags,
+                       BlockDriverState *bs, uint64_t perm,
+                       uint64_t shared_perm, int64_t speed, int flags,
                        BlockCompletionFunc *cb, void *opaque, Error **errp)
 {
     BlockBackend *blk;
@@ -160,8 +161,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
         }
     }
 
-    /* FIXME Use real permissions */
-    blk = blk_new(0, BLK_PERM_ALL);
+    blk = blk_new(perm, shared_perm);
     ret = blk_insert_bs(blk, bs, errp);
     if (ret < 0) {
         blk_unref(blk);
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
index 8223822..3f86cc5 100644
--- a/include/block/blockjob_int.h
+++ b/include/block/blockjob_int.h
@@ -119,6 +119,7 @@ struct BlockJobDriver {
  * generated automatically.
  * @job_type: The class object for the newly-created job.
  * @bs: The block
+ * @perm, @shared_perm: Permissions to request for @bs
  * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
  * @cb: Completion function for the job.
  * @opaque: Opaque pointer value passed to @cb.
@@ -134,7 +135,8 @@ struct BlockJobDriver {
  * called from a wrapper that is specific to the job type.
  */
 void *block_job_create(const char *job_id, const BlockJobDriver *driver,
-                       BlockDriverState *bs, int64_t speed, int flags,
+                       BlockDriverState *bs, uint64_t perm,
+                       uint64_t shared_perm, int64_t speed, int flags,
                        BlockCompletionFunc *cb, void *opaque, Error **errp);
 
 /**
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
index f6dfd08..4ccbda1 100644
--- a/tests/test-blockjob-txn.c
+++ b/tests/test-blockjob-txn.c
@@ -101,9 +101,9 @@ static BlockJob *test_block_job_start(unsigned int iterations,
     g_assert_nonnull(bs);
 
     snprintf(job_id, sizeof(job_id), "job%u", counter++);
-    s = block_job_create(job_id, &test_block_job_driver, bs, 0,
-                         BLOCK_JOB_DEFAULT, test_block_job_cb,
-                         data, &error_abort);
+    s = block_job_create(job_id, &test_block_job_driver, bs,
+                         0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT,
+                         test_block_job_cb, data, &error_abort);
     s->iterations = iterations;
     s->use_timer = use_timer;
     s->rc = rc;
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 143ce96..1afe17b 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -30,8 +30,9 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
     BlockJob *job;
     Error *errp = NULL;
 
-    job = block_job_create(id, &test_block_job_driver, blk_bs(blk), 0,
-                           BLOCK_JOB_DEFAULT, block_job_cb, NULL, &errp);
+    job = block_job_create(id, &test_block_job_driver, blk_bs(blk),
+                           0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, block_job_cb,
+                           NULL, &errp);
     if (should_succeed) {
         g_assert_null(errp);
         g_assert_nonnull(job);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 22/41] block: Add BdrvChildRole.get_link_name()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (20 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 21/41] blockjob: Add permissions to block_job_create() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 12:54   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 23/41] block: Include details on permission errors in message Kevin Wolf
                   ` (19 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

For meaningful error messages in the permission system, we want to allow
the parent of a BdrvChild to generate some kind of human-readable
identifier for the link represented by the BdrvChild.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   |  8 ++++++++
 block/block-backend.c     | 20 ++++++++++++++++++++
 include/block/block_int.h |  4 ++++
 3 files changed, 32 insertions(+)

diff --git a/block.c b/block.c
index fd06549..2116542 100644
--- a/block.c
+++ b/block.c
@@ -800,6 +800,13 @@ const BdrvChildRole child_format = {
     .drained_end     = bdrv_child_cb_drained_end,
 };
 
+static char *bdrv_backing_link_name(BdrvChild *c)
+{
+    BlockDriverState *parent = c->opaque;
+    return g_strdup_printf("backing file link from %s",
+                           bdrv_get_device_or_node_name(parent));
+}
+
 /*
  * Returns the options and flags that bs->backing should get, based on the
  * given options and flags for the parent BDS
@@ -825,6 +832,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
 }
 
 static const BdrvChildRole child_backing = {
+    .get_link_name   = bdrv_backing_link_name,
     .inherit_options = bdrv_backing_options,
     .drained_begin   = bdrv_child_cb_drained_begin,
     .drained_end     = bdrv_child_cb_drained_end,
diff --git a/block/block-backend.c b/block/block-backend.c
index 0c23add..a314284 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -80,6 +80,7 @@ static const AIOCBInfo block_backend_aiocb_info = {
 
 static void drive_info_del(DriveInfo *dinfo);
 static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
+static char *blk_get_attached_dev_id(BlockBackend *blk);
 
 /* All BlockBackends */
 static QTAILQ_HEAD(, BlockBackend) block_backends =
@@ -102,6 +103,24 @@ static void blk_root_drained_end(BdrvChild *child);
 static void blk_root_change_media(BdrvChild *child, bool load);
 static void blk_root_resize(BdrvChild *child);
 
+static char *blk_root_get_link_name(BdrvChild *child)
+{
+    BlockBackend *blk = child->opaque;
+    char *dev_id;
+
+    if (blk->name) {
+        return g_strdup(blk->name);
+    }
+
+    dev_id = blk_get_attached_dev_id(blk);
+    if (*dev_id) {
+        return dev_id;
+    } else {
+        g_free(dev_id);
+        return g_strdup("a block device");
+    }
+}
+
 static const char *blk_root_get_name(BdrvChild *child)
 {
     return blk_name(child->opaque);
@@ -113,6 +132,7 @@ static const BdrvChildRole child_root = {
     .change_media       = blk_root_change_media,
     .resize             = blk_root_resize,
     .get_name           = blk_root_get_name,
+    .get_link_name      = blk_root_get_link_name,
 
     .drained_begin      = blk_root_drained_begin,
     .drained_end        = blk_root_drained_end,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 7558f99..a5557b1 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -438,6 +438,10 @@ struct BdrvChildRole {
      * name), or NULL if the parent can't provide a better name. */
     const char* (*get_name)(BdrvChild *child);
 
+    /* Returns a malloced name that describes the link from the parent to the
+     * child. The caller is responsible for freeing the memory. */
+    char* (*get_link_name)(BdrvChild *child);
+
     /*
      * If this pair of functions is implemented, the parent doesn't issue new
      * requests after returning from .drained_begin() until .drained_end() is
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 23/41] block: Include details on permission errors in message
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (21 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 22/41] block: Add BdrvChildRole.get_link_name() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 13:16   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 24/41] block: Add BdrvChildRole.stay_at_node Kevin Wolf
                   ` (18 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Instead of just telling that there was some conflict, we can be specific
and tell which permissions were in conflict and which way the conflict
is.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 55 insertions(+), 11 deletions(-)

diff --git a/block.c b/block.c
index 2116542..d743f50 100644
--- a/block.c
+++ b/block.c
@@ -1381,6 +1381,43 @@ static void bdrv_update_perm(BlockDriverState *bs)
     bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
 }
 
+static char *bdrv_child_link_name(BdrvChild *c)
+{
+    if (c->role->get_link_name) {
+        return c->role->get_link_name(c);
+    }
+
+    return g_strdup("another user");
+}
+
+static char *bdrv_perm_names(uint64_t perm)
+{
+    struct perm_name {
+        uint64_t perm;
+        const char *name;
+    } permissions[] = {
+        { BLK_PERM_CONSISTENT_READ, "consistent read" },
+        { BLK_PERM_WRITE,           "write" },
+        { BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
+        { BLK_PERM_RESIZE,          "resize" },
+        { BLK_PERM_GRAPH_MOD,       "change children" },
+        { 0, NULL }
+    };
+
+    char *result = g_strdup("");
+    struct perm_name *p;
+
+    for (p = permissions; p->name; p++) {
+        if (perm & p->perm) {
+            char *old = result;
+            result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name);
+            g_free(old);
+        }
+    }
+
+    return result;
+}
+
 static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
                                   uint64_t new_shared_perm,
                                   BdrvChild *ignore_child, Error **errp)
@@ -1397,17 +1434,24 @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
             continue;
         }
 
-        if ((new_used_perm & c->shared_perm) != new_used_perm ||
-            (c->perm & new_shared_perm) != c->perm)
-        {
-            const char *user = NULL;
-            if (c->role->get_name) {
-                user = c->role->get_name(c);
-                if (user && !*user) {
-                    user = NULL;
-                }
-            }
-            error_setg(errp, "Conflicts with %s", user ?: "another operation");
+        if ((new_used_perm & c->shared_perm) != new_used_perm) {
+            char *link = bdrv_child_link_name(c);
+            char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
+            error_setg(errp, "Conflicts with %s, which does not allow '%s' "
+                             "on %s",
+                       link, perm_names, bdrv_get_node_name(c->bs));
+            g_free(link);
+            g_free(perm_names);
+            return -EPERM;
+        }
+
+        if ((c->perm & new_shared_perm) != c->perm) {
+            char *link = bdrv_child_link_name(c);
+            char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
+            error_setg(errp, "Conflicts with %s, which uses '%s' on %s",
+                       link, perm_names, bdrv_get_node_name(c->bs));
+            g_free(link);
+            g_free(perm_names);
             return -EPERM;
         }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 24/41] block: Add BdrvChildRole.stay_at_node
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (22 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 23/41] block: Include details on permission errors in message Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 25/41] blockjob: Add permissions to block_job_add_bdrv() Kevin Wolf
                   ` (17 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

When the parents' child links are updated in bdrv_append() or
bdrv_replace_in_backing_chain(), this should affect all child links of
BlockBackends or other nodes, but not on child links held for other
purposes (like for setting permissions). This patch allows to control
the behaviour per BdrvChildRole.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 3 +++
 include/block/block_int.h | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/block.c b/block.c
index d743f50..842ac78 100644
--- a/block.c
+++ b/block.c
@@ -2713,6 +2713,9 @@ static void change_parent_backing_link(BlockDriverState *from,
     BdrvChild *c, *next, *to_c;
 
     QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
+        if (c->role->stay_at_node) {
+            continue;
+        }
         if (c->role == &child_backing) {
             /* @from is generally not allowed to be a backing file, except for
              * when @to is the overlay. In that case, @from may not be replaced
diff --git a/include/block/block_int.h b/include/block/block_int.h
index a5557b1..b24fc49 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -427,6 +427,10 @@ typedef struct BdrvAioNotifier {
 } BdrvAioNotifier;
 
 struct BdrvChildRole {
+    /* If true, bdrv_replace_in_backing_chain() doesn't change the node this
+     * BdrvChild points to. */
+    bool stay_at_node;
+
     void (*inherit_options)(int *child_flags, QDict *child_options,
                             int parent_flags, QDict *parent_options);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 25/41] blockjob: Add permissions to block_job_add_bdrv()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (23 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 24/41] block: Add BdrvChildRole.stay_at_node Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 13:38   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 26/41] block: Factor out bdrv_open_driver() Kevin Wolf
                   ` (16 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Block jobs don't actually do I/O through the the reference they create
with block_job_add_bdrv(), but they might want to use the permisssion
system to express what the block job does to intermediate nodes. This
adds permissions to block_job_add_bdrv() to provide the means to request
permissions.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/backup.c           |  3 ++-
 block/commit.c           |  7 +++++--
 block/mirror.c           |  7 +++++--
 block/stream.c           |  3 ++-
 blockjob.c               | 27 +++++++++++++++++++++------
 include/block/blockjob.h |  4 +++-
 6 files changed, 38 insertions(+), 13 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 12ab25c..22171f4 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -657,7 +657,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
     }
 
-    block_job_add_bdrv(&job->common, target);
+    /* FIXME Use real permissions */
+    block_job_add_bdrv(&job->common, target, 0, BLK_PERM_ALL, &error_abort);
     job->common.len = len;
     block_job_txn_add_job(txn, &job->common);
 
diff --git a/block/commit.c b/block/commit.c
index 60d29a9..68fa2a4 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -267,13 +267,16 @@ void commit_start(const char *job_id, BlockDriverState *bs,
      * disappear from the chain after this operation. */
     assert(bdrv_chain_contains(top, base));
     for (iter = top; iter != backing_bs(base); iter = backing_bs(iter)) {
-        block_job_add_bdrv(&s->common, iter);
+        /* FIXME Use real permissions */
+        block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
     }
     /* overlay_bs must be blocked because it needs to be modified to
      * update the backing image string, but if it's the root node then
      * don't block it again */
     if (bs != overlay_bs) {
-        block_job_add_bdrv(&s->common, overlay_bs);
+        /* FIXME Use real permissions */
+        block_job_add_bdrv(&s->common, overlay_bs, 0, BLK_PERM_ALL,
+                           &error_abort);
     }
 
     /* FIXME Use real permissions */
diff --git a/block/mirror.c b/block/mirror.c
index 22680d7..9532b18 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1020,13 +1020,16 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
         return;
     }
 
-    block_job_add_bdrv(&s->common, target);
+    /* FIXME Use real permissions */
+    block_job_add_bdrv(&s->common, target, 0, BLK_PERM_ALL, &error_abort);
+
     /* In commit_active_start() all intermediate nodes disappear, so
      * any jobs in them must be blocked */
     if (bdrv_chain_contains(bs, target)) {
         BlockDriverState *iter;
         for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) {
-            block_job_add_bdrv(&s->common, iter);
+            /* FIXME Use real permissions */
+            block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
         }
     }
 
diff --git a/block/stream.c b/block/stream.c
index 7f49279..47f0ffb 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -248,7 +248,8 @@ void stream_start(const char *job_id, BlockDriverState *bs,
     /* Block all intermediate nodes between bs and base, because they
      * will disappear from the chain after this operation */
     for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
-        block_job_add_bdrv(&s->common, iter);
+        /* FIXME Use real permissions */
+        block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
     }
 
     s->base = base;
diff --git a/blockjob.c b/blockjob.c
index 27833c7..0bcc099 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -55,6 +55,10 @@ struct BlockJobTxn {
 
 static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
 
+static const BdrvChildRole child_job = {
+    .stay_at_node   = true,
+};
+
 BlockJob *block_job_next(BlockJob *job)
 {
     if (!job) {
@@ -115,11 +119,22 @@ static void block_job_detach_aio_context(void *opaque)
     block_job_unref(job);
 }
 
-void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
+int block_job_add_bdrv(BlockJob *job, BlockDriverState *bs,
+                       uint64_t perm, uint64_t shared_perm, Error **errp)
 {
-    job->nodes = g_slist_prepend(job->nodes, bs);
+    BdrvChild *c;
+
+    c = bdrv_root_attach_child(bs, "job", &child_job, perm, shared_perm,
+                               NULL, errp);
+    if (c == NULL) {
+        return -EPERM;
+    }
+
+    job->nodes = g_slist_prepend(job->nodes, c);
     bdrv_ref(bs);
     bdrv_op_block_all(bs, job->blocker);
+
+    return 0;
 }
 
 void *block_job_create(const char *job_id, const BlockJobDriver *driver,
@@ -171,7 +186,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
     job = g_malloc0(driver->instance_size);
     error_setg(&job->blocker, "block device is in use by block job: %s",
                BlockJobType_lookup[driver->job_type]);
-    block_job_add_bdrv(job, bs);
+    block_job_add_bdrv(job, bs, 0, BLK_PERM_ALL, &error_abort);
     bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
 
     job->driver        = driver;
@@ -238,9 +253,9 @@ void block_job_unref(BlockJob *job)
         BlockDriverState *bs = blk_bs(job->blk);
         bs->job = NULL;
         for (l = job->nodes; l; l = l->next) {
-            bs = l->data;
-            bdrv_op_unblock_all(bs, job->blocker);
-            bdrv_unref(bs);
+            BdrvChild *c = l->data;
+            bdrv_op_unblock_all(c->bs, job->blocker);
+            bdrv_root_unref_child(c);
         }
         g_slist_free(job->nodes);
         blk_remove_aio_context_notifier(job->blk,
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 1acb256..b0b1134 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -170,12 +170,14 @@ BlockJob *block_job_get(const char *id);
  * block_job_add_bdrv:
  * @job: A block job
  * @bs: A BlockDriverState that is involved in @job
+ * @perm, @shared_perm: Permissions to request on the node
  *
  * Add @bs to the list of BlockDriverState that are involved in
  * @job. This means that all operations will be blocked on @bs while
  * @job exists.
  */
-void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs);
+int block_job_add_bdrv(BlockJob *job, BlockDriverState *bs,
+                       uint64_t perm, uint64_t shared_perm, Error **errp);
 
 /**
  * block_job_set_speed:
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 26/41] block: Factor out bdrv_open_driver()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (24 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 25/41] blockjob: Add permissions to block_job_add_bdrv() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 27/41] block: Add bdrv_new_open_driver() Kevin Wolf
                   ` (15 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This is a function that doesn't do any option parsing, but just does
some basic BlockDriverState setup and calls the .bdrv_open() function of
the block driver.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c | 112 +++++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 65 insertions(+), 47 deletions(-)

diff --git a/block.c b/block.c
index 842ac78..9c80cba 100644
--- a/block.c
+++ b/block.c
@@ -934,6 +934,67 @@ out:
     g_free(gen_node_name);
 }
 
+static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
+                            const char *node_name, QDict *options,
+                            int open_flags, Error **errp)
+{
+    Error *local_err = NULL;
+    int ret;
+
+    bdrv_assign_node_name(bs, node_name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return -EINVAL;
+    }
+
+    bs->drv = drv;
+    bs->opaque = g_malloc0(drv->instance_size);
+
+    if (drv->bdrv_file_open) {
+        assert(!drv->bdrv_needs_filename || bs->filename[0]);
+        ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
+    } else {
+        ret = drv->bdrv_open(bs, options, open_flags, &local_err);
+    }
+
+    if (ret < 0) {
+        if (local_err) {
+            error_propagate(errp, local_err);
+        } else if (bs->filename[0]) {
+            error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename);
+        } else {
+            error_setg_errno(errp, -ret, "Could not open image");
+        }
+        goto free_and_fail;
+    }
+
+    ret = refresh_total_sectors(bs, bs->total_sectors);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not refresh total sector count");
+        goto free_and_fail;
+    }
+
+    bdrv_refresh_limits(bs, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto free_and_fail;
+    }
+
+    assert(bdrv_opt_mem_align(bs) != 0);
+    assert(bdrv_min_mem_align(bs) != 0);
+    assert(is_power_of_2(bs->bl.request_alignment));
+
+    return 0;
+
+free_and_fail:
+    /* FIXME Close bs first if already opened*/
+    g_free(bs->opaque);
+    bs->opaque = NULL;
+    bs->drv = NULL;
+    return ret;
+}
+
 QemuOptsList bdrv_runtime_opts = {
     .name = "bdrv_common",
     .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
@@ -1028,14 +1089,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
     trace_bdrv_open_common(bs, filename ?: "", bs->open_flags,
                            drv->format_name);
 
-    node_name = qemu_opt_get(opts, "node-name");
-    bdrv_assign_node_name(bs, node_name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        ret = -EINVAL;
-        goto fail_opts;
-    }
-
     bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
 
     if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
@@ -1101,54 +1154,19 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
     }
     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename);
 
-    bs->drv = drv;
-    bs->opaque = g_malloc0(drv->instance_size);
-
     /* Open the image, either directly or using a protocol */
     open_flags = bdrv_open_flags(bs, bs->open_flags);
-    if (drv->bdrv_file_open) {
-        assert(file == NULL);
-        assert(!drv->bdrv_needs_filename || filename != NULL);
-        ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
-    } else {
-        ret = drv->bdrv_open(bs, options, open_flags, &local_err);
-    }
-
-    if (ret < 0) {
-        if (local_err) {
-            error_propagate(errp, local_err);
-        } else if (bs->filename[0]) {
-            error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename);
-        } else {
-            error_setg_errno(errp, -ret, "Could not open image");
-        }
-        goto free_and_fail;
-    }
+    node_name = qemu_opt_get(opts, "node-name");
 
-    ret = refresh_total_sectors(bs, bs->total_sectors);
+    assert(!drv->bdrv_file_open || file == NULL);
+    ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp);
     if (ret < 0) {
-        error_setg_errno(errp, -ret, "Could not refresh total sector count");
-        goto free_and_fail;
-    }
-
-    bdrv_refresh_limits(bs, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        ret = -EINVAL;
-        goto free_and_fail;
+        goto fail_opts;
     }
 
-    assert(bdrv_opt_mem_align(bs) != 0);
-    assert(bdrv_min_mem_align(bs) != 0);
-    assert(is_power_of_2(bs->bl.request_alignment));
-
     qemu_opts_del(opts);
     return 0;
 
-free_and_fail:
-    g_free(bs->opaque);
-    bs->opaque = NULL;
-    bs->drv = NULL;
 fail_opts:
     qemu_opts_del(opts);
     return ret;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 27/41] block: Add bdrv_new_open_driver()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (25 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 26/41] block: Factor out bdrv_open_driver() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-20 14:20   ` Max Reitz
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 28/41] commit: Use real permissions in commit block job Kevin Wolf
                   ` (14 subsequent siblings)
  41 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This function allows to create more or less normal BlockDriverStates
even for BlockDrivers that aren't globally registered (e.g. helper
filters for block jobs).

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c               | 31 ++++++++++++++++++++++++++++++-
 include/block/block.h |  2 ++
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/block.c b/block.c
index 9c80cba..7d84d43 100644
--- a/block.c
+++ b/block.c
@@ -948,13 +948,16 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
     }
 
     bs->drv = drv;
+    bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
     bs->opaque = g_malloc0(drv->instance_size);
 
     if (drv->bdrv_file_open) {
         assert(!drv->bdrv_needs_filename || bs->filename[0]);
         ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
-    } else {
+    } else if (drv->bdrv_open) {
         ret = drv->bdrv_open(bs, options, open_flags, &local_err);
+    } else {
+        ret = 0;
     }
 
     if (ret < 0) {
@@ -995,6 +998,32 @@ free_and_fail:
     return ret;
 }
 
+int bdrv_new_open_driver(BlockDriver *drv, BlockDriverState **result,
+                         const char *node_name, int flags, Error **errp)
+{
+    BlockDriverState *bs;
+    int ret;
+
+    bs = bdrv_new();
+    bs->open_flags = flags;
+    bs->explicit_options = qdict_new();
+    bs->options = qdict_new();
+    bs->opaque = NULL;
+
+    update_options_from_flags(bs->options, flags);
+
+    ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
+    if (ret < 0) {
+        QDECREF(bs->explicit_options);
+        QDECREF(bs->options);
+        bdrv_unref(bs);
+        return ret;
+    }
+
+    *result = bs;
+    return 0;
+}
+
 QemuOptsList bdrv_runtime_opts = {
     .name = "bdrv_common",
     .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
diff --git a/include/block/block.h b/include/block/block.h
index 93812df..3238850 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -215,6 +215,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
                            const char *bdref_key, Error **errp);
 BlockDriverState *bdrv_open(const char *filename, const char *reference,
                             QDict *options, int flags, Error **errp);
+int bdrv_new_open_driver(BlockDriver *drv, BlockDriverState **result,
+                         const char *node_name, int flags, Error **errp);
 BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
                                     BlockDriverState *bs,
                                     QDict *options, int flags);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 28/41] commit: Use real permissions in commit block job
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (26 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 27/41] block: Add bdrv_new_open_driver() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 29/41] commit: Use real permissions for HMP 'commit' Kevin Wolf
                   ` (13 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This is probably one of the most interesting conversions to the new
op blocker system because a commit block job intentionally leaves some
intermediate block nodes in the backing chain that aren't valid on their
own any more; only the whole chain together results in a valid view.

In order to provide the 'consistent read' permission to the parents of
the 'top' node of the commit job, a new filter block driver is inserted
above 'top' which doesn't require 'consistent read' on its backing
chain. Subsequently, the commit job can block 'consistent read' on all
intermediate nodes without causing a conflict.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/commit.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 89 insertions(+), 16 deletions(-)

diff --git a/block/commit.c b/block/commit.c
index 68fa2a4..49ffddb 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -36,6 +36,7 @@ typedef struct CommitBlockJob {
     BlockJob common;
     RateLimit limit;
     BlockDriverState *active;
+    BlockDriverState *commit_top_bs;
     BlockBackend *top;
     BlockBackend *base;
     BlockdevOnError on_error;
@@ -83,12 +84,19 @@ static void commit_complete(BlockJob *job, void *opaque)
     BlockDriverState *active = s->active;
     BlockDriverState *top = blk_bs(s->top);
     BlockDriverState *base = blk_bs(s->base);
-    BlockDriverState *overlay_bs = bdrv_find_overlay(active, top);
+    BlockDriverState *overlay_bs = bdrv_find_overlay(active, s->commit_top_bs);
     int ret = data->ret;
+    bool remove_commit_top_bs = false;
 
     if (!block_job_is_cancelled(&s->common) && ret == 0) {
         /* success */
-        ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
+        ret = bdrv_drop_intermediate(active, s->commit_top_bs, base,
+                                     s->backing_file_str);
+    } else if (overlay_bs) {
+        /* XXX Can (or should) we somehow keep 'consistent read' blocked even
+         * after the failed/cancelled commit job is gone? If we already wrote
+         * something to base, the intermediate images aren't valid any more. */
+        remove_commit_top_bs = true;
     }
 
     /* restore base open flags here if appropriate (e.g., change the base back
@@ -105,6 +113,13 @@ static void commit_complete(BlockJob *job, void *opaque)
     blk_unref(s->base);
     block_job_completed(&s->common, ret);
     g_free(data);
+
+    /* If bdrv_drop_intermediate() didn't already do that, remove the commit
+     * filter driver from the backing chain. Do this as the final step so that
+     * the 'consistent read' permission can be granted.  */
+    if (remove_commit_top_bs) {
+        bdrv_set_backing_hd(overlay_bs, top);
+    }
 }
 
 static void coroutine_fn commit_run(void *opaque)
@@ -208,6 +223,34 @@ static const BlockJobDriver commit_job_driver = {
     .start         = commit_run,
 };
 
+static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
+    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
+{
+    return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
+}
+
+static void bdrv_commit_top_close(BlockDriverState *bs)
+{
+}
+
+static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
+                                       const BdrvChildRole *role,
+                                       uint64_t perm, uint64_t shared,
+                                       uint64_t *nperm, uint64_t *nshared)
+{
+    *nperm = 0;
+    *nshared = BLK_PERM_ALL;
+}
+
+/* Dummy node that provides consistent read to its users without requiring it
+ * from its backing file and that allows writes on the backing file chain. */
+static BlockDriver bdrv_commit_top = {
+    .format_name        = "commit_top",
+    .bdrv_co_preadv     = bdrv_commit_top_preadv,
+    .bdrv_close         = bdrv_commit_top_close,
+    .bdrv_child_perm    = bdrv_commit_top_child_perm,
+};
+
 void commit_start(const char *job_id, BlockDriverState *bs,
                   BlockDriverState *base, BlockDriverState *top, int64_t speed,
                   BlockdevOnError on_error, const char *backing_file_str,
@@ -219,6 +262,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
     int orig_base_flags;
     BlockDriverState *iter;
     BlockDriverState *overlay_bs;
+    BlockDriverState *commit_top_bs;
     Error *local_err = NULL;
     int ret;
 
@@ -235,7 +279,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
         return;
     }
 
-    /* FIXME Use real permissions */
     s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
                          speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
     if (!s) {
@@ -262,33 +305,60 @@ void commit_start(const char *job_id, BlockDriverState *bs,
         }
     }
 
+    /* Insert commit_top block node above top, so we can block consistent read
+     * on the backing chain below it */
+    ret = bdrv_new_open_driver(&bdrv_commit_top, &commit_top_bs, NULL,
+                               BDRV_O_RDWR, errp);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    bdrv_set_backing_hd(commit_top_bs, top);
+    bdrv_set_backing_hd(overlay_bs, commit_top_bs);
+
+    s->commit_top_bs = commit_top_bs;
+    bdrv_unref(commit_top_bs);
 
     /* Block all nodes between top and base, because they will
      * disappear from the chain after this operation. */
     assert(bdrv_chain_contains(top, base));
-    for (iter = top; iter != backing_bs(base); iter = backing_bs(iter)) {
-        /* FIXME Use real permissions */
-        block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
+    for (iter = top; iter != base; iter = backing_bs(iter)) {
+        /* XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves
+         * at s->base. The other options would be a second filter driver above
+         * s->base. */
+        ret = block_job_add_bdrv(&s->common, iter, 0,
+                                 BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE,
+                                 errp);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+
+    ret = block_job_add_bdrv(&s->common, base, 0, BLK_PERM_ALL, errp);
+    if (ret < 0) {
+        goto fail;
     }
+
     /* overlay_bs must be blocked because it needs to be modified to
-     * update the backing image string, but if it's the root node then
-     * don't block it again */
-    if (bs != overlay_bs) {
-        /* FIXME Use real permissions */
-        block_job_add_bdrv(&s->common, overlay_bs, 0, BLK_PERM_ALL,
-                           &error_abort);
+     * update the backing image string. */
+    ret = block_job_add_bdrv(&s->common, overlay_bs, BLK_PERM_GRAPH_MOD,
+                             BLK_PERM_ALL, errp);
+    if (ret < 0) {
+        goto fail;
     }
 
-    /* FIXME Use real permissions */
-    s->base = blk_new(0, BLK_PERM_ALL);
+    s->base = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
+                      BLK_PERM_CONSISTENT_READ
+                      | BLK_PERM_GRAPH_MOD
+                      | BLK_PERM_WRITE_UNCHANGED);
     ret = blk_insert_bs(s->base, base, errp);
     if (ret < 0) {
         goto fail;
     }
 
-    /* FIXME Use real permissions */
+    /* Required permissions are already taken with block_job_add_bdrv() */
     s->top = blk_new(0, BLK_PERM_ALL);
-    ret = blk_insert_bs(s->top, top, errp);
+    blk_insert_bs(s->top, top, errp);
     if (ret < 0) {
         goto fail;
     }
@@ -313,6 +383,9 @@ fail:
     if (s->top) {
         blk_unref(s->top);
     }
+    if (commit_top_bs) {
+        bdrv_set_backing_hd(overlay_bs, top);
+    }
     block_job_unref(&s->common);
 }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 29/41] commit: Use real permissions for HMP 'commit'
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (27 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 28/41] commit: Use real permissions in commit block job Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 30/41] backup: Use real permissions in backup block job Kevin Wolf
                   ` (12 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This is a little simpler than the commit block job because it's
synchronous and only commits into the immediate backing file, but
otherwise doing more or less the same.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/commit.c | 31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)

diff --git a/block/commit.c b/block/commit.c
index 49ffddb..581d161 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -396,11 +396,14 @@ fail:
 int bdrv_commit(BlockDriverState *bs)
 {
     BlockBackend *src, *backing;
+    BlockDriverState *backing_file_bs = NULL;
+    BlockDriverState *commit_top_bs = NULL;
     BlockDriver *drv = bs->drv;
     int64_t sector, total_sectors, length, backing_length;
     int n, ro, open_flags;
     int ret = 0;
     uint8_t *buf = NULL;
+    Error *local_err = NULL;
 
     if (!drv)
         return -ENOMEDIUM;
@@ -423,17 +426,31 @@ int bdrv_commit(BlockDriverState *bs)
         }
     }
 
-    /* FIXME Use real permissions */
-    src = blk_new(0, BLK_PERM_ALL);
-    backing = blk_new(0, BLK_PERM_ALL);
+    src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
+    backing = blk_new(BLK_PERM_WRITE, BLK_PERM_ALL);
+
+    ret = blk_insert_bs(src, bs, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+        goto ro_cleanup;
+    }
+
+    /* Insert commit_top block node above backing, so we can write to it */
+    backing_file_bs = backing_bs(bs);
 
-    ret = blk_insert_bs(src, bs, NULL);
+    ret = bdrv_new_open_driver(&bdrv_commit_top, &commit_top_bs, NULL,
+                               BDRV_O_RDWR, &local_err);
     if (ret < 0) {
+        error_report_err(local_err);
         goto ro_cleanup;
     }
 
-    ret = blk_insert_bs(backing, bs->backing->bs, NULL);
+    bdrv_set_backing_hd(commit_top_bs, backing_file_bs);
+    bdrv_set_backing_hd(bs, commit_top_bs);
+
+    ret = blk_insert_bs(backing, backing_file_bs, &local_err);
     if (ret < 0) {
+        error_report_err(local_err);
         goto ro_cleanup;
     }
 
@@ -507,6 +524,10 @@ int bdrv_commit(BlockDriverState *bs)
 ro_cleanup:
     qemu_vfree(buf);
 
+    if (backing_file_bs) {
+        bdrv_set_backing_hd(bs, backing_file_bs);
+    }
+    bdrv_unref(commit_top_bs);
     blk_unref(src);
     blk_unref(backing);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 30/41] backup: Use real permissions in backup block job
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (28 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 29/41] commit: Use real permissions for HMP 'commit' Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 31/41] block: Fix pending requests check in bdrv_append() Kevin Wolf
                   ` (11 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

The backup block job doesn't have very complicated requirements: It
needs to read from the source and write to the target, but it's fine
with either side being changed. The only restriction is that we can't
resize the image because the job uses a cached value.

qemu-iotests 055 needs to be changed because it used a target which was
already attached to a virtio-blk device. The permission system correctly
forbids this (virtio-blk can't accept another writer with its default
share-rw=off).

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/backup.c         | 15 ++++++++++-----
 tests/qemu-iotests/055 | 11 +++++++----
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 22171f4..47fadfb 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -618,15 +618,20 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         goto error;
     }
 
-    /* FIXME Use real permissions */
-    job = block_job_create(job_id, &backup_job_driver, bs, 0, BLK_PERM_ALL,
+    /* job->common.len is fixed, so we can't allow resize */
+    job = block_job_create(job_id, &backup_job_driver, bs,
+                           BLK_PERM_CONSISTENT_READ,
+                           BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
+                           BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
                            speed, creation_flags, cb, opaque, errp);
     if (!job) {
         goto error;
     }
 
-    /* FIXME Use real permissions */
-    job->target = blk_new(0, BLK_PERM_ALL);
+    /* The target must match the source in size, so no resize here either */
+    job->target = blk_new(BLK_PERM_WRITE,
+                          BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
+                          BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
     ret = blk_insert_bs(job->target, target, errp);
     if (ret < 0) {
         goto error;
@@ -657,7 +662,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
     }
 
-    /* FIXME Use real permissions */
+    /* Required permissions are already taken with target's blk_new() */
     block_job_add_bdrv(&job->common, target, 0, BLK_PERM_ALL, &error_abort);
     job->common.len = len;
     block_job_txn_add_job(txn, &job->common);
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
index 1d3fd04..aafcd24 100755
--- a/tests/qemu-iotests/055
+++ b/tests/qemu-iotests/055
@@ -48,7 +48,8 @@ class TestSingleDrive(iotests.QMPTestCase):
     def setUp(self):
         qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
 
-        self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
+        self.vm = iotests.VM().add_drive(test_img)
+        self.vm.add_drive(blockdev_target_img, interface="none")
         if iotests.qemu_default_machine == 'pc':
             self.vm.add_drive(None, 'media=cdrom', 'ide')
         self.vm.launch()
@@ -164,7 +165,8 @@ class TestSetSpeed(iotests.QMPTestCase):
     def setUp(self):
         qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
 
-        self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
+        self.vm = iotests.VM().add_drive(test_img)
+        self.vm.add_drive(blockdev_target_img, interface="none")
         self.vm.launch()
 
     def tearDown(self):
@@ -247,7 +249,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
     def setUp(self):
         qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
 
-        self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
+        self.vm = iotests.VM().add_drive(test_img)
+        self.vm.add_drive(blockdev_target_img, interface="none")
         if iotests.qemu_default_machine == 'pc':
             self.vm.add_drive(None, 'media=cdrom', 'ide')
         self.vm.launch()
@@ -460,7 +463,7 @@ class TestDriveCompression(iotests.QMPTestCase):
 
         qemu_img('create', '-f', fmt, blockdev_target_img,
                  str(TestDriveCompression.image_len), *args)
-        self.vm.add_drive(blockdev_target_img, format=fmt)
+        self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
 
         self.vm.launch()
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 31/41] block: Fix pending requests check in bdrv_append()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (29 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 30/41] backup: Use real permissions in backup block job Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 32/41] block: BdrvChildRole.attach/detach() callbacks Kevin Wolf
                   ` (10 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

bdrv_append() cares about isolation of the node that it modifies, but
not about activity in some subtree below it. Instead of using the
recursive bdrv_requests_pending(), directly check bs->in_flight, which
considers only the node in question.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/block.c b/block.c
index 7d84d43..1e01647 100644
--- a/block.c
+++ b/block.c
@@ -2802,8 +2802,8 @@ static void change_parent_backing_link(BlockDriverState *from,
  */
 void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
 {
-    assert(!bdrv_requests_pending(bs_top));
-    assert(!bdrv_requests_pending(bs_new));
+    assert(!atomic_read(&bs_top->in_flight));
+    assert(!atomic_read(&bs_new->in_flight));
 
     bdrv_ref(bs_top);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 32/41] block: BdrvChildRole.attach/detach() callbacks
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (30 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 31/41] block: Fix pending requests check in bdrv_append() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 33/41] block: Allow backing file links in change_parent_backing_link() Kevin Wolf
                   ` (9 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Backing files are somewhat special compared to other kinds of children
because they are attached and detached using bdrv_set_backing_hd()
rather than the normal set of functions, which does a few more things
like setting backing blockers, toggling the BDRV_O_NO_BACKING flag,
setting parent_bs->backing_file, etc.

These special features are a reason why change_parent_backing_link()
can't handle backing files yet. With abstracting the additional features
into .attach/.detach callbacks, we get a step closer to a function that
can actually deal with this.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                   | 94 +++++++++++++++++++++++++++++------------------
 include/block/block_int.h |  3 ++
 2 files changed, 62 insertions(+), 35 deletions(-)

diff --git a/block.c b/block.c
index 1e01647..ff328f1 100644
--- a/block.c
+++ b/block.c
@@ -800,6 +800,57 @@ const BdrvChildRole child_format = {
     .drained_end     = bdrv_child_cb_drained_end,
 };
 
+static void bdrv_backing_attach(BdrvChild *c)
+{
+    BlockDriverState *parent = c->opaque;
+    BlockDriverState *backing_hd = c->bs;
+
+    assert(!parent->backing_blocker);
+    error_setg(&parent->backing_blocker,
+               "node is used as backing hd of '%s'",
+               bdrv_get_device_or_node_name(parent));
+
+    parent->open_flags &= ~BDRV_O_NO_BACKING;
+    pstrcpy(parent->backing_file, sizeof(parent->backing_file),
+            backing_hd->filename);
+    pstrcpy(parent->backing_format, sizeof(parent->backing_format),
+            backing_hd->drv ? backing_hd->drv->format_name : "");
+
+    bdrv_op_block_all(backing_hd, parent->backing_blocker);
+    /* Otherwise we won't be able to commit or stream */
+    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET,
+                    parent->backing_blocker);
+    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM,
+                    parent->backing_blocker);
+    /*
+     * We do backup in 3 ways:
+     * 1. drive backup
+     *    The target bs is new opened, and the source is top BDS
+     * 2. blockdev backup
+     *    Both the source and the target are top BDSes.
+     * 3. internal backup(used for block replication)
+     *    Both the source and the target are backing file
+     *
+     * In case 1 and 2, neither the source nor the target is the backing file.
+     * In case 3, we will block the top BDS, so there is only one block job
+     * for the top BDS and its backing chain.
+     */
+    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE,
+                    parent->backing_blocker);
+    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
+                    parent->backing_blocker);
+}
+
+static void bdrv_backing_detach(BdrvChild *c)
+{
+    BlockDriverState *parent = c->opaque;
+
+    assert(parent->backing_blocker);
+    bdrv_op_unblock_all(c->bs, parent->backing_blocker);
+    error_free(parent->backing_blocker);
+    parent->backing_blocker = NULL;
+}
+
 static char *bdrv_backing_link_name(BdrvChild *c)
 {
     BlockDriverState *parent = c->opaque;
@@ -832,6 +883,8 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
 }
 
 static const BdrvChildRole child_backing = {
+    .attach          = bdrv_backing_attach,
+    .detach          = bdrv_backing_detach,
     .get_link_name   = bdrv_backing_link_name,
     .inherit_options = bdrv_backing_options,
     .drained_begin   = bdrv_child_cb_drained_begin,
@@ -1606,6 +1659,9 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
         if (old_bs->quiesce_counter && child->role->drained_end) {
             child->role->drained_end(child);
         }
+        if (child->role->detach) {
+            child->role->detach(child);
+        }
         QLIST_REMOVE(child, next_parent);
         bdrv_update_perm(old_bs);
     }
@@ -1618,6 +1674,9 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
             child->role->drained_begin(child);
         }
         bdrv_update_perm(new_bs);
+        if (child->role->attach) {
+            child->role->attach(child);
+        }
     }
 }
 
@@ -1758,52 +1817,17 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
     }
 
     if (bs->backing) {
-        assert(bs->backing_blocker);
-        bdrv_op_unblock_all(bs->backing->bs, bs->backing_blocker);
         bdrv_unref_child(bs, bs->backing);
-    } else if (backing_hd) {
-        error_setg(&bs->backing_blocker,
-                   "node is used as backing hd of '%s'",
-                   bdrv_get_device_or_node_name(bs));
     }
 
     if (!backing_hd) {
-        error_free(bs->backing_blocker);
-        bs->backing_blocker = NULL;
         bs->backing = NULL;
         goto out;
     }
     /* FIXME Error handling */
     bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
                                     &error_abort);
-    bs->open_flags &= ~BDRV_O_NO_BACKING;
-    pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
-    pstrcpy(bs->backing_format, sizeof(bs->backing_format),
-            backing_hd->drv ? backing_hd->drv->format_name : "");
 
-    bdrv_op_block_all(backing_hd, bs->backing_blocker);
-    /* Otherwise we won't be able to commit or stream */
-    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET,
-                    bs->backing_blocker);
-    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM,
-                    bs->backing_blocker);
-    /*
-     * We do backup in 3 ways:
-     * 1. drive backup
-     *    The target bs is new opened, and the source is top BDS
-     * 2. blockdev backup
-     *    Both the source and the target are top BDSes.
-     * 3. internal backup(used for block replication)
-     *    Both the source and the target are backing file
-     *
-     * In case 1 and 2, neither the source nor the target is the backing file.
-     * In case 3, we will block the top BDS, so there is only one block job
-     * for the top BDS and its backing chain.
-     */
-    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE,
-                    bs->backing_blocker);
-    bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
-                    bs->backing_blocker);
 out:
     bdrv_refresh_limits(bs, NULL);
 }
diff --git a/include/block/block_int.h b/include/block/block_int.h
index b24fc49..d37fcc2 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -456,6 +456,9 @@ struct BdrvChildRole {
      */
     void (*drained_begin)(BdrvChild *child);
     void (*drained_end)(BdrvChild *child);
+
+    void (*attach)(BdrvChild *child);
+    void (*detach)(BdrvChild *child);
 };
 
 extern const BdrvChildRole child_file;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 33/41] block: Allow backing file links in change_parent_backing_link()
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (31 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 32/41] block: BdrvChildRole.attach/detach() callbacks Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 34/41] mirror: Use real permissions in mirror/active commit block job Kevin Wolf
                   ` (8 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Now that the backing file child role implements .attach/.detach
callbacks, nothing prevents us from modifying the graph even if that
involves changing backing file links.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/block.c b/block.c
index ff328f1..8224dde 100644
--- a/block.c
+++ b/block.c
@@ -2788,9 +2788,9 @@ static void change_parent_backing_link(BlockDriverState *from,
             continue;
         }
         if (c->role == &child_backing) {
-            /* @from is generally not allowed to be a backing file, except for
-             * when @to is the overlay. In that case, @from may not be replaced
-             * by @to as @to's backing node. */
+            /* If @from is a backing file of @to, ignore the child to avoid
+             * creating a loop. We only want to change the pointer of other
+             * parents. */
             QLIST_FOREACH(to_c, &to->children, next) {
                 if (to_c == c) {
                     break;
@@ -2801,7 +2801,6 @@ static void change_parent_backing_link(BlockDriverState *from,
             }
         }
 
-        assert(c->role != &child_backing);
         bdrv_ref(to);
         bdrv_replace_child(c, to);
         bdrv_unref(from);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 34/41] mirror: Use real permissions in mirror/active commit block job
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (32 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 33/41] block: Allow backing file links in change_parent_backing_link() Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 35/41] stream: Use real permissions in streaming " Kevin Wolf
                   ` (7 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

The mirror block job is mainly used for two different scenarios:
Mirroring to an otherwise unused, independent target node, or for active
commit where the target node is part of the backing chain of the source.

Similarly to the commit block job patch, we need to insert a new filter
node to keep the permissions correct during active commit.

Note that one change this implies is that job->blk points to
mirror_top_bs as its root now, and mirror_top_bs (rather than the actual
source node) contains the bs->job pointer. This requires qemu-img commit
to get the job by name now rather than just taking bs->job.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/mirror.c             | 160 ++++++++++++++++++++++++++++++++++++++-------
 qemu-img.c                 |   6 +-
 tests/qemu-iotests/141     |   2 +-
 tests/qemu-iotests/141.out |   4 +-
 4 files changed, 143 insertions(+), 29 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index 9532b18..50d2585 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -38,7 +38,10 @@ typedef struct MirrorBlockJob {
     BlockJob common;
     RateLimit limit;
     BlockBackend *target;
+    BlockDriverState *mirror_top_bs;
+    BlockDriverState *source;
     BlockDriverState *base;
+
     /* The name of the graph node to replace */
     char *replaces;
     /* The BDS to replace */
@@ -319,7 +322,7 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
 
 static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
 {
-    BlockDriverState *source = blk_bs(s->common.blk);
+    BlockDriverState *source = s->source;
     int64_t sector_num, first_chunk;
     uint64_t delay_ns = 0;
     /* At least the first dirty chunk is mirrored in one iteration. */
@@ -489,12 +492,14 @@ static void mirror_exit(BlockJob *job, void *opaque)
     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
     MirrorExitData *data = opaque;
     AioContext *replace_aio_context = NULL;
-    BlockDriverState *src = blk_bs(s->common.blk);
+    BlockDriverState *src = s->source;
     BlockDriverState *target_bs = blk_bs(s->target);
+    BlockDriverState *mirror_top_bs = s->mirror_top_bs;
 
     /* Make sure that the source BDS doesn't go away before we called
      * block_job_completed(). */
     bdrv_ref(src);
+    bdrv_ref(mirror_top_bs);
 
     if (s->to_replace) {
         replace_aio_context = bdrv_get_aio_context(s->to_replace);
@@ -516,13 +521,6 @@ static void mirror_exit(BlockJob *job, void *opaque)
         bdrv_drained_begin(target_bs);
         bdrv_replace_in_backing_chain(to_replace, target_bs);
         bdrv_drained_end(target_bs);
-
-        /* We just changed the BDS the job BB refers to, so switch the BB back
-         * so the cleanup does the right thing. We don't need any permissions
-         * any more now. */
-        blk_remove_bs(job->blk);
-        blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
-        blk_insert_bs(job->blk, src, &error_abort);
     }
     if (s->to_replace) {
         bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
@@ -535,9 +533,23 @@ static void mirror_exit(BlockJob *job, void *opaque)
     g_free(s->replaces);
     blk_unref(s->target);
     s->target = NULL;
+
+    /* Remove the mirror filter driver from the graph */
+    bdrv_replace_in_backing_chain(mirror_top_bs, backing_bs(mirror_top_bs));
+
+    /* We just changed the BDS the job BB refers to (with either or both of the
+     * bdrv_replace_in_backing_chain() calls), so switch the BB back so the
+     * cleanup does the right thing. We don't need any permissions any more
+     * now. */
+    blk_remove_bs(job->blk);
+    blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
+    blk_insert_bs(job->blk, mirror_top_bs, &error_abort);
+
     block_job_completed(&s->common, data->ret);
+
     g_free(data);
     bdrv_drained_end(src);
+    bdrv_unref(mirror_top_bs);
     bdrv_unref(src);
 }
 
@@ -557,7 +569,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
 {
     int64_t sector_num, end;
     BlockDriverState *base = s->base;
-    BlockDriverState *bs = blk_bs(s->common.blk);
+    BlockDriverState *bs = s->source;
     BlockDriverState *target_bs = blk_bs(s->target);
     int ret, n;
 
@@ -636,7 +648,7 @@ static void coroutine_fn mirror_run(void *opaque)
 {
     MirrorBlockJob *s = opaque;
     MirrorExitData *data;
-    BlockDriverState *bs = blk_bs(s->common.blk);
+    BlockDriverState *bs = s->source;
     BlockDriverState *target_bs = blk_bs(s->target);
     bool need_drain = true;
     int64_t length;
@@ -849,7 +861,7 @@ static void mirror_complete(BlockJob *job, Error **errp)
     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
     BlockDriverState *src, *target;
 
-    src = blk_bs(job->blk);
+    src = s->source;
     target = blk_bs(s->target);
 
     if (!s->synced) {
@@ -881,6 +893,10 @@ static void mirror_complete(BlockJob *job, Error **errp)
         replace_aio_context = bdrv_get_aio_context(s->to_replace);
         aio_context_acquire(replace_aio_context);
 
+        /* TODO Translate this into permission system. Current definition of
+         * GRAPH_MOD would require to request it for the parents; they might
+         * not even be BlockDriverStates, however, so a BdrvChild can't address
+         * them. May need redefinition of GRAPH_MOD. */
         error_setg(&s->replace_blocker,
                    "block device is in use by block-job-complete");
         bdrv_op_block_all(s->to_replace, s->replace_blocker);
@@ -951,6 +967,46 @@ static const BlockJobDriver commit_active_job_driver = {
     .drain                  = mirror_drain,
 };
 
+static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs,
+    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
+{
+    return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
+}
+
+static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs,
+    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
+{
+    return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
+}
+
+static void bdrv_mirror_top_close(BlockDriverState *bs)
+{
+}
+
+static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
+                                       const BdrvChildRole *role,
+                                       uint64_t perm, uint64_t shared,
+                                       uint64_t *nperm, uint64_t *nshared)
+{
+    /* Must be able to forward guest writes to the real image */
+    *nperm = 0;
+    if (perm & BLK_PERM_WRITE) {
+        *nperm |= BLK_PERM_WRITE;
+    }
+
+    *nshared = BLK_PERM_ALL;
+}
+
+/* Dummy node that provides consistent read to its users without requiring it
+ * from its backing file and that allows writes on the backing file chain. */
+static BlockDriver bdrv_mirror_top = {
+    .format_name        = "mirror_top",
+    .bdrv_co_preadv     = bdrv_mirror_top_preadv,
+    .bdrv_co_pwritev    = bdrv_mirror_top_pwritev,
+    .bdrv_close         = bdrv_mirror_top_close,
+    .bdrv_child_perm    = bdrv_mirror_top_child_perm,
+};
+
 static void mirror_start_job(const char *job_id, BlockDriverState *bs,
                              int creation_flags, BlockDriverState *target,
                              const char *replaces, int64_t speed,
@@ -966,6 +1022,9 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
                              bool auto_complete)
 {
     MirrorBlockJob *s;
+    BlockDriverState *mirror_top_bs;
+    bool target_graph_mod;
+    bool target_is_backing;
     int ret;
 
     if (granularity == 0) {
@@ -983,20 +1042,54 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
         buf_size = DEFAULT_MIRROR_BUF_SIZE;
     }
 
-    /* FIXME Use real permissions */
-    s = block_job_create(job_id, driver, bs, 0, BLK_PERM_ALL, speed,
+    /* In the case of active commit, add dummy driver to provide consistent
+     * reads on the top, while disabling it in the intermediate nodes */
+    ret = bdrv_new_open_driver(&bdrv_mirror_top, &mirror_top_bs, NULL,
+                               BDRV_O_RDWR, errp);
+    if (ret < 0) {
+        return;
+    }
+    mirror_top_bs->total_sectors = bs->total_sectors;
+
+    /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
+     * it alive until block_job_create() even if bs has no parent. */
+    bdrv_ref(mirror_top_bs);
+    bdrv_drained_begin(bs);
+    bdrv_append(mirror_top_bs, bs);
+    bdrv_drained_end(bs);
+
+    /* Make sure that the source is not resized while the job is running */
+    s = block_job_create(job_id, driver, mirror_top_bs,
+                         BLK_PERM_CONSISTENT_READ,
+                         BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
+                         BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
                          creation_flags, cb, opaque, errp);
+    bdrv_unref(mirror_top_bs);
     if (!s) {
-        return;
+        goto fail;
     }
-
-    /* FIXME Use real permissions */
-    s->target = blk_new(0, BLK_PERM_ALL);
+    s->source = bs;
+    s->mirror_top_bs = mirror_top_bs;
+
+    /* No resize for the target either; while the mirror is still running, a
+     * consistent read isn't necessarily possible. We could possibly allow
+     * writes and graph modifications, though it would likely defeat the
+     * purpose of a mirror, so leave them blocked for now.
+     *
+     * In the case of active commit, things look a bit different, though,
+     * because the target is an already populated backing file in active use.
+     * We can allow anything ecept resize there.*/
+    target_is_backing = bdrv_chain_contains(bs, target);
+    target_graph_mod = (backing_mode != MIRROR_LEAVE_BACKING_CHAIN);
+    s->target = blk_new(BLK_PERM_WRITE |
+                        (target_graph_mod ? BLK_PERM_GRAPH_MOD : 0),
+                        BLK_PERM_WRITE_UNCHANGED |
+                        (target_is_backing ? BLK_PERM_CONSISTENT_READ |
+                                             BLK_PERM_WRITE |
+                                             BLK_PERM_GRAPH_MOD : 0));
     ret = blk_insert_bs(s->target, target, errp);
     if (ret < 0) {
-        blk_unref(s->target);
-        block_job_unref(&s->common);
-        return;
+        goto fail;
     }
 
     s->replaces = g_strdup(replaces);
@@ -1020,21 +1113,38 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
         return;
     }
 
-    /* FIXME Use real permissions */
+    /* Required permissions are already taken with blk_new() */
     block_job_add_bdrv(&s->common, target, 0, BLK_PERM_ALL, &error_abort);
 
     /* In commit_active_start() all intermediate nodes disappear, so
      * any jobs in them must be blocked */
-    if (bdrv_chain_contains(bs, target)) {
+    if (target_is_backing) {
         BlockDriverState *iter;
         for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) {
-            /* FIXME Use real permissions */
-            block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
+            /* XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves
+             * at s->base. The other options would be a second filter driver above
+             * s->base. */
+            ret = block_job_add_bdrv(&s->common, iter, 0,
+                                     BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE,
+                                     errp);
+            if (ret < 0) {
+                goto fail;
+            }
         }
     }
 
     trace_mirror_start(bs, s, opaque);
     block_job_start(&s->common);
+    return;
+
+fail:
+    if (s) {
+        g_free(s->replaces);
+        blk_unref(s->target);
+        block_job_unref(&s->common);
+    }
+
+    bdrv_replace_in_backing_chain(mirror_top_bs, backing_bs(mirror_top_bs));
 }
 
 void mirror_start(const char *job_id, BlockDriverState *bs,
diff --git a/qemu-img.c b/qemu-img.c
index cff22e3..742faed 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -795,6 +795,8 @@ static void run_block_job(BlockJob *job, Error **errp)
 {
     AioContext *aio_context = blk_get_aio_context(job->blk);
 
+    /* FIXME In error cases, the job simply goes away and we access a dangling
+     * pointer below. */
     aio_context_acquire(aio_context);
     do {
         aio_poll(aio_context, true);
@@ -816,6 +818,7 @@ static int img_commit(int argc, char **argv)
     const char *filename, *fmt, *cache, *base;
     BlockBackend *blk;
     BlockDriverState *bs, *base_bs;
+    BlockJob *job;
     bool progress = false, quiet = false, drop = false;
     bool writethrough;
     Error *local_err = NULL;
@@ -951,7 +954,8 @@ static int img_commit(int argc, char **argv)
         bdrv_ref(bs);
     }
 
-    run_block_job(bs->job, &local_err);
+    job = block_job_get("commit");
+    run_block_job(job, &local_err);
     if (local_err) {
         goto unref_backing;
     }
diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
index 3ba79f0..6d8f0a1 100755
--- a/tests/qemu-iotests/141
+++ b/tests/qemu-iotests/141
@@ -67,7 +67,7 @@ test_blockjob()
     _send_qemu_cmd $QEMU_HANDLE \
         "{'execute': 'x-blockdev-del',
           'arguments': {'node-name': 'drv0'}}" \
-        'error'
+        'error' | _filter_generated_node_ids
 
     _send_qemu_cmd $QEMU_HANDLE \
         "{'execute': 'block-job-cancel',
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
index 195ca1a..82e763b 100644
--- a/tests/qemu-iotests/141.out
+++ b/tests/qemu-iotests/141.out
@@ -20,7 +20,7 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
 Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
 {"return": {}}
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
 {"return": {}}
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
 {"return": {}}
@@ -30,7 +30,7 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
 {"return": {}}
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
 {"return": {}}
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
 {"return": {}}
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
 {"return": {}}
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 35/41] stream: Use real permissions in streaming block job
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (33 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 34/41] mirror: Use real permissions in mirror/active commit block job Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 36/41] hmp: Request permissions in qemu-io Kevin Wolf
                   ` (6 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

The correct permissions are relatively obvious here (and explained in
code comments). For intermediate streaming, we need to reopen the top
node read-write before creating the job now because the permissions
system catches attempts to get the BLK_PERM_WRITE_UNCHANGED permission
on a read-only node.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/stream.c | 38 ++++++++++++++++++++++++++------------
 1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/block/stream.c b/block/stream.c
index 47f0ffb..e562f57 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -229,27 +229,35 @@ void stream_start(const char *job_id, BlockDriverState *bs,
     BlockDriverState *iter;
     int orig_bs_flags;
 
-    /* FIXME Use real permissions */
-    s = block_job_create(job_id, &stream_job_driver, bs, 0, BLK_PERM_ALL,
-                         speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
-    if (!s) {
-        return;
-    }
-
     /* Make sure that the image is opened in read-write mode */
     orig_bs_flags = bdrv_get_flags(bs);
     if (!(orig_bs_flags & BDRV_O_RDWR)) {
         if (bdrv_reopen(bs, orig_bs_flags | BDRV_O_RDWR, errp) != 0) {
-            block_job_unref(&s->common);
             return;
         }
     }
 
-    /* Block all intermediate nodes between bs and base, because they
-     * will disappear from the chain after this operation */
+    /* Prevent concurrent jobs trying to modify the graph structure here, we
+     * already have our own plans. Also don't allow resize as the image size is
+     * queried only at the job start and then cached. */
+    s = block_job_create(job_id, &stream_job_driver, bs,
+                         BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
+                         BLK_PERM_GRAPH_MOD,
+                         BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
+                         BLK_PERM_WRITE,
+                         speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
+    if (!s) {
+        goto fail;
+    }
+
+    /* Block all intermediate nodes between bs and base, because they will
+     * disappear from the chain after this operation. The streaming job reads
+     * every block only once, assuming that it doesn't change, so block writes
+     * and resizes. */
     for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
-        /* FIXME Use real permissions */
-        block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
+        block_job_add_bdrv(&s->common, iter, 0,
+                           BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED,
+                           &error_abort);
     }
 
     s->base = base;
@@ -259,4 +267,10 @@ void stream_start(const char *job_id, BlockDriverState *bs,
     s->on_error = on_error;
     trace_stream_start(bs, base, s);
     block_job_start(&s->common);
+    return;
+
+fail:
+    if (orig_bs_flags != bdrv_get_flags(bs)) {
+        bdrv_reopen(bs, s->bs_flags, NULL);
+    }
 }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 36/41] hmp: Request permissions in qemu-io
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (34 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 35/41] stream: Use real permissions in streaming " Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 37/41] migration/block: Use real permissions Kevin Wolf
                   ` (5 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

The HMP command 'qemu-io' is a bit tricky because it wants to work on
the original BlockBackend, but additional permissions could be required.
The details are explained in a comment in the code, but in summary, just
request whatever permissions the current qemu-io command needs.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/block-backend.c          |  6 ++++++
 hmp.c                          | 26 +++++++++++++++++++++++++-
 include/qemu-io.h              |  1 +
 include/sysemu/block-backend.h |  1 +
 qemu-io-cmds.c                 | 28 ++++++++++++++++++++++++++++
 5 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/block/block-backend.c b/block/block-backend.c
index a314284..94db555 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -573,6 +573,12 @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
     return 0;
 }
 
+void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
+{
+    *perm = blk->perm;
+    *shared_perm = blk->shared_perm;
+}
+
 static int blk_do_attach_dev(BlockBackend *blk, void *dev)
 {
     if (blk->dev) {
diff --git a/hmp.c b/hmp.c
index 801fddb..fde5016 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2045,7 +2045,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
     if (!blk) {
         BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
         if (bs) {
-            /* FIXME Use real permissions */
             blk = local_blk = blk_new(0, BLK_PERM_ALL);
             ret = blk_insert_bs(blk, bs, &err);
             if (ret < 0) {
@@ -2059,6 +2058,31 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
     aio_context = blk_get_aio_context(blk);
     aio_context_acquire(aio_context);
 
+    /*
+     * Notably absent: Proper permission management. This is sad, but it seems
+     * almost impossible to achieve without changing the semantics and thereby
+     * limiting the use cases of the qemu-io HMP command.
+     *
+     * In an ideal world we would unconditionally create a new BlockBackend for
+     * qemuio_command(), but we have commands like 'reopen' and want them to
+     * take effect on the exact BlockBackend whose name the user passed instead
+     * of just on a temporary copy of it.
+     *
+     * Another problem is that deleting the temporary BlockBackend involves
+     * draining all requests on it first, but some qemu-iotests cases want to
+     * issue multiple aio_read/write requests and expect them to complete in
+     * the background while the monitor has already returned.
+     *
+     * This is also what prevents us from saving the original permissions and
+     * restoring them later: We can't revoke permissions until all requests
+     * have completed, and we don't know when that is nor can we really let
+     * anything else run before we have revoken them to avoid race conditions.
+     *
+     * What happens now is that command() in qemu-io-cmds.c can extend the
+     * permissions if necessary for the qemu-io command. And they simply stay
+     * extended, possibly resulting in a read-only guest device keeping write
+     * permissions. Ugly, but it appears to be the lesser evil.
+     */
     qemuio_command(blk, command);
 
     aio_context_release(aio_context);
diff --git a/include/qemu-io.h b/include/qemu-io.h
index 4d402b9..196fde0 100644
--- a/include/qemu-io.h
+++ b/include/qemu-io.h
@@ -36,6 +36,7 @@ typedef struct cmdinfo {
     const char  *args;
     const char  *oneline;
     helpfunc_t  help;
+    uint64_t    perm;
 } cmdinfo_t;
 
 extern bool qemuio_misalign;
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 65bd081..b400212 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -101,6 +101,7 @@ bool bdrv_has_blk(BlockDriverState *bs);
 bool bdrv_is_root_node(BlockDriverState *bs);
 int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
                  Error **errp);
+void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm);
 
 void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow);
 void blk_iostatus_enable(BlockBackend *blk);
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index e415b03..035cb96 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -83,6 +83,29 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
         }
         return 0;
     }
+
+    /* Request additional permissions if necessary for this command. The caller
+     * is responsible for restoring the original permissions afterwards if this
+     * is what it wants. */
+    if (ct->perm && blk_is_available(blk)) {
+        uint64_t orig_perm, orig_shared_perm;
+        blk_get_perm(blk, &orig_perm, &orig_shared_perm);
+
+        if (ct->perm & ~orig_perm) {
+            uint64_t new_perm;
+            Error *local_err = NULL;
+            int ret;
+
+            new_perm = orig_perm | ct->perm;
+
+            ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err);
+            if (ret < 0) {
+                error_report_err(local_err);
+                return 0;
+            }
+        }
+    }
+
     optind = 0;
     return ct->cfunc(blk, argc, argv);
 }
@@ -916,6 +939,7 @@ static const cmdinfo_t write_cmd = {
     .name       = "write",
     .altname    = "w",
     .cfunc      = write_f,
+    .perm       = BLK_PERM_WRITE,
     .argmin     = 2,
     .argmax     = -1,
     .args       = "[-bcCfquz] [-P pattern] off len",
@@ -1091,6 +1115,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv);
 static const cmdinfo_t writev_cmd = {
     .name       = "writev",
     .cfunc      = writev_f,
+    .perm       = BLK_PERM_WRITE,
     .argmin     = 2,
     .argmax     = -1,
     .args       = "[-Cfq] [-P pattern] off len [len..]",
@@ -1390,6 +1415,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv);
 static const cmdinfo_t aio_write_cmd = {
     .name       = "aio_write",
     .cfunc      = aio_write_f,
+    .perm       = BLK_PERM_WRITE,
     .argmin     = 2,
     .argmax     = -1,
     .args       = "[-Cfiquz] [-P pattern] off len [len..]",
@@ -1554,6 +1580,7 @@ static const cmdinfo_t truncate_cmd = {
     .name       = "truncate",
     .altname    = "t",
     .cfunc      = truncate_f,
+    .perm       = BLK_PERM_WRITE | BLK_PERM_RESIZE,
     .argmin     = 1,
     .argmax     = 1,
     .args       = "off",
@@ -1651,6 +1678,7 @@ static const cmdinfo_t discard_cmd = {
     .name       = "discard",
     .altname    = "d",
     .cfunc      = discard_f,
+    .perm       = BLK_PERM_WRITE,
     .argmin     = 2,
     .argmax     = -1,
     .args       = "[-Cq] off len",
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 37/41] migration/block: Use real permissions
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (35 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 36/41] hmp: Request permissions in qemu-io Kevin Wolf
@ 2017-02-13 17:22 ` Kevin Wolf
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 38/41] nbd/server: Use real permissions for NBD exports Kevin Wolf
                   ` (4 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:22 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Request BLK_PERM_CONSISTENT_READ for the source of block migration, and
handle potential permission errors as good as we can in this place
(which is not very good, but it matches the other failure cases).

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 migration/block.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/migration/block.c b/migration/block.c
index d259936..958d0fc 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -379,7 +379,7 @@ static void unset_dirty_tracking(void)
     }
 }
 
-static void init_blk_migration(QEMUFile *f)
+static int init_blk_migration(QEMUFile *f)
 {
     BlockDriverState *bs;
     BlkMigDevState *bmds;
@@ -390,6 +390,8 @@ static void init_blk_migration(QEMUFile *f)
         BlkMigDevState *bmds;
         BlockDriverState *bs;
     } *bmds_bs;
+    Error *local_err = NULL;
+    int ret;
 
     block_mig_state.submitted = 0;
     block_mig_state.read_done = 0;
@@ -411,12 +413,12 @@ static void init_blk_migration(QEMUFile *f)
 
         sectors = bdrv_nb_sectors(bs);
         if (sectors <= 0) {
+            ret = sectors;
             goto out;
         }
 
         bmds = g_new0(BlkMigDevState, 1);
-        /* FIXME Use real permissions */
-        bmds->blk = blk_new(0, BLK_PERM_ALL);
+        bmds->blk = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
         bmds->blk_name = g_strdup(bdrv_get_device_name(bs));
         bmds->bulk_completed = 0;
         bmds->total_sectors = sectors;
@@ -446,7 +448,11 @@ static void init_blk_migration(QEMUFile *f)
         BlockDriverState *bs = bmds_bs[i].bs;
 
         if (bmds) {
-            blk_insert_bs(bmds->blk, bs, &error_abort);
+            ret = blk_insert_bs(bmds->blk, bs, &local_err);
+            if (ret < 0) {
+                error_report_err(local_err);
+                goto out;
+            }
 
             alloc_aio_bitmap(bmds);
             error_setg(&bmds->blocker, "block device is in use by migration");
@@ -454,8 +460,10 @@ static void init_blk_migration(QEMUFile *f)
         }
     }
 
+    ret = 0;
 out:
     g_free(bmds_bs);
+    return ret;
 }
 
 /* Called with no lock taken.  */
@@ -706,7 +714,10 @@ static int block_save_setup(QEMUFile *f, void *opaque)
             block_mig_state.submitted, block_mig_state.transferred);
 
     qemu_mutex_lock_iothread();
-    init_blk_migration(f);
+    ret = init_blk_migration(f);
+    if (ret < 0) {
+        return ret;
+    }
 
     /* start track dirty blocks */
     ret = set_dirty_tracking();
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 38/41] nbd/server: Use real permissions for NBD exports
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (36 preceding siblings ...)
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 37/41] migration/block: Use real permissions Kevin Wolf
@ 2017-02-13 17:23 ` Kevin Wolf
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 39/41] tests: Remove FIXME comments Kevin Wolf
                   ` (3 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:23 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

NBD can't cope with device size changes, so resize must be forbidden,
but otherwise we can tolerate anything. Depending on whether the export
is writable or not, we only require consistent reads and writes.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 nbd/server.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/nbd/server.c b/nbd/server.c
index deb4358..4bb37d6 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -889,10 +889,17 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
 {
     BlockBackend *blk;
     NBDExport *exp = g_malloc0(sizeof(NBDExport));
+    uint64_t perm;
     int ret;
 
-    /* FIXME Use real permissions */
-    blk = blk_new(0, BLK_PERM_ALL);
+    /* Don't allow resize while the NBD server is running, otherwise we don't
+     * care what happens with the node. */
+    perm = BLK_PERM_CONSISTENT_READ;
+    if ((nbdflags & NBD_FLAG_READ_ONLY) == 0) {
+        perm |= BLK_PERM_WRITE;
+    }
+    blk = blk_new(perm, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
+                        BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD);
     ret = blk_insert_bs(blk, bs, errp);
     if (ret < 0) {
         goto fail;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 39/41] tests: Remove FIXME comments
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (37 preceding siblings ...)
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 38/41] nbd/server: Use real permissions for NBD exports Kevin Wolf
@ 2017-02-13 17:23 ` Kevin Wolf
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 40/41] block: Pass BdrvChild to bdrv_aligned_preadv/pwritev Kevin Wolf
                   ` (2 subsequent siblings)
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:23 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

Not requesting any permissions is actually correct for these test cases
because no actual I/O or other operation covered by the permission
system is performed.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/test-blockjob.c | 2 +-
 tests/test-throttle.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 1afe17b..740e740 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -54,7 +54,7 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
  * BlockDriverState inserted. */
 static BlockBackend *create_blk(const char *name)
 {
-    /* FIXME Use real permissions */
+    /* No I/O is performed on this device */
     BlockBackend *blk = blk_new(0, BLK_PERM_ALL);
     BlockDriverState *bs;
 
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 5846433..bd7c501 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -593,7 +593,7 @@ static void test_groups(void)
     BlockBackend *blk1, *blk2, *blk3;
     BlockBackendPublic *blkp1, *blkp2, *blkp3;
 
-    /* FIXME Use real permissions */
+    /* No actual I/O is performed on these devices */
     blk1 = blk_new(0, BLK_PERM_ALL);
     blk2 = blk_new(0, BLK_PERM_ALL);
     blk3 = blk_new(0, BLK_PERM_ALL);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 40/41] block: Pass BdrvChild to bdrv_aligned_preadv/pwritev
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (38 preceding siblings ...)
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 39/41] tests: Remove FIXME comments Kevin Wolf
@ 2017-02-13 17:23 ` Kevin Wolf
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 41/41] block: Assertions for write permissions Kevin Wolf
  2017-02-13 18:44 ` [Qemu-devel] [RFC PATCH 00/41] New op blocker system no-reply
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:23 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This is where we want to check the permissions, so we need to have the
BdrvChild around where they are stored.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/io.c | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/block/io.c b/block/io.c
index c42b34a..cb2feff 100644
--- a/block/io.c
+++ b/block/io.c
@@ -1001,10 +1001,11 @@ err:
  * handles copy on read, zeroing after EOF, and fragmentation of large
  * reads; any other features must be implemented by the caller.
  */
-static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
+static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
     BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
     int64_t align, QEMUIOVector *qiov, int flags)
 {
+    BlockDriverState *bs = child->bs;
     int64_t total_bytes, max_bytes;
     int ret = 0;
     uint64_t bytes_remaining = bytes;
@@ -1158,7 +1159,7 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
     }
 
     tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
-    ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align,
+    ret = bdrv_aligned_preadv(child, &req, offset, bytes, align,
                               use_local_qiov ? &local_qiov : qiov,
                               flags);
     tracked_request_end(&req);
@@ -1306,10 +1307,11 @@ fail:
  * Forwards an already correctly aligned write request to the BlockDriver,
  * after possibly fragmenting it.
  */
-static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
+static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
     BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
     int64_t align, QEMUIOVector *qiov, int flags)
 {
+    BlockDriverState *bs = child->bs;
     BlockDriver *drv = bs->drv;
     bool waited;
     int ret;
@@ -1397,12 +1399,13 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
     return ret;
 }
 
-static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
+static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
                                                 int64_t offset,
                                                 unsigned int bytes,
                                                 BdrvRequestFlags flags,
                                                 BdrvTrackedRequest *req)
 {
+    BlockDriverState *bs = child->bs;
     uint8_t *buf = NULL;
     QEMUIOVector local_qiov;
     struct iovec iov;
@@ -1430,7 +1433,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
         mark_request_serialising(req, align);
         wait_serialising_requests(req);
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
-        ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align,
+        ret = bdrv_aligned_preadv(child, req, offset & ~(align - 1), align,
                                   align, &local_qiov, 0);
         if (ret < 0) {
             goto fail;
@@ -1438,7 +1441,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
 
         memset(buf + head_padding_bytes, 0, zero_bytes);
-        ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align,
+        ret = bdrv_aligned_pwritev(child, req, offset & ~(align - 1), align,
                                    align, &local_qiov,
                                    flags & ~BDRV_REQ_ZERO_WRITE);
         if (ret < 0) {
@@ -1452,7 +1455,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
     if (bytes >= align) {
         /* Write the aligned part in the middle. */
         uint64_t aligned_bytes = bytes & ~(align - 1);
-        ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes, align,
+        ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align,
                                    NULL, flags);
         if (ret < 0) {
             goto fail;
@@ -1468,7 +1471,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
         mark_request_serialising(req, align);
         wait_serialising_requests(req);
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
-        ret = bdrv_aligned_preadv(bs, req, offset, align,
+        ret = bdrv_aligned_preadv(child, req, offset, align,
                                   align, &local_qiov, 0);
         if (ret < 0) {
             goto fail;
@@ -1476,7 +1479,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
 
         memset(buf, 0, bytes);
-        ret = bdrv_aligned_pwritev(bs, req, offset, align, align,
+        ret = bdrv_aligned_pwritev(child, req, offset, align, align,
                                    &local_qiov, flags & ~BDRV_REQ_ZERO_WRITE);
     }
 fail:
@@ -1523,7 +1526,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
     tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE);
 
     if (!qiov) {
-        ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req);
+        ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req);
         goto out;
     }
 
@@ -1542,7 +1545,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
         qemu_iovec_init_external(&head_qiov, &head_iov, 1);
 
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
-        ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align,
+        ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
                                   align, &head_qiov, 0);
         if (ret < 0) {
             goto fail;
@@ -1584,8 +1587,8 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
         qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
 
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
-        ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align,
-                                  align, &tail_qiov, 0);
+        ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
+                                  align, align, &tail_qiov, 0);
         if (ret < 0) {
             goto fail;
         }
@@ -1603,7 +1606,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
         bytes = ROUND_UP(bytes, align);
     }
 
-    ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, align,
+    ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align,
                                use_local_qiov ? &local_qiov : qiov,
                                flags);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* [Qemu-devel] [RFC PATCH 41/41] block: Assertions for write permissions
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (39 preceding siblings ...)
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 40/41] block: Pass BdrvChild to bdrv_aligned_preadv/pwritev Kevin Wolf
@ 2017-02-13 17:23 ` Kevin Wolf
  2017-02-13 18:44 ` [Qemu-devel] [RFC PATCH 00/41] New op blocker system no-reply
  41 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-13 17:23 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, mreitz, jcody, famz, qemu-devel

This adds assertions that ensure that the necessary write permissions
have been granted before someone attempts to write to a node.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/io.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/block/io.c b/block/io.c
index cb2feff..74929e5 100644
--- a/block/io.c
+++ b/block/io.c
@@ -925,9 +925,11 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
     return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov);
 }
 
-static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
+static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
         int64_t offset, unsigned int bytes, QEMUIOVector *qiov)
 {
+    BlockDriverState *bs = child->bs;
+
     /* Perform I/O through a temporary buffer so that users who scribble over
      * their read buffer while the operation is in progress do not end up
      * modifying the image file.  This is critical for zero-copy guest I/O
@@ -943,6 +945,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
     size_t skip_bytes;
     int ret;
 
+    assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE));
+
     /* Cover entire cluster so no additional backing file I/O is required when
      * allocating cluster in the image file.
      */
@@ -1051,7 +1055,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
         }
 
         if (!ret || pnum != nb_sectors) {
-            ret = bdrv_co_do_copy_on_readv(bs, offset, bytes, qiov);
+            ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov);
             goto out;
         }
     }
@@ -1334,6 +1338,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
     assert(!waited || !req->serialising);
     assert(req->overlap_offset <= offset);
     assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
+    assert(child->perm & BLK_PERM_WRITE);
 
     ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/41] New op blocker system
  2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
                   ` (40 preceding siblings ...)
  2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 41/41] block: Assertions for write permissions Kevin Wolf
@ 2017-02-13 18:44 ` no-reply
  41 siblings, 0 replies; 76+ messages in thread
From: no-reply @ 2017-02-13 18:44 UTC (permalink / raw)
  To: kwolf; +Cc: famz, qemu-block, jcody

Hi,

Your series seems to have some coding style problems. See output below for
more information:

Subject: [Qemu-devel] [RFC PATCH 00/41] New op blocker system
Message-id: 1487006583-24350-1-git-send-email-kwolf@redhat.com
Type: series

=== TEST SCRIPT BEGIN ===
#!/bin/bash

BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0

# Useful git options
git config --local diff.renamelimit 0
git config --local diff.renames True

commits="$(git log --format=%H --reverse $BASE..)"
for c in $commits; do
    echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
    if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
        failed=1
        echo
    fi
    n=$((n+1))
done

exit $failed
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 - [tag update]      patchew/1486747506-15876-1-git-send-email-abologna@redhat.com -> patchew/1486747506-15876-1-git-send-email-abologna@redhat.com
 * [new tag]         patchew/1487006583-24350-1-git-send-email-kwolf@redhat.com -> patchew/1487006583-24350-1-git-send-email-kwolf@redhat.com
Switched to a new branch 'test'
74e9065 block: Assertions for write permissions
e39ccbe block: Pass BdrvChild to bdrv_aligned_preadv/pwritev
7b6003a tests: Remove FIXME comments
779ef72 nbd/server: Use real permissions for NBD exports
2a88fb5 migration/block: Use real permissions
b7208e1 hmp: Request permissions in qemu-io
2fd0afd stream: Use real permissions in streaming block job
d1bf1e8 mirror: Use real permissions in mirror/active commit block job
b53e0a7 block: Allow backing file links in change_parent_backing_link()
04ce3a6 block: BdrvChildRole.attach/detach() callbacks
d27507e block: Fix pending requests check in bdrv_append()
c28f58a backup: Use real permissions in backup block job
9c179f9 commit: Use real permissions for HMP 'commit'
d0affd9 commit: Use real permissions in commit block job
fc0d620 block: Add bdrv_new_open_driver()
c4670f2 block: Factor out bdrv_open_driver()
a0b9c4e blockjob: Add permissions to block_job_add_bdrv()
7ae2792 block: Add BdrvChildRole.stay_at_node
6790ced block: Include details on permission errors in message
135cb50 block: Add BdrvChildRole.get_link_name()
aee95b7 blockjob: Add permissions to block_job_create()
e65d733 hw/block: Introduce share-rw qdev property
abf8d2f hw/block: Request permissions
016d652 block: Allow error return in BlockDevOps.change_media_cb()
f0ce22b block: Request real permissions in blk_new_open()
0560a02 block: Add error parameter to blk_insert_bs()
616c5f5 block: Add permissions to blk_new()
9d7081b block: Add permissions to BlockBackend
53aa264 block: Request real permissions in bdrv_attach_child()
2dc4523 block: Require .bdrv_child_perm() with child nodes
a1cb388 vvfat: Implement .bdrv_child_perm()
c3c7960 block: Request child permissions in format drivers
49ff6dd block: Default .bdrv_child_perm() for format drivers
126dedf block: Request child permissions in filter drivers
6eebee7 block: Default .bdrv_child_perm() for filter drivers
017216c block: Involve block drivers in permission granting
18c9ee8 tests: Use opened block node for block job tests
aa32b84 block: Let callers request permissions when attaching a child node
7abb303 block: Add Error argument to bdrv_attach_child()
0a3b05b block: Add op blocker permission constants
e693833 block: Attach bs->file only during .bdrv_open()

=== OUTPUT BEGIN ===
Checking PATCH 1/41: block: Attach bs->file only during .bdrv_open()...
Checking PATCH 2/41: block: Add op blocker permission constants...
Checking PATCH 3/41: block: Add Error argument to bdrv_attach_child()...
Checking PATCH 4/41: block: Let callers request permissions when attaching a child node...
Checking PATCH 5/41: tests: Use opened block node for block job tests...
Checking PATCH 6/41: block: Involve block drivers in permission granting...
ERROR: "foo* bar" should be "foo *bar"
#228: FILE: include/block/block_int.h:357:
+     void (*bdrv_child_perm)(BlockDriverState* bs, BdrvChild *c,

total: 1 errors, 0 warnings, 214 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking PATCH 7/41: block: Default .bdrv_child_perm() for filter drivers...
Checking PATCH 8/41: block: Request child permissions in filter drivers...
Checking PATCH 9/41: block: Default .bdrv_child_perm() for format drivers...
Checking PATCH 10/41: block: Request child permissions in format drivers...
Checking PATCH 11/41: vvfat: Implement .bdrv_child_perm()...
Checking PATCH 12/41: block: Require .bdrv_child_perm() with child nodes...
Checking PATCH 13/41: block: Request real permissions in bdrv_attach_child()...
Checking PATCH 14/41: block: Add permissions to BlockBackend...
Checking PATCH 15/41: block: Add permissions to blk_new()...
Checking PATCH 16/41: block: Add error parameter to blk_insert_bs()...
Checking PATCH 17/41: block: Request real permissions in blk_new_open()...
Checking PATCH 18/41: block: Allow error return in BlockDevOps.change_media_cb()...
Checking PATCH 19/41: hw/block: Request permissions...
Checking PATCH 20/41: hw/block: Introduce share-rw qdev property...
WARNING: line over 80 characters
#67: FILE: include/hw/block/block.h:57:
+    DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, ON_OFF_AUTO_AUTO), \

total: 0 errors, 1 warnings, 413 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 21/41: blockjob: Add permissions to block_job_create()...
Checking PATCH 22/41: block: Add BdrvChildRole.get_link_name()...
Checking PATCH 23/41: block: Include details on permission errors in message...
Checking PATCH 24/41: block: Add BdrvChildRole.stay_at_node...
Checking PATCH 25/41: blockjob: Add permissions to block_job_add_bdrv()...
Checking PATCH 26/41: block: Factor out bdrv_open_driver()...
Checking PATCH 27/41: block: Add bdrv_new_open_driver()...
Checking PATCH 28/41: commit: Use real permissions in commit block job...
Checking PATCH 29/41: commit: Use real permissions for HMP 'commit'...
Checking PATCH 30/41: backup: Use real permissions in backup block job...
Checking PATCH 31/41: block: Fix pending requests check in bdrv_append()...
Checking PATCH 32/41: block: BdrvChildRole.attach/detach() callbacks...
Checking PATCH 33/41: block: Allow backing file links in change_parent_backing_link()...
Checking PATCH 34/41: mirror: Use real permissions in mirror/active commit block job...
WARNING: line over 80 characters
#275: FILE: block/mirror.c:1124:
+            /* XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves

WARNING: line over 80 characters
#276: FILE: block/mirror.c:1125:
+             * at s->base. The other options would be a second filter driver above

total: 0 errors, 2 warnings, 311 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 35/41: stream: Use real permissions in streaming block job...
Checking PATCH 36/41: hmp: Request permissions in qemu-io...
Checking PATCH 37/41: migration/block: Use real permissions...
Checking PATCH 38/41: nbd/server: Use real permissions for NBD exports...
Checking PATCH 39/41: tests: Remove FIXME comments...
Checking PATCH 40/41: block: Pass BdrvChild to bdrv_aligned_preadv/pwritev...
Checking PATCH 41/41: block: Assertions for write permissions...
=== OUTPUT END ===

Test command exited with code: 1


---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@freelists.org

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting Kevin Wolf
@ 2017-02-14  5:51   ` Fam Zheng
  2017-02-14 10:36     ` Kevin Wolf
  0 siblings, 1 reply; 76+ messages in thread
From: Fam Zheng @ 2017-02-14  5:51 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, mreitz, jcody, qemu-devel

On Mon, 02/13 18:22, Kevin Wolf wrote:
> +int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
> +                            Error **errp)
> +{
> +    int ret;
> +
> +    ret = bdrv_child_check_perm(c, perm, shared, errp);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    bdrv_child_set_perm(c, perm, shared);

This has an issue of TOCTOU, which means image locking cannot fit in easily.
Maybe squash them into one callback (.bdrv_try_set_perm) that can return error?

> +
>      return 0;
>  }
>  
> @@ -1322,6 +1445,7 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
>              child->role->drained_end(child);
>          }
>          QLIST_REMOVE(child, next_parent);
> +        bdrv_update_perm(old_bs);
>      }
>  
>      child->bs = new_bs;
> @@ -1331,6 +1455,7 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
>          if (new_bs->quiesce_counter && child->role->drained_begin) {
>              child->role->drained_begin(child);
>          }
> +        bdrv_update_perm(new_bs);
>      }
>  }
>  
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index f36b064..8578e17 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -320,6 +320,45 @@ struct BlockDriver {
>      void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child,
>                             Error **errp);
>  
> +    /**
> +     * Checks whether the requested set of cumulative permissions in @perm
> +     * can be granted for accessing @bs and whether no other users are using
> +     * permissions other than those given in @shared (both arguments take
> +     * BLK_PERM_* bitmasks).
> +     *
> +     * If both conditions are met, 0 is returned. Otherwise, -errno is returned
> +     * and errp is set to an error describing the conflict.
> +     */
> +    int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm,
> +                           uint64_t shared, Error **errp);
> +
> +    /**
> +     * Called to inform the driver that the set of cumulative set of used
> +     * permissions for @bs has changed to @perm, and the set of sharable
> +     * permission to @shared. The driver can use this to propagate changes to
> +     * its children (i.e. request permissions only if a parent actually needs
> +     * them).
> +     *
> +     * If permissions are added to @perm or dropped from @shared, callers must
> +     * use bdrv_check_perm() first to ensure that this operation is valid.
> +     * Dropping from @perm or adding to @shared is always allowed without a
> +     * previous check.
> +     */
> +    void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared);
> +
> +    /**
> +     * Returns in @nperm and @nshared the permissions that the driver for @bs
> +     * needs on its child @c, based on the cumulative permissions requested by
> +     * the parents in @parent_perm and @parent_shared.
> +     *
> +     * If @c is NULL, return the permissions for attaching a new child for the
> +     * given @role.
> +     */
> +     void (*bdrv_child_perm)(BlockDriverState* bs, BdrvChild *c,
> +                             const BdrvChildRole *role,
> +                             uint64_t parent_perm, uint64_t parent_shared,
> +                             uint64_t *nperm, uint64_t *nshared);
> +
>      QLIST_ENTRY(BlockDriver) list;
>  };
>  
> @@ -832,6 +871,13 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
>                                    void *opaque, Error **errp);
>  void bdrv_root_unref_child(BdrvChild *child);
>  
> +int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
> +                          Error **errp);
> +void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
> +int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
> +                            Error **errp);
> +
> +
>  const char *bdrv_get_parent_name(const BlockDriverState *bs);
>  void blk_dev_change_media_cb(BlockBackend *blk, bool load);
>  bool blk_dev_has_removable_media(BlockBackend *blk);
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers Kevin Wolf
@ 2017-02-14  6:01   ` Fam Zheng
  2017-02-14 10:37     ` Kevin Wolf
  2017-02-15 17:11   ` Max Reitz
  1 sibling, 1 reply; 76+ messages in thread
From: Fam Zheng @ 2017-02-14  6:01 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, mreitz, jcody, qemu-devel

On Mon, 02/13 18:22, Kevin Wolf wrote:
> Almost all format drivers have the same characteristics as far as
> permissions are concerned: They have one or more children for storing
> their own data and, more importantly, metadata (can be written to and
> grow even without external write requests, must be protected against
> other writers and present consistent data) and optionally a backing file
> (this is just data, so like for a filter, it only depends on what the
> parent nodes need).
> 
> This provides a default implementation that can be shared by most of
> our format drivers.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                   | 37 +++++++++++++++++++++++++++++++++++++
>  include/block/block_int.h |  8 ++++++++
>  2 files changed, 45 insertions(+)
> 
> diff --git a/block.c b/block.c
> index 290768d..8e99bb5 100644
> --- a/block.c
> +++ b/block.c
> @@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
>                 (c->shared_perm & DEFAULT_PERM_UNCHANGED);
>  }
>  
> +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> +                               const BdrvChildRole *role,
> +                               uint64_t perm, uint64_t shared,
> +                               uint64_t *nperm, uint64_t *nshared)
> +{
> +    bool backing = (role == &child_backing);
> +    assert(role == &child_backing || role == &child_file);
> +
> +    if (!backing) {
> +        /* Apart from the modifications below, the same permissions are
> +         * forwarded and left alone as for filters */
> +        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
> +
> +        /* Format drivers may touch metadata even if the guest doesn't write */
> +        if (!bdrv_is_read_only(bs)) {
> +            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
> +        }
> +
> +        /* bs->file always needs to be consistent because of the metadata. We
> +         * can never allow other users to resize or write to it. */
> +        perm |= BLK_PERM_CONSISTENT_READ;
> +        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
> +    } else {
> +        /* We want consistent read from backing files if the parent needs it.
> +         * No other operations are performed on backing files. */
> +        perm &= BLK_PERM_CONSISTENT_READ;

Do you mean "perm &= ~BLK_PERM_CONSISTENT_READ"?

> +
> +        /* If the parent can deal with changing data, we're okay with a
> +         * writable backing file. */
> +        shared &= BLK_PERM_WRITE;
> +        shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
> +                  BLK_PERM_WRITE_UNCHANGED;
> +    }
> +
> +    *nperm = perm;
> +    *nshared = shared;
> +}
>  
>  static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
>  {
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index 2d74f92..46f51a6 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -885,6 +885,14 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
>                                 uint64_t perm, uint64_t shared,
>                                 uint64_t *nperm, uint64_t *nshared);
>  
> +/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
> + * (non-raw) image formats: Like above for bs->backing, but for bs->file it
> + * requires WRITE | RESIZE for read-write images, always requires
> + * CONSISTENT_READ and doesn't share WRITE. */
> +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> +                               const BdrvChildRole *role,
> +                               uint64_t perm, uint64_t shared,
> +                               uint64_t *nperm, uint64_t *nshared);
>  
>  const char *bdrv_get_parent_name(const BlockDriverState *bs);
>  void blk_dev_change_media_cb(BlockBackend *blk, bool load);
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs() Kevin Wolf
@ 2017-02-14  6:58   ` Fam Zheng
  2017-02-20 11:04   ` Max Reitz
  1 sibling, 0 replies; 76+ messages in thread
From: Fam Zheng @ 2017-02-14  6:58 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, mreitz, jcody, qemu-devel

On Mon, 02/13 18:22, Kevin Wolf wrote:
> Mow that blk_insert_bs() requests the BlockBackend permissions for the

Now? :)

> node it attaches to, it can fail. Instead of aborting, pass the errors
> to the callers.
> 

[snip]

> @@ -332,11 +348,17 @@ int bdrv_commit(BlockDriverState *bs)
>  
>      /* FIXME Use real permissions */
>      src = blk_new(0, BLK_PERM_ALL);
> -    blk_insert_bs(src, bs);
> -
> -    /* FIXME Use real permissions */
>      backing = blk_new(0, BLK_PERM_ALL);
> -    blk_insert_bs(backing, bs->backing->bs);
> +
> +    ret = blk_insert_bs(src, bs, NULL);
> +    if (ret < 0) {
> +        goto ro_cleanup;
> +    }
> +
> +    ret = blk_insert_bs(backing, bs->backing->bs, NULL);

Some code in this function uses local_err + error_report_err while this and some
others use NULL as errp. It's better to consistently use local_err, something
for another day.

> +    if (ret < 0) {
> +        goto ro_cleanup;
> +    }
>  
>      length = blk_getlength(src);
>      if (length < 0) {

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting
  2017-02-14  5:51   ` Fam Zheng
@ 2017-02-14 10:36     ` Kevin Wolf
  2017-02-14 11:23       ` Fam Zheng
  0 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-14 10:36 UTC (permalink / raw)
  To: Fam Zheng; +Cc: qemu-block, mreitz, jcody, qemu-devel

Am 14.02.2017 um 06:51 hat Fam Zheng geschrieben:
> On Mon, 02/13 18:22, Kevin Wolf wrote:
> > +int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
> > +                            Error **errp)
> > +{
> > +    int ret;
> > +
> > +    ret = bdrv_child_check_perm(c, perm, shared, errp);
> > +    if (ret < 0) {
> > +        return ret;
> > +    }
> > +
> > +    bdrv_child_set_perm(c, perm, shared);
> 
> This has an issue of TOCTOU, which means image locking cannot fit in easily.
> Maybe squash them into one callback (.bdrv_try_set_perm) that can return error?

That doesn't work, it would leave us with broken error handling. If one
driver in the middle of the update process fails to update the
permissions, we would end up with half of the nodes having the old
permissions and half having the new ones.

I think the file driver needs to lock the file already on check, and
then we need to add a callback for the failure case so that it gives up
the lock again. In other words, we might need a transaction with
prepare/commit/abort here (*sigh*). Hm, or maybe just prepare/abort
could be enough? Needs some thinking about.

Kevin

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-14  6:01   ` Fam Zheng
@ 2017-02-14 10:37     ` Kevin Wolf
  2017-02-14 11:13       ` Fam Zheng
  0 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-14 10:37 UTC (permalink / raw)
  To: Fam Zheng; +Cc: qemu-block, mreitz, jcody, qemu-devel

Am 14.02.2017 um 07:01 hat Fam Zheng geschrieben:
> On Mon, 02/13 18:22, Kevin Wolf wrote:
> > Almost all format drivers have the same characteristics as far as
> > permissions are concerned: They have one or more children for storing
> > their own data and, more importantly, metadata (can be written to and
> > grow even without external write requests, must be protected against
> > other writers and present consistent data) and optionally a backing file
> > (this is just data, so like for a filter, it only depends on what the
> > parent nodes need).
> > 
> > This provides a default implementation that can be shared by most of
> > our format drivers.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  block.c                   | 37 +++++++++++++++++++++++++++++++++++++
> >  include/block/block_int.h |  8 ++++++++
> >  2 files changed, 45 insertions(+)
> > 
> > diff --git a/block.c b/block.c
> > index 290768d..8e99bb5 100644
> > --- a/block.c
> > +++ b/block.c
> > @@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
> >                 (c->shared_perm & DEFAULT_PERM_UNCHANGED);
> >  }
> >  
> > +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> > +                               const BdrvChildRole *role,
> > +                               uint64_t perm, uint64_t shared,
> > +                               uint64_t *nperm, uint64_t *nshared)
> > +{
> > +    bool backing = (role == &child_backing);
> > +    assert(role == &child_backing || role == &child_file);
> > +
> > +    if (!backing) {
> > +        /* Apart from the modifications below, the same permissions are
> > +         * forwarded and left alone as for filters */
> > +        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
> > +
> > +        /* Format drivers may touch metadata even if the guest doesn't write */
> > +        if (!bdrv_is_read_only(bs)) {
> > +            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
> > +        }
> > +
> > +        /* bs->file always needs to be consistent because of the metadata. We
> > +         * can never allow other users to resize or write to it. */
> > +        perm |= BLK_PERM_CONSISTENT_READ;
> > +        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
> > +    } else {
> > +        /* We want consistent read from backing files if the parent needs it.
> > +         * No other operations are performed on backing files. */
> > +        perm &= BLK_PERM_CONSISTENT_READ;
> 
> Do you mean "perm &= ~BLK_PERM_CONSISTENT_READ"?

No. As the comment explains, we want to "forward" the CONSISTENT_READ
permission and clear everything else.

Kevin

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-14 10:37     ` Kevin Wolf
@ 2017-02-14 11:13       ` Fam Zheng
  0 siblings, 0 replies; 76+ messages in thread
From: Fam Zheng @ 2017-02-14 11:13 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: jcody, qemu-devel, qemu-block, mreitz

On Tue, 02/14 11:37, Kevin Wolf wrote:
> Am 14.02.2017 um 07:01 hat Fam Zheng geschrieben:
> > On Mon, 02/13 18:22, Kevin Wolf wrote:
> > > Almost all format drivers have the same characteristics as far as
> > > permissions are concerned: They have one or more children for storing
> > > their own data and, more importantly, metadata (can be written to and
> > > grow even without external write requests, must be protected against
> > > other writers and present consistent data) and optionally a backing file
> > > (this is just data, so like for a filter, it only depends on what the
> > > parent nodes need).
> > > 
> > > This provides a default implementation that can be shared by most of
> > > our format drivers.
> > > 
> > > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > > ---
> > >  block.c                   | 37 +++++++++++++++++++++++++++++++++++++
> > >  include/block/block_int.h |  8 ++++++++
> > >  2 files changed, 45 insertions(+)
> > > 
> > > diff --git a/block.c b/block.c
> > > index 290768d..8e99bb5 100644
> > > --- a/block.c
> > > +++ b/block.c
> > > @@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
> > >                 (c->shared_perm & DEFAULT_PERM_UNCHANGED);
> > >  }
> > >  
> > > +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> > > +                               const BdrvChildRole *role,
> > > +                               uint64_t perm, uint64_t shared,
> > > +                               uint64_t *nperm, uint64_t *nshared)
> > > +{
> > > +    bool backing = (role == &child_backing);
> > > +    assert(role == &child_backing || role == &child_file);
> > > +
> > > +    if (!backing) {
> > > +        /* Apart from the modifications below, the same permissions are
> > > +         * forwarded and left alone as for filters */
> > > +        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
> > > +
> > > +        /* Format drivers may touch metadata even if the guest doesn't write */
> > > +        if (!bdrv_is_read_only(bs)) {
> > > +            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
> > > +        }
> > > +
> > > +        /* bs->file always needs to be consistent because of the metadata. We
> > > +         * can never allow other users to resize or write to it. */
> > > +        perm |= BLK_PERM_CONSISTENT_READ;
> > > +        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
> > > +    } else {
> > > +        /* We want consistent read from backing files if the parent needs it.
> > > +         * No other operations are performed on backing files. */
> > > +        perm &= BLK_PERM_CONSISTENT_READ;
> > 
> > Do you mean "perm &= ~BLK_PERM_CONSISTENT_READ"?
> 
> No. As the comment explains, we want to "forward" the CONSISTENT_READ
> permission and clear everything else.

I see, my thinko.

Fam

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting
  2017-02-14 10:36     ` Kevin Wolf
@ 2017-02-14 11:23       ` Fam Zheng
  0 siblings, 0 replies; 76+ messages in thread
From: Fam Zheng @ 2017-02-14 11:23 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, mreitz, jcody, qemu-devel

On Tue, 02/14 11:36, Kevin Wolf wrote:
> Am 14.02.2017 um 06:51 hat Fam Zheng geschrieben:
> > On Mon, 02/13 18:22, Kevin Wolf wrote:
> > > +int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
> > > +                            Error **errp)
> > > +{
> > > +    int ret;
> > > +
> > > +    ret = bdrv_child_check_perm(c, perm, shared, errp);
> > > +    if (ret < 0) {
> > > +        return ret;
> > > +    }
> > > +
> > > +    bdrv_child_set_perm(c, perm, shared);
> > 
> > This has an issue of TOCTOU, which means image locking cannot fit in easily.
> > Maybe squash them into one callback (.bdrv_try_set_perm) that can return error?
> 
> That doesn't work, it would leave us with broken error handling. If one
> driver in the middle of the update process fails to update the
> permissions, we would end up with half of the nodes having the old
> permissions and half having the new ones.
> 
> I think the file driver needs to lock the file already on check, and
> then we need to add a callback for the failure case so that it gives up
> the lock again. In other words, we might need a transaction with
> prepare/commit/abort here (*sigh*). Hm, or maybe just prepare/abort
> could be enough? Needs some thinking about.

Can perm change be wedged into the reopen transaction? RO flag change there
already requires transactional lock mode update in file driver. I wonder if
the bdrv_reopen call sites can ever have (transactional) perm change
requirements as well..

Fam

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open() Kevin Wolf
@ 2017-02-15 14:34   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 14:34 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6243 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> The way that attaching bs->file worked was a bit unusual in that it was
> the only child that would be attached to a node which is not opened yet.
> Because of this, the block layer couldn't know yet which permissions the
> driver would eventually need.
> 
> This patch moves the point where bs->file is attached to the beginning
> of the individual .bdrv_open() implementations, so drivers already know
> what they are going to do with the child. This is also more consistent
> with how driver-specific children work.

Can't say I'm convinced by this because I personally do consider
bs->file to be special. It's fine and maybe even good if it's not like
driver-specific children (or even bs->backing).

But the fact that it doesn't work with a good op blocker model is more
than compelling.

> bdrv_open() still needs its own BdrvChild to perform image probing, but
> instead of directly assigning this BdrvChild to the BDS, it becomes a
> temporary one and the node name is passed as an option to the drivers,
> so that they can simply use bdrv_open_child() to create another
> reference for their own use.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                       | 34 +++++++++++++++++++++++-----------
>  block/bochs.c                 |  6 ++++++
>  block/cloop.c                 |  6 ++++++
>  block/crypto.c                |  6 ++++++
>  block/dmg.c                   |  6 ++++++
>  block/parallels.c             |  6 ++++++
>  block/qcow.c                  |  6 ++++++
>  block/qcow2.c                 | 18 +++++++++++++++---
>  block/qed.c                   | 18 +++++++++++++++---
>  block/raw-format.c            |  6 ++++++
>  block/replication.c           |  6 ++++++
>  block/vdi.c                   |  6 ++++++
>  block/vhdx.c                  |  6 ++++++
>  block/vmdk.c                  |  6 ++++++
>  block/vpc.c                   |  6 ++++++
>  tests/qemu-iotests/051.out    |  4 ++--
>  tests/qemu-iotests/051.pc.out |  4 ++--
>  17 files changed, 129 insertions(+), 21 deletions(-)
> 
> diff --git a/block.c b/block.c
> index 743c349..0618f4b 100644
> --- a/block.c
> +++ b/block.c
> @@ -1103,13 +1103,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
>          assert(!drv->bdrv_needs_filename || filename != NULL);
>          ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
>      } else {
> -        if (file == NULL) {
> -            error_setg(errp, "Can't use '%s' as a block driver for the "
> -                       "protocol level", drv->format_name);
> -            ret = -EINVAL;
> -            goto free_and_fail;
> -        }

Interesting. I like that this explicit check becomes unnecessary. I'm
not sure whether I like how the error messages changes, but I can't say
that I was a fan of this one to begin with.

> -        bs->file = file;
>          ret = drv->bdrv_open(bs, options, open_flags, &local_err);
>      }
>  
> @@ -1145,7 +1138,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
>      return 0;
>  
>  free_and_fail:
> -    bs->file = NULL;
>      g_free(bs->opaque);
>      bs->opaque = NULL;
>      bs->drv = NULL;
> @@ -1368,7 +1360,18 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
>      }
>  
>      if (child->bs->inherits_from == parent) {
> -        child->bs->inherits_from = NULL;
> +        BdrvChild *c;
> +
> +        /* Remove inherits_from only when the last reference between parent and
> +         * child->bs goes away. */
> +        QLIST_FOREACH(c, &parent->children, next) {
> +            if (c != child && c->bs == child->bs) {
> +                break;
> +            }
> +        }

That's pretty kaputt. I'm not sure if I like it.

(But I like the reason for this even less, see below.)

> +        if (c == NULL) {
> +            child->bs->inherits_from = NULL;
> +        }
>      }
>  
>      bdrv_root_unref_child(child);
> @@ -1789,13 +1792,19 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
>          qdict_del(options, "backing");
>      }
>  
> -    /* Open image file without format layer */
> +    /* Open image file without format layer. This BdrvChild is only used for
> +     * probing, the block drivers will do their own bdrv_open_child() for the
> +     * same BDS, which is why we put the node name back into options. */
>      if ((flags & BDRV_O_PROTOCOL) == 0) {
>          file = bdrv_open_child(filename, options, "file", bs,
>                                 &child_file, true, &local_err);
>          if (local_err) {
>              goto fail;
>          }
> +        if (file != NULL) {
> +            qdict_put(options, "file",
> +                      qstring_from_str(bdrv_get_node_name(file->bs)));
> +        }

Verrry interesting. I would say "Well, I guess it works" but I can't say
I'm glad with how it requires multiple BdrvChild pointing to the same
BDS attached to the same parent. Not only that, but both children have
the same BdrvChild.name which I don't like at all. In my opinion, that
name is supposed to be a unique ID and I don't find the fact that it's
just temporary a very good excuse. Neither do I find "Well, they have
the same ID and point to the same BDS, so it doesn't really matter" very
compelling. (Worsened by the fact that the block driver can just choose
to attach some other BDS as its file, then both "file" children no
longer point to the same BDS.)

Actually, I'd be happier if you just invoked bdrv_ref() and
bdrv_unref_child() (or just bdrv_detach_child(), I don't think NULLing
inherits_from is important here) right after bdrv_open_child().

We want @file for probing and probing only. It should be independent of
@bs now. The only reason I'm not proposing using bdrv_open_inherit()
directly is because using bdrv_open_child() saves us from having to
extract the file's options. But I don't see why @file should be
connected to @bs after this point (until the block driver potentially
decides to re-attach it as bs->file, but bdrv_open_child() will handle
all of that).

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 03/41] block: Add Error argument to bdrv_attach_child()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 03/41] block: Add Error argument to bdrv_attach_child() Kevin Wolf
@ 2017-02-15 14:48   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 14:48 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3803 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> It will have to return an error soon, so prepare the callers for it.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c               | 13 ++++++++++---
>  block/quorum.c        |  8 +++++++-
>  include/block/block.h |  3 ++-
>  3 files changed, 19 insertions(+), 5 deletions(-)
> 
> diff --git a/block.c b/block.c
> index 0618f4b..9895aaf 100644
> --- a/block.c
> +++ b/block.c
> @@ -1323,7 +1323,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
>  BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>                               BlockDriverState *child_bs,
>                               const char *child_name,
> -                             const BdrvChildRole *child_role)
> +                             const BdrvChildRole *child_role,
> +                             Error **errp)
>  {
>      BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role,
>                                                parent_bs);
> @@ -1424,7 +1425,9 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
>          bs->backing = NULL;
>          goto out;
>      }
> -    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
> +    /* FIXME Error handling */
> +    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
> +                                    &error_abort);

Not sure if this counts as "prepare the callers for it". ;-)

>      bs->open_flags &= ~BDRV_O_NO_BACKING;
>      pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
>      pstrcpy(bs->backing_format, sizeof(bs->backing_format),
> @@ -1594,7 +1597,11 @@ BdrvChild *bdrv_open_child(const char *filename,
>          goto done;
>      }
>  
> -    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
> +    c = bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
> +    if (!c) {
> +        bdrv_unref(bs);
> +        goto done;
> +    }
>  
>  done:
>      qdict_del(options, bdref_key);
> diff --git a/block/quorum.c b/block/quorum.c
> index 86e2072..45bf010 100644
> --- a/block/quorum.c
> +++ b/block/quorum.c
> @@ -1032,10 +1032,16 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
>  
>      /* We can safely add the child now */
>      bdrv_ref(child_bs);
> -    child = bdrv_attach_child(bs, child_bs, indexstr, &child_format);
> +
> +    child = bdrv_attach_child(bs, child_bs, indexstr, &child_format, errp);
> +    if (child == NULL) {
> +        bdrv_unref(child_bs);
> +        goto out;

Might make sense to decrement next_child_index here, but it's not necessary.

(That means: Patch is fine, I'm just nagging because I like to nag.)

Max

> +    }
>      s->children = g_renew(BdrvChild *, s->children, s->num_children + 1);
>      s->children[s->num_children++] = child;
>  
> +out:
>      bdrv_drained_end(bs);
>  }
>  
> diff --git a/include/block/block.h b/include/block/block.h
> index 4e81f20..93812df 100644
> --- a/include/block/block.h
> +++ b/include/block/block.h
> @@ -482,7 +482,8 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child);
>  BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>                               BlockDriverState *child_bs,
>                               const char *child_name,
> -                             const BdrvChildRole *child_role);
> +                             const BdrvChildRole *child_role,
> +                             Error **errp);
>  
>  bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp);
>  void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason);
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 07/41] block: Default .bdrv_child_perm() for filter drivers
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 07/41] block: Default .bdrv_child_perm() for filter drivers Kevin Wolf
@ 2017-02-15 17:00   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 17:00 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3359 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Most filters need permissions related to read and write for their
> children, but only if the node has a parent that wants to use the same
> operation on the filter. The same is true for resize.
> 
> This adds a default implementation that simply forwards all necessary
> permissions to all children of the node and leaves the other permissions
> unchanged.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                   | 24 ++++++++++++++++++++++++
>  include/block/block_int.h |  8 ++++++++
>  2 files changed, 32 insertions(+)
> 
> diff --git a/block.c b/block.c
> index c27cdce..290768d 100644
> --- a/block.c
> +++ b/block.c
> @@ -1436,6 +1436,30 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
>      return 0;
>  }
>  
> +#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
> +                                 | BLK_PERM_WRITE \
> +                                 | BLK_PERM_WRITE_UNCHANGED \
> +                                 | BLK_PERM_RESIZE)
> +#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
> +
> +void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
> +                               const BdrvChildRole *role,
> +                               uint64_t perm, uint64_t shared,
> +                               uint64_t *nperm, uint64_t *nshared)
> +{
> +    if (c == NULL) {
> +        *nperm = 0;
> +        *nshared = BLK_PERM_ALL;
> +        return;
> +    }
> +
> +    *nperm   = (perm & DEFAULT_PERM_PASSTHROUGH) |
> +               (c->perm & DEFAULT_PERM_UNCHANGED);
> +    *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) |
> +               (c->shared_perm & DEFAULT_PERM_UNCHANGED);

In my opinion, aligning both equal signs without aligning the ampersands
etc. actually hurts readability. I would not align the equal signs.

Just a stylistic pick, nothing else.

(btw, if it isn't clear: Any patch I'm not replying to looks good to me
-- apart from patch 6 where Fam has already raised the concern I have,
too. I don't give R-bs though because this is still an RFC O:-))

Max

> +}
> +
> +
>  static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
>  {
>      BlockDriverState *old_bs = child->bs;
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index 8578e17..2d74f92 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -877,6 +877,14 @@ void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
>  int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
>                              Error **errp);
>  
> +/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
> + * block filters: Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED and RESIZE to
> + * all children */
> +void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
> +                               const BdrvChildRole *role,
> +                               uint64_t perm, uint64_t shared,
> +                               uint64_t *nperm, uint64_t *nshared);
> +
>  
>  const char *bdrv_get_parent_name(const BlockDriverState *bs);
>  void blk_dev_change_media_cb(BlockBackend *blk, bool load);
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers Kevin Wolf
  2017-02-14  6:01   ` Fam Zheng
@ 2017-02-15 17:11   ` Max Reitz
  2017-02-15 17:29     ` Kevin Wolf
  1 sibling, 1 reply; 76+ messages in thread
From: Max Reitz @ 2017-02-15 17:11 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4089 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Almost all format drivers have the same characteristics as far as
> permissions are concerned: They have one or more children for storing
> their own data and, more importantly, metadata (can be written to and
> grow even without external write requests, must be protected against
> other writers and present consistent data) and optionally a backing file
> (this is just data, so like for a filter, it only depends on what the
> parent nodes need).
> 
> This provides a default implementation that can be shared by most of
> our format drivers.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                   | 37 +++++++++++++++++++++++++++++++++++++
>  include/block/block_int.h |  8 ++++++++
>  2 files changed, 45 insertions(+)
> 
> diff --git a/block.c b/block.c
> index 290768d..8e99bb5 100644
> --- a/block.c
> +++ b/block.c
> @@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
>                 (c->shared_perm & DEFAULT_PERM_UNCHANGED);
>  }
>  
> +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> +                               const BdrvChildRole *role,
> +                               uint64_t perm, uint64_t shared,
> +                               uint64_t *nperm, uint64_t *nshared)
> +{
> +    bool backing = (role == &child_backing);
> +    assert(role == &child_backing || role == &child_file);
> +
> +    if (!backing) {
> +        /* Apart from the modifications below, the same permissions are
> +         * forwarded and left alone as for filters */
> +        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
> +
> +        /* Format drivers may touch metadata even if the guest doesn't write */
> +        if (!bdrv_is_read_only(bs)) {
> +            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
> +        }
> +
> +        /* bs->file always needs to be consistent because of the metadata. We
> +         * can never allow other users to resize or write to it. */
> +        perm |= BLK_PERM_CONSISTENT_READ;
> +        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
> +    } else {
> +        /* We want consistent read from backing files if the parent needs it.
> +         * No other operations are performed on backing files. */
> +        perm &= BLK_PERM_CONSISTENT_READ;
> +
> +        /* If the parent can deal with changing data, we're okay with a
> +         * writable backing file. */

Are we OK with a resizable backing file, too? I'm not sure, actually.
Maybe we should just forbid it and hope nobody asks for it.

Max

> +        shared &= BLK_PERM_WRITE;
> +        shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
> +                  BLK_PERM_WRITE_UNCHANGED;
> +    }
> +
> +    *nperm = perm;
> +    *nshared = shared;
> +}
>  
>  static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
>  {
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index 2d74f92..46f51a6 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -885,6 +885,14 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
>                                 uint64_t perm, uint64_t shared,
>                                 uint64_t *nperm, uint64_t *nshared);
>  
> +/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
> + * (non-raw) image formats: Like above for bs->backing, but for bs->file it
> + * requires WRITE | RESIZE for read-write images, always requires
> + * CONSISTENT_READ and doesn't share WRITE. */
> +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> +                               const BdrvChildRole *role,
> +                               uint64_t perm, uint64_t shared,
> +                               uint64_t *nperm, uint64_t *nshared);
>  
>  const char *bdrv_get_parent_name(const BlockDriverState *bs);
>  void blk_dev_change_media_cb(BlockBackend *blk, bool load);
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 10/41] block: Request child permissions in format drivers
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 10/41] block: Request child permissions in " Kevin Wolf
@ 2017-02-15 17:26   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 17:26 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 7531 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> This makes use of the .bdrv_child_perm() implementation for formats that
> we just added. All format drivers expose the permissions they actually
> need nows, so that they can be set accordingly and updated when parents
> are attached or detached.
> 
> The only format not included here is raw, which was already converted
> with the other filter drivers.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/bochs.c     | 1 +

In theory it might make sense to have another category "read-only format
drivers". In practice those are the drivers we don't care much about,
soooo...

>  block/cloop.c     | 1 +
>  block/crypto.c    | 1 +

I'm not sure whether the crypto driver qualifies more as a format driver
than as a filter driver. I guess saying it's a format driver is a bit
more strict and it's better to be strict when it doubt.

Max

>  block/dmg.c       | 1 +
>  block/parallels.c | 1 +
>  block/qcow.c      | 1 +
>  block/qcow2.c     | 1 +
>  block/qed.c       | 1 +
>  block/vdi.c       | 1 +
>  block/vhdx.c      | 1 +
>  block/vmdk.c      | 1 +
>  block/vpc.c       | 1 +
>  12 files changed, 12 insertions(+)
> 
> diff --git a/block/bochs.c b/block/bochs.c
> index 7dd2ac4..516da56 100644
> --- a/block/bochs.c
> +++ b/block/bochs.c
> @@ -293,6 +293,7 @@ static BlockDriver bdrv_bochs = {
>      .instance_size	= sizeof(BDRVBochsState),
>      .bdrv_probe		= bochs_probe,
>      .bdrv_open		= bochs_open,
> +    .bdrv_child_perm     = bdrv_format_default_perms,
>      .bdrv_refresh_limits = bochs_refresh_limits,
>      .bdrv_co_preadv = bochs_co_preadv,
>      .bdrv_close		= bochs_close,
> diff --git a/block/cloop.c b/block/cloop.c
> index 877c9b0..a6c7b9d 100644
> --- a/block/cloop.c
> +++ b/block/cloop.c
> @@ -290,6 +290,7 @@ static BlockDriver bdrv_cloop = {
>      .instance_size  = sizeof(BDRVCloopState),
>      .bdrv_probe     = cloop_probe,
>      .bdrv_open      = cloop_open,
> +    .bdrv_child_perm     = bdrv_format_default_perms,
>      .bdrv_refresh_limits = cloop_refresh_limits,
>      .bdrv_co_preadv = cloop_co_preadv,
>      .bdrv_close     = cloop_close,
> diff --git a/block/crypto.c b/block/crypto.c
> index 200fd0b..ca46883 100644
> --- a/block/crypto.c
> +++ b/block/crypto.c
> @@ -628,6 +628,7 @@ BlockDriver bdrv_crypto_luks = {
>      .bdrv_probe         = block_crypto_probe_luks,
>      .bdrv_open          = block_crypto_open_luks,
>      .bdrv_close         = block_crypto_close,
> +    .bdrv_child_perm    = bdrv_format_default_perms,
>      .bdrv_create        = block_crypto_create_luks,
>      .bdrv_truncate      = block_crypto_truncate,
>      .create_opts        = &block_crypto_create_opts_luks,
> diff --git a/block/dmg.c b/block/dmg.c
> index 8e387cd..a7d25fc 100644
> --- a/block/dmg.c
> +++ b/block/dmg.c
> @@ -697,6 +697,7 @@ static BlockDriver bdrv_dmg = {
>      .bdrv_probe     = dmg_probe,
>      .bdrv_open      = dmg_open,
>      .bdrv_refresh_limits = dmg_refresh_limits,
> +    .bdrv_child_perm     = bdrv_format_default_perms,
>      .bdrv_co_preadv = dmg_co_preadv,
>      .bdrv_close     = dmg_close,
>  };
> diff --git a/block/parallels.c b/block/parallels.c
> index d3970e1..b79e7df 100644
> --- a/block/parallels.c
> +++ b/block/parallels.c
> @@ -762,6 +762,7 @@ static BlockDriver bdrv_parallels = {
>      .bdrv_probe		= parallels_probe,
>      .bdrv_open		= parallels_open,
>      .bdrv_close		= parallels_close,
> +    .bdrv_child_perm          = bdrv_format_default_perms,
>      .bdrv_co_get_block_status = parallels_co_get_block_status,
>      .bdrv_has_zero_init       = bdrv_has_zero_init_1,
>      .bdrv_co_flush_to_os      = parallels_co_flush_to_os,
> diff --git a/block/qcow.c b/block/qcow.c
> index a6dfe1a..3a95d4f 100644
> --- a/block/qcow.c
> +++ b/block/qcow.c
> @@ -1052,6 +1052,7 @@ static BlockDriver bdrv_qcow = {
>      .bdrv_probe		= qcow_probe,
>      .bdrv_open		= qcow_open,
>      .bdrv_close		= qcow_close,
> +    .bdrv_child_perm        = bdrv_format_default_perms,
>      .bdrv_reopen_prepare    = qcow_reopen_prepare,
>      .bdrv_create            = qcow_create,
>      .bdrv_has_zero_init     = bdrv_has_zero_init_1,
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 4684554..dac3fb8 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -3399,6 +3399,7 @@ BlockDriver bdrv_qcow2 = {
>      .bdrv_reopen_commit   = qcow2_reopen_commit,
>      .bdrv_reopen_abort    = qcow2_reopen_abort,
>      .bdrv_join_options    = qcow2_join_options,
> +    .bdrv_child_perm      = bdrv_format_default_perms,
>      .bdrv_create        = qcow2_create,
>      .bdrv_has_zero_init = bdrv_has_zero_init_1,
>      .bdrv_co_get_block_status = qcow2_co_get_block_status,
> diff --git a/block/qed.c b/block/qed.c
> index 1ea5114..eda9402 100644
> --- a/block/qed.c
> +++ b/block/qed.c
> @@ -1678,6 +1678,7 @@ static BlockDriver bdrv_qed = {
>      .bdrv_open                = bdrv_qed_open,
>      .bdrv_close               = bdrv_qed_close,
>      .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare,
> +    .bdrv_child_perm          = bdrv_format_default_perms,
>      .bdrv_create              = bdrv_qed_create,
>      .bdrv_has_zero_init       = bdrv_has_zero_init_1,
>      .bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
> diff --git a/block/vdi.c b/block/vdi.c
> index 18b4773..fd6e26d 100644
> --- a/block/vdi.c
> +++ b/block/vdi.c
> @@ -891,6 +891,7 @@ static BlockDriver bdrv_vdi = {
>      .bdrv_open = vdi_open,
>      .bdrv_close = vdi_close,
>      .bdrv_reopen_prepare = vdi_reopen_prepare,
> +    .bdrv_child_perm          = bdrv_format_default_perms,
>      .bdrv_create = vdi_create,
>      .bdrv_has_zero_init = bdrv_has_zero_init_1,
>      .bdrv_co_get_block_status = vdi_co_get_block_status,
> diff --git a/block/vhdx.c b/block/vhdx.c
> index 4dc2743..8c5ade2 100644
> --- a/block/vhdx.c
> +++ b/block/vhdx.c
> @@ -1983,6 +1983,7 @@ static BlockDriver bdrv_vhdx = {
>      .bdrv_open              = vhdx_open,
>      .bdrv_close             = vhdx_close,
>      .bdrv_reopen_prepare    = vhdx_reopen_prepare,
> +    .bdrv_child_perm        = bdrv_format_default_perms,
>      .bdrv_co_readv          = vhdx_co_readv,
>      .bdrv_co_writev         = vhdx_co_writev,
>      .bdrv_create            = vhdx_create,
> diff --git a/block/vmdk.c b/block/vmdk.c
> index 9d68ec5..f5e2fb5 100644
> --- a/block/vmdk.c
> +++ b/block/vmdk.c
> @@ -2359,6 +2359,7 @@ static BlockDriver bdrv_vmdk = {
>      .bdrv_open                    = vmdk_open,
>      .bdrv_check                   = vmdk_check,
>      .bdrv_reopen_prepare          = vmdk_reopen_prepare,
> +    .bdrv_child_perm              = bdrv_format_default_perms,
>      .bdrv_co_preadv               = vmdk_co_preadv,
>      .bdrv_co_pwritev              = vmdk_co_pwritev,
>      .bdrv_co_pwritev_compressed   = vmdk_co_pwritev_compressed,
> diff --git a/block/vpc.c b/block/vpc.c
> index d0df2a1..b9c9832 100644
> --- a/block/vpc.c
> +++ b/block/vpc.c
> @@ -1067,6 +1067,7 @@ static BlockDriver bdrv_vpc = {
>      .bdrv_open              = vpc_open,
>      .bdrv_close             = vpc_close,
>      .bdrv_reopen_prepare    = vpc_reopen_prepare,
> +    .bdrv_child_perm        = bdrv_format_default_perms,
>      .bdrv_create            = vpc_create,
>  
>      .bdrv_co_preadv             = vpc_co_preadv,
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-15 17:11   ` Max Reitz
@ 2017-02-15 17:29     ` Kevin Wolf
  2017-02-15 17:33       ` Max Reitz
  0 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-15 17:29 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4994 bytes --]

Am 15.02.2017 um 18:11 hat Max Reitz geschrieben:
> On 13.02.2017 18:22, Kevin Wolf wrote:
> > Almost all format drivers have the same characteristics as far as
> > permissions are concerned: They have one or more children for storing
> > their own data and, more importantly, metadata (can be written to and
> > grow even without external write requests, must be protected against
> > other writers and present consistent data) and optionally a backing file
> > (this is just data, so like for a filter, it only depends on what the
> > parent nodes need).
> > 
> > This provides a default implementation that can be shared by most of
> > our format drivers.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  block.c                   | 37 +++++++++++++++++++++++++++++++++++++
> >  include/block/block_int.h |  8 ++++++++
> >  2 files changed, 45 insertions(+)
> > 
> > diff --git a/block.c b/block.c
> > index 290768d..8e99bb5 100644
> > --- a/block.c
> > +++ b/block.c
> > @@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
> >                 (c->shared_perm & DEFAULT_PERM_UNCHANGED);
> >  }
> >  
> > +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> > +                               const BdrvChildRole *role,
> > +                               uint64_t perm, uint64_t shared,
> > +                               uint64_t *nperm, uint64_t *nshared)
> > +{
> > +    bool backing = (role == &child_backing);
> > +    assert(role == &child_backing || role == &child_file);
> > +
> > +    if (!backing) {
> > +        /* Apart from the modifications below, the same permissions are
> > +         * forwarded and left alone as for filters */
> > +        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
> > +
> > +        /* Format drivers may touch metadata even if the guest doesn't write */
> > +        if (!bdrv_is_read_only(bs)) {
> > +            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
> > +        }
> > +
> > +        /* bs->file always needs to be consistent because of the metadata. We
> > +         * can never allow other users to resize or write to it. */
> > +        perm |= BLK_PERM_CONSISTENT_READ;
> > +        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
> > +    } else {
> > +        /* We want consistent read from backing files if the parent needs it.
> > +         * No other operations are performed on backing files. */
> > +        perm &= BLK_PERM_CONSISTENT_READ;
> > +
> > +        /* If the parent can deal with changing data, we're okay with a
> > +         * writable backing file. */
> 
> Are we OK with a resizable backing file, too? I'm not sure, actually.
> Maybe we should just forbid it and hope nobody asks for it.

That's pretty much the same thought that I had, so unless I'm mistaken,
this is exactly what the patch implements.

I can't think of a reason why resizing a backing file would hurt as long
as the parent allows writes to the backing file (though we might have to
audit that no driver caches the backing file size), but then I can't
think of a reason either why anyone would want to do this.

If we ever find out that there is a good reason, we can still change it.

...

Hm, scratch that. I guess a commit block job with a smaller backing file
is enough... Good thing that I neglected to get the RESIZE permission
for s->base there. :-)

Kevin

> Max
> 
> > +        shared &= BLK_PERM_WRITE;
> > +        shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
> > +                  BLK_PERM_WRITE_UNCHANGED;
> > +    }
> > +
> > +    *nperm = perm;
> > +    *nshared = shared;
> > +}
> >  
> >  static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
> >  {
> > diff --git a/include/block/block_int.h b/include/block/block_int.h
> > index 2d74f92..46f51a6 100644
> > --- a/include/block/block_int.h
> > +++ b/include/block/block_int.h
> > @@ -885,6 +885,14 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
> >                                 uint64_t perm, uint64_t shared,
> >                                 uint64_t *nperm, uint64_t *nshared);
> >  
> > +/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
> > + * (non-raw) image formats: Like above for bs->backing, but for bs->file it
> > + * requires WRITE | RESIZE for read-write images, always requires
> > + * CONSISTENT_READ and doesn't share WRITE. */
> > +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
> > +                               const BdrvChildRole *role,
> > +                               uint64_t perm, uint64_t shared,
> > +                               uint64_t *nperm, uint64_t *nshared);
> >  
> >  const char *bdrv_get_parent_name(const BlockDriverState *bs);
> >  void blk_dev_change_media_cb(BlockBackend *blk, bool load);
> > 
> 
> 




[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm() Kevin Wolf
@ 2017-02-15 17:30   ` Max Reitz
  2017-02-15 17:42     ` Kevin Wolf
  0 siblings, 1 reply; 76+ messages in thread
From: Max Reitz @ 2017-02-15 17:30 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1754 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> vvfat is the last remaining driver that can have children, but doesn't
> implement .bdrv_child_perm() yet. The default handlers aren't suitable
> here, so let's implement a very simple driver-specific one that protects
> the internal child from being used by other users as good as our
> permissions permit.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/vvfat.c | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/block/vvfat.c b/block/vvfat.c
> index c6bf67e..7246432 100644
> --- a/block/vvfat.c
> +++ b/block/vvfat.c
> @@ -3052,6 +3052,18 @@ err:
>      return ret;
>  }
>  
> +static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
> +                             const BdrvChildRole *role,
> +                             uint64_t perm, uint64_t shared,
> +                             uint64_t *nperm, uint64_t *nshared)
> +{
> +    assert(role == &child_vvfat_qcow);
> +
> +    /* This is a private node, nobody should try to attach to it */
> +    *nperm = BLK_PERM_WRITE;
> +    *nshared = 0;

0 for shared is probably enough to ward every other access off, but
maybe we should still pro forma request consistent read access...?

Max

> +}
> +
>  static void vvfat_close(BlockDriverState *bs)
>  {
>      BDRVVVFATState *s = bs->opaque;
> @@ -3077,6 +3089,7 @@ static BlockDriver bdrv_vvfat = {
>      .bdrv_file_open         = vvfat_open,
>      .bdrv_refresh_limits    = vvfat_refresh_limits,
>      .bdrv_close             = vvfat_close,
> +    .bdrv_child_perm        = vvfat_child_perm,
>  
>      .bdrv_co_preadv         = vvfat_co_preadv,
>      .bdrv_co_pwritev        = vvfat_co_pwritev,
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers
  2017-02-15 17:29     ` Kevin Wolf
@ 2017-02-15 17:33       ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 17:33 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3654 bytes --]

On 15.02.2017 18:29, Kevin Wolf wrote:
> Am 15.02.2017 um 18:11 hat Max Reitz geschrieben:
>> On 13.02.2017 18:22, Kevin Wolf wrote:
>>> Almost all format drivers have the same characteristics as far as
>>> permissions are concerned: They have one or more children for storing
>>> their own data and, more importantly, metadata (can be written to and
>>> grow even without external write requests, must be protected against
>>> other writers and present consistent data) and optionally a backing file
>>> (this is just data, so like for a filter, it only depends on what the
>>> parent nodes need).
>>>
>>> This provides a default implementation that can be shared by most of
>>> our format drivers.
>>>
>>> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>>> ---
>>>  block.c                   | 37 +++++++++++++++++++++++++++++++++++++
>>>  include/block/block_int.h |  8 ++++++++
>>>  2 files changed, 45 insertions(+)
>>>
>>> diff --git a/block.c b/block.c
>>> index 290768d..8e99bb5 100644
>>> --- a/block.c
>>> +++ b/block.c
>>> @@ -1459,6 +1459,43 @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
>>>                 (c->shared_perm & DEFAULT_PERM_UNCHANGED);
>>>  }
>>>  
>>> +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
>>> +                               const BdrvChildRole *role,
>>> +                               uint64_t perm, uint64_t shared,
>>> +                               uint64_t *nperm, uint64_t *nshared)
>>> +{
>>> +    bool backing = (role == &child_backing);
>>> +    assert(role == &child_backing || role == &child_file);
>>> +
>>> +    if (!backing) {
>>> +        /* Apart from the modifications below, the same permissions are
>>> +         * forwarded and left alone as for filters */
>>> +        bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
>>> +
>>> +        /* Format drivers may touch metadata even if the guest doesn't write */
>>> +        if (!bdrv_is_read_only(bs)) {
>>> +            perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
>>> +        }
>>> +
>>> +        /* bs->file always needs to be consistent because of the metadata. We
>>> +         * can never allow other users to resize or write to it. */
>>> +        perm |= BLK_PERM_CONSISTENT_READ;
>>> +        shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
>>> +    } else {
>>> +        /* We want consistent read from backing files if the parent needs it.
>>> +         * No other operations are performed on backing files. */
>>> +        perm &= BLK_PERM_CONSISTENT_READ;
>>> +
>>> +        /* If the parent can deal with changing data, we're okay with a
>>> +         * writable backing file. */
>>
>> Are we OK with a resizable backing file, too? I'm not sure, actually.
>> Maybe we should just forbid it and hope nobody asks for it.
> 
> That's pretty much the same thought that I had, so unless I'm mistaken,
> this is exactly what the patch implements.

Yes, it is. I'm just dumping random thoughts. :-)

> I can't think of a reason why resizing a backing file would hurt as long
> as the parent allows writes to the backing file (though we might have to
> audit that no driver caches the backing file size), but then I can't
> think of a reason either why anyone would want to do this.
> 
> If we ever find out that there is a good reason, we can still change it.
> 
> ...
> 
> Hm, scratch that. I guess a commit block job with a smaller backing file
> is enough... Good thing that I neglected to get the RESIZE permission
> for s->base there. :-)

TIL commit resizes the base in that case.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm()
  2017-02-15 17:30   ` Max Reitz
@ 2017-02-15 17:42     ` Kevin Wolf
  0 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-15 17:42 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2120 bytes --]

Am 15.02.2017 um 18:30 hat Max Reitz geschrieben:
> On 13.02.2017 18:22, Kevin Wolf wrote:
> > vvfat is the last remaining driver that can have children, but doesn't
> > implement .bdrv_child_perm() yet. The default handlers aren't suitable
> > here, so let's implement a very simple driver-specific one that protects
> > the internal child from being used by other users as good as our
> > permissions permit.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  block/vvfat.c | 13 +++++++++++++
> >  1 file changed, 13 insertions(+)
> > 
> > diff --git a/block/vvfat.c b/block/vvfat.c
> > index c6bf67e..7246432 100644
> > --- a/block/vvfat.c
> > +++ b/block/vvfat.c
> > @@ -3052,6 +3052,18 @@ err:
> >      return ret;
> >  }
> >  
> > +static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
> > +                             const BdrvChildRole *role,
> > +                             uint64_t perm, uint64_t shared,
> > +                             uint64_t *nperm, uint64_t *nshared)
> > +{
> > +    assert(role == &child_vvfat_qcow);
> > +
> > +    /* This is a private node, nobody should try to attach to it */
> > +    *nperm = BLK_PERM_WRITE;
> > +    *nshared = 0;
> 
> 0 for shared is probably enough to ward every other access off, but
> maybe we should still pro forma request consistent read access...?

Makes sense, yes.

But you missed the real bug I hid there for you:

qemu-system-x86_64: block.c:1530: bdrv_check_update_perm: Assertion
`new_shared_perm & BLK_PERM_WRITE_UNCHANGED' failed.

Kevin

> Max
> 
> > +}
> > +
> >  static void vvfat_close(BlockDriverState *bs)
> >  {
> >      BDRVVVFATState *s = bs->opaque;
> > @@ -3077,6 +3089,7 @@ static BlockDriver bdrv_vvfat = {
> >      .bdrv_file_open         = vvfat_open,
> >      .bdrv_refresh_limits    = vvfat_refresh_limits,
> >      .bdrv_close             = vvfat_close,
> > +    .bdrv_child_perm        = vvfat_child_perm,
> >  
> >      .bdrv_co_preadv         = vvfat_co_preadv,
> >      .bdrv_co_pwritev        = vvfat_co_pwritev,
> > 
> 
> 




[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 13/41] block: Request real permissions in bdrv_attach_child()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 13/41] block: Request real permissions in bdrv_attach_child() Kevin Wolf
@ 2017-02-15 19:23   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 19:23 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2183 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Now that all block drivers with children tell us what permissions they
> need from each of their children, bdrv_attach_child() can use this
> information and make the right requirements while trying to attach new
> children.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c | 17 +++++++++++++++--
>  1 file changed, 15 insertions(+), 2 deletions(-)
> 
> diff --git a/block.c b/block.c
> index 682548a..fd06549 100644
> --- a/block.c
> +++ b/block.c
> @@ -1558,10 +1558,21 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
>                               Error **errp)
>  {
>      BdrvChild *child;
> +    uint64_t perm, shared_perm;
> +
> +    /* XXX This check is a workaround for bdrv_open_inherit(), which attaches
> +     * bs->file before bs is opened for format probing. The 'else' branch
> +     * really shouldn't exist. */
> +    if (parent_bs->drv) {
> +        parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
> +                                        0, BLK_PERM_ALL, &perm, &shared_perm);
> +    } else {
> +        perm = 0;
> +        shared_perm = BLK_PERM_ALL;
> +    }
>  
> -    /* FIXME Use real permissions */
>      child = bdrv_root_attach_child(child_bs, child_name, child_role,
> -                                   0, BLK_PERM_ALL, parent_bs, errp);
> +                                   perm, shared_perm, parent_bs, errp);
>      if (child == NULL) {
>          return NULL;
>      }
> @@ -2041,6 +2052,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
>       * probing, the block drivers will do their own bdrv_open_child() for the
>       * same BDS, which is why we put the node name back into options. */
>      if ((flags & BDRV_O_PROTOCOL) == 0) {
> +        /* FIXME Really shouldn't try to open a child for a still closed bs,
> +         * see bdrv_attach_child() */

That gives me hope for my remark on patch 1. :-)

Max

>          file = bdrv_open_child(filename, options, "file", bs,
>                                 &child_file, true, &local_err);
>          if (local_err) {
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 14/41] block: Add permissions to BlockBackend
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 14/41] block: Add permissions to BlockBackend Kevin Wolf
@ 2017-02-15 19:26   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-15 19:26 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 658 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> The BlockBackend can now store the permissions that its user requires.
> This is necessary because nodes can be ejected from or inserted into a
> BlockBackend and all of these operations must make sure that the user
> still gets what it requested initially.

"it"?

> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/block-backend.c          | 27 +++++++++++++++++++++++++++
>  include/sysemu/block-backend.h |  2 ++
>  2 files changed, 29 insertions(+)

(Writing my name under this single word is a bit weird. Thankfully, this
reply now consists of more than just that word.)

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 15/41] block: Add permissions to blk_new()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 15/41] block: Add permissions to blk_new() Kevin Wolf
@ 2017-02-20 10:29   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 10:29 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4314 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> We want every user to be specific about the permissions it needs, so
> we'll pass the initial permissions as parameters to blk_new(). A user
> only needs to call blk_set_perm() if it wants to change the permissions
> after the fact.
> 
> The permissions are stored in the BlockBackend and applied whenever a
> BlockDriverState should be attached in blk_insert_bs().
> 
> This does not include actually choosing the right set of permissions
> yet. Instead, the usual FIXME comment is added to each place and will be
> addressed in individual patches.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/backup.c                   |  3 ++-
>  block/block-backend.c            | 12 ++++++------
>  block/commit.c                   | 12 ++++++++----
>  block/mirror.c                   |  3 ++-
>  blockdev.c                       |  2 +-
>  blockjob.c                       |  3 ++-
>  hmp.c                            |  3 ++-
>  hw/block/fdc.c                   |  3 ++-
>  hw/core/qdev-properties-system.c |  3 ++-
>  hw/ide/qdev.c                    |  3 ++-
>  hw/scsi/scsi-disk.c              |  3 ++-
>  include/sysemu/block-backend.h   |  2 +-
>  migration/block.c                |  3 ++-
>  nbd/server.c                     |  3 ++-
>  tests/test-blockjob.c            |  3 ++-
>  tests/test-throttle.c            |  7 ++++---
>  16 files changed, 42 insertions(+), 26 deletions(-)

[...]

> diff --git a/block/block-backend.c b/block/block-backend.c
> index 8f0348d..a219f0b 100644
> --- a/block/block-backend.c
> +++ b/block/block-backend.c
> @@ -123,14 +123,14 @@ static const BdrvChildRole child_root = {
>   * Store an error through @errp on failure, unless it's null.
>   * Return the new BlockBackend on success, null on failure.
>   */
> -BlockBackend *blk_new(void)
> +BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)

I think it would be a good idea to document what these parameters do, at
least by pointing to the definition of the permission flags.

Also, this makes me think that the permission flags should not be in
block_int.h but in block.h. This function is available outside of the
core block layer.

>  {
>      BlockBackend *blk;
>  
>      blk = g_new0(BlockBackend, 1);
>      blk->refcnt = 1;
> -    blk->perm = 0;
> -    blk->shared_perm = BLK_PERM_ALL;
> +    blk->perm = perm;
> +    blk->shared_perm = shared_perm;
>      blk_set_enable_write_cache(blk, true);
>  
>      qemu_co_queue_init(&blk->public.throttled_reqs[0]);
> @@ -161,7 +161,7 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
>      BlockBackend *blk;
>      BlockDriverState *bs;
>  
> -    blk = blk_new();
> +    blk = blk_new(0, BLK_PERM_ALL);
>      bs = bdrv_open(filename, reference, options, flags, errp);
>      if (!bs) {
>          blk_unref(blk);
> @@ -505,9 +505,9 @@ void blk_remove_bs(BlockBackend *blk)
>  void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
>  {
>      bdrv_ref(bs);
> -    /* FIXME Use real permissions */
>      blk->root = bdrv_root_attach_child(bs, "root", &child_root,
> -                                       0, BLK_PERM_ALL, blk, &error_abort);
> +                                       blk->perm, blk->shared_perm, blk,
> +                                       &error_abort);

And the error_abort does not qualify as a FIXME?

>  
>      notifier_list_notify(&blk->insert_bs_notifiers, blk);
>      if (blk->public.throttle_state) {

[...]

> diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
> index 068c9e4..1dd1cfa 100644
> --- a/tests/test-blockjob.c
> +++ b/tests/test-blockjob.c
> @@ -53,7 +53,8 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
>   * BlockDriverState inserted. */
>  static BlockBackend *create_blk(const char *name)
>  {
> -    BlockBackend *blk = blk_new();
> +    /* FIXME Use real permissions */
> +    BlockBackend *blk = blk_new(0, BLK_PERM_ALL);
>      BlockDriverState *bs;
>  
>      bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);

Pretty much independent of this patch, but: blk_new_open() would
probably be the better choice then to call blk_new() + bdrv_open() +
blk_insert_bs().

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs() Kevin Wolf
  2017-02-14  6:58   ` Fam Zheng
@ 2017-02-20 11:04   ` Max Reitz
  2017-02-20 11:22     ` Kevin Wolf
  1 sibling, 1 reply; 76+ messages in thread
From: Max Reitz @ 2017-02-20 11:04 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1986 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Mow that blk_insert_bs() requests the BlockBackend permissions for the
> node it attaches to, it can fail. Instead of aborting, pass the errors
> to the callers.

So it does qualify as a FIXME, but just for a single patch. Good. :-)

> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/backup.c                   |  5 ++++-
>  block/block-backend.c            | 12 ++++++++----
>  block/commit.c                   | 38 ++++++++++++++++++++++++++++++--------
>  block/mirror.c                   | 15 ++++++++++++---
>  blockdev.c                       |  6 +++++-
>  blockjob.c                       |  7 ++++++-
>  hmp.c                            |  6 +++++-
>  hw/core/qdev-properties-system.c |  7 ++++++-
>  include/sysemu/block-backend.h   |  2 +-
>  migration/block.c                |  2 +-
>  nbd/server.c                     |  6 +++++-
>  tests/test-blockjob.c            |  2 +-
>  12 files changed, 84 insertions(+), 24 deletions(-)

[...]

> diff --git a/migration/block.c b/migration/block.c
> index 6b7ffd4..d259936 100644
> --- a/migration/block.c
> +++ b/migration/block.c
> @@ -446,7 +446,7 @@ static void init_blk_migration(QEMUFile *f)
>          BlockDriverState *bs = bmds_bs[i].bs;
>  
>          if (bmds) {
> -            blk_insert_bs(bmds->blk, bs);
> +            blk_insert_bs(bmds->blk, bs, &error_abort);

I don't think it's obvious why this is correct. I assume it is because
this was a legal configuration on the source, so it must be a legal
configuration on the destination.

But I'd certainly appreciate a comment to make that explicitly clear,
especially considering that it isn't obvious that blk_insert_bs() can
fail only because of op blockers and thus will always work if the
configuration is known to be legal.

Max

>  
>              alloc_aio_bitmap(bmds);
>              error_setg(&bmds->blocker, "block device is in use by migration");


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 17/41] block: Request real permissions in blk_new_open()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 17/41] block: Request real permissions in blk_new_open() Kevin Wolf
@ 2017-02-20 11:16   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 11:16 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1896 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> We can figure out the necessary permissions from the flags that the
> caller passed.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/block-backend.c | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/block/block-backend.c b/block/block-backend.c
> index 1f80854..e10a278 100644
> --- a/block/block-backend.c
> +++ b/block/block-backend.c
> @@ -160,6 +160,7 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
>  {
>      BlockBackend *blk;
>      BlockDriverState *bs;
> +    uint64_t perm;
>  
>      blk = blk_new(0, BLK_PERM_ALL);

What about this?

Max

>      bs = bdrv_open(filename, reference, options, flags, errp);
> @@ -168,9 +169,20 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
>          return NULL;
>      }
>  
> -    /* FIXME Use real permissions */
> +    /* blk_new_open() is mainly used in .bdrv_create implementations and the
> +     * tools where sharing isn't a concern because the BDS stays private, so we
> +     * just request permission according to the flags.
> +     *
> +     * The exceptions are xen_disk and blockdev_init(); in these cases, the
> +     * caller of blk_new_open() doesn't make use of the permissions, but they
> +     * shouldn't hurt either. We can still share everything here because the
> +     * guest devices will add their own blockers if they can't share. */
> +    perm = BLK_PERM_CONSISTENT_READ;
> +    if (flags & BDRV_O_RDWR) {
> +        perm |= BLK_PERM_WRITE;
> +    }
>      blk->root = bdrv_root_attach_child(bs, "root", &child_root,
> -                                       0, BLK_PERM_ALL, blk, &error_abort);
> +                                       perm, BLK_PERM_ALL, blk, &error_abort);
>  
>      return blk;
>  }
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs()
  2017-02-20 11:04   ` Max Reitz
@ 2017-02-20 11:22     ` Kevin Wolf
  2017-02-20 11:22       ` Max Reitz
  0 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-20 11:22 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2212 bytes --]

Am 20.02.2017 um 12:04 hat Max Reitz geschrieben:
> On 13.02.2017 18:22, Kevin Wolf wrote:
> > Mow that blk_insert_bs() requests the BlockBackend permissions for the
> > node it attaches to, it can fail. Instead of aborting, pass the errors
> > to the callers.
> 
> So it does qualify as a FIXME, but just for a single patch. Good. :-)
> 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  block/backup.c                   |  5 ++++-
> >  block/block-backend.c            | 12 ++++++++----
> >  block/commit.c                   | 38 ++++++++++++++++++++++++++++++--------
> >  block/mirror.c                   | 15 ++++++++++++---
> >  blockdev.c                       |  6 +++++-
> >  blockjob.c                       |  7 ++++++-
> >  hmp.c                            |  6 +++++-
> >  hw/core/qdev-properties-system.c |  7 ++++++-
> >  include/sysemu/block-backend.h   |  2 +-
> >  migration/block.c                |  2 +-
> >  nbd/server.c                     |  6 +++++-
> >  tests/test-blockjob.c            |  2 +-
> >  12 files changed, 84 insertions(+), 24 deletions(-)
> 
> [...]
> 
> > diff --git a/migration/block.c b/migration/block.c
> > index 6b7ffd4..d259936 100644
> > --- a/migration/block.c
> > +++ b/migration/block.c
> > @@ -446,7 +446,7 @@ static void init_blk_migration(QEMUFile *f)
> >          BlockDriverState *bs = bmds_bs[i].bs;
> >  
> >          if (bmds) {
> > -            blk_insert_bs(bmds->blk, bs);
> > +            blk_insert_bs(bmds->blk, bs, &error_abort);
> 
> I don't think it's obvious why this is correct. I assume it is because
> this was a legal configuration on the source, so it must be a legal
> configuration on the destination.
> 
> But I'd certainly appreciate a comment to make that explicitly clear,
> especially considering that it isn't obvious that blk_insert_bs() can
> fail only because of op blockers and thus will always work if the
> configuration is known to be legal.

Actually, it's just because the requested permissions for bmds->blk are
still 0, BLK_PERM_ALL here. Once the FIXME is addressed (patch 37) and
the real permissions are requested, we get some error handling here.

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs()
  2017-02-20 11:22     ` Kevin Wolf
@ 2017-02-20 11:22       ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 11:22 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2293 bytes --]

On 20.02.2017 12:22, Kevin Wolf wrote:
> Am 20.02.2017 um 12:04 hat Max Reitz geschrieben:
>> On 13.02.2017 18:22, Kevin Wolf wrote:
>>> Mow that blk_insert_bs() requests the BlockBackend permissions for the
>>> node it attaches to, it can fail. Instead of aborting, pass the errors
>>> to the callers.
>>
>> So it does qualify as a FIXME, but just for a single patch. Good. :-)
>>
>>> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>>> ---
>>>  block/backup.c                   |  5 ++++-
>>>  block/block-backend.c            | 12 ++++++++----
>>>  block/commit.c                   | 38 ++++++++++++++++++++++++++++++--------
>>>  block/mirror.c                   | 15 ++++++++++++---
>>>  blockdev.c                       |  6 +++++-
>>>  blockjob.c                       |  7 ++++++-
>>>  hmp.c                            |  6 +++++-
>>>  hw/core/qdev-properties-system.c |  7 ++++++-
>>>  include/sysemu/block-backend.h   |  2 +-
>>>  migration/block.c                |  2 +-
>>>  nbd/server.c                     |  6 +++++-
>>>  tests/test-blockjob.c            |  2 +-
>>>  12 files changed, 84 insertions(+), 24 deletions(-)
>>
>> [...]
>>
>>> diff --git a/migration/block.c b/migration/block.c
>>> index 6b7ffd4..d259936 100644
>>> --- a/migration/block.c
>>> +++ b/migration/block.c
>>> @@ -446,7 +446,7 @@ static void init_blk_migration(QEMUFile *f)
>>>          BlockDriverState *bs = bmds_bs[i].bs;
>>>  
>>>          if (bmds) {
>>> -            blk_insert_bs(bmds->blk, bs);
>>> +            blk_insert_bs(bmds->blk, bs, &error_abort);
>>
>> I don't think it's obvious why this is correct. I assume it is because
>> this was a legal configuration on the source, so it must be a legal
>> configuration on the destination.
>>
>> But I'd certainly appreciate a comment to make that explicitly clear,
>> especially considering that it isn't obvious that blk_insert_bs() can
>> fail only because of op blockers and thus will always work if the
>> configuration is known to be legal.
> 
> Actually, it's just because the requested permissions for bmds->blk are
> still 0, BLK_PERM_ALL here. Once the FIXME is addressed (patch 37) and
> the real permissions are requested, we get some error handling here.

OK, good, thanks.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 18/41] block: Allow error return in BlockDevOps.change_media_cb()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 18/41] block: Allow error return in BlockDevOps.change_media_cb() Kevin Wolf
@ 2017-02-20 11:31   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 11:31 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2062 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Some devices allow a media change between read-only and read-write
> media. They need to adapt the permissions in their .change_media_cb()
> implementation, which can fail. So add an Error parameter to the
> function.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/block-backend.c          | 12 +++++++++---
>  blockdev.c                     | 19 +++++++++++++++----
>  hw/block/fdc.c                 |  2 +-
>  hw/ide/core.c                  |  2 +-
>  hw/scsi/scsi-disk.c            |  2 +-
>  hw/sd/sd.c                     |  2 +-
>  include/block/block_int.h      |  2 +-
>  include/sysemu/block-backend.h |  2 +-
>  8 files changed, 30 insertions(+), 13 deletions(-)
> 
> diff --git a/block/block-backend.c b/block/block-backend.c
> index e10a278..0c23add 100644
> --- a/block/block-backend.c
> +++ b/block/block-backend.c
> @@ -671,15 +671,21 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
>   * Else, notify of media eject.
>   * Also send DEVICE_TRAY_MOVED events as appropriate.
>   */
> -void blk_dev_change_media_cb(BlockBackend *blk, bool load)
> +void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)

May deserve a comment that this function never fails with
load == false. The assertion is not too hidden, but it's not extremely
visible either.

Max

>  {
>      if (blk->dev_ops && blk->dev_ops->change_media_cb) {
>          bool tray_was_open, tray_is_open;
> +        Error *local_err = NULL;
>  
>          assert(!blk->legacy_dev);
>  
>          tray_was_open = blk_dev_is_tray_open(blk);
> -        blk->dev_ops->change_media_cb(blk->dev_opaque, load);
> +        blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
> +        if (local_err) {
> +            assert(load == true);
> +            error_propagate(errp, local_err);
> +            return;
> +        }
>          tray_is_open = blk_dev_is_tray_open(blk);
>  
>          if (tray_was_open != tray_is_open) {


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions Kevin Wolf
@ 2017-02-20 12:25   ` Max Reitz
  2017-02-20 13:02     ` Kevin Wolf
  0 siblings, 1 reply; 76+ messages in thread
From: Max Reitz @ 2017-02-20 12:25 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2523 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> This makes all device emulations with a qdev drive property request
> permissions on their BlockBackend. We don't block anything yet.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  hw/block/block.c                 | 19 ++++++++++++++++++-
>  hw/block/fdc.c                   | 25 +++++++++++++++++++++++--
>  hw/block/m25p80.c                |  8 ++++++++
>  hw/block/nand.c                  |  7 +++++++
>  hw/block/nvme.c                  |  8 +++++++-
>  hw/block/onenand.c               |  7 +++++++
>  hw/block/pflash_cfi01.c          | 18 ++++++++++++------
>  hw/block/pflash_cfi02.c          | 19 +++++++++++++------
>  hw/block/virtio-blk.c            |  8 +++++++-
>  hw/core/qdev-properties-system.c |  1 -
>  hw/ide/qdev.c                    |  7 +++++--
>  hw/nvram/spapr_nvram.c           |  8 ++++++++
>  hw/scsi/scsi-disk.c              |  8 ++++++--

Since I have no idea how scsi-generic and all of that works, just an
innocent question: Do we need to set permissions there, too?

>  hw/sd/sd.c                       |  6 ++++++
>  hw/usb/dev-storage.c             |  6 +++++-
>  include/hw/block/block.h         |  3 ++-
>  tests/qemu-iotests/051.pc.out    |  6 +++---
>  17 files changed, 137 insertions(+), 27 deletions(-)

[...]

> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index 2d6eb46..190573c 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -1215,6 +1215,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp)
>  {
>      Flash *s = M25P80(ss);
>      M25P80Class *mc = M25P80_GET_CLASS(s);
> +    int ret;
>  
>      s->pi = mc->pi;
>  
> @@ -1222,6 +1223,13 @@ static void m25p80_realize(SSISlave *ss, Error **errp)
>      s->dirty_page = -1;
>  
>      if (s->blk) {
> +        uint64_t perm = BLK_PERM_CONSISTENT_READ |
> +                        (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
> +        ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);

Not sure whether it should be changed in this patch, but I don't know
whether BLK_PERM_ALL is right here; and from a quick glance it doesn't
look like any of the following patches change it.

(Same for all of the block device emulation code that invokes
blk_set_perm() directly.)

Max

> +        if (ret < 0) {
> +            return;
> +        }
> +
>          DB_PRINT_L(0, "Binding to IF_MTD drive\n");
>          s->storage = blk_blockalign(s->blk, s->size);
>  

[...]


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property Kevin Wolf
@ 2017-02-20 12:28   ` Max Reitz
  2017-02-20 13:05     ` Kevin Wolf
  0 siblings, 1 reply; 76+ messages in thread
From: Max Reitz @ 2017-02-20 12:28 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1990 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> By default, don't allow another writer for block devices that are
> attached to a guest device. For the cases where this setup is intended
> (e.g. using a cluster filesystem on the disk), the new option can be
> used to allow it.
> 
> This change affects only devices using DEFINE_BLOCK_PROPERTIES().
> Devices directly using DEFINE_PROP_DRIVE() still accept writers
> unconditionally.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  hw/block/block.c           | 14 ++++++------
>  include/hw/block/block.h   |  4 +++-
>  tests/qemu-iotests/172.out | 53 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 64 insertions(+), 7 deletions(-)
> 
> diff --git a/hw/block/block.c b/hw/block/block.c
> index c3d3901..3c218eb 100644
> --- a/hw/block/block.c
> +++ b/hw/block/block.c
> @@ -56,7 +56,7 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
>  {
>      BlockBackend *blk = conf->blk;
>      BlockdevOnError rerror, werror;
> -    uint64_t perm;
> +    uint64_t perm, shared_perm;
>      bool wce;
>      int ret;
>  
> @@ -65,11 +65,13 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
>          perm |= BLK_PERM_WRITE;
>      }
>  
> -    /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
> -    ret = blk_set_perm(blk, perm,
> -                       BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
> -                       BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE | BLK_PERM_WRITE,
> -                       errp);
> +    shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
> +                  BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE;

I'm not so sure BLK_PERM_RESIZE belongs here.

Max

> +    if (conf->share_rw) {
> +        shared_perm |= BLK_PERM_WRITE;
> +    }
> +
> +    ret = blk_set_perm(blk, perm, shared_perm, errp);
>      if (ret < 0) {
>          return;
>      }

[...]



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 22/41] block: Add BdrvChildRole.get_link_name()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 22/41] block: Add BdrvChildRole.get_link_name() Kevin Wolf
@ 2017-02-20 12:54   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 12:54 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3312 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> For meaningful error messages in the permission system, we want to allow
> the parent of a BdrvChild to generate some kind of human-readable
> identifier for the link represented by the BdrvChild.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c                   |  8 ++++++++
>  block/block-backend.c     | 20 ++++++++++++++++++++
>  include/block/block_int.h |  4 ++++
>  3 files changed, 32 insertions(+)
> 
> diff --git a/block.c b/block.c
> index fd06549..2116542 100644
> --- a/block.c
> +++ b/block.c
> @@ -800,6 +800,13 @@ const BdrvChildRole child_format = {
>      .drained_end     = bdrv_child_cb_drained_end,
>  };
>  
> +static char *bdrv_backing_link_name(BdrvChild *c)
> +{
> +    BlockDriverState *parent = c->opaque;
> +    return g_strdup_printf("backing file link from %s",
> +                           bdrv_get_device_or_node_name(parent));

So in this case it's really about the edge in the BDS graph.

> +}
> +
>  /*
>   * Returns the options and flags that bs->backing should get, based on the
>   * given options and flags for the parent BDS
> @@ -825,6 +832,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
>  }
>  
>  static const BdrvChildRole child_backing = {
> +    .get_link_name   = bdrv_backing_link_name,
>      .inherit_options = bdrv_backing_options,
>      .drained_begin   = bdrv_child_cb_drained_begin,
>      .drained_end     = bdrv_child_cb_drained_end,
> diff --git a/block/block-backend.c b/block/block-backend.c
> index 0c23add..a314284 100644
> --- a/block/block-backend.c
> +++ b/block/block-backend.c
> @@ -80,6 +80,7 @@ static const AIOCBInfo block_backend_aiocb_info = {
>  
>  static void drive_info_del(DriveInfo *dinfo);
>  static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
> +static char *blk_get_attached_dev_id(BlockBackend *blk);
>  
>  /* All BlockBackends */
>  static QTAILQ_HEAD(, BlockBackend) block_backends =
> @@ -102,6 +103,24 @@ static void blk_root_drained_end(BdrvChild *child);
>  static void blk_root_change_media(BdrvChild *child, bool load);
>  static void blk_root_resize(BdrvChild *child);
>  
> +static char *blk_root_get_link_name(BdrvChild *child)
> +{
> +    BlockBackend *blk = child->opaque;
> +    char *dev_id;
> +
> +    if (blk->name) {
> +        return g_strdup(blk->name);

This is considering the BB to be an edge in the BDS graph. I find that a
bit weird, and the fact that it makes complete and total sense after
thinking for some time doesn't change the fact that it is weird.

> +    }
> +
> +    dev_id = blk_get_attached_dev_id(blk);
> +    if (*dev_id) {
> +        return dev_id;

But the device model OTOH is definitely not an edge in the BDS graph.

> +    } else {
> +        g_free(dev_id);
> +        return g_strdup("a block device");
> +    }
> +}
> +
>  static const char *blk_root_get_name(BdrvChild *child)
>  {
>      return blk_name(child->opaque);

[...]

Also, I'm curious what you are going to choose as a replacement if the
function pointer is not set. I have a couple of ideas, but most of them
are either not correct or would remove the need for
bdrv_backing_link_name()... Well, I'm curious. :-)

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions
  2017-02-20 12:25   ` Max Reitz
@ 2017-02-20 13:02     ` Kevin Wolf
  0 siblings, 0 replies; 76+ messages in thread
From: Kevin Wolf @ 2017-02-20 13:02 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3149 bytes --]

Am 20.02.2017 um 13:25 hat Max Reitz geschrieben:
> On 13.02.2017 18:22, Kevin Wolf wrote:
> > This makes all device emulations with a qdev drive property request
> > permissions on their BlockBackend. We don't block anything yet.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  hw/block/block.c                 | 19 ++++++++++++++++++-
> >  hw/block/fdc.c                   | 25 +++++++++++++++++++++++--
> >  hw/block/m25p80.c                |  8 ++++++++
> >  hw/block/nand.c                  |  7 +++++++
> >  hw/block/nvme.c                  |  8 +++++++-
> >  hw/block/onenand.c               |  7 +++++++
> >  hw/block/pflash_cfi01.c          | 18 ++++++++++++------
> >  hw/block/pflash_cfi02.c          | 19 +++++++++++++------
> >  hw/block/virtio-blk.c            |  8 +++++++-
> >  hw/core/qdev-properties-system.c |  1 -
> >  hw/ide/qdev.c                    |  7 +++++--
> >  hw/nvram/spapr_nvram.c           |  8 ++++++++
> >  hw/scsi/scsi-disk.c              |  8 ++++++--
> 
> Since I have no idea how scsi-generic and all of that works, just an
> innocent question: Do we need to set permissions there, too?

I couldn't see any use for it. With an SG BDS, you can't really do
anything anyway except directly attach it to scsi-generic. And the only
thing that scsi-generic does is invoking ioctls, which the block layer
doesn't understand but just pass though.

So I didn't feel that op blockers could provide anything here.

> >  hw/sd/sd.c                       |  6 ++++++
> >  hw/usb/dev-storage.c             |  6 +++++-
> >  include/hw/block/block.h         |  3 ++-
> >  tests/qemu-iotests/051.pc.out    |  6 +++---
> >  17 files changed, 137 insertions(+), 27 deletions(-)
> 
> [...]
> 
> > diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> > index 2d6eb46..190573c 100644
> > --- a/hw/block/m25p80.c
> > +++ b/hw/block/m25p80.c
> > @@ -1215,6 +1215,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp)
> >  {
> >      Flash *s = M25P80(ss);
> >      M25P80Class *mc = M25P80_GET_CLASS(s);
> > +    int ret;
> >  
> >      s->pi = mc->pi;
> >  
> > @@ -1222,6 +1223,13 @@ static void m25p80_realize(SSISlave *ss, Error **errp)
> >      s->dirty_page = -1;
> >  
> >      if (s->blk) {
> > +        uint64_t perm = BLK_PERM_CONSISTENT_READ |
> > +                        (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
> > +        ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
> 
> Not sure whether it should be changed in this patch, but I don't know
> whether BLK_PERM_ALL is right here; and from a quick glance it doesn't
> look like any of the following patches change it.
> 
> (Same for all of the block device emulation code that invokes
> blk_set_perm() directly.)

Yeah, I'm not completely sure about the real requirements here either.
What I do know is that currently nothing is blocked (so doing the same
in the future won't make things worse at least), and that I don't want
to break exotic devices that I can't really test. So for these devices I
tended to be more permissive in case of doubt.

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property
  2017-02-20 12:28   ` Max Reitz
@ 2017-02-20 13:05     ` Kevin Wolf
  2017-02-20 13:17       ` Max Reitz
  0 siblings, 1 reply; 76+ messages in thread
From: Kevin Wolf @ 2017-02-20 13:05 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2154 bytes --]

Am 20.02.2017 um 13:28 hat Max Reitz geschrieben:
> On 13.02.2017 18:22, Kevin Wolf wrote:
> > By default, don't allow another writer for block devices that are
> > attached to a guest device. For the cases where this setup is intended
> > (e.g. using a cluster filesystem on the disk), the new option can be
> > used to allow it.
> > 
> > This change affects only devices using DEFINE_BLOCK_PROPERTIES().
> > Devices directly using DEFINE_PROP_DRIVE() still accept writers
> > unconditionally.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  hw/block/block.c           | 14 ++++++------
> >  include/hw/block/block.h   |  4 +++-
> >  tests/qemu-iotests/172.out | 53 ++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 64 insertions(+), 7 deletions(-)
> > 
> > diff --git a/hw/block/block.c b/hw/block/block.c
> > index c3d3901..3c218eb 100644
> > --- a/hw/block/block.c
> > +++ b/hw/block/block.c
> > @@ -56,7 +56,7 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
> >  {
> >      BlockBackend *blk = conf->blk;
> >      BlockdevOnError rerror, werror;
> > -    uint64_t perm;
> > +    uint64_t perm, shared_perm;
> >      bool wce;
> >      int ret;
> >  
> > @@ -65,11 +65,13 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
> >          perm |= BLK_PERM_WRITE;
> >      }
> >  
> > -    /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
> > -    ret = blk_set_perm(blk, perm,
> > -                       BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
> > -                       BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE | BLK_PERM_WRITE,
> > -                       errp);
> > +    shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
> > +                  BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE;
> 
> I'm not so sure BLK_PERM_RESIZE belongs here.

Where does it belong in your opinion?

An option that I considered was adding BLK_PERM_RESIZE in
blk_set_dev_ops() if op.resize_cb != NULL, but it felt a bit too
implicit to me.

Or I guess we can just add another bool parameter?

Kevin

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 23/41] block: Include details on permission errors in message
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 23/41] block: Include details on permission errors in message Kevin Wolf
@ 2017-02-20 13:16   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 13:16 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4376 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Instead of just telling that there was some conflict, we can be specific
> and tell which permissions were in conflict and which way the conflict
> is.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 55 insertions(+), 11 deletions(-)
> 
> diff --git a/block.c b/block.c
> index 2116542..d743f50 100644
> --- a/block.c
> +++ b/block.c
> @@ -1381,6 +1381,43 @@ static void bdrv_update_perm(BlockDriverState *bs)
>      bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
>  }
>  
> +static char *bdrv_child_link_name(BdrvChild *c)
> +{
> +    if (c->role->get_link_name) {
> +        return c->role->get_link_name(c);
> +    }
> +
> +    return g_strdup("another user");

...now I'm a bit disappointed, not least because "another user" is not a
link name.

But I don't think we want a link name anyway. I'd write it differently
altogether. Looking at the code below, this will be used as "Conflicts
with ${link_name}".

Now, "Conflicts with backing file link from foo" reads weird. In fact,
anything that actually reports a "link" is weird. I'd propose the following:

"Conflicts with [use by] ${user} (used as ${c->name})"

user is c->role->get_name() if that exists, or "another user" if it does
not. If you implemented get_name() instead of get_link_name() for the
backing file role, there wouldn't be any need for get_link_name() at
all, IMO.

Also, you can implement c->role->get_name() not only for child_backing,
but also for child_file and child_format.

Max

> +}
> +
> +static char *bdrv_perm_names(uint64_t perm)
> +{
> +    struct perm_name {
> +        uint64_t perm;
> +        const char *name;
> +    } permissions[] = {
> +        { BLK_PERM_CONSISTENT_READ, "consistent read" },
> +        { BLK_PERM_WRITE,           "write" },
> +        { BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
> +        { BLK_PERM_RESIZE,          "resize" },
> +        { BLK_PERM_GRAPH_MOD,       "change children" },
> +        { 0, NULL }
> +    };
> +
> +    char *result = g_strdup("");
> +    struct perm_name *p;
> +
> +    for (p = permissions; p->name; p++) {
> +        if (perm & p->perm) {
> +            char *old = result;
> +            result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name);
> +            g_free(old);
> +        }
> +    }
> +
> +    return result;
> +}
> +
>  static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
>                                    uint64_t new_shared_perm,
>                                    BdrvChild *ignore_child, Error **errp)
> @@ -1397,17 +1434,24 @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
>              continue;
>          }
>  
> -        if ((new_used_perm & c->shared_perm) != new_used_perm ||
> -            (c->perm & new_shared_perm) != c->perm)
> -        {
> -            const char *user = NULL;
> -            if (c->role->get_name) {
> -                user = c->role->get_name(c);
> -                if (user && !*user) {
> -                    user = NULL;
> -                }
> -            }
> -            error_setg(errp, "Conflicts with %s", user ?: "another operation");
> +        if ((new_used_perm & c->shared_perm) != new_used_perm) {
> +            char *link = bdrv_child_link_name(c);
> +            char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
> +            error_setg(errp, "Conflicts with %s, which does not allow '%s' "
> +                             "on %s",
> +                       link, perm_names, bdrv_get_node_name(c->bs));
> +            g_free(link);
> +            g_free(perm_names);
> +            return -EPERM;
> +        }
> +
> +        if ((c->perm & new_shared_perm) != c->perm) {
> +            char *link = bdrv_child_link_name(c);
> +            char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
> +            error_setg(errp, "Conflicts with %s, which uses '%s' on %s",
> +                       link, perm_names, bdrv_get_node_name(c->bs));
> +            g_free(link);
> +            g_free(perm_names);
>              return -EPERM;
>          }
>  
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property
  2017-02-20 13:05     ` Kevin Wolf
@ 2017-02-20 13:17       ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 13:17 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-block, jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2362 bytes --]

On 20.02.2017 14:05, Kevin Wolf wrote:
> Am 20.02.2017 um 13:28 hat Max Reitz geschrieben:
>> On 13.02.2017 18:22, Kevin Wolf wrote:
>>> By default, don't allow another writer for block devices that are
>>> attached to a guest device. For the cases where this setup is intended
>>> (e.g. using a cluster filesystem on the disk), the new option can be
>>> used to allow it.
>>>
>>> This change affects only devices using DEFINE_BLOCK_PROPERTIES().
>>> Devices directly using DEFINE_PROP_DRIVE() still accept writers
>>> unconditionally.
>>>
>>> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>>> ---
>>>  hw/block/block.c           | 14 ++++++------
>>>  include/hw/block/block.h   |  4 +++-
>>>  tests/qemu-iotests/172.out | 53 ++++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 64 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/hw/block/block.c b/hw/block/block.c
>>> index c3d3901..3c218eb 100644
>>> --- a/hw/block/block.c
>>> +++ b/hw/block/block.c
>>> @@ -56,7 +56,7 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
>>>  {
>>>      BlockBackend *blk = conf->blk;
>>>      BlockdevOnError rerror, werror;
>>> -    uint64_t perm;
>>> +    uint64_t perm, shared_perm;
>>>      bool wce;
>>>      int ret;
>>>  
>>> @@ -65,11 +65,13 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
>>>          perm |= BLK_PERM_WRITE;
>>>      }
>>>  
>>> -    /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
>>> -    ret = blk_set_perm(blk, perm,
>>> -                       BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
>>> -                       BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE | BLK_PERM_WRITE,
>>> -                       errp);
>>> +    shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
>>> +                  BLK_PERM_GRAPH_MOD | BLK_PERM_RESIZE;
>>
>> I'm not so sure BLK_PERM_RESIZE belongs here.
> 
> Where does it belong in your opinion?

Not unconditionally here, probably.

> An option that I considered was adding BLK_PERM_RESIZE in
> blk_set_dev_ops() if op.resize_cb != NULL, but it felt a bit too
> implicit to me.
> 
> Or I guess we can just add another bool parameter?

It all depends on whether the device model can handle online resizes. I
think a bool parameter would be fine.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 25/41] blockjob: Add permissions to block_job_add_bdrv()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 25/41] blockjob: Add permissions to block_job_add_bdrv() Kevin Wolf
@ 2017-02-20 13:38   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 13:38 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 5725 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> Block jobs don't actually do I/O through the the reference they create
> with block_job_add_bdrv(), but they might want to use the permisssion
> system to express what the block job does to intermediate nodes. This
> adds permissions to block_job_add_bdrv() to provide the means to request
> permissions.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block/backup.c           |  3 ++-
>  block/commit.c           |  7 +++++--
>  block/mirror.c           |  7 +++++--
>  block/stream.c           |  3 ++-
>  blockjob.c               | 27 +++++++++++++++++++++------
>  include/block/blockjob.h |  4 +++-
>  6 files changed, 38 insertions(+), 13 deletions(-)
> 
> diff --git a/block/backup.c b/block/backup.c
> index 12ab25c..22171f4 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -657,7 +657,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
>          job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
>      }
>  
> -    block_job_add_bdrv(&job->common, target);
> +    /* FIXME Use real permissions */
> +    block_job_add_bdrv(&job->common, target, 0, BLK_PERM_ALL, &error_abort);
>      job->common.len = len;
>      block_job_txn_add_job(txn, &job->common);
>  
> diff --git a/block/commit.c b/block/commit.c
> index 60d29a9..68fa2a4 100644
> --- a/block/commit.c
> +++ b/block/commit.c
> @@ -267,13 +267,16 @@ void commit_start(const char *job_id, BlockDriverState *bs,
>       * disappear from the chain after this operation. */
>      assert(bdrv_chain_contains(top, base));
>      for (iter = top; iter != backing_bs(base); iter = backing_bs(iter)) {
> -        block_job_add_bdrv(&s->common, iter);
> +        /* FIXME Use real permissions */
> +        block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
>      }
>      /* overlay_bs must be blocked because it needs to be modified to
>       * update the backing image string, but if it's the root node then
>       * don't block it again */
>      if (bs != overlay_bs) {
> -        block_job_add_bdrv(&s->common, overlay_bs);
> +        /* FIXME Use real permissions */
> +        block_job_add_bdrv(&s->common, overlay_bs, 0, BLK_PERM_ALL,
> +                           &error_abort);
>      }
>  
>      /* FIXME Use real permissions */
> diff --git a/block/mirror.c b/block/mirror.c
> index 22680d7..9532b18 100644
> --- a/block/mirror.c
> +++ b/block/mirror.c
> @@ -1020,13 +1020,16 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
>          return;
>      }
>  
> -    block_job_add_bdrv(&s->common, target);
> +    /* FIXME Use real permissions */
> +    block_job_add_bdrv(&s->common, target, 0, BLK_PERM_ALL, &error_abort);
> +
>      /* In commit_active_start() all intermediate nodes disappear, so
>       * any jobs in them must be blocked */
>      if (bdrv_chain_contains(bs, target)) {
>          BlockDriverState *iter;
>          for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) {
> -            block_job_add_bdrv(&s->common, iter);
> +            /* FIXME Use real permissions */
> +            block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
>          }
>      }
>  
> diff --git a/block/stream.c b/block/stream.c
> index 7f49279..47f0ffb 100644
> --- a/block/stream.c
> +++ b/block/stream.c
> @@ -248,7 +248,8 @@ void stream_start(const char *job_id, BlockDriverState *bs,
>      /* Block all intermediate nodes between bs and base, because they
>       * will disappear from the chain after this operation */
>      for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
> -        block_job_add_bdrv(&s->common, iter);
> +        /* FIXME Use real permissions */
> +        block_job_add_bdrv(&s->common, iter, 0, BLK_PERM_ALL, &error_abort);
>      }
>  
>      s->base = base;
> diff --git a/blockjob.c b/blockjob.c
> index 27833c7..0bcc099 100644
> --- a/blockjob.c
> +++ b/blockjob.c
> @@ -55,6 +55,10 @@ struct BlockJobTxn {
>  
>  static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
>  
> +static const BdrvChildRole child_job = {
> +    .stay_at_node   = true,

(1) Not a big fan of such alignment without further users.

(2) No problem though because I'd like to ask for get_link_name() and/or
get_name() here.

It should be easy to generate a nice description when using the BlockJob
as BdrvChild.opaque.

> +};
> +
>  BlockJob *block_job_next(BlockJob *job)
>  {
>      if (!job) {
> @@ -115,11 +119,22 @@ static void block_job_detach_aio_context(void *opaque)
>      block_job_unref(job);
>  }
>  
> -void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
> +int block_job_add_bdrv(BlockJob *job, BlockDriverState *bs,
> +                       uint64_t perm, uint64_t shared_perm, Error **errp)
>  {
> -    job->nodes = g_slist_prepend(job->nodes, bs);
> +    BdrvChild *c;
> +
> +    c = bdrv_root_attach_child(bs, "job", &child_job, perm, shared_perm,

"job" doesn't make much sense as the child's name, it's rather a generic
name for the parent.

"job-child" or something caller-provided would be better. (I'd vote for
caller-provided. We don't have that many callers.)

Max

> +                               NULL, errp);
> +    if (c == NULL) {
> +        return -EPERM;
> +    }
> +
> +    job->nodes = g_slist_prepend(job->nodes, c);
>      bdrv_ref(bs);
>      bdrv_op_block_all(bs, job->blocker);
> +
> +    return 0;
>  }
>  
>  void *block_job_create(const char *job_id, const BlockJobDriver *driver,

[...]



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 27/41] block: Add bdrv_new_open_driver()
  2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 27/41] block: Add bdrv_new_open_driver() Kevin Wolf
@ 2017-02-20 14:20   ` Max Reitz
  0 siblings, 0 replies; 76+ messages in thread
From: Max Reitz @ 2017-02-20 14:20 UTC (permalink / raw)
  To: Kevin Wolf, qemu-block; +Cc: jcody, famz, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3271 bytes --]

On 13.02.2017 18:22, Kevin Wolf wrote:
> This function allows to create more or less normal BlockDriverStates
> even for BlockDrivers that aren't globally registered (e.g. helper
> filters for block jobs).
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  block.c               | 31 ++++++++++++++++++++++++++++++-
>  include/block/block.h |  2 ++
>  2 files changed, 32 insertions(+), 1 deletion(-)
> 
> diff --git a/block.c b/block.c
> index 9c80cba..7d84d43 100644
> --- a/block.c
> +++ b/block.c
> @@ -948,13 +948,16 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
>      }
>  
>      bs->drv = drv;
> +    bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
>      bs->opaque = g_malloc0(drv->instance_size);
>  
>      if (drv->bdrv_file_open) {
>          assert(!drv->bdrv_needs_filename || bs->filename[0]);
>          ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
> -    } else {
> +    } else if (drv->bdrv_open) {
>          ret = drv->bdrv_open(bs, options, open_flags, &local_err);
> +    } else {
> +        ret = 0;
>      }
>  
>      if (ret < 0) {
> @@ -995,6 +998,32 @@ free_and_fail:
>      return ret;
>  }
>  
> +int bdrv_new_open_driver(BlockDriver *drv, BlockDriverState **result,

Really? After we put much work into getting bdrv_open() to return a BDS
pointer? :-/

Do you need to distinguish based on -EPERM somewhere? I'd much rather
have the BDS pointer returned, personally. If you need the integer
return value somewhere, couldn't that be a parameter (which can
optionally be NULL if the caller doesn't need it)?

Max

> +                         const char *node_name, int flags, Error **errp)
> +{
> +    BlockDriverState *bs;
> +    int ret;
> +
> +    bs = bdrv_new();
> +    bs->open_flags = flags;
> +    bs->explicit_options = qdict_new();
> +    bs->options = qdict_new();
> +    bs->opaque = NULL;
> +
> +    update_options_from_flags(bs->options, flags);
> +
> +    ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
> +    if (ret < 0) {
> +        QDECREF(bs->explicit_options);
> +        QDECREF(bs->options);
> +        bdrv_unref(bs);
> +        return ret;
> +    }
> +
> +    *result = bs;
> +    return 0;
> +}
> +
>  QemuOptsList bdrv_runtime_opts = {
>      .name = "bdrv_common",
>      .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
> diff --git a/include/block/block.h b/include/block/block.h
> index 93812df..3238850 100644
> --- a/include/block/block.h
> +++ b/include/block/block.h
> @@ -215,6 +215,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
>                             const char *bdref_key, Error **errp);
>  BlockDriverState *bdrv_open(const char *filename, const char *reference,
>                              QDict *options, int flags, Error **errp);
> +int bdrv_new_open_driver(BlockDriver *drv, BlockDriverState **result,
> +                         const char *node_name, int flags, Error **errp);
>  BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
>                                      BlockDriverState *bs,
>                                      QDict *options, int flags);
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 76+ messages in thread

end of thread, other threads:[~2017-02-20 14:20 UTC | newest]

Thread overview: 76+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-13 17:22 [Qemu-devel] [RFC PATCH 00/41] New op blocker system Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 01/41] block: Attach bs->file only during .bdrv_open() Kevin Wolf
2017-02-15 14:34   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 02/41] block: Add op blocker permission constants Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 03/41] block: Add Error argument to bdrv_attach_child() Kevin Wolf
2017-02-15 14:48   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 04/41] block: Let callers request permissions when attaching a child node Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 05/41] tests: Use opened block node for block job tests Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 06/41] block: Involve block drivers in permission granting Kevin Wolf
2017-02-14  5:51   ` Fam Zheng
2017-02-14 10:36     ` Kevin Wolf
2017-02-14 11:23       ` Fam Zheng
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 07/41] block: Default .bdrv_child_perm() for filter drivers Kevin Wolf
2017-02-15 17:00   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 08/41] block: Request child permissions in " Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 09/41] block: Default .bdrv_child_perm() for format drivers Kevin Wolf
2017-02-14  6:01   ` Fam Zheng
2017-02-14 10:37     ` Kevin Wolf
2017-02-14 11:13       ` Fam Zheng
2017-02-15 17:11   ` Max Reitz
2017-02-15 17:29     ` Kevin Wolf
2017-02-15 17:33       ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 10/41] block: Request child permissions in " Kevin Wolf
2017-02-15 17:26   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 11/41] vvfat: Implement .bdrv_child_perm() Kevin Wolf
2017-02-15 17:30   ` Max Reitz
2017-02-15 17:42     ` Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 12/41] block: Require .bdrv_child_perm() with child nodes Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 13/41] block: Request real permissions in bdrv_attach_child() Kevin Wolf
2017-02-15 19:23   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 14/41] block: Add permissions to BlockBackend Kevin Wolf
2017-02-15 19:26   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 15/41] block: Add permissions to blk_new() Kevin Wolf
2017-02-20 10:29   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 16/41] block: Add error parameter to blk_insert_bs() Kevin Wolf
2017-02-14  6:58   ` Fam Zheng
2017-02-20 11:04   ` Max Reitz
2017-02-20 11:22     ` Kevin Wolf
2017-02-20 11:22       ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 17/41] block: Request real permissions in blk_new_open() Kevin Wolf
2017-02-20 11:16   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 18/41] block: Allow error return in BlockDevOps.change_media_cb() Kevin Wolf
2017-02-20 11:31   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 19/41] hw/block: Request permissions Kevin Wolf
2017-02-20 12:25   ` Max Reitz
2017-02-20 13:02     ` Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 20/41] hw/block: Introduce share-rw qdev property Kevin Wolf
2017-02-20 12:28   ` Max Reitz
2017-02-20 13:05     ` Kevin Wolf
2017-02-20 13:17       ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 21/41] blockjob: Add permissions to block_job_create() Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 22/41] block: Add BdrvChildRole.get_link_name() Kevin Wolf
2017-02-20 12:54   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 23/41] block: Include details on permission errors in message Kevin Wolf
2017-02-20 13:16   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 24/41] block: Add BdrvChildRole.stay_at_node Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 25/41] blockjob: Add permissions to block_job_add_bdrv() Kevin Wolf
2017-02-20 13:38   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 26/41] block: Factor out bdrv_open_driver() Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 27/41] block: Add bdrv_new_open_driver() Kevin Wolf
2017-02-20 14:20   ` Max Reitz
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 28/41] commit: Use real permissions in commit block job Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 29/41] commit: Use real permissions for HMP 'commit' Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 30/41] backup: Use real permissions in backup block job Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 31/41] block: Fix pending requests check in bdrv_append() Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 32/41] block: BdrvChildRole.attach/detach() callbacks Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 33/41] block: Allow backing file links in change_parent_backing_link() Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 34/41] mirror: Use real permissions in mirror/active commit block job Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 35/41] stream: Use real permissions in streaming " Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 36/41] hmp: Request permissions in qemu-io Kevin Wolf
2017-02-13 17:22 ` [Qemu-devel] [RFC PATCH 37/41] migration/block: Use real permissions Kevin Wolf
2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 38/41] nbd/server: Use real permissions for NBD exports Kevin Wolf
2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 39/41] tests: Remove FIXME comments Kevin Wolf
2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 40/41] block: Pass BdrvChild to bdrv_aligned_preadv/pwritev Kevin Wolf
2017-02-13 17:23 ` [Qemu-devel] [RFC PATCH 41/41] block: Assertions for write permissions Kevin Wolf
2017-02-13 18:44 ` [Qemu-devel] [RFC PATCH 00/41] New op blocker system no-reply

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.