mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
sanitize xattr and io_uring interactions with it,
add *xattrat() syscalls, sanitize struct filename handling in there. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCZzdj4gAKCRBZ7Krx/gZQ 6/02AQC8ndn9i1wLGRb5DdZYGNWUDhXCdPrZCF2nyvU2swCIPwEAm1H5F/bxBXeT 6qCLHThVw4KTJOT2aDY03ELrxbi8Vg4= =35Oj -----END PGP SIGNATURE----- Merge tag 'pull-xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs Pull xattr updates from Al Viro: "Sanitize xattr and io_uring interactions with it, add *xattrat() syscalls, sanitize struct filename handling in there" * tag 'pull-xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: xattr: remove redundant check on variable err fs/xattr: add *at family syscalls new helpers: file_removexattr(), filename_removexattr() new helpers: file_listxattr(), filename_listxattr() replace do_getxattr() with saner helpers. replace do_setxattr() with saner helpers. new helper: import_xattr_name() fs: rename struct xattr_ctx to kernel_xattr_ctx xattr: switch to CLASS(fd) io_[gs]etxattr_prep(): just use getname() io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE getname_maybe_null() - the third variant of pathname copy-in teach filename_lookup() to treat NULL filename as ""
This commit is contained in:
commit
82339c4911
@ -502,3 +502,7 @@
|
||||
570 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
571 common lsm_list_modules sys_lsm_list_modules
|
||||
572 common mseal sys_mseal
|
||||
573 common setxattrat sys_setxattrat
|
||||
574 common getxattrat sys_getxattrat
|
||||
575 common listxattrat sys_listxattrat
|
||||
576 common removexattrat sys_removexattrat
|
||||
|
@ -477,3 +477,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -474,3 +474,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -462,3 +462,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -468,3 +468,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -401,3 +401,7 @@
|
||||
460 n32 lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 n32 lsm_list_modules sys_lsm_list_modules
|
||||
462 n32 mseal sys_mseal
|
||||
463 n32 setxattrat sys_setxattrat
|
||||
464 n32 getxattrat sys_getxattrat
|
||||
465 n32 listxattrat sys_listxattrat
|
||||
466 n32 removexattrat sys_removexattrat
|
||||
|
@ -377,3 +377,7 @@
|
||||
460 n64 lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 n64 lsm_list_modules sys_lsm_list_modules
|
||||
462 n64 mseal sys_mseal
|
||||
463 n64 setxattrat sys_setxattrat
|
||||
464 n64 getxattrat sys_getxattrat
|
||||
465 n64 listxattrat sys_listxattrat
|
||||
466 n64 removexattrat sys_removexattrat
|
||||
|
@ -450,3 +450,7 @@
|
||||
460 o32 lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 o32 lsm_list_modules sys_lsm_list_modules
|
||||
462 o32 mseal sys_mseal
|
||||
463 o32 setxattrat sys_setxattrat
|
||||
464 o32 getxattrat sys_getxattrat
|
||||
465 o32 listxattrat sys_listxattrat
|
||||
466 o32 removexattrat sys_removexattrat
|
||||
|
@ -461,3 +461,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -553,3 +553,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -465,3 +465,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat sys_removexattrat
|
||||
|
@ -466,3 +466,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -508,3 +508,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -468,3 +468,7 @@
|
||||
460 i386 lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 i386 lsm_list_modules sys_lsm_list_modules
|
||||
462 i386 mseal sys_mseal
|
||||
463 i386 setxattrat sys_setxattrat
|
||||
464 i386 getxattrat sys_getxattrat
|
||||
465 i386 listxattrat sys_listxattrat
|
||||
466 i386 removexattrat sys_removexattrat
|
||||
|
@ -386,6 +386,10 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
||||
#
|
||||
# Due to a historical design error, certain syscalls are numbered differently
|
||||
|
@ -433,3 +433,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
@ -267,7 +267,7 @@ struct xattr_name {
|
||||
char name[XATTR_NAME_MAX + 1];
|
||||
};
|
||||
|
||||
struct xattr_ctx {
|
||||
struct kernel_xattr_ctx {
|
||||
/* Value of attribute */
|
||||
union {
|
||||
const void __user *cvalue;
|
||||
@ -280,14 +280,15 @@ struct xattr_ctx {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
ssize_t file_getxattr(struct file *file, struct kernel_xattr_ctx *ctx);
|
||||
ssize_t filename_getxattr(int dfd, struct filename *filename,
|
||||
unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
|
||||
int file_setxattr(struct file *file, struct kernel_xattr_ctx *ctx);
|
||||
int filename_setxattr(int dfd, struct filename *filename,
|
||||
unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
|
||||
int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
|
||||
int import_xattr_name(struct xattr_name *kname, const char __user *name);
|
||||
|
||||
ssize_t do_getxattr(struct mnt_idmap *idmap,
|
||||
struct dentry *d,
|
||||
struct xattr_ctx *ctx);
|
||||
|
||||
int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
|
||||
int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct xattr_ctx *ctx);
|
||||
int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
|
||||
|
||||
#ifdef CONFIG_FS_POSIX_ACL
|
||||
|
34
fs/namei.c
34
fs/namei.c
@ -211,22 +211,38 @@ getname_flags(const char __user *filename, int flags)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct filename *
|
||||
getname_uflags(const char __user *filename, int uflags)
|
||||
struct filename *getname_uflags(const char __user *filename, int uflags)
|
||||
{
|
||||
int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
|
||||
|
||||
return getname_flags(filename, flags);
|
||||
}
|
||||
|
||||
struct filename *
|
||||
getname(const char __user * filename)
|
||||
struct filename *getname(const char __user * filename)
|
||||
{
|
||||
return getname_flags(filename, 0);
|
||||
}
|
||||
|
||||
struct filename *
|
||||
getname_kernel(const char * filename)
|
||||
struct filename *__getname_maybe_null(const char __user *pathname)
|
||||
{
|
||||
struct filename *name;
|
||||
char c;
|
||||
|
||||
/* try to save on allocations; loss on um, though */
|
||||
if (get_user(c, pathname))
|
||||
return ERR_PTR(-EFAULT);
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
name = getname_flags(pathname, LOOKUP_EMPTY);
|
||||
if (!IS_ERR(name) && !(name->name[0])) {
|
||||
putname(name);
|
||||
name = NULL;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
struct filename *getname_kernel(const char * filename)
|
||||
{
|
||||
struct filename *result;
|
||||
int len = strlen(filename) + 1;
|
||||
@ -264,7 +280,7 @@ EXPORT_SYMBOL(getname_kernel);
|
||||
|
||||
void putname(struct filename *name)
|
||||
{
|
||||
if (IS_ERR(name))
|
||||
if (IS_ERR_OR_NULL(name))
|
||||
return;
|
||||
|
||||
if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))
|
||||
@ -629,6 +645,7 @@ struct nameidata {
|
||||
unsigned seq;
|
||||
} *stack, internal[EMBEDDED_LEVELS];
|
||||
struct filename *name;
|
||||
const char *pathname;
|
||||
struct nameidata *saved;
|
||||
unsigned root_seq;
|
||||
int dfd;
|
||||
@ -647,6 +664,7 @@ static void __set_nameidata(struct nameidata *p, int dfd, struct filename *name)
|
||||
p->depth = 0;
|
||||
p->dfd = dfd;
|
||||
p->name = name;
|
||||
p->pathname = likely(name) ? name->name : "";
|
||||
p->path.mnt = NULL;
|
||||
p->path.dentry = NULL;
|
||||
p->total_link_count = old ? old->total_link_count : 0;
|
||||
@ -2480,7 +2498,7 @@ OK:
|
||||
static const char *path_init(struct nameidata *nd, unsigned flags)
|
||||
{
|
||||
int error;
|
||||
const char *s = nd->name->name;
|
||||
const char *s = nd->pathname;
|
||||
|
||||
/* LOOKUP_CACHED requires RCU, ask caller to retry */
|
||||
if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED)
|
||||
|
28
fs/stat.c
28
fs/stat.c
@ -368,18 +368,11 @@ int vfs_fstatat(int dfd, const char __user *filename,
|
||||
{
|
||||
int ret;
|
||||
int statx_flags = flags | AT_NO_AUTOMOUNT;
|
||||
struct filename *name;
|
||||
struct filename *name = getname_maybe_null(filename, flags);
|
||||
|
||||
/*
|
||||
* Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH)
|
||||
*
|
||||
* If AT_EMPTY_PATH is set, we expect the common case to be that
|
||||
* empty path, and avoid doing all the extra pathname work.
|
||||
*/
|
||||
if (flags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
|
||||
if (!name && dfd >= 0)
|
||||
return vfs_fstat(dfd, stat);
|
||||
|
||||
name = getname_flags(filename, getname_statx_lookup_flags(statx_flags));
|
||||
ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
|
||||
putname(name);
|
||||
|
||||
@ -816,24 +809,11 @@ SYSCALL_DEFINE5(statx,
|
||||
struct statx __user *, buffer)
|
||||
{
|
||||
int ret;
|
||||
unsigned lflags;
|
||||
struct filename *name;
|
||||
struct filename *name = getname_maybe_null(filename, flags);
|
||||
|
||||
/*
|
||||
* Short-circuit handling of NULL and "" paths.
|
||||
*
|
||||
* For a NULL path we require and accept only the AT_EMPTY_PATH flag
|
||||
* (possibly |'d with AT_STATX flags).
|
||||
*
|
||||
* However, glibc on 32-bit architectures implements fstatat as statx
|
||||
* with the "" pathname and AT_NO_AUTOMOUNT | AT_EMPTY_PATH flags.
|
||||
* Supporting this results in the uglification below.
|
||||
*/
|
||||
lflags = flags & ~(AT_NO_AUTOMOUNT | AT_STATX_SYNC_TYPE);
|
||||
if (lflags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
|
||||
if (!name && dfd >= 0)
|
||||
return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer);
|
||||
|
||||
name = getname_flags(filename, getname_statx_lookup_flags(flags));
|
||||
ret = do_statx(dfd, name, flags, mask, buffer);
|
||||
putname(name);
|
||||
|
||||
|
446
fs/xattr.c
446
fs/xattr.c
@ -586,25 +586,32 @@ retry_deleg:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfs_removexattr);
|
||||
|
||||
int import_xattr_name(struct xattr_name *kname, const char __user *name)
|
||||
{
|
||||
int error = strncpy_from_user(kname->name, name,
|
||||
sizeof(kname->name));
|
||||
if (error == 0 || error == sizeof(kname->name))
|
||||
return -ERANGE;
|
||||
if (error < 0)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended attribute SET operations
|
||||
*/
|
||||
|
||||
int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
|
||||
int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
|
||||
return -EINVAL;
|
||||
|
||||
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)
|
||||
error = import_xattr_name(ctx->kname, name);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = 0;
|
||||
if (ctx->size) {
|
||||
if (ctx->size > XATTR_SIZE_MAX)
|
||||
return -E2BIG;
|
||||
@ -619,8 +626,8 @@ int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
|
||||
return error;
|
||||
}
|
||||
|
||||
int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct xattr_ctx *ctx)
|
||||
static int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
if (is_posix_acl_xattr(ctx->kname->name))
|
||||
return do_set_acl(idmap, dentry, ctx->kname->name,
|
||||
@ -630,32 +637,32 @@ int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
ctx->kvalue, ctx->size, ctx->flags);
|
||||
}
|
||||
|
||||
static int path_setxattr(const char __user *pathname,
|
||||
const char __user *name, const void __user *value,
|
||||
size_t size, int flags, unsigned int lookup_flags)
|
||||
int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
int error = mnt_want_write_file(f);
|
||||
|
||||
if (!error) {
|
||||
audit_file(f);
|
||||
error = do_setxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
|
||||
mnt_drop_write_file(f);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* unconditionally consumes filename */
|
||||
int filename_setxattr(int dfd, struct filename *filename,
|
||||
unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
struct xattr_name kname;
|
||||
struct xattr_ctx ctx = {
|
||||
.cvalue = value,
|
||||
.kvalue = NULL,
|
||||
.size = size,
|
||||
.kname = &kname,
|
||||
.flags = flags,
|
||||
};
|
||||
struct path path;
|
||||
int error;
|
||||
|
||||
error = setxattr_copy(name, &ctx);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
retry:
|
||||
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
||||
error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
|
||||
if (error)
|
||||
goto out;
|
||||
error = mnt_want_write(path.mnt);
|
||||
if (!error) {
|
||||
error = do_setxattr(mnt_idmap(path.mnt), path.dentry, &ctx);
|
||||
error = do_setxattr(mnt_idmap(path.mnt), path.dentry, ctx);
|
||||
mnt_drop_write(path.mnt);
|
||||
}
|
||||
path_put(&path);
|
||||
@ -665,80 +672,121 @@ retry:
|
||||
}
|
||||
|
||||
out:
|
||||
kvfree(ctx.kvalue);
|
||||
putname(filename);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
|
||||
const char __user *, name, const void __user *, value,
|
||||
size_t, size, int, flags)
|
||||
{
|
||||
return path_setxattr(pathname, name, value, size, flags, LOOKUP_FOLLOW);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname,
|
||||
const char __user *, name, const void __user *, value,
|
||||
size_t, size, int, flags)
|
||||
{
|
||||
return path_setxattr(pathname, name, value, size, flags, 0);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
|
||||
const void __user *,value, size_t, size, int, flags)
|
||||
static int path_setxattrat(int dfd, const char __user *pathname,
|
||||
unsigned int at_flags, const char __user *name,
|
||||
const void __user *value, size_t size, int flags)
|
||||
{
|
||||
struct xattr_name kname;
|
||||
struct xattr_ctx ctx = {
|
||||
struct kernel_xattr_ctx ctx = {
|
||||
.cvalue = value,
|
||||
.kvalue = NULL,
|
||||
.size = size,
|
||||
.kname = &kname,
|
||||
.flags = flags,
|
||||
};
|
||||
struct filename *filename;
|
||||
unsigned int lookup_flags = 0;
|
||||
int error;
|
||||
|
||||
CLASS(fd, f)(fd);
|
||||
if (!fd_file(f))
|
||||
return -EBADF;
|
||||
if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(at_flags & AT_SYMLINK_NOFOLLOW))
|
||||
lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
audit_file(fd_file(f));
|
||||
error = setxattr_copy(name, &ctx);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = mnt_want_write_file(fd_file(f));
|
||||
if (!error) {
|
||||
error = do_setxattr(file_mnt_idmap(fd_file(f)),
|
||||
fd_file(f)->f_path.dentry, &ctx);
|
||||
mnt_drop_write_file(fd_file(f));
|
||||
filename = getname_maybe_null(pathname, at_flags);
|
||||
if (!filename) {
|
||||
CLASS(fd, f)(dfd);
|
||||
if (fd_empty(f))
|
||||
error = -EBADF;
|
||||
else
|
||||
error = file_setxattr(fd_file(f), &ctx);
|
||||
} else {
|
||||
error = filename_setxattr(dfd, filename, lookup_flags, &ctx);
|
||||
}
|
||||
kvfree(ctx.kvalue);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE6(setxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
|
||||
const char __user *, name, const struct xattr_args __user *, uargs,
|
||||
size_t, usize)
|
||||
{
|
||||
struct xattr_args args = {};
|
||||
int error;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
|
||||
BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
|
||||
|
||||
if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
|
||||
return -EINVAL;
|
||||
if (usize > PAGE_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return path_setxattrat(dfd, pathname, at_flags, name,
|
||||
u64_to_user_ptr(args.value), args.size,
|
||||
args.flags);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
|
||||
const char __user *, name, const void __user *, value,
|
||||
size_t, size, int, flags)
|
||||
{
|
||||
return path_setxattrat(AT_FDCWD, pathname, 0, name, value, size, flags);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname,
|
||||
const char __user *, name, const void __user *, value,
|
||||
size_t, size, int, flags)
|
||||
{
|
||||
return path_setxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name,
|
||||
value, size, flags);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
|
||||
const void __user *,value, size_t, size, int, flags)
|
||||
{
|
||||
return path_setxattrat(fd, NULL, AT_EMPTY_PATH, name,
|
||||
value, size, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended attribute GET operations
|
||||
*/
|
||||
ssize_t
|
||||
static ssize_t
|
||||
do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
|
||||
struct xattr_ctx *ctx)
|
||||
struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
ssize_t error;
|
||||
char *kname = ctx->kname->name;
|
||||
void *kvalue = NULL;
|
||||
|
||||
if (ctx->size) {
|
||||
if (ctx->size > XATTR_SIZE_MAX)
|
||||
ctx->size = XATTR_SIZE_MAX;
|
||||
ctx->kvalue = kvzalloc(ctx->size, GFP_KERNEL);
|
||||
if (!ctx->kvalue)
|
||||
kvalue = kvzalloc(ctx->size, GFP_KERNEL);
|
||||
if (!kvalue)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (is_posix_acl_xattr(ctx->kname->name))
|
||||
error = do_get_acl(idmap, d, kname, ctx->kvalue, ctx->size);
|
||||
if (is_posix_acl_xattr(kname))
|
||||
error = do_get_acl(idmap, d, kname, kvalue, ctx->size);
|
||||
else
|
||||
error = vfs_getxattr(idmap, d, kname, ctx->kvalue, ctx->size);
|
||||
error = vfs_getxattr(idmap, d, kname, kvalue, ctx->size);
|
||||
if (error > 0) {
|
||||
if (ctx->size && copy_to_user(ctx->value, ctx->kvalue, error))
|
||||
if (ctx->size && copy_to_user(ctx->value, kvalue, error))
|
||||
error = -EFAULT;
|
||||
} else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) {
|
||||
/* The file system tried to returned a value bigger
|
||||
@ -746,79 +794,114 @@ do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
|
||||
error = -E2BIG;
|
||||
}
|
||||
|
||||
kvfree(kvalue);
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
getxattr(struct mnt_idmap *idmap, struct dentry *d,
|
||||
const char __user *name, void __user *value, size_t size)
|
||||
ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
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(idmap, d, &ctx);
|
||||
|
||||
kvfree(ctx.kvalue);
|
||||
return error;
|
||||
audit_file(f);
|
||||
return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
|
||||
}
|
||||
|
||||
static ssize_t path_getxattr(const char __user *pathname,
|
||||
const char __user *name, void __user *value,
|
||||
size_t size, unsigned int lookup_flags)
|
||||
/* unconditionally consumes filename */
|
||||
ssize_t filename_getxattr(int dfd, struct filename *filename,
|
||||
unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
|
||||
{
|
||||
struct path path;
|
||||
ssize_t error;
|
||||
retry:
|
||||
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
||||
error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
error = getxattr(mnt_idmap(path.mnt), path.dentry, name, value, size);
|
||||
goto out;
|
||||
error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx);
|
||||
path_put(&path);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
goto retry;
|
||||
}
|
||||
out:
|
||||
putname(filename);
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t path_getxattrat(int dfd, const char __user *pathname,
|
||||
unsigned int at_flags, const char __user *name,
|
||||
void __user *value, size_t size)
|
||||
{
|
||||
struct xattr_name kname;
|
||||
struct kernel_xattr_ctx ctx = {
|
||||
.value = value,
|
||||
.size = size,
|
||||
.kname = &kname,
|
||||
.flags = 0,
|
||||
};
|
||||
struct filename *filename;
|
||||
ssize_t error;
|
||||
|
||||
if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
error = import_xattr_name(&kname, name);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
filename = getname_maybe_null(pathname, at_flags);
|
||||
if (!filename) {
|
||||
CLASS(fd, f)(dfd);
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
return file_getxattr(fd_file(f), &ctx);
|
||||
} else {
|
||||
int lookup_flags = 0;
|
||||
if (!(at_flags & AT_SYMLINK_NOFOLLOW))
|
||||
lookup_flags = LOOKUP_FOLLOW;
|
||||
return filename_getxattr(dfd, filename, lookup_flags, &ctx);
|
||||
}
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE6(getxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
|
||||
const char __user *, name, struct xattr_args __user *, uargs, size_t, usize)
|
||||
{
|
||||
struct xattr_args args = {};
|
||||
int error;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
|
||||
BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
|
||||
|
||||
if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
|
||||
return -EINVAL;
|
||||
if (usize > PAGE_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (args.flags != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return path_getxattrat(dfd, pathname, at_flags, name,
|
||||
u64_to_user_ptr(args.value), args.size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(getxattr, const char __user *, pathname,
|
||||
const char __user *, name, void __user *, value, size_t, size)
|
||||
{
|
||||
return path_getxattr(pathname, name, value, size, LOOKUP_FOLLOW);
|
||||
return path_getxattrat(AT_FDCWD, pathname, 0, name, value, size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
|
||||
const char __user *, name, void __user *, value, size_t, size)
|
||||
{
|
||||
return path_getxattr(pathname, name, value, size, 0);
|
||||
return path_getxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name,
|
||||
value, size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
|
||||
void __user *, value, size_t, size)
|
||||
{
|
||||
struct fd f = fdget(fd);
|
||||
ssize_t error = -EBADF;
|
||||
|
||||
if (!fd_file(f))
|
||||
return error;
|
||||
audit_file(fd_file(f));
|
||||
error = getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
|
||||
name, value, size);
|
||||
fdput(f);
|
||||
return error;
|
||||
return path_getxattrat(fd, NULL, AT_EMPTY_PATH, name, value, size);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -853,47 +936,80 @@ listxattr(struct dentry *d, char __user *list, size_t size)
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t path_listxattr(const char __user *pathname, char __user *list,
|
||||
size_t size, unsigned int lookup_flags)
|
||||
static
|
||||
ssize_t file_listxattr(struct file *f, char __user *list, size_t size)
|
||||
{
|
||||
audit_file(f);
|
||||
return listxattr(f->f_path.dentry, list, size);
|
||||
}
|
||||
|
||||
/* unconditionally consumes filename */
|
||||
static
|
||||
ssize_t filename_listxattr(int dfd, struct filename *filename,
|
||||
unsigned int lookup_flags,
|
||||
char __user *list, size_t size)
|
||||
{
|
||||
struct path path;
|
||||
ssize_t error;
|
||||
retry:
|
||||
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
||||
error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
goto out;
|
||||
error = listxattr(path.dentry, list, size);
|
||||
path_put(&path);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
goto retry;
|
||||
}
|
||||
out:
|
||||
putname(filename);
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t path_listxattrat(int dfd, const char __user *pathname,
|
||||
unsigned int at_flags, char __user *list,
|
||||
size_t size)
|
||||
{
|
||||
struct filename *filename;
|
||||
int lookup_flags;
|
||||
|
||||
if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
filename = getname_maybe_null(pathname, at_flags);
|
||||
if (!filename) {
|
||||
CLASS(fd, f)(dfd);
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
return file_listxattr(fd_file(f), list, size);
|
||||
}
|
||||
|
||||
lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
|
||||
return filename_listxattr(dfd, filename, lookup_flags, list, size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(listxattrat, int, dfd, const char __user *, pathname,
|
||||
unsigned int, at_flags,
|
||||
char __user *, list, size_t, size)
|
||||
{
|
||||
return path_listxattrat(dfd, pathname, at_flags, list, size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
|
||||
size_t, size)
|
||||
{
|
||||
return path_listxattr(pathname, list, size, LOOKUP_FOLLOW);
|
||||
return path_listxattrat(AT_FDCWD, pathname, 0, list, size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
|
||||
size_t, size)
|
||||
{
|
||||
return path_listxattr(pathname, list, size, 0);
|
||||
return path_listxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, list, size);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
|
||||
{
|
||||
struct fd f = fdget(fd);
|
||||
ssize_t error = -EBADF;
|
||||
|
||||
if (!fd_file(f))
|
||||
return error;
|
||||
audit_file(fd_file(f));
|
||||
error = listxattr(fd_file(f)->f_path.dentry, list, size);
|
||||
fdput(f);
|
||||
return error;
|
||||
return path_listxattrat(fd, NULL, AT_EMPTY_PATH, list, size);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -907,25 +1023,33 @@ removexattr(struct mnt_idmap *idmap, struct dentry *d, const char *name)
|
||||
return vfs_removexattr(idmap, d, name);
|
||||
}
|
||||
|
||||
static int path_removexattr(const char __user *pathname,
|
||||
const char __user *name, unsigned int lookup_flags)
|
||||
static int file_removexattr(struct file *f, struct xattr_name *kname)
|
||||
{
|
||||
int error = mnt_want_write_file(f);
|
||||
|
||||
if (!error) {
|
||||
audit_file(f);
|
||||
error = removexattr(file_mnt_idmap(f),
|
||||
f->f_path.dentry, kname->name);
|
||||
mnt_drop_write_file(f);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* unconditionally consumes filename */
|
||||
static int filename_removexattr(int dfd, struct filename *filename,
|
||||
unsigned int lookup_flags, struct xattr_name *kname)
|
||||
{
|
||||
struct path path;
|
||||
int error;
|
||||
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;
|
||||
retry:
|
||||
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
||||
error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
goto out;
|
||||
error = mnt_want_write(path.mnt);
|
||||
if (!error) {
|
||||
error = removexattr(mnt_idmap(path.mnt), path.dentry, kname);
|
||||
error = removexattr(mnt_idmap(path.mnt), path.dentry, kname->name);
|
||||
mnt_drop_write(path.mnt);
|
||||
}
|
||||
path_put(&path);
|
||||
@ -933,45 +1057,58 @@ retry:
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
goto retry;
|
||||
}
|
||||
out:
|
||||
putname(filename);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int path_removexattrat(int dfd, const char __user *pathname,
|
||||
unsigned int at_flags, const char __user *name)
|
||||
{
|
||||
struct xattr_name kname;
|
||||
struct filename *filename;
|
||||
unsigned int lookup_flags;
|
||||
int error;
|
||||
|
||||
if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
error = import_xattr_name(&kname, name);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
filename = getname_maybe_null(pathname, at_flags);
|
||||
if (!filename) {
|
||||
CLASS(fd, f)(dfd);
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
return file_removexattr(fd_file(f), &kname);
|
||||
}
|
||||
lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
|
||||
return filename_removexattr(dfd, filename, lookup_flags, &kname);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(removexattrat, int, dfd, const char __user *, pathname,
|
||||
unsigned int, at_flags, const char __user *, name)
|
||||
{
|
||||
return path_removexattrat(dfd, pathname, at_flags, name);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
|
||||
const char __user *, name)
|
||||
{
|
||||
return path_removexattr(pathname, name, LOOKUP_FOLLOW);
|
||||
return path_removexattrat(AT_FDCWD, pathname, 0, name);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
|
||||
const char __user *, name)
|
||||
{
|
||||
return path_removexattr(pathname, name, 0);
|
||||
return path_removexattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
|
||||
{
|
||||
struct fd f = fdget(fd);
|
||||
char kname[XATTR_NAME_MAX + 1];
|
||||
int error = -EBADF;
|
||||
|
||||
if (!fd_file(f))
|
||||
return error;
|
||||
audit_file(fd_file(f));
|
||||
|
||||
error = strncpy_from_user(kname, name, sizeof(kname));
|
||||
if (error == 0 || error == sizeof(kname))
|
||||
error = -ERANGE;
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = mnt_want_write_file(fd_file(f));
|
||||
if (!error) {
|
||||
error = removexattr(file_mnt_idmap(fd_file(f)),
|
||||
fd_file(f)->f_path.dentry, kname);
|
||||
mnt_drop_write_file(fd_file(f));
|
||||
}
|
||||
fdput(f);
|
||||
return error;
|
||||
return path_removexattrat(fd, NULL, AT_EMPTY_PATH, name);
|
||||
}
|
||||
|
||||
int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
|
||||
@ -1005,9 +1142,10 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
{
|
||||
const struct xattr_handler *handler, * const *handlers = dentry->d_sb->s_xattr;
|
||||
ssize_t remaining_size = buffer_size;
|
||||
int err = 0;
|
||||
|
||||
for_each_xattr_handler(handlers, handler) {
|
||||
int err;
|
||||
|
||||
if (!handler->name || (handler->list && !handler->list(dentry)))
|
||||
continue;
|
||||
err = xattr_list_one(&buffer, &remaining_size, handler->name);
|
||||
@ -1015,7 +1153,7 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err ? err : buffer_size - remaining_size;
|
||||
return buffer_size - remaining_size;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_listxattr);
|
||||
|
||||
|
@ -11,9 +11,15 @@ __NR_lchown,
|
||||
__NR_fchown,
|
||||
#endif
|
||||
__NR_setxattr,
|
||||
#ifdef __NR_setxattrat
|
||||
__NR_setxattrat,
|
||||
#endif
|
||||
__NR_lsetxattr,
|
||||
__NR_fsetxattr,
|
||||
__NR_removexattr,
|
||||
#ifdef __NR_removexattrat
|
||||
__NR_removexattrat,
|
||||
#endif
|
||||
__NR_lremovexattr,
|
||||
__NR_fremovexattr,
|
||||
#ifdef __NR_fchownat
|
||||
|
@ -2789,6 +2789,16 @@ extern struct filename *getname_flags(const char __user *, int);
|
||||
extern struct filename *getname_uflags(const char __user *, int);
|
||||
extern struct filename *getname(const char __user *);
|
||||
extern struct filename *getname_kernel(const char *);
|
||||
extern struct filename *__getname_maybe_null(const char __user *);
|
||||
static inline struct filename *getname_maybe_null(const char __user *name, int flags)
|
||||
{
|
||||
if (!(flags & AT_EMPTY_PATH))
|
||||
return getname(name);
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
return __getname_maybe_null(name);
|
||||
}
|
||||
extern void putname(struct filename *name);
|
||||
|
||||
extern int finish_open(struct file *file, struct dentry *dentry,
|
||||
|
@ -77,6 +77,7 @@ struct cachestat_range;
|
||||
struct cachestat;
|
||||
struct statmount;
|
||||
struct mnt_id_req;
|
||||
struct xattr_args;
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/aio_abi.h>
|
||||
@ -338,23 +339,35 @@ asmlinkage long sys_io_uring_register(unsigned int fd, unsigned int op,
|
||||
void __user *arg, unsigned int nr_args);
|
||||
asmlinkage long sys_setxattr(const char __user *path, const char __user *name,
|
||||
const void __user *value, size_t size, int flags);
|
||||
asmlinkage long sys_setxattrat(int dfd, const char __user *path, unsigned int at_flags,
|
||||
const char __user *name,
|
||||
const struct xattr_args __user *args, size_t size);
|
||||
asmlinkage long sys_lsetxattr(const char __user *path, const char __user *name,
|
||||
const void __user *value, size_t size, int flags);
|
||||
asmlinkage long sys_fsetxattr(int fd, const char __user *name,
|
||||
const void __user *value, size_t size, int flags);
|
||||
asmlinkage long sys_getxattr(const char __user *path, const char __user *name,
|
||||
void __user *value, size_t size);
|
||||
asmlinkage long sys_getxattrat(int dfd, const char __user *path, unsigned int at_flags,
|
||||
const char __user *name,
|
||||
struct xattr_args __user *args, size_t size);
|
||||
asmlinkage long sys_lgetxattr(const char __user *path, const char __user *name,
|
||||
void __user *value, size_t size);
|
||||
asmlinkage long sys_fgetxattr(int fd, const char __user *name,
|
||||
void __user *value, size_t size);
|
||||
asmlinkage long sys_listxattr(const char __user *path, char __user *list,
|
||||
size_t size);
|
||||
asmlinkage long sys_listxattrat(int dfd, const char __user *path,
|
||||
unsigned int at_flags,
|
||||
char __user *list, size_t size);
|
||||
asmlinkage long sys_llistxattr(const char __user *path, char __user *list,
|
||||
size_t size);
|
||||
asmlinkage long sys_flistxattr(int fd, char __user *list, size_t size);
|
||||
asmlinkage long sys_removexattr(const char __user *path,
|
||||
const char __user *name);
|
||||
asmlinkage long sys_removexattrat(int dfd, const char __user *path,
|
||||
unsigned int at_flags,
|
||||
const char __user *name);
|
||||
asmlinkage long sys_lremovexattr(const char __user *path,
|
||||
const char __user *name);
|
||||
asmlinkage long sys_fremovexattr(int fd, const char __user *name);
|
||||
|
@ -19,6 +19,10 @@
|
||||
#include <linux/user_namespace.h>
|
||||
#include <uapi/linux/xattr.h>
|
||||
|
||||
/* List of all open_how "versions". */
|
||||
#define XATTR_ARGS_SIZE_VER0 16 /* sizeof first published struct */
|
||||
#define XATTR_ARGS_SIZE_LATEST XATTR_ARGS_SIZE_VER0
|
||||
|
||||
struct inode;
|
||||
struct dentry;
|
||||
|
||||
|
@ -841,8 +841,17 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules)
|
||||
#define __NR_mseal 462
|
||||
__SYSCALL(__NR_mseal, sys_mseal)
|
||||
|
||||
#define __NR_setxattrat 463
|
||||
__SYSCALL(__NR_setxattrat, sys_setxattrat)
|
||||
#define __NR_getxattrat 464
|
||||
__SYSCALL(__NR_getxattrat, sys_getxattrat)
|
||||
#define __NR_listxattrat 465
|
||||
__SYSCALL(__NR_listxattrat, sys_listxattrat)
|
||||
#define __NR_removexattrat 466
|
||||
__SYSCALL(__NR_removexattrat, sys_removexattrat)
|
||||
|
||||
#undef __NR_syscalls
|
||||
#define __NR_syscalls 463
|
||||
#define __NR_syscalls 467
|
||||
|
||||
/*
|
||||
* 32 bit systems traditionally used different
|
||||
|
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/libc-compat.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifndef _UAPI_LINUX_XATTR_H
|
||||
#define _UAPI_LINUX_XATTR_H
|
||||
@ -20,6 +21,12 @@
|
||||
|
||||
#define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
|
||||
#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
|
||||
|
||||
struct xattr_args {
|
||||
__aligned_u64 __user value;
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Namespaces */
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
struct io_xattr {
|
||||
struct file *file;
|
||||
struct xattr_ctx ctx;
|
||||
struct kernel_xattr_ctx ctx;
|
||||
struct filename *filename;
|
||||
};
|
||||
|
||||
@ -48,13 +48,10 @@ static int __io_getxattr_prep(struct io_kiocb *req,
|
||||
const char __user *name;
|
||||
int ret;
|
||||
|
||||
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.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
|
||||
ix->ctx.size = READ_ONCE(sqe->len);
|
||||
ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
|
||||
|
||||
@ -65,11 +62,8 @@ static int __io_getxattr_prep(struct io_kiocb *req,
|
||||
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) {
|
||||
ret = import_xattr_name(ix->ctx.kname, name);
|
||||
if (ret) {
|
||||
kfree(ix->ctx.kname);
|
||||
return ret;
|
||||
}
|
||||
@ -90,19 +84,20 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
const char __user *path;
|
||||
int ret;
|
||||
|
||||
if (unlikely(req->flags & REQ_F_FIXED_FILE))
|
||||
return -EBADF;
|
||||
|
||||
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);
|
||||
if (IS_ERR(ix->filename)) {
|
||||
ret = PTR_ERR(ix->filename);
|
||||
ix->filename = NULL;
|
||||
}
|
||||
ix->filename = getname(path);
|
||||
if (IS_ERR(ix->filename))
|
||||
return PTR_ERR(ix->filename);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
@ -112,10 +107,7 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
||||
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
|
||||
|
||||
ret = do_getxattr(file_mnt_idmap(req->file),
|
||||
req->file->f_path.dentry,
|
||||
&ix->ctx);
|
||||
|
||||
ret = file_getxattr(req->file, &ix->ctx);
|
||||
io_xattr_finish(req, ret);
|
||||
return IOU_OK;
|
||||
}
|
||||
@ -123,24 +115,12 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
struct path path;
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
|
||||
|
||||
retry:
|
||||
ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
|
||||
if (!ret) {
|
||||
ret = do_getxattr(mnt_idmap(path.mnt), path.dentry, &ix->ctx);
|
||||
|
||||
path_put(&path);
|
||||
if (retry_estale(ret, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
|
||||
ix->filename = NULL;
|
||||
io_xattr_finish(req, ret);
|
||||
return IOU_OK;
|
||||
}
|
||||
@ -152,9 +132,6 @@ static int __io_setxattr_prep(struct io_kiocb *req,
|
||||
const char __user *name;
|
||||
int ret;
|
||||
|
||||
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));
|
||||
@ -183,19 +160,20 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
const char __user *path;
|
||||
int ret;
|
||||
|
||||
if (unlikely(req->flags & REQ_F_FIXED_FILE))
|
||||
return -EBADF;
|
||||
|
||||
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);
|
||||
if (IS_ERR(ix->filename)) {
|
||||
ret = PTR_ERR(ix->filename);
|
||||
ix->filename = NULL;
|
||||
}
|
||||
ix->filename = getname(path);
|
||||
if (IS_ERR(ix->filename))
|
||||
return PTR_ERR(ix->filename);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||
@ -203,28 +181,14 @@ 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,
|
||||
const struct path *path)
|
||||
int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
|
||||
int ret;
|
||||
|
||||
ret = mnt_want_write(path->mnt);
|
||||
if (!ret) {
|
||||
ret = do_setxattr(mnt_idmap(path->mnt), path->dentry, &ix->ctx);
|
||||
mnt_drop_write(path->mnt);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
|
||||
|
||||
ret = __io_setxattr(req, issue_flags, &req->file->f_path);
|
||||
ret = file_setxattr(req->file, &ix->ctx);
|
||||
io_xattr_finish(req, ret);
|
||||
return IOU_OK;
|
||||
}
|
||||
@ -232,23 +196,12 @@ int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
struct path path;
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
|
||||
ix->filename = NULL;
|
||||
io_xattr_finish(req, ret);
|
||||
return IOU_OK;
|
||||
}
|
||||
|
@ -403,3 +403,7 @@
|
||||
460 common lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 common lsm_list_modules sys_lsm_list_modules
|
||||
462 common mseal sys_mseal
|
||||
463 common setxattrat sys_setxattrat
|
||||
464 common getxattrat sys_getxattrat
|
||||
465 common listxattrat sys_listxattrat
|
||||
466 common removexattrat sys_removexattrat
|
||||
|
Loading…
Reference in New Issue
Block a user