Commit 1c43366d authored by Miao Xie's avatar Miao Xie Committed by Chris Mason

Btrfs: fix unprotected assignment of the target device

We didn't protect the assignment of the target device, it might cause the
problem that the super block update was skipped because we might find wrong
size of the target device during the assignment. Fix it by moving the
assignment sentences into the initialization function of the target device.
And there is another merit that we can check if the target device is suitable
more early.
Signed-off-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent c7662111
...@@ -330,29 +330,19 @@ int btrfs_dev_replace_start(struct btrfs_root *root, ...@@ -330,29 +330,19 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
return -EINVAL; return -EINVAL;
mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->volume_mutex);
ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name,
&tgt_device);
if (ret) {
btrfs_err(fs_info, "target device %s is invalid!",
args->start.tgtdev_name);
mutex_unlock(&fs_info->volume_mutex);
return -EINVAL;
}
ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid, ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid,
args->start.srcdev_name, args->start.srcdev_name,
&src_device); &src_device);
mutex_unlock(&fs_info->volume_mutex);
if (ret) { if (ret) {
ret = -EINVAL; mutex_unlock(&fs_info->volume_mutex);
goto leave_no_lock; return ret;
} }
if (tgt_device->total_bytes < src_device->total_bytes) { ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name,
btrfs_err(fs_info, "target device is smaller than source device!"); src_device, &tgt_device);
ret = -EINVAL; mutex_unlock(&fs_info->volume_mutex);
goto leave_no_lock; if (ret)
} return ret;
btrfs_dev_replace_lock(dev_replace); btrfs_dev_replace_lock(dev_replace);
switch (dev_replace->replace_state) { switch (dev_replace->replace_state) {
...@@ -380,10 +370,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root, ...@@ -380,10 +370,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
src_device->devid, src_device->devid,
rcu_str_deref(tgt_device->name)); rcu_str_deref(tgt_device->name));
tgt_device->total_bytes = src_device->total_bytes;
tgt_device->disk_total_bytes = src_device->disk_total_bytes;
tgt_device->bytes_used = src_device->bytes_used;
/* /*
* from now on, the writes to the srcdev are all duplicated to * from now on, the writes to the srcdev are all duplicated to
* go to the tgtdev as well (refer to btrfs_map_block()). * go to the tgtdev as well (refer to btrfs_map_block()).
...@@ -426,9 +412,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, ...@@ -426,9 +412,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
dev_replace->srcdev = NULL; dev_replace->srcdev = NULL;
dev_replace->tgtdev = NULL; dev_replace->tgtdev = NULL;
btrfs_dev_replace_unlock(dev_replace); btrfs_dev_replace_unlock(dev_replace);
leave_no_lock: btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
if (tgt_device)
btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
return ret; return ret;
} }
......
...@@ -2295,6 +2295,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -2295,6 +2295,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
} }
int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
struct btrfs_device *srcdev,
struct btrfs_device **device_out) struct btrfs_device **device_out)
{ {
struct request_queue *q; struct request_queue *q;
...@@ -2307,24 +2308,37 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, ...@@ -2307,24 +2308,37 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
int ret = 0; int ret = 0;
*device_out = NULL; *device_out = NULL;
if (fs_info->fs_devices->seeding) if (fs_info->fs_devices->seeding) {
btrfs_err(fs_info, "the filesystem is a seed filesystem!");
return -EINVAL; return -EINVAL;
}
bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL, bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL,
fs_info->bdev_holder); fs_info->bdev_holder);
if (IS_ERR(bdev)) if (IS_ERR(bdev)) {
btrfs_err(fs_info, "target device %s is invalid!", device_path);
return PTR_ERR(bdev); return PTR_ERR(bdev);
}
filemap_write_and_wait(bdev->bd_inode->i_mapping); filemap_write_and_wait(bdev->bd_inode->i_mapping);
devices = &fs_info->fs_devices->devices; devices = &fs_info->fs_devices->devices;
list_for_each_entry(device, devices, dev_list) { list_for_each_entry(device, devices, dev_list) {
if (device->bdev == bdev) { if (device->bdev == bdev) {
btrfs_err(fs_info, "target device is in the filesystem!");
ret = -EEXIST; ret = -EEXIST;
goto error; goto error;
} }
} }
if (i_size_read(bdev->bd_inode) < srcdev->total_bytes) {
btrfs_err(fs_info, "target device is smaller than source device!");
ret = -EINVAL;
goto error;
}
device = btrfs_alloc_device(NULL, &devid, NULL); device = btrfs_alloc_device(NULL, &devid, NULL);
if (IS_ERR(device)) { if (IS_ERR(device)) {
ret = PTR_ERR(device); ret = PTR_ERR(device);
...@@ -2348,8 +2362,9 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, ...@@ -2348,8 +2362,9 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
device->io_width = root->sectorsize; device->io_width = root->sectorsize;
device->io_align = root->sectorsize; device->io_align = root->sectorsize;
device->sector_size = root->sectorsize; device->sector_size = root->sectorsize;
device->total_bytes = i_size_read(bdev->bd_inode); device->total_bytes = srcdev->total_bytes;
device->disk_total_bytes = device->total_bytes; device->disk_total_bytes = srcdev->disk_total_bytes;
device->bytes_used = srcdev->bytes_used;
device->dev_root = fs_info->dev_root; device->dev_root = fs_info->dev_root;
device->bdev = bdev; device->bdev = bdev;
device->in_fs_metadata = 1; device->in_fs_metadata = 1;
......
...@@ -322,6 +322,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid, ...@@ -322,6 +322,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
int btrfs_shrink_device(struct btrfs_device *device, u64 new_size); int btrfs_shrink_device(struct btrfs_device *device, u64 new_size);
int btrfs_init_new_device(struct btrfs_root *root, char *path); int btrfs_init_new_device(struct btrfs_root *root, char *path);
int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
struct btrfs_device *srcdev,
struct btrfs_device **device_out); struct btrfs_device **device_out);
int btrfs_balance(struct btrfs_balance_control *bctl, int btrfs_balance(struct btrfs_balance_control *bctl,
struct btrfs_ioctl_balance_args *bargs); struct btrfs_ioctl_balance_args *bargs);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment