Release 4.11 fs/xattr.c
/*
File: fs/xattr.c
Extended attribute handling.
Copyright (C) 2001 by Andreas Gruenbacher <a.gruenbacher@computer.org>
Copyright (C) 2001 SGI - Silicon Graphics, Inc <linux-xfs@oss.sgi.com>
Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
*/
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/xattr.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/evm.h>
#include <linux/syscalls.h>
#include <linux/export.h>
#include <linux/fsnotify.h>
#include <linux/audit.h>
#include <linux/vmalloc.h>
#include <linux/posix_acl_xattr.h>
#include <linux/uaccess.h>
static const char *
strcmp_prefix(const char *a, const char *a_prefix)
{
while (*a_prefix && *a == *a_prefix) {
a++;
a_prefix++;
}
return *a_prefix ? NULL : a;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
/*
* In order to implement different sets of xattr operations for each xattr
* prefix, a filesystem should create a null-terminated array of struct
* xattr_handler (one for each prefix) and hang a pointer to it off of the
* s_xattr field of the superblock.
*/
#define for_each_xattr_handler(handlers, handler) \
if (handlers) \
for ((handler) = *(handlers)++; \
(handler) != NULL; \
(handler) = *(handlers)++)
/*
* Find the xattr_handler with the matching prefix.
*/
static const struct xattr_handler *
xattr_resolve_name(struct inode *inode, const char **name)
{
const struct xattr_handler **handlers = inode->i_sb->s_xattr;
const struct xattr_handler *handler;
if (!(inode->i_opflags & IOP_XATTR)) {
if (unlikely(is_bad_inode(inode)))
return ERR_PTR(-EIO);
return ERR_PTR(-EOPNOTSUPP);
}
for_each_xattr_handler(handlers, handler) {
const char *n;
n = strcmp_prefix(*name, xattr_prefix(handler));
if (n) {
if (!handler->prefix ^ !*n) {
if (*n)
continue;
return ERR_PTR(-EINVAL);
}
*name = n;
return handler;
}
}
return ERR_PTR(-EOPNOTSUPP);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 149 | 100.00% | 3 | 100.00% |
Total | 149 | 100.00% | 3 | 100.00% |
/*
* Check permissions for extended attribute access. This is a bit complicated
* because different namespaces have very different rules.
*/
static int
xattr_permission(struct inode *inode, const char *name, int mask)
{
/*
* We can never set or remove an extended attribute on a read-only
* filesystem or on an immutable / append-only inode.
*/
if (mask & MAY_WRITE) {
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
return -EPERM;
/*
* Updating an xattr will likely cause i_uid and i_gid
* to be writen back improperly if their true value is
* unknown to the vfs.
*/
if (HAS_UNMAPPED_ID(inode))
return -EPERM;
}
/*
* No restriction for security.* and system.* from the VFS. Decision
* on these is left to the underlying filesystem / security module.
*/
if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) ||
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
return 0;
/*
* The trusted.* namespace can only be accessed by privileged users.
*/
if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) {
if (!capable(CAP_SYS_ADMIN))
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
return 0;
}
/*
* In the user.* namespace, only regular files and directories can have
* extended attributes. For sticky directories, only the owner and
* privileged users can write attributes.
*/
if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) {
if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
(mask & MAY_WRITE) && !inode_owner_or_capable(inode))
return -EPERM;
}
return inode_permission(inode, mask);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 139 | 66.83% | 1 | 14.29% |
Andreas Gruenbacher | 54 | 25.96% | 2 | 28.57% |
Eric W. Biedermann | 12 | 5.77% | 1 | 14.29% |
Serge E. Hallyn | 1 | 0.48% | 1 | 14.29% |
Al Viro | 1 | 0.48% | 1 | 14.29% |
Satyam Sharma | 1 | 0.48% | 1 | 14.29% |
Total | 208 | 100.00% | 7 | 100.00% |
int
__vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
const struct xattr_handler *handler;
handler = xattr_resolve_name(inode, &name);
if (IS_ERR(handler))
return PTR_ERR(handler);
if (!handler->set)
return -EOPNOTSUPP;
if (size == 0)
value = ""; /* empty EA, do not remove */
return handler->set(handler, dentry, inode, name, value, size, flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 102 | 100.00% | 2 | 100.00% |
Total | 102 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(__vfs_setxattr);
/**
* __vfs_setxattr_noperm - perform setxattr operation without performing
* permission checks.
*
* @dentry - object to perform setxattr on
* @name - xattr name to set
* @value - value to set @name to
* @size - size of @value
* @flags - flags to pass into filesystem operations
*
* returns the result of the internal setxattr or setsecurity operations.
*
* This function requires the caller to lock the inode's i_mutex before it
* is executed. It also assumes that the caller will make the appropriate
* permission checks.
*/
int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
int error = -EAGAIN;
int issec = !strncmp(name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN);
if (issec)
inode->i_flags &= ~S_NOSEC;
if (inode->i_opflags & IOP_XATTR) {
error = __vfs_setxattr(dentry, inode, name, value, size, flags);
if (!error) {
fsnotify_xattr(dentry);
security_inode_post_setxattr(dentry, name, value,
size, flags);
}
} else {
if (unlikely(is_bad_inode(inode)))
return -EIO;
}
if (error == -EAGAIN) {
error = -EOPNOTSUPP;
if (issec) {
const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
error = security_inode_setsecurity(inode, suffix, value,
size, flags);
if (!error)
fsnotify_xattr(dentry);
}
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Christoph Hellwig | 107 | 56.02% | 1 | 11.11% |
Andreas Gruenbacher | 49 | 25.65% | 4 | 44.44% |
Andi Kleen | 24 | 12.57% | 1 | 11.11% |
David P. Quigley | 7 | 3.66% | 1 | 11.11% |
David Howells | 2 | 1.05% | 1 | 11.11% |
Al Viro | 2 | 1.05% | 1 | 11.11% |
Total | 191 | 100.00% | 9 | 100.00% |
int
vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
int error;
error = xattr_permission(inode, name, MAY_WRITE);
if (error)
return error;
inode_lock(inode);
error = security_inode_setxattr(dentry, name, value, size, flags);
if (error)
goto out;
error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
out:
inode_unlock(inode);
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David P. Quigley | 96 | 88.89% | 1 | 33.33% |
Christoph Hellwig | 10 | 9.26% | 1 | 33.33% |
Al Viro | 2 | 1.85% | 1 | 33.33% |
Total | 108 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL_GPL(vfs_setxattr);
ssize_t
xattr_getsecurity(struct inode *inode, const char *name, void *value,
size_t size)
{
void *buffer = NULL;
ssize_t len;
if (!value || !size) {
len = security_inode_getsecurity(inode, name, &buffer, false);
goto out_noalloc;
}
len = security_inode_getsecurity(inode, name, &buffer, true);
if (len < 0)
return len;
if (size < len) {
len = -ERANGE;
goto out;
}
memcpy(value, buffer, len);
out:
security_release_secctx(buffer, len);
out_noalloc:
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David P. Quigley | 119 | 99.17% | 1 | 50.00% |
Christoph Hellwig | 1 | 0.83% | 1 | 50.00% |
Total | 120 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(xattr_getsecurity);
/*
* vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
*
* Allocate memory, if not already allocated, or re-allocate correct size,
* before retrieving the extended attribute.
*
* Returns the result of alloc, if failed, or the getxattr operation.
*/
ssize_t
vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
size_t xattr_size, gfp_t flags)
{
const struct xattr_handler *handler;
struct inode *inode = dentry->d_inode;
char *value = *xattr_value;
int error;
error = xattr_permission(inode, name, MAY_READ);
if (error)
return error;
handler = xattr_resolve_name(inode, &name);
if (IS_ERR(handler))
return PTR_ERR(handler);
if (!handler->get)
return -EOPNOTSUPP;
error = handler->get(handler, dentry, inode, name, NULL, 0);
if (error < 0)
return error;
if (!value || (error > xattr_size)) {
value = krealloc(*xattr_value, error + 1, flags);
if (!value)
return -ENOMEM;
memset(value, 0, error + 1);
}
error = handler->get(handler, dentry, inode, name, value, error);
*xattr_value = value;
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mimi Zohar | 162 | 79.02% | 1 | 33.33% |
Andreas Gruenbacher | 39 | 19.02% | 1 | 33.33% |
Al Viro | 4 | 1.95% | 1 | 33.33% |
Total | 205 | 100.00% | 3 | 100.00% |
ssize_t
__vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
void *value, size_t size)
{
const struct xattr_handler *handler;
handler = xattr_resolve_name(inode, &name);
if (IS_ERR(handler))
return PTR_ERR(handler);
if (!handler->get)
return -EOPNOTSUPP;
return handler->get(handler, dentry, inode, name, value, size);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 84 | 98.82% | 2 | 66.67% |
David P. Quigley | 1 | 1.18% | 1 | 33.33% |
Total | 85 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL(__vfs_getxattr);
ssize_t
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
{
struct inode *inode = dentry->d_inode;
int error;
error = xattr_permission(inode, name, MAY_READ);
if (error)
return error;
error = security_inode_getxattr(dentry, name);
if (error)
return error;
if (!strncmp(name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN)) {
const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
int ret = xattr_getsecurity(inode, suffix, value, size);
/*
* Only overwrite the return value if a security module
* is actually active.
*/
if (ret == -EOPNOTSUPP)
goto nolsm;
return ret;
}
nolsm:
return __vfs_getxattr(dentry, inode, name, value, size);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Christoph Hellwig | 89 | 65.93% | 1 | 14.29% |
David P. Quigley | 20 | 14.81% | 2 | 28.57% |
Andrew Morton | 20 | 14.81% | 1 | 14.29% |
Andreas Gruenbacher | 3 | 2.22% | 1 | 14.29% |
Al Viro | 2 | 1.48% | 1 | 14.29% |
David Howells | 1 | 0.74% | 1 | 14.29% |
Total | 135 | 100.00% | 7 | 100.00% |
EXPORT_SYMBOL_GPL(vfs_getxattr);
ssize_t
vfs_listxattr(struct dentry *dentry, char *list, size_t size)
{
struct inode *inode = d_inode(dentry);
ssize_t error;
error = security_inode_listxattr(dentry);
if (error)
return error;
if (inode->i_op->listxattr && (inode->i_opflags & IOP_XATTR)) {
error = -EOPNOTSUPP;
error = inode->i_op->listxattr(dentry, list, size);
} else {
error = security_inode_listsecurity(inode, list, size);
if (size && error > size)
error = -ERANGE;
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Bill Nottingham | 83 | 74.11% | 1 | 50.00% |
Andreas Gruenbacher | 29 | 25.89% | 1 | 50.00% |
Total | 112 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(vfs_listxattr);
int
__vfs_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode = d_inode(dentry);
const struct xattr_handler *handler;
handler = xattr_resolve_name(inode, &name);
if (IS_ERR(handler))
return PTR_ERR(handler);
if (!handler->set)
return -EOPNOTSUPP;
return handler->set(handler, dentry, inode, name, NULL, 0, XATTR_REPLACE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 56 | 65.88% | 2 | 50.00% |
Christoph Hellwig | 28 | 32.94% | 1 | 25.00% |
David Howells | 1 | 1.18% | 1 | 25.00% |
Total | 85 | 100.00% | 4 | 100.00% |
EXPORT_SYMBOL(__vfs_removexattr);
int
vfs_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode = dentry->d_inode;
int error;
error = xattr_permission(inode, name, MAY_WRITE);
if (error)
return error;
inode_lock(inode);
error = security_inode_removexattr(dentry, name);
if (error)
goto out;
error = __vfs_removexattr(dentry, name);
if (!error) {
fsnotify_xattr(dentry);
evm_inode_post_removexattr(dentry, name);
}
out:
inode_unlock(inode);
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Christoph Hellwig | 35 | 33.65% | 1 | 14.29% |
Andreas Gruenbacher | 27 | 25.96% | 1 | 14.29% |
Andrew Morton | 18 | 17.31% | 1 | 14.29% |
Mimi Zohar | 13 | 12.50% | 2 | 28.57% |
Dmitry Kasatkin | 9 | 8.65% | 1 | 14.29% |
Al Viro | 2 | 1.92% | 1 | 14.29% |
Total | 104 | 100.00% | 7 | 100.00% |
EXPORT_SYMBOL_GPL(vfs_removexattr);
/*
* Extended attribute SET operations
*/
static long
setxattr(struct dentry *d, const char __user *name, const void __user *value,
size_t size, int flags)
{
int error;
void *kvalue = NULL;
char kname[XATTR_NAME_MAX + 1];
if (flags & ~(XATTR_CREATE|XATTR_REPLACE))
return -EINVAL;
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)
return -E2BIG;
kvalue = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!kvalue) {
kvalue = vmalloc(size);
if (!kvalue)
return -ENOMEM;
}
if (copy_from_user(kvalue, value, size)) {
error = -EFAULT;
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(kvalue, size);
}
error = vfs_setxattr(d, kname, kvalue, size, flags);
out:
kvfree(kvalue);
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 78 | 34.06% | 1 | 9.09% |
Andrew Morton | 57 | 24.89% | 2 | 18.18% |
Nathan Scott | 32 | 13.97% | 1 | 9.09% |
Eric W. Biedermann | 31 | 13.54% | 1 | 9.09% |
Stephen D. Smalley | 15 | 6.55% | 1 | 9.09% |
Christoph Hellwig | 7 | 3.06% | 1 | 9.09% |
Richard Weinberger | 3 | 1.31% | 1 | 9.09% |
Andries E. Brouwer | 2 | 0.87% | 1 | 9.09% |
Li Zefan | 2 | 0.87% | 1 | 9.09% |
David Howells | 2 | 0.87% | 1 | 9.09% |
Total | 229 | 100.00% | 11 | 100.00% |
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)
{
struct path path;
int error;
retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
if (error)
return error;
error = mnt_want_write(path.mnt);
if (!error) {
error = setxattr(path.dentry, name, value, size, flags);
mnt_drop_write(path.mnt);
}
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 43 | 32.82% | 1 | 14.29% |
Eric Biggers | 32 | 24.43% | 1 | 14.29% |
Jeff Layton | 25 | 19.08% | 1 | 14.29% |
Dave Hansen | 23 | 17.56% | 1 | 14.29% |
Jan Blunck | 4 | 3.05% | 2 | 28.57% |
Al Viro | 4 | 3.05% | 1 | 14.29% |
Total | 131 | 100.00% | 7 | 100.00% |
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)
{
struct fd f = fdget(fd);
int error = -EBADF;
if (!f.file)
return error;
audit_file(f.file);
error = mnt_want_write_file(f.file);
if (!error) {
error = setxattr(f.file->f_path.dentry, name, value, size, flags);
mnt_drop_write_file(f.file);
}
fdput(f);
return error;
}
/*
* Extended attribute GET operations
*/
static ssize_t
getxattr(struct dentry *d, const char __user *name, void __user *value,
size_t size)
{
ssize_t error;
void *kvalue = NULL;
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 (size > XATTR_SIZE_MAX)
size = XATTR_SIZE_MAX;
kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!kvalue) {
kvalue = vmalloc(size);
if (!kvalue)
return -ENOMEM;
}
}
error = vfs_getxattr(d, kname, kvalue, 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(kvalue, size);
if (size && copy_to_user(value, kvalue, error))
error = -EFAULT;
} else if (error == -ERANGE && 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 85 | 36.80% | 1 | 6.25% |
Andrew Morton | 45 | 19.48% | 2 | 12.50% |
Nathan Scott | 35 | 15.15% | 3 | 18.75% |
Eric W. Biedermann | 31 | 13.42% | 1 | 6.25% |
Sasha Levin | 14 | 6.06% | 1 | 6.25% |
Stephen D. Smalley | 10 | 4.33% | 2 | 12.50% |
Richard Weinberger | 3 | 1.30% | 1 | 6.25% |
Greg Kroah-Hartman | 3 | 1.30% | 1 | 6.25% |
Andries E. Brouwer | 2 | 0.87% | 1 | 6.25% |
James Morris | 1 | 0.43% | 1 | 6.25% |
Christoph Hellwig | 1 | 0.43% | 1 | 6.25% |
David Howells | 1 | 0.43% | 1 | 6.25% |
Total | 231 | 100.00% | 16 | 100.00% |
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(path.dentry, name, value, size);
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 40 | 39.22% | 1 | 14.29% |
Eric Biggers | 28 | 27.45% | 1 | 14.29% |
Jeff Layton | 25 | 24.51% | 1 | 14.29% |
Jan Blunck | 4 | 3.92% | 2 | 28.57% |
Al Viro | 4 | 3.92% | 1 | 14.29% |
Nathan Scott | 1 | 0.98% | 1 | 14.29% |
Total | 102 | 100.00% | 7 | 100.00% |
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);
}
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);
}
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 (!f.file)
return error;
audit_file(f.file);
error = getxattr(f.file->f_path.dentry, name, value, size);
fdput(f);
return error;
}
/*
* Extended attribute LIST operations
*/
static ssize_t
listxattr(struct dentry *d, char __user *list, size_t size)
{
ssize_t error;
char *klist = NULL;
if (size) {
if (size > XATTR_LIST_MAX)
size = XATTR_LIST_MAX;
klist = kmalloc(size, __GFP_NOWARN | GFP_KERNEL);
if (!klist) {
klist = vmalloc(size);
if (!klist)
return -ENOMEM;
}
}
error = vfs_listxattr(d, klist, size);
if (error > 0) {
if (size && copy_to_user(list, klist, error))
error = -EFAULT;
} else if (error == -ERANGE && size >= XATTR_LIST_MAX) {
/* The file system tried to returned a list bigger
than XATTR_LIST_MAX bytes. Not possible. */
error = -E2BIG;
}
kvfree(klist);
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 64 | 44.76% | 1 | 9.09% |
Andrew Morton | 58 | 40.56% | 3 | 27.27% |
Stephen D. Smalley | 11 | 7.69% | 1 | 9.09% |
Nathan Scott | 3 | 2.10% | 2 | 18.18% |
Richard Weinberger | 3 | 2.10% | 1 | 9.09% |
Dave Jones | 2 | 1.40% | 1 | 9.09% |
Bill Nottingham | 1 | 0.70% | 1 | 9.09% |
Andries E. Brouwer | 1 | 0.70% | 1 | 9.09% |
Total | 143 | 100.00% | 11 | 100.00% |
static ssize_t path_listxattr(const char __user *pathname, char __user *list,
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 = listxattr(path.dentry, list, size);
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 38 | 40.43% | 1 | 14.29% |
Jeff Layton | 25 | 26.60% | 1 | 14.29% |
Eric Biggers | 22 | 23.40% | 1 | 14.29% |
Jan Blunck | 4 | 4.26% | 2 | 28.57% |
Al Viro | 4 | 4.26% | 1 | 14.29% |
Nathan Scott | 1 | 1.06% | 1 | 14.29% |
Total | 94 | 100.00% | 7 | 100.00% |
SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
size_t, size)
{
return path_listxattr(pathname, list, size, LOOKUP_FOLLOW);
}
SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
size_t, size)
{
return path_listxattr(pathname, list, size, 0);
}
SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
{
struct fd f = fdget(fd);
ssize_t error = -EBADF;
if (!f.file)
return error;
audit_file(f.file);
error = listxattr(f.file->f_path.dentry, list, size);
fdput(f);
return error;
}
/*
* Extended attribute REMOVE operations
*/
static long
removexattr(struct dentry *d, const char __user *name)
{
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;
return vfs_removexattr(d, kname);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 41 | 53.25% | 1 | 20.00% |
Nathan Scott | 32 | 41.56% | 1 | 20.00% |
Christoph Hellwig | 2 | 2.60% | 1 | 20.00% |
Andries E. Brouwer | 1 | 1.30% | 1 | 20.00% |
David Howells | 1 | 1.30% | 1 | 20.00% |
Total | 77 | 100.00% | 5 | 100.00% |
static int path_removexattr(const char __user *pathname,
const char __user *name, unsigned int lookup_flags)
{
struct path path;
int error;
retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
if (error)
return error;
error = mnt_want_write(path.mnt);
if (!error) {
error = removexattr(path.dentry, name);
mnt_drop_write(path.mnt);
}
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 37 | 32.74% | 1 | 14.29% |
Jeff Layton | 25 | 22.12% | 1 | 14.29% |
Dave Hansen | 23 | 20.35% | 1 | 14.29% |
Eric Biggers | 20 | 17.70% | 1 | 14.29% |
Al Viro | 4 | 3.54% | 1 | 14.29% |
Jan Blunck | 4 | 3.54% | 2 | 28.57% |
Total | 113 | 100.00% | 7 | 100.00% |
SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
const char __user *, name)
{
return path_removexattr(pathname, name, LOOKUP_FOLLOW);
}
SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
const char __user *, name)
{
return path_removexattr(pathname, name, 0);
}
SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
{
struct fd f = fdget(fd);
int error = -EBADF;
if (!f.file)
return error;
audit_file(f.file);
error = mnt_want_write_file(f.file);
if (!error) {
error = removexattr(f.file->f_path.dentry, name);
mnt_drop_write_file(f.file);
}
fdput(f);
return error;
}
/*
* Combine the results of the list() operation from every xattr_handler in the
* list.
*/
ssize_t
generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
{
const struct xattr_handler *handler, **handlers = dentry->d_sb->s_xattr;
unsigned int size = 0;
if (!buffer) {
for_each_xattr_handler(handlers, handler) {
if (!handler->name ||
(handler->list && !handler->list(dentry)))
continue;
size += strlen(handler->name) + 1;
}
} else {
char *buf = buffer;
size_t len;
for_each_xattr_handler(handlers, handler) {
if (!handler->name ||
(handler->list && !handler->list(dentry)))
continue;
len = strlen(handler->name);
if (len + 1 > buffer_size)
return -ERANGE;
memcpy(buf, handler->name, len + 1);
buf += len + 1;
buffer_size -= len + 1;
}
size = buf - buffer;
}
return size;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Morris | 101 | 54.89% | 1 | 16.67% |
Andreas Gruenbacher | 77 | 41.85% | 3 | 50.00% |
Christoph Hellwig | 5 | 2.72% | 1 | 16.67% |
Stephen Hemminger | 1 | 0.54% | 1 | 16.67% |
Total | 184 | 100.00% | 6 | 100.00% |
EXPORT_SYMBOL(generic_listxattr);
/**
* xattr_full_name - Compute full attribute name from suffix
*
* @handler: handler of the xattr_handler operation
* @name: name passed to the xattr_handler operation
*
* The get and set xattr handler operations are called with the remainder of
* the attribute name after skipping the handler's prefix: for example, "foo"
* is passed to the get operation of a handler with prefix "user." to get
* attribute "user.foo". The full name is still "there" in the name though.
*
* Note: the list xattr handler operation when called from the vfs is passed a
* NULL name; some file systems use this operation internally, with varying
* semantics.
*/
const char *xattr_full_name(const struct xattr_handler *handler,
const char *name)
{
size_t prefix_len = strlen(xattr_prefix(handler));
return name - prefix_len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 34 | 100.00% | 2 | 100.00% |
Total | 34 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(xattr_full_name);
/*
* Allocate new xattr and copy in the value; but leave the name to callers.
*/
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size)
{
struct simple_xattr *new_xattr;
size_t len;
/* wrap around? */
len = sizeof(*new_xattr) + size;
if (len < sizeof(*new_xattr))
return NULL;
new_xattr = kmalloc(len, GFP_KERNEL);
if (!new_xattr)
return NULL;
new_xattr->size = size;
memcpy(new_xattr->value, value, size);
return new_xattr;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 83 | 98.81% | 1 | 50.00% |
Hugh Dickins | 1 | 1.19% | 1 | 50.00% |
Total | 84 | 100.00% | 2 | 100.00% |
/*
* xattr GET operation for in-memory/pseudo filesystems
*/
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
void *buffer, size_t size)
{
struct simple_xattr *xattr;
int ret = -ENODATA;
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
if (strcmp(name, xattr->name))
continue;
ret = xattr->size;
if (buffer) {
if (size < xattr->size)
ret = -ERANGE;
else
memcpy(buffer, xattr->value, xattr->size);
}
break;
}
spin_unlock(&xattrs->lock);
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 114 | 100.00% | 1 | 100.00% |
Total | 114 | 100.00% | 1 | 100.00% |
/**
* simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems
* @xattrs: target simple_xattr list
* @name: name of the extended attribute
* @value: value of the xattr. If %NULL, will remove the attribute.
* @size: size of the new xattr
* @flags: %XATTR_{CREATE|REPLACE}
*
* %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails
* with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist;
* otherwise, fails with -ENODATA.
*
* Returns 0 on success, -errno on failure.
*/
int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
const void *value, size_t size, int flags)
{
struct simple_xattr *xattr;
struct simple_xattr *new_xattr = NULL;
int err = 0;
/* value == NULL means remove */
if (value) {
new_xattr = simple_xattr_alloc(value, size);
if (!new_xattr)
return -ENOMEM;
new_xattr->name = kstrdup(name, GFP_KERNEL);
if (!new_xattr->name) {
kfree(new_xattr);
return -ENOMEM;
}
}
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
if (!strcmp(name, xattr->name)) {
if (flags & XATTR_CREATE) {
xattr = new_xattr;
err = -EEXIST;
} else if (new_xattr) {
list_replace(&xattr->list, &new_xattr->list);
} else {
list_del(&xattr->list);
}
goto out;
}
}
if (flags & XATTR_REPLACE) {
xattr = new_xattr;
err = -ENODATA;
} else {
list_add(&new_xattr->list, &xattrs->head);
xattr = NULL;
}
out:
spin_unlock(&xattrs->lock);
if (xattr) {
kfree(xattr->name);
kfree(xattr);
}
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 245 | 98.79% | 1 | 33.33% |
David Rientjes | 2 | 0.81% | 1 | 33.33% |
Andreas Gruenbacher | 1 | 0.40% | 1 | 33.33% |
Total | 248 | 100.00% | 3 | 100.00% |
static bool xattr_is_trusted(const char *name)
{
return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 22 | 100.00% | 1 | 100.00% |
Total | 22 | 100.00% | 1 | 100.00% |
static int xattr_list_one(char **buffer, ssize_t *remaining_size,
const char *name)
{
size_t len = strlen(name) + 1;
if (*buffer) {
if (*remaining_size < len)
return -ERANGE;
memcpy(*buffer, name, len);
*buffer += len;
}
*remaining_size -= len;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 71 | 100.00% | 1 | 100.00% |
Total | 71 | 100.00% | 1 | 100.00% |
/*
* xattr LIST operation for in-memory/pseudo filesystems
*/
ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
char *buffer, size_t size)
{
bool trusted = capable(CAP_SYS_ADMIN);
struct simple_xattr *xattr;
ssize_t remaining_size = size;
int err = 0;
#ifdef CONFIG_FS_POSIX_ACL
if (inode->i_acl) {
err = xattr_list_one(&buffer, &remaining_size,
XATTR_NAME_POSIX_ACL_ACCESS);
if (err)
return err;
}
if (inode->i_default_acl) {
err = xattr_list_one(&buffer, &remaining_size,
XATTR_NAME_POSIX_ACL_DEFAULT);
if (err)
return err;
}
#endif
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
/* skip "trusted." attributes for unprivileged callers */
if (!trusted && xattr_is_trusted(xattr->name))
continue;
err = xattr_list_one(&buffer, &remaining_size, xattr->name);
if (err)
break;
}
spin_unlock(&xattrs->lock);
return err ? err : size - remaining_size;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 84 | 48.00% | 1 | 33.33% |
Andreas Gruenbacher | 84 | 48.00% | 1 | 33.33% |
Mateusz Guzik | 7 | 4.00% | 1 | 33.33% |
Total | 175 | 100.00% | 3 | 100.00% |
/*
* Adds an extended attribute to the list
*/
void simple_xattr_list_add(struct simple_xattrs *xattrs,
struct simple_xattr *new_xattr)
{
spin_lock(&xattrs->lock);
list_add(&new_xattr->list, &xattrs->head);
spin_unlock(&xattrs->lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Aristeu Sergio Rozanski Filho | 44 | 100.00% | 1 | 100.00% |
Total | 44 | 100.00% | 1 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andreas Gruenbacher | 938 | 20.90% | 16 | 17.98% |
Linus Torvalds | 640 | 14.26% | 2 | 2.25% |
Aristeu Sergio Rozanski Filho | 596 | 13.28% | 2 | 2.25% |
Andrew Morton | 341 | 7.60% | 5 | 5.62% |
Christoph Hellwig | 303 | 6.75% | 3 | 3.37% |
David P. Quigley | 249 | 5.55% | 3 | 3.37% |
Eric Biggers | 224 | 4.99% | 1 | 1.12% |
Mimi Zohar | 179 | 3.99% | 3 | 3.37% |
Heiko Carstens | 140 | 3.12% | 2 | 2.25% |
Jeff Layton | 113 | 2.52% | 8 | 8.99% |
James Morris | 111 | 2.47% | 2 | 2.25% |
Al Viro | 109 | 2.43% | 8 | 8.99% |
Nathan Scott | 106 | 2.36% | 3 | 3.37% |
Dave Hansen | 89 | 1.98% | 1 | 1.12% |
Bill Nottingham | 89 | 1.98% | 1 | 1.12% |
Eric W. Biedermann | 77 | 1.72% | 2 | 2.25% |
Stephen D. Smalley | 36 | 0.80% | 2 | 2.25% |
Amy Griffis | 25 | 0.56% | 2 | 2.25% |
Andi Kleen | 24 | 0.53% | 1 | 1.12% |
Jan Blunck | 16 | 0.36% | 2 | 2.25% |
Sasha Levin | 14 | 0.31% | 1 | 1.12% |
Richard Weinberger | 9 | 0.20% | 1 | 1.12% |
Dmitry Kasatkin | 9 | 0.20% | 1 | 1.12% |
David Howells | 8 | 0.18% | 1 | 1.12% |
Mateusz Guzik | 7 | 0.16% | 1 | 1.12% |
Greg Kroah-Hartman | 6 | 0.13% | 2 | 2.25% |
Josef 'Jeff' Sipek | 6 | 0.13% | 1 | 1.12% |
Andries E. Brouwer | 6 | 0.13% | 1 | 1.12% |
Robert Love | 3 | 0.07% | 1 | 1.12% |
Arnd Bergmann | 3 | 0.07% | 1 | 1.12% |
David Rientjes | 2 | 0.04% | 1 | 1.12% |
Dave Jones | 2 | 0.04% | 1 | 1.12% |
Nicholas Piggin | 2 | 0.04% | 1 | 1.12% |
Li Zefan | 2 | 0.04% | 1 | 1.12% |
Hugh Dickins | 1 | 0.02% | 1 | 1.12% |
Serge E. Hallyn | 1 | 0.02% | 1 | 1.12% |
Satyam Sharma | 1 | 0.02% | 1 | 1.12% |
Paul Gortmaker | 1 | 0.02% | 1 | 1.12% |
Stephen Hemminger | 1 | 0.02% | 1 | 1.12% |
Total | 4489 | 100.00% | 89 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.