diff --git a/fs/internal.h b/fs/internal.h index be7c0da3bcec..8001efd1f047 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -280,11 +280,9 @@ struct kernel_xattr_ctx { unsigned int flags; }; - -ssize_t do_getxattr(struct mnt_idmap *idmap, - struct dentry *d, - struct kernel_xattr_ctx *ctx); - +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); diff --git a/fs/xattr.c b/fs/xattr.c index 38bf8cfbd464..d55f3d1e7589 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -744,27 +744,28 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, /* * Extended attribute GET operations */ -ssize_t +static ssize_t do_getxattr(struct mnt_idmap *idmap, struct dentry *d, 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 @@ -772,18 +773,45 @@ 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) +{ + audit_file(f); + return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx); +} + +/* 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 = filename_lookup(dfd, filename, lookup_flags, &path, NULL); + if (error) + 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_getxattr(const char __user *pathname, + const char __user *name, void __user *value, + size_t size, unsigned int lookup_flags) { ssize_t error; struct xattr_name kname; struct kernel_xattr_ctx ctx = { .value = value, - .kvalue = NULL, .size = size, .kname = &kname, .flags = 0, @@ -792,30 +820,7 @@ getxattr(struct mnt_idmap *idmap, struct dentry *d, error = import_xattr_name(&kname, name); if (error) return error; - - error = do_getxattr(idmap, d, &ctx); - - kvfree(ctx.kvalue); - return error; -} - -static ssize_t path_getxattr(const char __user *pathname, - const char __user *name, void __user *value, - size_t size, unsigned int lookup_flags) -{ - struct path path; - ssize_t error; -retry: - error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); - if (error) - return error; - error = getxattr(mnt_idmap(path.mnt), path.dentry, name, value, size); - path_put(&path); - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; - } - return error; + return filename_getxattr(AT_FDCWD, getname(pathname), lookup_flags, &ctx); } SYSCALL_DEFINE4(getxattr, const char __user *, pathname, @@ -833,13 +838,22 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname, SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, void __user *, value, size_t, size) { + ssize_t error; + struct xattr_name kname; + struct kernel_xattr_ctx ctx = { + .value = value, + .size = size, + .kname = &kname, + .flags = 0, + }; CLASS(fd, f)(fd); if (fd_empty(f)) return -EBADF; - audit_file(fd_file(f)); - return getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry, - name, value, size); + error = import_xattr_name(&kname, name); + if (error) + return error; + return file_getxattr(fd_file(f), &ctx); } /* diff --git a/io_uring/xattr.c b/io_uring/xattr.c index 2671ad05d63b..de5064fcae8a 100644 --- a/io_uring/xattr.c +++ b/io_uring/xattr.c @@ -51,7 +51,7 @@ static int __io_getxattr_prep(struct io_kiocb *req, 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); @@ -94,12 +94,10 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) path = u64_to_user_ptr(READ_ONCE(sqe->addr3)); ix->filename = getname(path); - if (IS_ERR(ix->filename)) { - ret = PTR_ERR(ix->filename); - ix->filename = NULL; - } + 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) @@ -109,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; } @@ -120,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; }