448 lines · 10.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/kernel.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/fdtable.h>#include <linux/fsnotify.h>#include <linux/namei.h>#include <linux/pipe_fs_i.h>#include <linux/watch_queue.h>#include <linux/io_uring.h> #include <uapi/linux/io_uring.h> #include "../fs/internal.h" #include "filetable.h"#include "io_uring.h"#include "rsrc.h"#include "openclose.h" struct io_open {	struct file			*file;	int				dfd;	u32				file_slot;	struct delayed_filename		filename;	struct open_how			how;	unsigned long			nofile;}; struct io_close {	struct file			*file;	int				fd;	u32				file_slot;}; struct io_fixed_install {	struct file			*file;	unsigned int			o_flags;}; static bool io_openat_force_async(struct io_open *open){	/*	 * Don't bother trying for O_TRUNC, O_CREAT, or O_TMPFILE open,	 * it'll always -EAGAIN. Note that we test for __O_TMPFILE because	 * O_TMPFILE includes O_DIRECTORY, which isn't a flag we need to force	 * async for.	 */	return open->how.flags & (O_TRUNC | O_CREAT | __O_TMPFILE);} static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);	const char __user *fname;	int ret; 	if (unlikely(sqe->buf_index))		return -EINVAL;	if (unlikely(req->flags & REQ_F_FIXED_FILE))		return -EBADF; 	/* open.how should be already initialised */	if (!(open->how.flags & O_PATH) && force_o_largefile())		open->how.flags |= O_LARGEFILE; 	open->dfd = READ_ONCE(sqe->fd);	fname = u64_to_user_ptr(READ_ONCE(sqe->addr));	ret = delayed_getname(&open->filename, fname);	if (unlikely(ret))		return ret;	req->flags |= REQ_F_NEED_CLEANUP; 	open->file_slot = READ_ONCE(sqe->file_index);	if (open->file_slot && (open->how.flags & O_CLOEXEC))		return -EINVAL; 	open->nofile = rlimit(RLIMIT_NOFILE);	if (io_openat_force_async(open))		req->flags |= REQ_F_FORCE_ASYNC;	return 0;} void io_openat_bpf_populate(struct io_uring_bpf_ctx *bctx, struct io_kiocb *req){	struct io_open *open = io_kiocb_to_cmd(req, struct io_open); 	bctx->open.flags = open->how.flags;	bctx->open.mode = open->how.mode;	bctx->open.resolve = open->how.resolve;} int io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);	u64 mode = READ_ONCE(sqe->len);	u64 flags = READ_ONCE(sqe->open_flags); 	open->how = build_open_how(flags, mode);	return __io_openat_prep(req, sqe);} int io_openat2_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);	struct open_how __user *how;	size_t len;	int ret; 	how = u64_to_user_ptr(READ_ONCE(sqe->addr2));	len = READ_ONCE(sqe->len);	if (len < OPEN_HOW_SIZE_VER0)		return -EINVAL; 	ret = copy_struct_from_user(&open->how, sizeof(open->how), how, len);	if (ret)		return ret; 	return __io_openat_prep(req, sqe);} int io_openat2(struct io_kiocb *req, unsigned int issue_flags){	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);	struct open_flags op;	struct file *file;	bool resolve_nonblock, nonblock_set;	bool fixed = !!open->file_slot;	CLASS(filename_complete_delayed, name)(&open->filename);	int ret; 	ret = build_open_flags(&open->how, &op);	if (ret)		goto err;	nonblock_set = op.open_flag & O_NONBLOCK;	resolve_nonblock = open->how.resolve & RESOLVE_CACHED;	if (issue_flags & IO_URING_F_NONBLOCK) {		WARN_ON_ONCE(io_openat_force_async(open));		op.lookup_flags |= LOOKUP_CACHED;		op.open_flag |= O_NONBLOCK;	} 	if (!fixed) {		ret = __get_unused_fd_flags(open->how.flags, open->nofile);		if (ret < 0)			goto err;	} 	file = do_file_open(open->dfd, name, &op);	if (IS_ERR(file)) {		/*		 * We could hang on to this 'fd' on retrying, but seems like		 * marginal gain for something that is now known to be a slower		 * path. So just put it, and we'll get a new one when we retry.		 */		if (!fixed)			put_unused_fd(ret); 		ret = PTR_ERR(file);		/* only retry if RESOLVE_CACHED wasn't already set by application */		if (ret == -EAGAIN && !resolve_nonblock &&		    (issue_flags & IO_URING_F_NONBLOCK)) {			ret = putname_to_delayed(&open->filename,						 no_free_ptr(name));			if (likely(!ret))				return -EAGAIN;		}		goto err;	} 	if ((issue_flags & IO_URING_F_NONBLOCK) && !nonblock_set)		file->f_flags &= ~O_NONBLOCK; 	if (!fixed)		fd_install(ret, file);	else		ret = io_fixed_fd_install(req, issue_flags, file,						open->file_slot);err:	req->flags &= ~REQ_F_NEED_CLEANUP;	if (ret < 0)		req_set_fail(req);	io_req_set_res(req, ret, 0);	return IOU_COMPLETE;} int io_openat(struct io_kiocb *req, unsigned int issue_flags){	return io_openat2(req, issue_flags);} void io_open_cleanup(struct io_kiocb *req){	struct io_open *open = io_kiocb_to_cmd(req, struct io_open); 	dismiss_delayed_filename(&open->filename);} int __io_close_fixed(struct io_ring_ctx *ctx, unsigned int issue_flags,		     unsigned int offset){	int ret; 	io_ring_submit_lock(ctx, issue_flags);	ret = io_fixed_fd_remove(ctx, offset);	io_ring_submit_unlock(ctx, issue_flags); 	return ret;} static inline int io_close_fixed(struct io_kiocb *req, unsigned int issue_flags){	struct io_close *close = io_kiocb_to_cmd(req, struct io_close); 	return __io_close_fixed(req->ctx, issue_flags, close->file_slot - 1);} int io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_close *close = io_kiocb_to_cmd(req, struct io_close); 	if (sqe->off || sqe->addr || sqe->len || sqe->rw_flags || sqe->buf_index)		return -EINVAL;	if (req->flags & REQ_F_FIXED_FILE)		return -EBADF; 	close->fd = READ_ONCE(sqe->fd);	close->file_slot = READ_ONCE(sqe->file_index);	if (close->file_slot && close->fd)		return -EINVAL; 	return 0;} int io_close(struct io_kiocb *req, unsigned int issue_flags){	struct files_struct *files = current->files;	struct io_close *close = io_kiocb_to_cmd(req, struct io_close);	struct file *file;	int ret = -EBADF; 	if (close->file_slot) {		ret = io_close_fixed(req, issue_flags);		goto err;	} 	spin_lock(&files->file_lock);	file = files_lookup_fd_locked(files, close->fd);	if (!file || io_is_uring_fops(file)) {		spin_unlock(&files->file_lock);		goto err;	} 	/* if the file has a flush method, be safe and punt to async */	if (file->f_op->flush && (issue_flags & IO_URING_F_NONBLOCK)) {		spin_unlock(&files->file_lock);		return -EAGAIN;	} 	file = file_close_fd_locked(files, close->fd);	spin_unlock(&files->file_lock);	if (!file)		goto err; 	/* No ->flush() or already async, safely close from here */	ret = filp_close(file, current->files);err:	if (ret < 0)		req_set_fail(req);	io_req_set_res(req, ret, 0);	return IOU_COMPLETE;} int io_install_fixed_fd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_fixed_install *ifi;	unsigned int flags; 	if (sqe->off || sqe->addr || sqe->len || sqe->buf_index ||	    sqe->splice_fd_in || sqe->addr3)		return -EINVAL; 	/* must be a fixed file */	if (!(req->flags & REQ_F_FIXED_FILE))		return -EBADF; 	flags = READ_ONCE(sqe->install_fd_flags);	if (flags & ~IORING_FIXED_FD_NO_CLOEXEC)		return -EINVAL; 	/* ensure the task's creds are used when installing/receiving fds */	if (req->flags & REQ_F_CREDS)		return -EPERM; 	/* default to O_CLOEXEC, disable if IORING_FIXED_FD_NO_CLOEXEC is set */	ifi = io_kiocb_to_cmd(req, struct io_fixed_install);	ifi->o_flags = O_CLOEXEC;	if (flags & IORING_FIXED_FD_NO_CLOEXEC)		ifi->o_flags = 0; 	return 0;} int io_install_fixed_fd(struct io_kiocb *req, unsigned int issue_flags){	struct io_fixed_install *ifi;	int ret; 	ifi = io_kiocb_to_cmd(req, struct io_fixed_install);	ret = receive_fd(req->file, NULL, ifi->o_flags);	if (ret < 0)		req_set_fail(req);	io_req_set_res(req, ret, 0);	return IOU_COMPLETE;} struct io_pipe {	struct file *file;	int __user *fds;	int flags;	int file_slot;	unsigned long nofile;}; int io_pipe_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe); 	if (sqe->fd || sqe->off || sqe->addr3)		return -EINVAL; 	p->fds = u64_to_user_ptr(READ_ONCE(sqe->addr));	p->flags = READ_ONCE(sqe->pipe_flags);	if (p->flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT | O_NOTIFICATION_PIPE))		return -EINVAL; 	p->file_slot = READ_ONCE(sqe->file_index);	p->nofile = rlimit(RLIMIT_NOFILE);	return 0;} static int io_pipe_fixed(struct io_kiocb *req, struct file **files,			 unsigned int issue_flags){	struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);	struct io_ring_ctx *ctx = req->ctx;	bool alloc_slot;	int ret, fds[2] = { -1, -1 };	int slot = p->file_slot; 	if (p->flags & O_CLOEXEC)		return -EINVAL; 	alloc_slot = slot == IORING_FILE_INDEX_ALLOC; 	io_ring_submit_lock(ctx, issue_flags); 	ret = __io_fixed_fd_install(ctx, files[0], slot);	if (ret < 0)		goto err;	fds[0] = alloc_slot ? ret : slot - 1;	files[0] = NULL; 	/*	 * If a specific slot is given, next one will be used for	 * the write side.	 */	if (!alloc_slot)		slot++; 	ret = __io_fixed_fd_install(ctx, files[1], slot);	if (ret < 0)		goto err;	fds[1] = alloc_slot ? ret : slot - 1;	files[1] = NULL; 	io_ring_submit_unlock(ctx, issue_flags); 	if (!copy_to_user(p->fds, fds, sizeof(fds)))		return 0; 	ret = -EFAULT;	io_ring_submit_lock(ctx, issue_flags);err:	if (fds[0] != -1)		io_fixed_fd_remove(ctx, fds[0]);	if (fds[1] != -1)		io_fixed_fd_remove(ctx, fds[1]);	io_ring_submit_unlock(ctx, issue_flags);	return ret;} static int io_pipe_fd(struct io_kiocb *req, struct file **files){	struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);	int ret, fds[2] = { -1, -1 }; 	ret = __get_unused_fd_flags(p->flags, p->nofile);	if (ret < 0)		goto err;	fds[0] = ret; 	ret = __get_unused_fd_flags(p->flags, p->nofile);	if (ret < 0)		goto err;	fds[1] = ret; 	if (!copy_to_user(p->fds, fds, sizeof(fds))) {		fd_install(fds[0], files[0]);		fd_install(fds[1], files[1]);		return 0;	}	ret = -EFAULT;err:	if (fds[0] != -1)		put_unused_fd(fds[0]);	if (fds[1] != -1)		put_unused_fd(fds[1]);	return ret;} int io_pipe(struct io_kiocb *req, unsigned int issue_flags){	struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);	struct file *files[2];	int ret; 	ret = create_pipe_files(files, p->flags);	if (ret)		return ret; 	if (!!p->file_slot)		ret = io_pipe_fixed(req, files, issue_flags);	else		ret = io_pipe_fd(req, files); 	io_req_set_res(req, ret, 0);	if (!ret)		return IOU_COMPLETE; 	req_set_fail(req);	if (files[0])		fput(files[0]);	if (files[1])		fput(files[1]);	return ret;}