[RFC PATCH 2/7] Allow userspace to get an FD to a newly-created DM device

From: Demi Marie Obenour
Date: Wed Jan 25 2023 - 22:34:27 EST


This allows creating a device-mapper device, opening it, and setting it
to be deleted when unused in a single atomic operation.

Signed-off-by: Demi Marie Obenour <demi@xxxxxxxxxxxxxxxxxxxxxx>
---
drivers/md/dm-ioctl.c | 67 +++++++++++++++++++++++++++++------
include/uapi/linux/dm-ioctl.h | 16 ++++++++-
2 files changed, 72 insertions(+), 11 deletions(-)

diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 36fc6ae4737a05ab53ab67a8ccee525cb5fda082..05438dedcd17b7cac470fcc5a9721d67daad4bfb 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -853,9 +853,21 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)

static int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_size)
{
- int r, m = DM_ANY_MINOR;
+ int r, m = DM_ANY_MINOR, fd;
struct mapped_device *md;

+ /* Do not allow unknown flags */
+ if (param->flags > (2 * DM_FILE_DESCRIPTOR_FLAG - 1))
+ return -EINVAL;
+
+ /*
+ * Do not allow creating a device that would just be destroyed
+ * before the ioctl returns.
+ */
+ if ((param->flags & DM_DEFERRED_REMOVE) &&
+ !(param->flags & DM_FILE_DESCRIPTOR_FLAG))
+ return -EINVAL;
+
r = check_name(param->name);
if (r)
return r;
@@ -867,20 +879,55 @@ static int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_si
if (r)
return r;

- r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
- if (r) {
- dm_put(md);
- dm_destroy(md);
- return r;
- }
-
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;

+ r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
+ if (r)
+ goto out_put;
+
+ if (param->flags & DM_FILE_DESCRIPTOR_FLAG) {
+ struct block_device *bdev = dm_disk(md)->part0;
+ struct file *file;
+
+ fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ r = fd;
+ goto out_put;
+ }
+
+ file = blkdev_get_file(bdev, O_RDWR|O_CLOEXEC, NULL);
+ if (IS_ERR(file)) {
+ r = PTR_ERR(file);
+ goto out_put_fd;
+ }
+
+ /*
+ * Simulate opening the device. The other checks in
+ * dm_blk_open() are not necessary becuase we have a reference
+ * to the `struct md`.
+ */
+ atomic_inc(&md->open_count);
+ fd_install(fd, file);
+ param->file_descriptor = fd;
+ }
+
+ /*
+ * If userspace requests it, automatically delete the device
+ * when it is no longer used
+ */
+ if (param->flags & DM_DEFERRED_REMOVE)
+ set_bit(DMF_DEFERRED_REMOVE, &md->flags);
+
__dev_status(md, param);
-
dm_put(md);
-
return 0;
+
+out_put_fd:
+ put_unused_fd(fd);
+out_put:
+ dm_put(md);
+ dm_destroy(md);
+ return r;
}

/*
diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h
index 7edf335778bae1cb206f6dd4d44e9cf7fb9da35c..30a6260ed7e06ff71fad1675dd4e7f9325d752a6 100644
--- a/include/uapi/linux/dm-ioctl.h
+++ b/include/uapi/linux/dm-ioctl.h
@@ -136,7 +136,13 @@ struct dm_ioctl {
* For output, the ioctls return the event number, not the cookie.
*/
__u32 event_nr; /* in/out */
- __u32 padding;
+
+ union {
+ /* Padding for named devices */
+ __u32 padding;
+ /* For anonymous devices, this is a file descriptor. */
+ __u32 file_descriptor;
+ };

__u64 dev; /* in/out */

@@ -382,4 +388,12 @@ enum {
*/
#define DM_IMA_MEASUREMENT_FLAG (1 << 19) /* In */

+/*
+ * If set in a DM_DEV_CREATE ioctl(), sets the file_descriptor field
+ * to a valid file descriptor. This can be combined with DM_DEFERRED_REMOVE
+ * to cause the device to be destroyed when the file descriptor is closed
+ * and is otherwise unused.
+ */
+#define DM_FILE_DESCRIPTOR_FLAG (1 << 20) /* In */
+
#endif /* _LINUX_DM_IOCTL_H */
--
Sincerely,
Demi Marie Obenour (she/her/hers)
Invisible Things Lab