Commit 1b6fe6e0 authored by Stefan Roesch's avatar Stefan Roesch Committed by Jens Axboe

io-uring: Make statx API stable

One of the key architectual tenets is to keep the parameters for
io-uring stable. After the call has been submitted, its value can
be changed. Unfortunaltely this is not the case for the current statx
implementation.

IO-Uring change:
This changes replaces the const char * filename pointer in the io_statx
structure with a struct filename *. In addition it also creates the
filename object during the prepare phase.

With this change, the opcode also needs to invoke cleanup, so the
filename object gets freed after processing the request.

fs change:
This replaces the const char* __user filename parameter in the two
functions do_statx and vfs_statx with a struct filename *. In addition
to be able to correctly construct a filename object a new helper
function getname_statx_lookup_flags is introduced. The function makes
sure that do_statx and vfs_statx is invoked with the correct lookup flags.
Signed-off-by: default avatarStefan Roesch <shr@fb.com>
Link: https://lore.kernel.org/r/20220225185326.1373304-2-shr@fb.comSigned-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent adc8682e
...@@ -184,7 +184,9 @@ int sb_init_dio_done_wq(struct super_block *sb); ...@@ -184,7 +184,9 @@ int sb_init_dio_done_wq(struct super_block *sb);
/* /*
* fs/stat.c: * 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); unsigned int mask, struct statx __user *buffer);
/* /*
......
...@@ -664,7 +664,7 @@ struct io_statx { ...@@ -664,7 +664,7 @@ struct io_statx {
int dfd; int dfd;
unsigned int mask; unsigned int mask;
unsigned int flags; unsigned int flags;
const char __user *filename; struct filename *filename;
struct statx __user *buffer; struct statx __user *buffer;
}; };
...@@ -4913,6 +4913,8 @@ static int io_fadvise(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4913,6 +4913,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) 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)) if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
return -EINVAL; return -EINVAL;
if (sqe->ioprio || sqe->buf_index || sqe->splice_fd_in) if (sqe->ioprio || sqe->buf_index || sqe->splice_fd_in)
...@@ -4922,10 +4924,22 @@ static int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) ...@@ -4922,10 +4924,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.dfd = READ_ONCE(sqe->fd);
req->statx.mask = READ_ONCE(sqe->len); 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.buffer = u64_to_user_ptr(READ_ONCE(sqe->addr2));
req->statx.flags = READ_ONCE(sqe->statx_flags); 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; return 0;
} }
...@@ -7007,6 +7021,10 @@ static void io_clean_op(struct io_kiocb *req) ...@@ -7007,6 +7021,10 @@ static void io_clean_op(struct io_kiocb *req)
putname(req->hardlink.oldpath); putname(req->hardlink.oldpath);
putname(req->hardlink.newpath); putname(req->hardlink.newpath);
break; break;
case IORING_OP_STATX:
if (req->statx.filename)
putname(req->statx.filename);
break;
} }
} }
if ((req->flags & REQ_F_POLLED) && req->apoll) { if ((req->flags & REQ_F_POLLED) && req->apoll) {
......
...@@ -184,6 +184,20 @@ int vfs_fstat(int fd, struct kstat *stat) ...@@ -184,6 +184,20 @@ int vfs_fstat(int fd, struct kstat *stat)
return error; 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 * vfs_statx - Get basic and extra attributes by filename
* @dfd: A file descriptor representing the base dir for a relative 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) ...@@ -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. * 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 kstat *stat, u32 request_mask)
{ {
struct path path; struct path path;
unsigned lookup_flags = 0; unsigned int lookup_flags = getname_statx_lookup_flags(flags);
int error; int error;
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |
AT_STATX_SYNC_TYPE)) AT_STATX_SYNC_TYPE))
return -EINVAL; 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: retry:
error = user_path_at(dfd, filename, lookup_flags, &path); error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
if (error) if (error)
goto out; goto out;
...@@ -240,8 +247,15 @@ static int vfs_statx(int dfd, const char __user *filename, int flags, ...@@ -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, int vfs_fstatat(int dfd, const char __user *filename,
struct kstat *stat, int flags) struct kstat *stat, int flags)
{ {
return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT, int ret;
stat, STATX_BASIC_STATS); 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 #ifdef __ARCH_WANT_OLD_STAT
...@@ -602,7 +616,7 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer) ...@@ -602,7 +616,7 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0; 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) unsigned int mask, struct statx __user *buffer)
{ {
struct kstat stat; struct kstat stat;
...@@ -636,7 +650,14 @@ SYSCALL_DEFINE5(statx, ...@@ -636,7 +650,14 @@ SYSCALL_DEFINE5(statx,
unsigned int, mask, unsigned int, mask,
struct statx __user *, buffer) 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 #ifdef CONFIG_COMPAT
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment