@@ -202,3 +202,22 @@ struct linux_dirent64;
int vfs_getdents(struct file *file, struct linux_dirent64 __user *dirent,
unsigned int count, loff_t *pos);
+
+ /*
+ * fs/xattr.c:
+ */
+struct xattr_ctx {
+ /* Value of attribute */
+ const void __user *value;
+ size_t size;
+ /* Attribute name */
+ char *kname;
+ int kname_sz;
+ unsigned int flags;
+};
+
+
+int setxattr_copy(const char __user *name, struct xattr_ctx *ctx,
+ void **xattr_val);
+int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ struct xattr_ctx *ctx, void *xattr_val);
@@ -25,6 +25,8 @@
#include <linux/uaccess.h>
+#include "internal.h"
+
static const char *
strcmp_prefix(const char *a, const char *a_prefix)
{
@@ -539,43 +541,84 @@ 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,
+ void **xattr_val)
{
- int error;
void *kvalue = NULL;
- char kname[XATTR_NAME_MAX + 1];
+ int error;
- 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, ctx->kname_sz);
+ if (error == 0 || error == ctx->kname_sz)
+ return -ERANGE;
if (error < 0)
return error;
- if (size) {
- if (size > XATTR_SIZE_MAX)
+ if (ctx->size) {
+ if (ctx->size > XATTR_SIZE_MAX)
return -E2BIG;
- kvalue = kvmalloc(size, GFP_KERNEL);
+
+ kvalue = kvmalloc(ctx->size, GFP_KERNEL);
if (!kvalue)
return -ENOMEM;
- if (copy_from_user(kvalue, value, size)) {
- error = -EFAULT;
- goto out;
+
+ if (copy_from_user(kvalue, ctx->value, ctx->size)) {
+ kvfree(kvalue);
+ return -EFAULT;
}
- 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, kvalue, size);
}
- error = vfs_setxattr(mnt_userns, d, kname, kvalue, size, flags);
-out:
- kvfree(kvalue);
+ *xattr_val = kvalue;
+ return 0;
+}
+
+static void setxattr_convert(struct user_namespace *mnt_userns,
+ struct xattr_ctx *ctx, void *xattr_value)
+{
+ if (ctx->size &&
+ ((strcmp(ctx->kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
+ (strcmp(ctx->kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)))
+ posix_acl_fix_xattr_from_user(mnt_userns, xattr_value, ctx->size);
+}
+
+int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ struct xattr_ctx *ctx, void *xattr_value)
+{
+ int error;
+
+ setxattr_convert(mnt_userns, ctx, xattr_value);
+ error = vfs_setxattr(mnt_userns, dentry, ctx->kname,
+ xattr_value, ctx->size, ctx->flags);
+
+ return error;
+}
+
+static long
+setxattr(struct user_namespace *mnt_userns, struct dentry *d,
+ const char __user *name, const void __user *value, size_t size,
+ int flags)
+{
+ char kname[XATTR_NAME_MAX + 1];
+ struct xattr_ctx ctx = {
+ .value = value,
+ .size = size,
+ .kname = kname,
+ .kname_sz = sizeof(kname),
+ .flags = flags,
+ };
+ void *xattr_value = NULL;
+ int error;
+
+ error = setxattr_copy(name, &ctx, &xattr_value);
+ if (error)
+ return error;
+
+ error = do_setxattr(mnt_userns, d, &ctx, xattr_value);
+ kvfree(xattr_value);
return error;
}
This splits of the setup part of the function setxattr in its own dedicated function called setxattr_copy. In addition it also exposes a new function called do_setxattr for making the setxattr call. This makes it possible to call these two functions from io_uring in the processing of an xattr request. Signed-off-by: Stefan Roesch <shr@fb.com> --- fs/internal.h | 19 +++++++++++ fs/xattr.c | 87 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 84 insertions(+), 22 deletions(-)