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", target, 0, BLK_PERM_ALL,
+                       &error_abort);
     job->common.len = len;
     block_job_txn_add_job(txn, &job->common);
 
 
      * 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, "intermediate node", 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 of top", overlay_bs, 0,
+                           BLK_PERM_ALL, &error_abort);
     }
 
     /* FIXME Use real permissions */
 
         return;
     }
 
-    block_job_add_bdrv(&s->common, target);
+    /* FIXME Use real permissions */
+    block_job_add_bdrv(&s->common, "target", 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, "intermediate node", iter, 0,
+                               BLK_PERM_ALL, &error_abort);
         }
     }
 
 
     /* 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, "intermediate node", iter, 0,
+                           BLK_PERM_ALL, &error_abort);
     }
 
     s->base = base;
 
 
 static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
 
+static char *child_job_get_parent_desc(BdrvChild *c)
+{
+    BlockJob *job = c->opaque;
+    return g_strdup_printf("%s job '%s'",
+                           BlockJobType_lookup[job->driver->job_type],
+                           job->id);
+}
+
+static const BdrvChildRole child_job = {
+    .get_parent_desc    = child_job_get_parent_desc,
+    .stay_at_node       = true,
+};
+
 BlockJob *block_job_next(BlockJob *job)
 {
     if (!job) {
     block_job_unref(job);
 }
 
-void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
+int block_job_add_bdrv(BlockJob *job, const char *name, 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, name, &child_job, perm, shared_perm,
+                               job, 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,
     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, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
     bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
 
     job->driver        = driver;
         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,
 
 /**
  * block_job_add_bdrv:
  * @job: A block job
+ * @name: The name to assign to the new BdrvChild
  * @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, const char *name, BlockDriverState *bs,
+                       uint64_t perm, uint64_t shared_perm, Error **errp);
 
 /**
  * block_job_set_speed: