Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions drivers/block/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -1226,13 +1226,24 @@ 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,
struct block_device *bdev, const struct loop_info64 *info)
{
int err;
bool partscan = false;
bool size_changed = false;
unsigned int memflags;

/*
* 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(bdev, loop_set_status, NULL);
if (err)
goto out_reread_partitions;
}

err = mutex_lock_killable(&lo->lo_mutex);
if (err)
return err;
Expand Down Expand Up @@ -1274,6 +1285,9 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
}
out_unlock:
mutex_unlock(&lo->lo_mutex);
if (!(mode & BLK_OPEN_EXCL))
bd_abort_claiming(bdev, loop_set_status);
out_reread_partitions:
if (partscan)
loop_reread_partitions(lo);

Expand Down Expand Up @@ -1353,25 +1367,29 @@ 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,
struct block_device *bdev,
const struct loop_info __user *arg)
{
struct loop_info info;
struct loop_info64 info64;

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, bdev, &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,
struct block_device *bdev,
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, bdev, &info64);
}

static int
Expand Down Expand Up @@ -1550,14 +1568,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, bdev, 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, bdev, argp);
break;
case LOOP_GET_STATUS64:
return loop_get_status64(lo, argp);
Expand Down Expand Up @@ -1651,16 +1669,17 @@ loop_info64_to_compat(const struct loop_info64 *info64,
}

static int
loop_set_status_compat(struct loop_device *lo,
const struct compat_loop_info __user *arg)
loop_set_status_compat(struct loop_device *lo, blk_mode_t mode,
struct block_device *bdev,
const struct compat_loop_info __user *arg)
{
struct loop_info64 info64;
int ret;

ret = loop_info64_from_compat(arg, &info64);
if (ret < 0)
return ret;
return loop_set_status(lo, &info64);
return loop_set_status(lo, mode, bdev, &info64);
}

static int
Expand All @@ -1686,7 +1705,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, bdev,
(const struct compat_loop_info __user *)arg);
break;
case LOOP_GET_STATUS:
Expand Down
Loading