Commit 09beaff7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.19/io_uring-xattr-2022-05-22' of git://git.kernel.dk/linux-block

Pull io_uring xattr support from Jens Axboe:
 "Support for the xattr variants"

* tag 'for-5.19/io_uring-xattr-2022-05-22' of git://git.kernel.dk/linux-block:
  io_uring: cleanup error-handling around io_req_complete
  io_uring: fix trace for reduced sqe padding
  io_uring: add fgetxattr and getxattr support
  io_uring: add fsetxattr and setxattr support
  fs: split off do_getxattr from getxattr
  fs: split off setxattr_copy and do_setxattr function from setxattr
parents 3a166bdb 4ffaa94b
...@@ -191,3 +191,32 @@ long splice_file_to_pipe(struct file *in, ...@@ -191,3 +191,32 @@ long splice_file_to_pipe(struct file *in,
struct pipe_inode_info *opipe, struct pipe_inode_info *opipe,
loff_t *offset, loff_t *offset,
size_t len, unsigned int flags); size_t len, unsigned int flags);
/*
* fs/xattr.c:
*/
struct xattr_name {
char name[XATTR_NAME_MAX + 1];
};
struct xattr_ctx {
/* Value of attribute */
union {
const void __user *cvalue;
void __user *value;
};
void *kvalue;
size_t size;
/* Attribute name */
struct xattr_name *kname;
unsigned int flags;
};
ssize_t do_getxattr(struct user_namespace *mnt_userns,
struct dentry *d,
struct xattr_ctx *ctx);
int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct xattr_ctx *ctx);
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
#include <linux/io_uring.h> #include <linux/io_uring.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/xattr.h>
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/io_uring.h> #include <trace/events/io_uring.h>
...@@ -803,6 +804,12 @@ struct io_async_rw { ...@@ -803,6 +804,12 @@ struct io_async_rw {
struct wait_page_queue wpq; struct wait_page_queue wpq;
}; };
struct io_xattr {
struct file *file;
struct xattr_ctx ctx;
struct filename *filename;
};
enum { enum {
REQ_F_FIXED_FILE_BIT = IOSQE_FIXED_FILE_BIT, REQ_F_FIXED_FILE_BIT = IOSQE_FIXED_FILE_BIT,
REQ_F_IO_DRAIN_BIT = IOSQE_IO_DRAIN_BIT, REQ_F_IO_DRAIN_BIT = IOSQE_IO_DRAIN_BIT,
...@@ -973,6 +980,7 @@ struct io_kiocb { ...@@ -973,6 +980,7 @@ struct io_kiocb {
struct io_symlink symlink; struct io_symlink symlink;
struct io_hardlink hardlink; struct io_hardlink hardlink;
struct io_msg msg; struct io_msg msg;
struct io_xattr xattr;
}; };
u8 opcode; u8 opcode;
...@@ -1281,6 +1289,14 @@ static const struct io_op_def io_op_defs[] = { ...@@ -1281,6 +1289,14 @@ static const struct io_op_def io_op_defs[] = {
.needs_file = 1, .needs_file = 1,
.iopoll = 1, .iopoll = 1,
}, },
[IORING_OP_FSETXATTR] = {
.needs_file = 1
},
[IORING_OP_SETXATTR] = {},
[IORING_OP_FGETXATTR] = {
.needs_file = 1
},
[IORING_OP_GETXATTR] = {},
}; };
/* requests with any of those set should undergo io_disarm_next() */ /* requests with any of those set should undergo io_disarm_next() */
...@@ -2333,6 +2349,8 @@ static inline void __io_req_complete(struct io_kiocb *req, unsigned issue_flags, ...@@ -2333,6 +2349,8 @@ static inline void __io_req_complete(struct io_kiocb *req, unsigned issue_flags,
static inline void io_req_complete(struct io_kiocb *req, s32 res) static inline void io_req_complete(struct io_kiocb *req, s32 res)
{ {
if (res < 0)
req_set_fail(req);
__io_req_complete(req, 0, res, 0); __io_req_complete(req, 0, res, 0);
} }
...@@ -4299,12 +4317,258 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4299,12 +4317,258 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
ren->newpath, ren->flags); ren->newpath, ren->flags);
req->flags &= ~REQ_F_NEED_CLEANUP; req->flags &= ~REQ_F_NEED_CLEANUP;
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
static inline void __io_xattr_finish(struct io_kiocb *req)
{
struct io_xattr *ix = &req->xattr;
if (ix->filename)
putname(ix->filename);
kfree(ix->ctx.kname);
kvfree(ix->ctx.kvalue);
}
static void io_xattr_finish(struct io_kiocb *req, int ret)
{
req->flags &= ~REQ_F_NEED_CLEANUP;
__io_xattr_finish(req);
io_req_complete(req, ret);
}
static int __io_getxattr_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe)
{
struct io_xattr *ix = &req->xattr;
const char __user *name;
int ret;
if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
return -EINVAL;
if (unlikely(sqe->ioprio))
return -EINVAL;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
ix->filename = NULL;
ix->ctx.kvalue = NULL;
name = u64_to_user_ptr(READ_ONCE(sqe->addr));
ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
ix->ctx.size = READ_ONCE(sqe->len);
ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
if (ix->ctx.flags)
return -EINVAL;
ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
if (!ix->ctx.kname)
return -ENOMEM;
ret = strncpy_from_user(ix->ctx.kname->name, name,
sizeof(ix->ctx.kname->name));
if (!ret || ret == sizeof(ix->ctx.kname->name))
ret = -ERANGE;
if (ret < 0) {
kfree(ix->ctx.kname);
return ret;
}
req->flags |= REQ_F_NEED_CLEANUP;
return 0;
}
static int io_fgetxattr_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe)
{
return __io_getxattr_prep(req, sqe);
}
static int io_getxattr_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe)
{
struct io_xattr *ix = &req->xattr;
const char __user *path;
int ret;
ret = __io_getxattr_prep(req, sqe);
if (ret)
return ret;
path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
if (IS_ERR(ix->filename)) {
ret = PTR_ERR(ix->filename);
ix->filename = NULL;
}
return ret;
}
static int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_xattr *ix = &req->xattr;
int ret;
if (issue_flags & IO_URING_F_NONBLOCK)
return -EAGAIN;
ret = do_getxattr(mnt_user_ns(req->file->f_path.mnt),
req->file->f_path.dentry,
&ix->ctx);
io_xattr_finish(req, ret);
return 0;
}
static int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_xattr *ix = &req->xattr;
unsigned int lookup_flags = LOOKUP_FOLLOW;
struct path path;
int ret;
if (issue_flags & IO_URING_F_NONBLOCK)
return -EAGAIN;
retry:
ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
if (!ret) {
ret = do_getxattr(mnt_user_ns(path.mnt),
path.dentry,
&ix->ctx);
path_put(&path);
if (retry_estale(ret, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
}
io_xattr_finish(req, ret);
return 0;
}
static int __io_setxattr_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe)
{
struct io_xattr *ix = &req->xattr;
const char __user *name;
int ret;
if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
return -EINVAL;
if (unlikely(sqe->ioprio))
return -EINVAL;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
ix->filename = NULL;
name = u64_to_user_ptr(READ_ONCE(sqe->addr));
ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
ix->ctx.kvalue = NULL;
ix->ctx.size = READ_ONCE(sqe->len);
ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
if (!ix->ctx.kname)
return -ENOMEM;
ret = setxattr_copy(name, &ix->ctx);
if (ret) {
kfree(ix->ctx.kname);
return ret;
}
req->flags |= REQ_F_NEED_CLEANUP;
return 0;
}
static int io_setxattr_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe)
{
struct io_xattr *ix = &req->xattr;
const char __user *path;
int ret;
ret = __io_setxattr_prep(req, sqe);
if (ret)
return ret;
path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
if (IS_ERR(ix->filename)) {
ret = PTR_ERR(ix->filename);
ix->filename = NULL;
}
return ret;
}
static int io_fsetxattr_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe)
{
return __io_setxattr_prep(req, sqe);
}
static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
struct path *path)
{
struct io_xattr *ix = &req->xattr;
int ret;
ret = mnt_want_write(path->mnt);
if (!ret) {
ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
mnt_drop_write(path->mnt);
}
return ret;
}
static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
{
int ret;
if (issue_flags & IO_URING_F_NONBLOCK)
return -EAGAIN;
ret = __io_setxattr(req, issue_flags, &req->file->f_path);
io_xattr_finish(req, ret);
return 0;
}
static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_xattr *ix = &req->xattr;
unsigned int lookup_flags = LOOKUP_FOLLOW;
struct path path;
int ret;
if (issue_flags & IO_URING_F_NONBLOCK)
return -EAGAIN;
retry:
ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
if (!ret) {
ret = __io_setxattr(req, issue_flags, &path);
path_put(&path);
if (retry_estale(ret, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
}
io_xattr_finish(req, ret);
return 0;
}
static int io_unlinkat_prep(struct io_kiocb *req, static int io_unlinkat_prep(struct io_kiocb *req,
const struct io_uring_sqe *sqe) const struct io_uring_sqe *sqe)
{ {
...@@ -4345,8 +4609,6 @@ static int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4345,8 +4609,6 @@ static int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags)
ret = do_unlinkat(un->dfd, un->filename); ret = do_unlinkat(un->dfd, un->filename);
req->flags &= ~REQ_F_NEED_CLEANUP; req->flags &= ~REQ_F_NEED_CLEANUP;
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -4385,8 +4647,6 @@ static int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4385,8 +4647,6 @@ static int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags)
ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode); ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode);
req->flags &= ~REQ_F_NEED_CLEANUP; req->flags &= ~REQ_F_NEED_CLEANUP;
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -4431,8 +4691,6 @@ static int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4431,8 +4691,6 @@ static int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags)
ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath); ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath);
req->flags &= ~REQ_F_NEED_CLEANUP; req->flags &= ~REQ_F_NEED_CLEANUP;
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -4480,8 +4738,6 @@ static int io_linkat(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4480,8 +4738,6 @@ static int io_linkat(struct io_kiocb *req, unsigned int issue_flags)
lnk->newpath, lnk->flags); lnk->newpath, lnk->flags);
req->flags &= ~REQ_F_NEED_CLEANUP; req->flags &= ~REQ_F_NEED_CLEANUP;
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -4515,8 +4771,6 @@ static int io_shutdown(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4515,8 +4771,6 @@ static int io_shutdown(struct io_kiocb *req, unsigned int issue_flags)
return -ENOTSOCK; return -ENOTSOCK;
ret = __sys_shutdown_sock(sock, req->shutdown.how); ret = __sys_shutdown_sock(sock, req->shutdown.how);
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
#else #else
...@@ -4574,7 +4828,7 @@ static int io_tee(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4574,7 +4828,7 @@ static int io_tee(struct io_kiocb *req, unsigned int issue_flags)
done: done:
if (ret != sp->len) if (ret != sp->len)
req_set_fail(req); req_set_fail(req);
io_req_complete(req, ret); __io_req_complete(req, 0, ret, 0);
return 0; return 0;
} }
...@@ -4619,7 +4873,7 @@ static int io_splice(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4619,7 +4873,7 @@ static int io_splice(struct io_kiocb *req, unsigned int issue_flags)
done: done:
if (ret != sp->len) if (ret != sp->len)
req_set_fail(req); req_set_fail(req);
io_req_complete(req, ret); __io_req_complete(req, 0, ret, 0);
return 0; return 0;
} }
...@@ -4714,8 +4968,6 @@ static int io_fsync(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4714,8 +4968,6 @@ static int io_fsync(struct io_kiocb *req, unsigned int issue_flags)
ret = vfs_fsync_range(req->file, req->sync.off, ret = vfs_fsync_range(req->file, req->sync.off,
end > 0 ? end : LLONG_MAX, end > 0 ? end : LLONG_MAX,
req->sync.flags & IORING_FSYNC_DATASYNC); req->sync.flags & IORING_FSYNC_DATASYNC);
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -4741,9 +4993,7 @@ static int io_fallocate(struct io_kiocb *req, unsigned int issue_flags) ...@@ -4741,9 +4993,7 @@ static int io_fallocate(struct io_kiocb *req, unsigned int issue_flags)
return -EAGAIN; return -EAGAIN;
ret = vfs_fallocate(req->file, req->sync.mode, req->sync.off, ret = vfs_fallocate(req->file, req->sync.mode, req->sync.off,
req->sync.len); req->sync.len);
if (ret < 0) if (ret >= 0)
req_set_fail(req);
else
fsnotify_modify(req->file); fsnotify_modify(req->file);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
...@@ -5255,8 +5505,6 @@ static int io_madvise(struct io_kiocb *req, unsigned int issue_flags) ...@@ -5255,8 +5505,6 @@ static int io_madvise(struct io_kiocb *req, unsigned int issue_flags)
return -EAGAIN; return -EAGAIN;
ret = do_madvise(current->mm, ma->addr, ma->len, ma->advice); ret = do_madvise(current->mm, ma->addr, ma->len, ma->advice);
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
#else #else
...@@ -5338,9 +5586,6 @@ static int io_statx(struct io_kiocb *req, unsigned int issue_flags) ...@@ -5338,9 +5586,6 @@ static int io_statx(struct io_kiocb *req, unsigned int issue_flags)
ret = do_statx(ctx->dfd, ctx->filename, ctx->flags, ctx->mask, ret = do_statx(ctx->dfd, ctx->filename, ctx->flags, ctx->mask,
ctx->buffer); ctx->buffer);
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -5433,8 +5678,6 @@ static int io_sync_file_range(struct io_kiocb *req, unsigned int issue_flags) ...@@ -5433,8 +5678,6 @@ static int io_sync_file_range(struct io_kiocb *req, unsigned int issue_flags)
ret = sync_file_range(req->file, req->sync.off, req->sync.len, ret = sync_file_range(req->file, req->sync.off, req->sync.len,
req->sync.flags); req->sync.flags);
if (ret < 0)
req_set_fail(req);
io_req_complete(req, ret); io_req_complete(req, ret);
return 0; return 0;
} }
...@@ -7400,6 +7643,14 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) ...@@ -7400,6 +7643,14 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
return io_linkat_prep(req, sqe); return io_linkat_prep(req, sqe);
case IORING_OP_MSG_RING: case IORING_OP_MSG_RING:
return io_msg_ring_prep(req, sqe); return io_msg_ring_prep(req, sqe);
case IORING_OP_FSETXATTR:
return io_fsetxattr_prep(req, sqe);
case IORING_OP_SETXATTR:
return io_setxattr_prep(req, sqe);
case IORING_OP_FGETXATTR:
return io_fgetxattr_prep(req, sqe);
case IORING_OP_GETXATTR:
return io_getxattr_prep(req, sqe);
} }
printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n",
...@@ -7549,6 +7800,12 @@ static void io_clean_op(struct io_kiocb *req) ...@@ -7549,6 +7800,12 @@ static void io_clean_op(struct io_kiocb *req)
if (req->statx.filename) if (req->statx.filename)
putname(req->statx.filename); putname(req->statx.filename);
break; break;
case IORING_OP_SETXATTR:
case IORING_OP_FSETXATTR:
case IORING_OP_GETXATTR:
case IORING_OP_FGETXATTR:
__io_xattr_finish(req);
break;
} }
} }
if ((req->flags & REQ_F_POLLED) && req->apoll) { if ((req->flags & REQ_F_POLLED) && req->apoll) {
...@@ -7705,6 +7962,18 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) ...@@ -7705,6 +7962,18 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
case IORING_OP_MSG_RING: case IORING_OP_MSG_RING:
ret = io_msg_ring(req, issue_flags); ret = io_msg_ring(req, issue_flags);
break; break;
case IORING_OP_FSETXATTR:
ret = io_fsetxattr(req, issue_flags);
break;
case IORING_OP_SETXATTR:
ret = io_setxattr(req, issue_flags);
break;
case IORING_OP_FGETXATTR:
ret = io_fgetxattr(req, issue_flags);
break;
case IORING_OP_GETXATTR:
ret = io_getxattr(req, issue_flags);
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
break; break;
...@@ -12444,6 +12713,7 @@ static int __init io_uring_init(void) ...@@ -12444,6 +12713,7 @@ static int __init io_uring_init(void)
BUILD_BUG_SQE_ELEM(42, __u16, personality); BUILD_BUG_SQE_ELEM(42, __u16, personality);
BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in); BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in);
BUILD_BUG_SQE_ELEM(44, __u32, file_index); BUILD_BUG_SQE_ELEM(44, __u32, file_index);
BUILD_BUG_SQE_ELEM(48, __u64, addr3);
BUILD_BUG_ON(sizeof(struct io_uring_files_update) != BUILD_BUG_ON(sizeof(struct io_uring_files_update) !=
sizeof(struct io_uring_rsrc_update)); sizeof(struct io_uring_rsrc_update));
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "internal.h"
static const char * static const char *
strcmp_prefix(const char *a, const char *a_prefix) strcmp_prefix(const char *a, const char *a_prefix)
{ {
...@@ -539,44 +541,76 @@ EXPORT_SYMBOL_GPL(vfs_removexattr); ...@@ -539,44 +541,76 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
/* /*
* Extended attribute SET operations * Extended attribute SET operations
*/ */
static long
setxattr(struct user_namespace *mnt_userns, struct dentry *d, int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
const char __user *name, const void __user *value, size_t size,
int flags)
{ {
int error; int error;
void *kvalue = NULL;
char kname[XATTR_NAME_MAX + 1];
if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
return -EINVAL; return -EINVAL;
error = strncpy_from_user(kname, name, sizeof(kname)); error = strncpy_from_user(ctx->kname->name, name,
if (error == 0 || error == sizeof(kname)) sizeof(ctx->kname->name));
error = -ERANGE; if (error == 0 || error == sizeof(ctx->kname->name))
return -ERANGE;
if (error < 0) if (error < 0)
return error; return error;
if (size) { error = 0;
if (size > XATTR_SIZE_MAX) if (ctx->size) {
if (ctx->size > XATTR_SIZE_MAX)
return -E2BIG; return -E2BIG;
kvalue = kvmalloc(size, GFP_KERNEL);
if (!kvalue) ctx->kvalue = vmemdup_user(ctx->cvalue, ctx->size);
return -ENOMEM; if (IS_ERR(ctx->kvalue)) {
if (copy_from_user(kvalue, value, size)) { error = PTR_ERR(ctx->kvalue);
error = -EFAULT; ctx->kvalue = NULL;
goto out;
} }
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
posix_acl_fix_xattr_from_user(mnt_userns, d_inode(d),
kvalue, size);
} }
error = vfs_setxattr(mnt_userns, d, kname, kvalue, size, flags); return error;
out: }
kvfree(kvalue);
static void setxattr_convert(struct user_namespace *mnt_userns,
struct dentry *d, struct xattr_ctx *ctx)
{
if (ctx->size &&
((strcmp(ctx->kname->name, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(ctx->kname->name, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)))
posix_acl_fix_xattr_from_user(mnt_userns, d_inode(d),
ctx->kvalue, ctx->size);
}
int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct xattr_ctx *ctx)
{
setxattr_convert(mnt_userns, dentry, ctx);
return vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
ctx->kvalue, ctx->size, ctx->flags);
}
static long
setxattr(struct user_namespace *mnt_userns, struct dentry *d,
const char __user *name, const void __user *value, size_t size,
int flags)
{
struct xattr_name kname;
struct xattr_ctx ctx = {
.cvalue = value,
.kvalue = NULL,
.size = size,
.kname = &kname,
.flags = flags,
};
int error;
error = setxattr_copy(name, &ctx);
if (error)
return error;
error = do_setxattr(mnt_userns, d, &ctx);
kvfree(ctx.kvalue);
return error; return error;
} }
...@@ -642,44 +676,61 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, ...@@ -642,44 +676,61 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
/* /*
* Extended attribute GET operations * Extended attribute GET operations
*/ */
static ssize_t ssize_t
getxattr(struct user_namespace *mnt_userns, struct dentry *d, do_getxattr(struct user_namespace *mnt_userns, struct dentry *d,
const char __user *name, void __user *value, size_t size) struct xattr_ctx *ctx)
{ {
ssize_t error; ssize_t error;
void *kvalue = NULL; char *kname = ctx->kname->name;
char kname[XATTR_NAME_MAX + 1];
error = strncpy_from_user(kname, name, sizeof(kname));
if (error == 0 || error == sizeof(kname))
error = -ERANGE;
if (error < 0)
return error;
if (size) { if (ctx->size) {
if (size > XATTR_SIZE_MAX) if (ctx->size > XATTR_SIZE_MAX)
size = XATTR_SIZE_MAX; ctx->size = XATTR_SIZE_MAX;
kvalue = kvzalloc(size, GFP_KERNEL); ctx->kvalue = kvzalloc(ctx->size, GFP_KERNEL);
if (!kvalue) if (!ctx->kvalue)
return -ENOMEM; return -ENOMEM;
} }
error = vfs_getxattr(mnt_userns, d, kname, kvalue, size); error = vfs_getxattr(mnt_userns, d, kname, ctx->kvalue, ctx->size);
if (error > 0) { if (error > 0) {
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) || if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)) (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
posix_acl_fix_xattr_to_user(mnt_userns, d_inode(d), posix_acl_fix_xattr_to_user(mnt_userns, d_inode(d),
kvalue, error); ctx->kvalue, error);
if (size && copy_to_user(value, kvalue, error)) if (ctx->size && copy_to_user(ctx->value, ctx->kvalue, error))
error = -EFAULT; error = -EFAULT;
} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { } else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) {
/* The file system tried to returned a value bigger /* The file system tried to returned a value bigger
than XATTR_SIZE_MAX bytes. Not possible. */ than XATTR_SIZE_MAX bytes. Not possible. */
error = -E2BIG; error = -E2BIG;
} }
kvfree(kvalue); return error;
}
static ssize_t
getxattr(struct user_namespace *mnt_userns, struct dentry *d,
const char __user *name, void __user *value, size_t size)
{
ssize_t error;
struct xattr_name kname;
struct xattr_ctx ctx = {
.value = value,
.kvalue = NULL,
.size = size,
.kname = &kname,
.flags = 0,
};
error = strncpy_from_user(kname.name, name, sizeof(kname.name));
if (error == 0 || error == sizeof(kname.name))
error = -ERANGE;
if (error < 0)
return error;
error = do_getxattr(mnt_userns, d, &ctx);
kvfree(ctx.kvalue);
return error; return error;
} }
......
...@@ -506,7 +506,7 @@ TRACE_EVENT(io_uring_req_failed, ...@@ -506,7 +506,7 @@ TRACE_EVENT(io_uring_req_failed,
__field( u16, personality ) __field( u16, personality )
__field( u32, file_index ) __field( u32, file_index )
__field( u64, pad1 ) __field( u64, pad1 )
__field( u64, pad2 ) __field( u64, addr3 )
__field( int, error ) __field( int, error )
), ),
...@@ -525,14 +525,15 @@ TRACE_EVENT(io_uring_req_failed, ...@@ -525,14 +525,15 @@ TRACE_EVENT(io_uring_req_failed,
__entry->personality = sqe->personality; __entry->personality = sqe->personality;
__entry->file_index = sqe->file_index; __entry->file_index = sqe->file_index;
__entry->pad1 = sqe->__pad2[0]; __entry->pad1 = sqe->__pad2[0];
__entry->pad2 = sqe->__pad2[1]; __entry->addr3 = sqe->addr3;
__entry->error = error; __entry->error = error;
), ),
TP_printk("ring %p, req %p, user_data 0x%llx, " TP_printk("ring %p, req %p, user_data 0x%llx, "
"op %d, flags 0x%x, prio=%d, off=%llu, addr=%llu, " "op %d, flags 0x%x, prio=%d, off=%llu, addr=%llu, "
"len=%u, rw_flags=0x%x, buf_index=%d, " "len=%u, rw_flags=0x%x, buf_index=%d, "
"personality=%d, file_index=%d, pad=0x%llx/%llx, error=%d", "personality=%d, file_index=%d, pad=0x%llx, addr3=%llx, "
"error=%d",
__entry->ctx, __entry->req, __entry->user_data, __entry->ctx, __entry->req, __entry->user_data,
__entry->opcode, __entry->flags, __entry->ioprio, __entry->opcode, __entry->flags, __entry->ioprio,
(unsigned long long)__entry->off, (unsigned long long)__entry->off,
...@@ -540,7 +541,7 @@ TRACE_EVENT(io_uring_req_failed, ...@@ -540,7 +541,7 @@ TRACE_EVENT(io_uring_req_failed,
__entry->op_flags, __entry->op_flags,
__entry->buf_index, __entry->personality, __entry->file_index, __entry->buf_index, __entry->personality, __entry->file_index,
(unsigned long long) __entry->pad1, (unsigned long long) __entry->pad1,
(unsigned long long) __entry->pad2, __entry->error) (unsigned long long) __entry->addr3, __entry->error)
); );
......
...@@ -45,6 +45,7 @@ struct io_uring_sqe { ...@@ -45,6 +45,7 @@ struct io_uring_sqe {
__u32 rename_flags; __u32 rename_flags;
__u32 unlink_flags; __u32 unlink_flags;
__u32 hardlink_flags; __u32 hardlink_flags;
__u32 xattr_flags;
}; };
__u64 user_data; /* data to be passed back at completion time */ __u64 user_data; /* data to be passed back at completion time */
/* pack this to avoid bogus arm OABI complaints */ /* pack this to avoid bogus arm OABI complaints */
...@@ -60,7 +61,8 @@ struct io_uring_sqe { ...@@ -60,7 +61,8 @@ struct io_uring_sqe {
__s32 splice_fd_in; __s32 splice_fd_in;
__u32 file_index; __u32 file_index;
}; };
__u64 __pad2[2]; __u64 addr3;
__u64 __pad2[1];
}; };
/* /*
...@@ -168,6 +170,10 @@ enum { ...@@ -168,6 +170,10 @@ enum {
IORING_OP_SYMLINKAT, IORING_OP_SYMLINKAT,
IORING_OP_LINKAT, IORING_OP_LINKAT,
IORING_OP_MSG_RING, IORING_OP_MSG_RING,
IORING_OP_FSETXATTR,
IORING_OP_SETXATTR,
IORING_OP_FGETXATTR,
IORING_OP_GETXATTR,
/* this goes last, obviously */ /* this goes last, obviously */
IORING_OP_LAST, IORING_OP_LAST,
......
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