diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9d47d16394fc..26ebfac37952 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2622,7 +2622,7 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) err_drop: mnt_drop_write_file(file); if (bdev_file) - bdev_fput(bdev_file); + btrfs_release_device_allow_freeze(bdev_file); out: btrfs_put_dev_args_from_path(&args); return ret; @@ -2672,7 +2672,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) mnt_drop_write_file(file); if (bdev_file) - bdev_fput(bdev_file); + btrfs_release_device_allow_freeze(bdev_file); out: btrfs_put_dev_args_from_path(&args); return ret; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 6eab4cc73ce4..6a0d22b6ce05 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1124,6 +1124,15 @@ void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices) mutex_unlock(&uuid_mutex); } +/* Release a device that was made unfreezable for a membership change. */ +void btrfs_release_device_allow_freeze(struct file *bdev_file) +{ + /* Yield before allow (strand-safe); file still open for the allow (UAF-safe). */ + bdev_yield_claim(bdev_file); + bdev_allow_freeze(file_bdev(bdev_file)); + bdev_fput(bdev_file); +} + static void btrfs_close_bdev(struct btrfs_device *device) { if (!device->bdev) @@ -2373,6 +2382,13 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, fs_info->fs_devices->rw_devices == 1) return BTRFS_ERROR_DEV_ONLY_WRITABLE; + /* Removal and freezing are mutually exclusive; refuse if frozen now. */ + if (device->bdev) { + ret = bdev_deny_freeze(device->bdev); + if (ret) + return ret; + } + if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { mutex_lock(&fs_info->chunk_mutex); list_del_init(&device->dev_alloc_list); @@ -2399,6 +2415,8 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, device->devid, ret); btrfs_abort_transaction(trans, ret); btrfs_end_transaction(trans); + if (device->bdev) + bdev_allow_freeze(device->bdev); return ret; } @@ -2490,6 +2508,8 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, return btrfs_commit_transaction(trans); error_undo: + if (device->bdev) + bdev_allow_freeze(device->bdev); if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { mutex_lock(&fs_info->chunk_mutex); list_add(&device->dev_alloc_list, diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 63be45c3298c..f8d3fd4dab55 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -744,6 +744,7 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, struct btrfs_device *btrfs_scan_one_device(const char *path, bool mount_arg_dev); int btrfs_forget_devices(dev_t devt); void btrfs_close_devices(struct btrfs_fs_devices *fs_devices); +void btrfs_release_device_allow_freeze(struct file *bdev_file); void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices); void btrfs_assign_next_active_device(struct btrfs_device *device, struct btrfs_device *this_dev);