Skip to content

Commit

Permalink
block: fine-granular CAP_SYS_ADMIN for Persistent Reservation
Browse files Browse the repository at this point in the history
Allow of unprivileged Persistent Reservation operations on devices
if the write permission check on the device node has passed.

brw-rw---- 1 root disk 259, 0 Jun 13 07:09 /dev/nvme0n1

In the example above, the "disk" group of nvme0n1 is also allowed to
make reservations on the device even without CAP_SYS_ADMIN.

Signed-off-by: Jingbo Xu <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
lostjeffle authored and axboe committed Jun 20, 2023
1 parent 1262962 commit 9a72a02
Showing 1 changed file with 22 additions and 19 deletions.
41 changes: 22 additions & 19 deletions block/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,25 +254,28 @@ int blkdev_compat_ptr_ioctl(struct block_device *bdev, blk_mode_t mode,
EXPORT_SYMBOL(blkdev_compat_ptr_ioctl);
#endif

static bool blkdev_pr_allowed(struct block_device *bdev)
static bool blkdev_pr_allowed(struct block_device *bdev, blk_mode_t mode)
{
/* no sense to make reservations for partitions */
if (bdev_is_partition(bdev))
return false;

if (capable(CAP_SYS_ADMIN))
return true;

return false;
/*
* Only allow unprivileged reservations if the file descriptor is open
* for writing.
*/
return mode & BLK_OPEN_WRITE;
}

static int blkdev_pr_register(struct block_device *bdev,
static int blkdev_pr_register(struct block_device *bdev, blk_mode_t mode,
struct pr_registration __user *arg)
{
const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
struct pr_registration reg;

if (!blkdev_pr_allowed(bdev))
if (!blkdev_pr_allowed(bdev, mode))
return -EPERM;
if (!ops || !ops->pr_register)
return -EOPNOTSUPP;
Expand All @@ -284,13 +287,13 @@ static int blkdev_pr_register(struct block_device *bdev,
return ops->pr_register(bdev, reg.old_key, reg.new_key, reg.flags);
}

static int blkdev_pr_reserve(struct block_device *bdev,
static int blkdev_pr_reserve(struct block_device *bdev, blk_mode_t mode,
struct pr_reservation __user *arg)
{
const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
struct pr_reservation rsv;

if (!blkdev_pr_allowed(bdev))
if (!blkdev_pr_allowed(bdev, mode))
return -EPERM;
if (!ops || !ops->pr_reserve)
return -EOPNOTSUPP;
Expand All @@ -302,13 +305,13 @@ static int blkdev_pr_reserve(struct block_device *bdev,
return ops->pr_reserve(bdev, rsv.key, rsv.type, rsv.flags);
}

static int blkdev_pr_release(struct block_device *bdev,
static int blkdev_pr_release(struct block_device *bdev, blk_mode_t mode,
struct pr_reservation __user *arg)
{
const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
struct pr_reservation rsv;

if (!blkdev_pr_allowed(bdev))
if (!blkdev_pr_allowed(bdev, mode))
return -EPERM;
if (!ops || !ops->pr_release)
return -EOPNOTSUPP;
Expand All @@ -320,13 +323,13 @@ static int blkdev_pr_release(struct block_device *bdev,
return ops->pr_release(bdev, rsv.key, rsv.type);
}

static int blkdev_pr_preempt(struct block_device *bdev,
static int blkdev_pr_preempt(struct block_device *bdev, blk_mode_t mode,
struct pr_preempt __user *arg, bool abort)
{
const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
struct pr_preempt p;

if (!blkdev_pr_allowed(bdev))
if (!blkdev_pr_allowed(bdev, mode))
return -EPERM;
if (!ops || !ops->pr_preempt)
return -EOPNOTSUPP;
Expand All @@ -338,13 +341,13 @@ static int blkdev_pr_preempt(struct block_device *bdev,
return ops->pr_preempt(bdev, p.old_key, p.new_key, p.type, abort);
}

static int blkdev_pr_clear(struct block_device *bdev,
static int blkdev_pr_clear(struct block_device *bdev, blk_mode_t mode,
struct pr_clear __user *arg)
{
const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
struct pr_clear c;

if (!blkdev_pr_allowed(bdev))
if (!blkdev_pr_allowed(bdev, mode))
return -EPERM;
if (!ops || !ops->pr_clear)
return -EOPNOTSUPP;
Expand Down Expand Up @@ -546,17 +549,17 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,
case BLKTRACETEARDOWN:
return blk_trace_ioctl(bdev, cmd, argp);
case IOC_PR_REGISTER:
return blkdev_pr_register(bdev, argp);
return blkdev_pr_register(bdev, mode, argp);
case IOC_PR_RESERVE:
return blkdev_pr_reserve(bdev, argp);
return blkdev_pr_reserve(bdev, mode, argp);
case IOC_PR_RELEASE:
return blkdev_pr_release(bdev, argp);
return blkdev_pr_release(bdev, mode, argp);
case IOC_PR_PREEMPT:
return blkdev_pr_preempt(bdev, argp, false);
return blkdev_pr_preempt(bdev, mode, argp, false);
case IOC_PR_PREEMPT_ABORT:
return blkdev_pr_preempt(bdev, argp, true);
return blkdev_pr_preempt(bdev, mode, argp, true);
case IOC_PR_CLEAR:
return blkdev_pr_clear(bdev, argp);
return blkdev_pr_clear(bdev, mode, argp);
default:
return -ENOIOCTLCMD;
}
Expand Down

0 comments on commit 9a72a02

Please sign in to comment.