diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 053a086d547e..f71f45141890 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1222,7 +1222,8 @@ static int loop_clr_fd(struct loop_device *lo) } static int -loop_set_status(struct loop_device *lo, const struct loop_info64 *info) +loop_set_status(struct loop_device *lo, blk_mode_t mode, + const struct loop_info64 *info) { int err; bool partscan = false; @@ -1237,6 +1238,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) goto out_unlock; } + /* + * If we don't hold exclusive handle for the device, upgrade to it + * here to avoid changing device under exclusive owner. + */ + if (!(mode & BLK_OPEN_EXCL)) { + err = bd_prepare_to_claim(lo->lo_device, + loop_set_status, NULL); + if (err) + goto out_unlock; + } + if (lo->lo_offset != info->lo_offset || lo->lo_sizelimit != info->lo_sizelimit) { size_changed = true; @@ -1260,6 +1272,9 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) /* update the direct I/O flag if lo_offset changed */ loop_update_dio(lo); + if (!(mode & BLK_OPEN_EXCL)) + bd_abort_claiming(lo->lo_device, loop_set_status); + out_unfreeze: blk_mq_unfreeze_queue(lo->lo_queue, memflags); if (partscan) @@ -1349,7 +1364,8 @@ loop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info) } static int -loop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg) +loop_set_status_old(struct loop_device *lo, blk_mode_t mode, + const struct loop_info __user *arg) { struct loop_info info; struct loop_info64 info64; @@ -1357,17 +1373,18 @@ loop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg) if (copy_from_user(&info, arg, sizeof (struct loop_info))) return -EFAULT; loop_info64_from_old(&info, &info64); - return loop_set_status(lo, &info64); + return loop_set_status(lo, mode, &info64); } static int -loop_set_status64(struct loop_device *lo, const struct loop_info64 __user *arg) +loop_set_status64(struct loop_device *lo, blk_mode_t mode, + const struct loop_info64 __user *arg) { struct loop_info64 info64; if (copy_from_user(&info64, arg, sizeof (struct loop_info64))) return -EFAULT; - return loop_set_status(lo, &info64); + return loop_set_status(lo, mode, &info64); } static int @@ -1546,14 +1563,14 @@ static int lo_ioctl(struct block_device *bdev, blk_mode_t mode, case LOOP_SET_STATUS: err = -EPERM; if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN)) - err = loop_set_status_old(lo, argp); + err = loop_set_status_old(lo, mode, argp); break; case LOOP_GET_STATUS: return loop_get_status_old(lo, argp); case LOOP_SET_STATUS64: err = -EPERM; if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN)) - err = loop_set_status64(lo, argp); + err = loop_set_status64(lo, mode, argp); break; case LOOP_GET_STATUS64: return loop_get_status64(lo, argp); @@ -1647,7 +1664,7 @@ loop_info64_to_compat(const struct loop_info64 *info64, } static int -loop_set_status_compat(struct loop_device *lo, +loop_set_status_compat(struct loop_device *lo, blk_mode_t mode, const struct compat_loop_info __user *arg) { struct loop_info64 info64; @@ -1656,7 +1673,7 @@ loop_set_status_compat(struct loop_device *lo, ret = loop_info64_from_compat(arg, &info64); if (ret < 0) return ret; - return loop_set_status(lo, &info64); + return loop_set_status(lo, mode, &info64); } static int @@ -1682,7 +1699,7 @@ static int lo_compat_ioctl(struct block_device *bdev, blk_mode_t mode, switch(cmd) { case LOOP_SET_STATUS: - err = loop_set_status_compat(lo, + err = loop_set_status_compat(lo, mode, (const struct compat_loop_info __user *)arg); break; case LOOP_GET_STATUS: