OSDN Git Service

btrfs: hold a ref on the root in btrfs_ioctl_send
authorJosef Bacik <josef@toxicpanda.com>
Fri, 24 Jan 2020 14:32:48 +0000 (09:32 -0500)
committerDavid Sterba <dsterba@suse.com>
Mon, 23 Mar 2020 16:01:31 +0000 (17:01 +0100)
We lookup all the clone roots and the parent root for send, so we need
to hold refs on all of these roots while we're processing them.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/send.c

index 27e5805..31495cd 100644 (file)
@@ -7201,10 +7201,16 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
                                ret = PTR_ERR(clone_root);
                                goto out;
                        }
+                       if (!btrfs_grab_fs_root(clone_root)) {
+                               srcu_read_unlock(&fs_info->subvol_srcu, index);
+                               ret = -ENOENT;
+                               goto out;
+                       }
                        spin_lock(&clone_root->root_item_lock);
                        if (!btrfs_root_readonly(clone_root) ||
                            btrfs_root_dead(clone_root)) {
                                spin_unlock(&clone_root->root_item_lock);
+                               btrfs_put_fs_root(clone_root);
                                srcu_read_unlock(&fs_info->subvol_srcu, index);
                                ret = -EPERM;
                                goto out;
@@ -7212,6 +7218,7 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
                        if (clone_root->dedupe_in_progress) {
                                dedupe_in_progress_warn(clone_root);
                                spin_unlock(&clone_root->root_item_lock);
+                               btrfs_put_fs_root(clone_root);
                                srcu_read_unlock(&fs_info->subvol_srcu, index);
                                ret = -EAGAIN;
                                goto out;
@@ -7240,6 +7247,12 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
                        ret = PTR_ERR(sctx->parent_root);
                        goto out;
                }
+               if (!btrfs_grab_fs_root(sctx->parent_root)) {
+                       srcu_read_unlock(&fs_info->subvol_srcu, index);
+                       ret = -ENOENT;
+                       sctx->parent_root = ERR_PTR(ret);
+                       goto out;
+               }
 
                spin_lock(&sctx->parent_root->root_item_lock);
                sctx->parent_root->send_in_progress++;
@@ -7267,7 +7280,8 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
         * is behind the current send position. This is checked while searching
         * for possible clone sources.
         */
-       sctx->clone_roots[sctx->clone_roots_cnt++].root = sctx->send_root;
+       sctx->clone_roots[sctx->clone_roots_cnt++].root =
+               btrfs_grab_fs_root(sctx->send_root);
 
        /* We do a bsearch later */
        sort(sctx->clone_roots, sctx->clone_roots_cnt,
@@ -7352,18 +7366,24 @@ out:
        }
 
        if (sort_clone_roots) {
-               for (i = 0; i < sctx->clone_roots_cnt; i++)
+               for (i = 0; i < sctx->clone_roots_cnt; i++) {
                        btrfs_root_dec_send_in_progress(
                                        sctx->clone_roots[i].root);
+                       btrfs_put_fs_root(sctx->clone_roots[i].root);
+               }
        } else {
-               for (i = 0; sctx && i < clone_sources_to_rollback; i++)
+               for (i = 0; sctx && i < clone_sources_to_rollback; i++) {
                        btrfs_root_dec_send_in_progress(
                                        sctx->clone_roots[i].root);
+                       btrfs_put_fs_root(sctx->clone_roots[i].root);
+               }
 
                btrfs_root_dec_send_in_progress(send_root);
        }
-       if (sctx && !IS_ERR_OR_NULL(sctx->parent_root))
+       if (sctx && !IS_ERR_OR_NULL(sctx->parent_root)) {
                btrfs_root_dec_send_in_progress(sctx->parent_root);
+               btrfs_put_fs_root(sctx->parent_root);
+       }
 
        kvfree(clone_sources_tmp);