IORING_OP_MKNODAT behaves like mknodat(2) and takes the same flags and arguments. Suggested-by: Christian Brauner <christian.brauner@xxxxxxxxxx> Link: https://lore.kernel.org/io-uring/20210514145259.wtl4xcsp52woi6ab@wittgenstein/ Signed-off-by: Dmitry Kadashev <dkadashev@xxxxxxxxx> --- fs/internal.h | 2 ++ fs/io_uring.c | 56 +++++++++++++++++++++++++++++++++++ fs/namei.c | 2 +- include/uapi/linux/io_uring.h | 2 ++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/fs/internal.h b/fs/internal.h index 15a7d210cc67..c6fb9974006f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -81,6 +81,8 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode); int do_symlinkat(struct filename *from, int newdfd, struct filename *to); int do_linkat(int olddfd, struct filename *old, int newdfd, struct filename *new, int flags); +int do_mknodat(int dfd, struct filename *name, umode_t mode, + unsigned int dev); /* * namespace.c diff --git a/fs/io_uring.c b/fs/io_uring.c index 31e1aa7dd90b..475632374af8 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -688,6 +688,14 @@ struct io_hardlink { int flags; }; +struct io_mknod { + struct file *file; + int dfd; + umode_t mode; + struct filename *filename; + unsigned int dev; +}; + struct io_completion { struct file *file; struct list_head list; @@ -835,6 +843,7 @@ struct io_kiocb { struct io_mkdir mkdir; struct io_symlink symlink; struct io_hardlink hardlink; + struct io_mknod mknod; /* use only after cleaning per-op data, see io_clean_op() */ struct io_completion compl; }; @@ -1050,6 +1059,7 @@ static const struct io_op_def io_op_defs[] = { [IORING_OP_MKDIRAT] = {}, [IORING_OP_SYMLINKAT] = {}, [IORING_OP_LINKAT] = {}, + [IORING_OP_MKNODAT] = {}, }; static bool io_disarm_next(struct io_kiocb *req); @@ -3687,6 +3697,44 @@ static int io_linkat(struct io_kiocb *req, int issue_flags) io_req_complete(req, ret); return 0; } +static int io_mknodat_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) +{ + struct io_mknod *mkn = &req->mknod; + const char __user *fname; + + if (unlikely(req->flags & REQ_F_FIXED_FILE)) + return -EBADF; + + mkn->dfd = READ_ONCE(sqe->fd); + mkn->mode = READ_ONCE(sqe->len); + fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); + mkn->dev = READ_ONCE(sqe->mknod_dev); + + mkn->filename = getname(fname); + if (IS_ERR(mkn->filename)) + return PTR_ERR(mkn->filename); + + req->flags |= REQ_F_NEED_CLEANUP; + return 0; +} + +static int io_mknodat(struct io_kiocb *req, int issue_flags) +{ + struct io_mknod *mkn = &req->mknod; + int ret; + + if (issue_flags & IO_URING_F_NONBLOCK) + return -EAGAIN; + + ret = do_mknodat(mkn->dfd, mkn->filename, mkn->mode, mkn->dev); + + req->flags &= ~REQ_F_NEED_CLEANUP; + if (ret < 0) + req_set_fail(req); + io_req_complete(req, ret); + return 0; +} static int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) @@ -6100,6 +6148,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return io_symlinkat_prep(req, sqe); case IORING_OP_LINKAT: return io_linkat_prep(req, sqe); + case IORING_OP_MKNODAT: + return io_mknodat_prep(req, sqe); } printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", @@ -6252,6 +6302,9 @@ static void io_clean_op(struct io_kiocb *req) putname(req->hardlink.oldpath); putname(req->hardlink.newpath); break; + case IORING_OP_MKNODAT: + putname(req->mknod.filename); + break; } req->flags &= ~REQ_F_NEED_CLEANUP; } @@ -6387,6 +6440,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) case IORING_OP_LINKAT: ret = io_linkat(req, issue_flags); break; + case IORING_OP_MKNODAT: + ret = io_mknodat(req, issue_flags); + break; default: ret = -EINVAL; break; diff --git a/fs/namei.c b/fs/namei.c index b85e457c43b7..a4b1848b5dd1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3743,7 +3743,7 @@ static int may_mknod(umode_t mode) } } -static int do_mknodat(int dfd, struct filename *name, umode_t mode, +int do_mknodat(int dfd, struct filename *name, umode_t mode, unsigned int dev) { struct user_namespace *mnt_userns; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 510e64a0a9c3..824b37f53a28 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -45,6 +45,7 @@ struct io_uring_sqe { __u32 rename_flags; __u32 unlink_flags; __u32 hardlink_flags; + __u32 mknod_dev; }; __u64 user_data; /* data to be passed back at completion time */ union { @@ -141,6 +142,7 @@ enum { IORING_OP_MKDIRAT, IORING_OP_SYMLINKAT, IORING_OP_LINKAT, + IORING_OP_MKNODAT, /* this goes last, obviously */ IORING_OP_LAST, -- 2.30.2