mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 20:48:49 +08:00
for-5.19/io_uring-xattr-2022-05-22
-----BEGIN PGP SIGNATURE----- iQJEBAABCAAuFiEEwPw5LcreJtl1+l5K99NY+ylx4KYFAmKKopkQHGF4Ym9lQGtl cm5lbC5kawAKCRD301j7KXHgpgakEACktFtUBQLrYOXbM/mVxMpR//ht4e29E8k0 j/DkqK0yDKn9VkvDryALguH+ixNSI9Z4N7xSELLb/meNQsbJ7YdprL3xJn3BoUgs 3zx44janE8J3Q5TsXvD2z2jPIMaT892t5+5aLFYZqP1g+KDXI8T1WpHsETMkKfRG ZPeerUrd0fhtnDpViaaYbRutIEt8V8tsPhh0XG/4GojWjUW0FTsRKBSGuQ0sQnUr aJDfF5VylOjOBzRGimGZ23vJIgtZ8UEpX0T2MxR5V6ffj4cI8bCFQOrphh7yHxF5 f09xte80zX6pow5AivIpultZShR6IoQG5DIvF59woNP16uXy5yUyVTQvdnt8RlyY RjLd8ro9Gt4wBQGqckJLyY/o1FGhaQ8S99wOixUlpb9qKAOGmQZI97FQKFENqx/1 Xe+bP6QmTt9uCXsYPIFBtZaaEv2u0yjHOyERFUSzKJQUuPTa5Rmen0EXYXRhe5/E p+sR3Qbk1wzlW7UHuCT2gcaI67SAFG+yDv1U6BAaVdcS71i0WCA+Q2a6AuB+NJzg ER4+JRoeOnjEXSP2UPvIUBL1Komdj4R2hnrOK4S80R3yQ3NaadrWywhBn5HNcniM wE2P6J0erzRFqyfBw9tyNLsZwR1iS7JqSD9/NuBLoWwb42O0l+WgqqwDTSxMsde4 egKBaidRqg== =CfhD -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
09beaff75e
@ -191,3 +191,32 @@ long splice_file_to_pipe(struct file *in,
|
||||
struct pipe_inode_info *opipe,
|
||||
loff_t *offset,
|
||||
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);
|
||||
|
322
fs/io_uring.c
322
fs/io_uring.c
@ -80,6 +80,7 @@
|
||||
#include <linux/io_uring.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/xattr.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/io_uring.h>
|
||||
@ -803,6 +804,12 @@ struct io_async_rw {
|
||||
struct wait_page_queue wpq;
|
||||
};
|
||||
|
||||
struct io_xattr {
|
||||
struct file *file;
|
||||
struct xattr_ctx ctx;
|
||||
struct filename *filename;
|
||||
};
|
||||
|
||||
enum {
|
||||
REQ_F_FIXED_FILE_BIT = IOSQE_FIXED_FILE_BIT,
|
||||
REQ_F_IO_DRAIN_BIT = IOSQE_IO_DRAIN_BIT,
|
||||
@ -973,6 +980,7 @@ struct io_kiocb {
|
||||
struct io_symlink symlink;
|
||||
struct io_hardlink hardlink;
|
||||
struct io_msg msg;
|
||||
struct io_xattr xattr;
|
||||
};
|
||||
|
||||
u8 opcode;
|
||||
@ -1281,6 +1289,14 @@ static const struct io_op_def io_op_defs[] = {
|
||||
.needs_file = 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() */
|
||||
@ -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)
|
||||
{
|
||||
if (res < 0)
|
||||
req_set_fail(req);
|
||||
__io_req_complete(req, 0, res, 0);
|
||||
}
|
||||
|
||||
@ -4299,12 +4317,258 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
|
||||
ren->newpath, ren->flags);
|
||||
|
||||
req->flags &= ~REQ_F_NEED_CLEANUP;
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
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,
|
||||
const struct io_uring_sqe *sqe)
|
||||
{
|
||||
@ -4345,8 +4609,6 @@ static int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags)
|
||||
ret = do_unlinkat(un->dfd, un->filename);
|
||||
|
||||
req->flags &= ~REQ_F_NEED_CLEANUP;
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
req->flags &= ~REQ_F_NEED_CLEANUP;
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
req->flags &= ~REQ_F_NEED_CLEANUP;
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -4480,8 +4738,6 @@ static int io_linkat(struct io_kiocb *req, unsigned int issue_flags)
|
||||
lnk->newpath, lnk->flags);
|
||||
|
||||
req->flags &= ~REQ_F_NEED_CLEANUP;
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -4515,8 +4771,6 @@ static int io_shutdown(struct io_kiocb *req, unsigned int issue_flags)
|
||||
return -ENOTSOCK;
|
||||
|
||||
ret = __sys_shutdown_sock(sock, req->shutdown.how);
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
#else
|
||||
@ -4574,7 +4828,7 @@ static int io_tee(struct io_kiocb *req, unsigned int issue_flags)
|
||||
done:
|
||||
if (ret != sp->len)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
__io_req_complete(req, 0, ret, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4619,7 +4873,7 @@ static int io_splice(struct io_kiocb *req, unsigned int issue_flags)
|
||||
done:
|
||||
if (ret != sp->len)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
__io_req_complete(req, 0, ret, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
end > 0 ? end : LLONG_MAX,
|
||||
req->sync.flags & IORING_FSYNC_DATASYNC);
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -4741,9 +4993,7 @@ static int io_fallocate(struct io_kiocb *req, unsigned int issue_flags)
|
||||
return -EAGAIN;
|
||||
ret = vfs_fallocate(req->file, req->sync.mode, req->sync.off,
|
||||
req->sync.len);
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
else
|
||||
if (ret >= 0)
|
||||
fsnotify_modify(req->file);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
@ -5255,8 +5505,6 @@ static int io_madvise(struct io_kiocb *req, unsigned int issue_flags)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = do_madvise(current->mm, ma->addr, ma->len, ma->advice);
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
#else
|
||||
@ -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,
|
||||
ctx->buffer);
|
||||
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -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,
|
||||
req->sync.flags);
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
io_req_complete(req, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -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);
|
||||
case IORING_OP_MSG_RING:
|
||||
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",
|
||||
@ -7549,6 +7800,12 @@ static void io_clean_op(struct io_kiocb *req)
|
||||
if (req->statx.filename)
|
||||
putname(req->statx.filename);
|
||||
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) {
|
||||
@ -7705,6 +7962,18 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
|
||||
case IORING_OP_MSG_RING:
|
||||
ret = io_msg_ring(req, issue_flags);
|
||||
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:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
@ -12444,6 +12713,7 @@ static int __init io_uring_init(void)
|
||||
BUILD_BUG_SQE_ELEM(42, __u16, personality);
|
||||
BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in);
|
||||
BUILD_BUG_SQE_ELEM(44, __u32, file_index);
|
||||
BUILD_BUG_SQE_ELEM(48, __u64, addr3);
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct io_uring_files_update) !=
|
||||
sizeof(struct io_uring_rsrc_update));
|
||||
|
143
fs/xattr.c
143
fs/xattr.c
@ -25,6 +25,8 @@
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static const char *
|
||||
strcmp_prefix(const char *a, const char *a_prefix)
|
||||
{
|
||||
@ -539,44 +541,76 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
|
||||
/*
|
||||
* Extended attribute SET operations
|
||||
*/
|
||||
static long
|
||||
setxattr(struct user_namespace *mnt_userns, struct dentry *d,
|
||||
const char __user *name, const void __user *value, size_t size,
|
||||
int flags)
|
||||
|
||||
int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
|
||||
{
|
||||
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;
|
||||
|
||||
error = strncpy_from_user(kname, name, sizeof(kname));
|
||||
if (error == 0 || error == sizeof(kname))
|
||||
error = -ERANGE;
|
||||
error = strncpy_from_user(ctx->kname->name, name,
|
||||
sizeof(ctx->kname->name));
|
||||
if (error == 0 || error == sizeof(ctx->kname->name))
|
||||
return -ERANGE;
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (size) {
|
||||
if (size > XATTR_SIZE_MAX)
|
||||
error = 0;
|
||||
if (ctx->size) {
|
||||
if (ctx->size > XATTR_SIZE_MAX)
|
||||
return -E2BIG;
|
||||
kvalue = kvmalloc(size, GFP_KERNEL);
|
||||
if (!kvalue)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(kvalue, value, size)) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
|
||||
ctx->kvalue = vmemdup_user(ctx->cvalue, ctx->size);
|
||||
if (IS_ERR(ctx->kvalue)) {
|
||||
error = PTR_ERR(ctx->kvalue);
|
||||
ctx->kvalue = NULL;
|
||||
}
|
||||
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);
|
||||
out:
|
||||
kvfree(kvalue);
|
||||
return error;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -642,44 +676,61 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
|
||||
/*
|
||||
* Extended attribute GET operations
|
||||
*/
|
||||
static ssize_t
|
||||
getxattr(struct user_namespace *mnt_userns, struct dentry *d,
|
||||
const char __user *name, void __user *value, size_t size)
|
||||
ssize_t
|
||||
do_getxattr(struct user_namespace *mnt_userns, struct dentry *d,
|
||||
struct xattr_ctx *ctx)
|
||||
{
|
||||
ssize_t error;
|
||||
void *kvalue = NULL;
|
||||
char kname[XATTR_NAME_MAX + 1];
|
||||
char *kname = ctx->kname->name;
|
||||
|
||||
error = strncpy_from_user(kname, name, sizeof(kname));
|
||||
if (error == 0 || error == sizeof(kname))
|
||||
error = -ERANGE;
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (size) {
|
||||
if (size > XATTR_SIZE_MAX)
|
||||
size = XATTR_SIZE_MAX;
|
||||
kvalue = kvzalloc(size, GFP_KERNEL);
|
||||
if (!kvalue)
|
||||
if (ctx->size) {
|
||||
if (ctx->size > XATTR_SIZE_MAX)
|
||||
ctx->size = XATTR_SIZE_MAX;
|
||||
ctx->kvalue = kvzalloc(ctx->size, GFP_KERNEL);
|
||||
if (!ctx->kvalue)
|
||||
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 ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
|
||||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
|
||||
posix_acl_fix_xattr_to_user(mnt_userns, d_inode(d),
|
||||
kvalue, error);
|
||||
if (size && copy_to_user(value, kvalue, error))
|
||||
ctx->kvalue, error);
|
||||
if (ctx->size && copy_to_user(ctx->value, ctx->kvalue, error))
|
||||
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
|
||||
than XATTR_SIZE_MAX bytes. Not possible. */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -506,7 +506,7 @@ TRACE_EVENT(io_uring_req_failed,
|
||||
__field( u16, personality )
|
||||
__field( u32, file_index )
|
||||
__field( u64, pad1 )
|
||||
__field( u64, pad2 )
|
||||
__field( u64, addr3 )
|
||||
__field( int, error )
|
||||
),
|
||||
|
||||
@ -525,14 +525,15 @@ TRACE_EVENT(io_uring_req_failed,
|
||||
__entry->personality = sqe->personality;
|
||||
__entry->file_index = sqe->file_index;
|
||||
__entry->pad1 = sqe->__pad2[0];
|
||||
__entry->pad2 = sqe->__pad2[1];
|
||||
__entry->addr3 = sqe->addr3;
|
||||
__entry->error = error;
|
||||
),
|
||||
|
||||
TP_printk("ring %p, req %p, user_data 0x%llx, "
|
||||
"op %d, flags 0x%x, prio=%d, off=%llu, addr=%llu, "
|
||||
"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->opcode, __entry->flags, __entry->ioprio,
|
||||
(unsigned long long)__entry->off,
|
||||
@ -540,7 +541,7 @@ TRACE_EVENT(io_uring_req_failed,
|
||||
__entry->op_flags,
|
||||
__entry->buf_index, __entry->personality, __entry->file_index,
|
||||
(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 {
|
||||
__u32 rename_flags;
|
||||
__u32 unlink_flags;
|
||||
__u32 hardlink_flags;
|
||||
__u32 xattr_flags;
|
||||
};
|
||||
__u64 user_data; /* data to be passed back at completion time */
|
||||
/* pack this to avoid bogus arm OABI complaints */
|
||||
@ -60,7 +61,8 @@ struct io_uring_sqe {
|
||||
__s32 splice_fd_in;
|
||||
__u32 file_index;
|
||||
};
|
||||
__u64 __pad2[2];
|
||||
__u64 addr3;
|
||||
__u64 __pad2[1];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -168,6 +170,10 @@ enum {
|
||||
IORING_OP_SYMLINKAT,
|
||||
IORING_OP_LINKAT,
|
||||
IORING_OP_MSG_RING,
|
||||
IORING_OP_FSETXATTR,
|
||||
IORING_OP_SETXATTR,
|
||||
IORING_OP_FGETXATTR,
|
||||
IORING_OP_GETXATTR,
|
||||
|
||||
/* this goes last, obviously */
|
||||
IORING_OP_LAST,
|
||||
|
Loading…
Reference in New Issue
Block a user