162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/fs.h>
562306a36Sopenharmony_ci#include <linux/file.h>
662306a36Sopenharmony_ci#include <linux/fdtable.h>
762306a36Sopenharmony_ci#include <linux/fsnotify.h>
862306a36Sopenharmony_ci#include <linux/namei.h>
962306a36Sopenharmony_ci#include <linux/io_uring.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <uapi/linux/io_uring.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "../fs/internal.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "io_uring.h"
1662306a36Sopenharmony_ci#include "rsrc.h"
1762306a36Sopenharmony_ci#include "openclose.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct io_open {
2062306a36Sopenharmony_ci	struct file			*file;
2162306a36Sopenharmony_ci	int				dfd;
2262306a36Sopenharmony_ci	u32				file_slot;
2362306a36Sopenharmony_ci	struct filename			*filename;
2462306a36Sopenharmony_ci	struct open_how			how;
2562306a36Sopenharmony_ci	unsigned long			nofile;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct io_close {
2962306a36Sopenharmony_ci	struct file			*file;
3062306a36Sopenharmony_ci	int				fd;
3162306a36Sopenharmony_ci	u32				file_slot;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic bool io_openat_force_async(struct io_open *open)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	/*
3762306a36Sopenharmony_ci	 * Don't bother trying for O_TRUNC, O_CREAT, or O_TMPFILE open,
3862306a36Sopenharmony_ci	 * it'll always -EAGAIN. Note that we test for __O_TMPFILE because
3962306a36Sopenharmony_ci	 * O_TMPFILE includes O_DIRECTORY, which isn't a flag we need to force
4062306a36Sopenharmony_ci	 * async for.
4162306a36Sopenharmony_ci	 */
4262306a36Sopenharmony_ci	return open->how.flags & (O_TRUNC | O_CREAT | __O_TMPFILE);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);
4862306a36Sopenharmony_ci	const char __user *fname;
4962306a36Sopenharmony_ci	int ret;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (unlikely(sqe->buf_index))
5262306a36Sopenharmony_ci		return -EINVAL;
5362306a36Sopenharmony_ci	if (unlikely(req->flags & REQ_F_FIXED_FILE))
5462306a36Sopenharmony_ci		return -EBADF;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* open.how should be already initialised */
5762306a36Sopenharmony_ci	if (!(open->how.flags & O_PATH) && force_o_largefile())
5862306a36Sopenharmony_ci		open->how.flags |= O_LARGEFILE;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	open->dfd = READ_ONCE(sqe->fd);
6162306a36Sopenharmony_ci	fname = u64_to_user_ptr(READ_ONCE(sqe->addr));
6262306a36Sopenharmony_ci	open->filename = getname(fname);
6362306a36Sopenharmony_ci	if (IS_ERR(open->filename)) {
6462306a36Sopenharmony_ci		ret = PTR_ERR(open->filename);
6562306a36Sopenharmony_ci		open->filename = NULL;
6662306a36Sopenharmony_ci		return ret;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	open->file_slot = READ_ONCE(sqe->file_index);
7062306a36Sopenharmony_ci	if (open->file_slot && (open->how.flags & O_CLOEXEC))
7162306a36Sopenharmony_ci		return -EINVAL;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	open->nofile = rlimit(RLIMIT_NOFILE);
7462306a36Sopenharmony_ci	req->flags |= REQ_F_NEED_CLEANUP;
7562306a36Sopenharmony_ci	if (io_openat_force_async(open))
7662306a36Sopenharmony_ci		req->flags |= REQ_F_FORCE_ASYNC;
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciint io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);
8362306a36Sopenharmony_ci	u64 mode = READ_ONCE(sqe->len);
8462306a36Sopenharmony_ci	u64 flags = READ_ONCE(sqe->open_flags);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	open->how = build_open_how(flags, mode);
8762306a36Sopenharmony_ci	return __io_openat_prep(req, sqe);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciint io_openat2_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);
9362306a36Sopenharmony_ci	struct open_how __user *how;
9462306a36Sopenharmony_ci	size_t len;
9562306a36Sopenharmony_ci	int ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	how = u64_to_user_ptr(READ_ONCE(sqe->addr2));
9862306a36Sopenharmony_ci	len = READ_ONCE(sqe->len);
9962306a36Sopenharmony_ci	if (len < OPEN_HOW_SIZE_VER0)
10062306a36Sopenharmony_ci		return -EINVAL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	ret = copy_struct_from_user(&open->how, sizeof(open->how), how, len);
10362306a36Sopenharmony_ci	if (ret)
10462306a36Sopenharmony_ci		return ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return __io_openat_prep(req, sqe);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciint io_openat2(struct io_kiocb *req, unsigned int issue_flags)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);
11262306a36Sopenharmony_ci	struct open_flags op;
11362306a36Sopenharmony_ci	struct file *file;
11462306a36Sopenharmony_ci	bool resolve_nonblock, nonblock_set;
11562306a36Sopenharmony_ci	bool fixed = !!open->file_slot;
11662306a36Sopenharmony_ci	int ret;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ret = build_open_flags(&open->how, &op);
11962306a36Sopenharmony_ci	if (ret)
12062306a36Sopenharmony_ci		goto err;
12162306a36Sopenharmony_ci	nonblock_set = op.open_flag & O_NONBLOCK;
12262306a36Sopenharmony_ci	resolve_nonblock = open->how.resolve & RESOLVE_CACHED;
12362306a36Sopenharmony_ci	if (issue_flags & IO_URING_F_NONBLOCK) {
12462306a36Sopenharmony_ci		WARN_ON_ONCE(io_openat_force_async(open));
12562306a36Sopenharmony_ci		op.lookup_flags |= LOOKUP_CACHED;
12662306a36Sopenharmony_ci		op.open_flag |= O_NONBLOCK;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (!fixed) {
13062306a36Sopenharmony_ci		ret = __get_unused_fd_flags(open->how.flags, open->nofile);
13162306a36Sopenharmony_ci		if (ret < 0)
13262306a36Sopenharmony_ci			goto err;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	file = do_filp_open(open->dfd, open->filename, &op);
13662306a36Sopenharmony_ci	if (IS_ERR(file)) {
13762306a36Sopenharmony_ci		/*
13862306a36Sopenharmony_ci		 * We could hang on to this 'fd' on retrying, but seems like
13962306a36Sopenharmony_ci		 * marginal gain for something that is now known to be a slower
14062306a36Sopenharmony_ci		 * path. So just put it, and we'll get a new one when we retry.
14162306a36Sopenharmony_ci		 */
14262306a36Sopenharmony_ci		if (!fixed)
14362306a36Sopenharmony_ci			put_unused_fd(ret);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		ret = PTR_ERR(file);
14662306a36Sopenharmony_ci		/* only retry if RESOLVE_CACHED wasn't already set by application */
14762306a36Sopenharmony_ci		if (ret == -EAGAIN &&
14862306a36Sopenharmony_ci		    (!resolve_nonblock && (issue_flags & IO_URING_F_NONBLOCK)))
14962306a36Sopenharmony_ci			return -EAGAIN;
15062306a36Sopenharmony_ci		goto err;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if ((issue_flags & IO_URING_F_NONBLOCK) && !nonblock_set)
15462306a36Sopenharmony_ci		file->f_flags &= ~O_NONBLOCK;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!fixed)
15762306a36Sopenharmony_ci		fd_install(ret, file);
15862306a36Sopenharmony_ci	else
15962306a36Sopenharmony_ci		ret = io_fixed_fd_install(req, issue_flags, file,
16062306a36Sopenharmony_ci						open->file_slot);
16162306a36Sopenharmony_cierr:
16262306a36Sopenharmony_ci	putname(open->filename);
16362306a36Sopenharmony_ci	req->flags &= ~REQ_F_NEED_CLEANUP;
16462306a36Sopenharmony_ci	if (ret < 0)
16562306a36Sopenharmony_ci		req_set_fail(req);
16662306a36Sopenharmony_ci	io_req_set_res(req, ret, 0);
16762306a36Sopenharmony_ci	return IOU_OK;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciint io_openat(struct io_kiocb *req, unsigned int issue_flags)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return io_openat2(req, issue_flags);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_civoid io_open_cleanup(struct io_kiocb *req)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct io_open *open = io_kiocb_to_cmd(req, struct io_open);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (open->filename)
18062306a36Sopenharmony_ci		putname(open->filename);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciint __io_close_fixed(struct io_ring_ctx *ctx, unsigned int issue_flags,
18462306a36Sopenharmony_ci		     unsigned int offset)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	int ret;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	io_ring_submit_lock(ctx, issue_flags);
18962306a36Sopenharmony_ci	ret = io_fixed_fd_remove(ctx, offset);
19062306a36Sopenharmony_ci	io_ring_submit_unlock(ctx, issue_flags);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return ret;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic inline int io_close_fixed(struct io_kiocb *req, unsigned int issue_flags)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct io_close *close = io_kiocb_to_cmd(req, struct io_close);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return __io_close_fixed(req->ctx, issue_flags, close->file_slot - 1);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciint io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct io_close *close = io_kiocb_to_cmd(req, struct io_close);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (sqe->off || sqe->addr || sqe->len || sqe->rw_flags || sqe->buf_index)
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci	if (req->flags & REQ_F_FIXED_FILE)
20962306a36Sopenharmony_ci		return -EBADF;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	close->fd = READ_ONCE(sqe->fd);
21262306a36Sopenharmony_ci	close->file_slot = READ_ONCE(sqe->file_index);
21362306a36Sopenharmony_ci	if (close->file_slot && close->fd)
21462306a36Sopenharmony_ci		return -EINVAL;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ciint io_close(struct io_kiocb *req, unsigned int issue_flags)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct files_struct *files = current->files;
22262306a36Sopenharmony_ci	struct io_close *close = io_kiocb_to_cmd(req, struct io_close);
22362306a36Sopenharmony_ci	struct fdtable *fdt;
22462306a36Sopenharmony_ci	struct file *file;
22562306a36Sopenharmony_ci	int ret = -EBADF;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (close->file_slot) {
22862306a36Sopenharmony_ci		ret = io_close_fixed(req, issue_flags);
22962306a36Sopenharmony_ci		goto err;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	spin_lock(&files->file_lock);
23362306a36Sopenharmony_ci	fdt = files_fdtable(files);
23462306a36Sopenharmony_ci	if (close->fd >= fdt->max_fds) {
23562306a36Sopenharmony_ci		spin_unlock(&files->file_lock);
23662306a36Sopenharmony_ci		goto err;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	file = rcu_dereference_protected(fdt->fd[close->fd],
23962306a36Sopenharmony_ci			lockdep_is_held(&files->file_lock));
24062306a36Sopenharmony_ci	if (!file || io_is_uring_fops(file)) {
24162306a36Sopenharmony_ci		spin_unlock(&files->file_lock);
24262306a36Sopenharmony_ci		goto err;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* if the file has a flush method, be safe and punt to async */
24662306a36Sopenharmony_ci	if (file->f_op->flush && (issue_flags & IO_URING_F_NONBLOCK)) {
24762306a36Sopenharmony_ci		spin_unlock(&files->file_lock);
24862306a36Sopenharmony_ci		return -EAGAIN;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	file = __close_fd_get_file(close->fd);
25262306a36Sopenharmony_ci	spin_unlock(&files->file_lock);
25362306a36Sopenharmony_ci	if (!file)
25462306a36Sopenharmony_ci		goto err;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* No ->flush() or already async, safely close from here */
25762306a36Sopenharmony_ci	ret = filp_close(file, current->files);
25862306a36Sopenharmony_cierr:
25962306a36Sopenharmony_ci	if (ret < 0)
26062306a36Sopenharmony_ci		req_set_fail(req);
26162306a36Sopenharmony_ci	io_req_set_res(req, ret, 0);
26262306a36Sopenharmony_ci	return IOU_OK;
26362306a36Sopenharmony_ci}
264