Commit b080cee7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'for-5.18/io_uring-statx-2022-03-18' of git://git.kernel.dk/linux-block

Pull io_uring statx fixes from Jens Axboe:
 "On top of the main io_uring branch, this is to ensure that the
  filename component of statx is stable after submit.

  That requires a few VFS related changes"

* tag 'for-5.18/io_uring-statx-2022-03-18' of git://git.kernel.dk/linux-block:
  io-uring: Make statx API stable
parents af472a9e 1b6fe6e0
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -184,7 +184,9 @@ int sb_init_dio_done_wq(struct super_block *sb);
/*
 * fs/stat.c:
 */
int do_statx(int dfd, const char __user *filename, unsigned flags,

int getname_statx_lookup_flags(int flags);
int do_statx(int dfd, struct filename *filename, unsigned int flags,
	     unsigned int mask, struct statx __user *buffer);

/*
+20 −2
Original line number Diff line number Diff line
@@ -675,7 +675,7 @@ struct io_statx {
	int				dfd;
	unsigned int			mask;
	unsigned int			flags;
	const char __user		*filename;
	struct filename			*filename;
	struct statx __user		*buffer;
};

@@ -5000,6 +5000,8 @@ static int io_fadvise(struct io_kiocb *req, unsigned int issue_flags)

static int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
	const char __user *path;

	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
		return -EINVAL;
	if (sqe->ioprio || sqe->buf_index || sqe->splice_fd_in)
@@ -5009,10 +5011,22 @@ static int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)

	req->statx.dfd = READ_ONCE(sqe->fd);
	req->statx.mask = READ_ONCE(sqe->len);
	req->statx.filename = u64_to_user_ptr(READ_ONCE(sqe->addr));
	path = u64_to_user_ptr(READ_ONCE(sqe->addr));
	req->statx.buffer = u64_to_user_ptr(READ_ONCE(sqe->addr2));
	req->statx.flags = READ_ONCE(sqe->statx_flags);

	req->statx.filename = getname_flags(path,
					getname_statx_lookup_flags(req->statx.flags),
					NULL);

	if (IS_ERR(req->statx.filename)) {
		int ret = PTR_ERR(req->statx.filename);

		req->statx.filename = NULL;
		return ret;
	}

	req->flags |= REQ_F_NEED_CLEANUP;
	return 0;
}

@@ -7112,6 +7126,10 @@ static void io_clean_op(struct io_kiocb *req)
			putname(req->hardlink.oldpath);
			putname(req->hardlink.newpath);
			break;
		case IORING_OP_STATX:
			if (req->statx.filename)
				putname(req->statx.filename);
			break;
		}
	}
	if ((req->flags & REQ_F_POLLED) && req->apoll) {
+35 −14
Original line number Diff line number Diff line
@@ -184,6 +184,20 @@ int vfs_fstat(int fd, struct kstat *stat)
	return error;
}

int getname_statx_lookup_flags(int flags)
{
	int lookup_flags = 0;

	if (!(flags & AT_SYMLINK_NOFOLLOW))
		lookup_flags |= LOOKUP_FOLLOW;
	if (!(flags & AT_NO_AUTOMOUNT))
		lookup_flags |= LOOKUP_AUTOMOUNT;
	if (flags & AT_EMPTY_PATH)
		lookup_flags |= LOOKUP_EMPTY;

	return lookup_flags;
}

/**
 * vfs_statx - Get basic and extra attributes by filename
 * @dfd: A file descriptor representing the base dir for a relative filename
@@ -199,26 +213,19 @@ int vfs_fstat(int fd, struct kstat *stat)
 *
 * 0 will be returned on success, and a -ve error code if unsuccessful.
 */
static int vfs_statx(int dfd, const char __user *filename, int flags,
static int vfs_statx(int dfd, struct filename *filename, int flags,
	      struct kstat *stat, u32 request_mask)
{
	struct path path;
	unsigned lookup_flags = 0;
	unsigned int lookup_flags = getname_statx_lookup_flags(flags);
	int error;

	if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |
		      AT_STATX_SYNC_TYPE))
		return -EINVAL;

	if (!(flags & AT_SYMLINK_NOFOLLOW))
		lookup_flags |= LOOKUP_FOLLOW;
	if (!(flags & AT_NO_AUTOMOUNT))
		lookup_flags |= LOOKUP_AUTOMOUNT;
	if (flags & AT_EMPTY_PATH)
		lookup_flags |= LOOKUP_EMPTY;

retry:
	error = user_path_at(dfd, filename, lookup_flags, &path);
	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
	if (error)
		goto out;

@@ -240,8 +247,15 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
int vfs_fstatat(int dfd, const char __user *filename,
			      struct kstat *stat, int flags)
{
	return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT,
			 stat, STATX_BASIC_STATS);
	int ret;
	int statx_flags = flags | AT_NO_AUTOMOUNT;
	struct filename *name;

	name = getname_flags(filename, getname_statx_lookup_flags(statx_flags), NULL);
	ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
	putname(name);

	return ret;
}

#ifdef __ARCH_WANT_OLD_STAT
@@ -602,7 +616,7 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
	return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}

int do_statx(int dfd, const char __user *filename, unsigned flags,
int do_statx(int dfd, struct filename *filename, unsigned int flags,
	     unsigned int mask, struct statx __user *buffer)
{
	struct kstat stat;
@@ -636,7 +650,14 @@ SYSCALL_DEFINE5(statx,
		unsigned int, mask,
		struct statx __user *, buffer)
{
	return do_statx(dfd, filename, flags, mask, buffer);
	int ret;
	struct filename *name;

	name = getname_flags(filename, getname_statx_lookup_flags(flags), NULL);
	ret = do_statx(dfd, name, flags, mask, buffer);
	putname(name);

	return ret;
}

#ifdef CONFIG_COMPAT