File: [Development] / linux-2.4-xfs / fs / xattr.c (download)
Revision 1.2, Wed Jan 18 14:21:44 2006 UTC (11 years, 8 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
CVS Tags: HEAD Changes since 1.1: +149 -84
lines
Backport the xattr.c VFS permission checking from 2.6 kernels.
Merge of 2.4.x-xfs-melb:linux:25016a by kenmcd.
|
/*
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>
*/
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/smp_lock.h>
#include <linux/file.h>
#include <linux/xattr.h>
#include <asm/uaccess.h>
/*
* 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_RDONLY(inode))
return -EROFS;
if (IS_IMMUTABLE(inode) || IS_APPEND(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 accessed by a privilegued user.
*/
if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
return (capable(CAP_SYS_ADMIN) ? 0 : -EPERM);
if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) {
if (!S_ISREG(inode->i_mode) &&
(!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
return -EPERM;
}
return permission(inode, mask);
}
int
vfs_setxattr(struct dentry *dentry, char *name, void *value,
size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
int error;
if (!inode->i_op->setxattr)
return -EOPNOTSUPP;
error = xattr_permission(inode, name, MAY_WRITE);
if (error)
return error;
down(&inode->i_sem);
lock_kernel();
error = inode->i_op->setxattr(dentry, name, value, size, flags);
unlock_kernel();
up(&inode->i_sem);
return error;
}
ssize_t
vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size)
{
struct inode *inode = dentry->d_inode;
int error;
if (!inode->i_op->getxattr)
return -EOPNOTSUPP;
error = xattr_permission(inode, name, MAY_READ);
if (error)
return error;
down(&inode->i_sem);
lock_kernel();
error = inode->i_op->getxattr(dentry, name, value, size);
unlock_kernel();
up(&inode->i_sem);
return error;
}
int
vfs_removexattr(struct dentry *dentry, char *name)
{
struct inode *inode = dentry->d_inode;
int error;
if (!inode->i_op->removexattr)
return -EOPNOTSUPP;
error = xattr_permission(inode, name, MAY_WRITE);
if (error)
return error;
down(&inode->i_sem);
lock_kernel();
error = inode->i_op->removexattr(dentry, name);
unlock_kernel();
up(&inode->i_sem);
return error;
}
/*
* Extended attribute SET operations
*/
static long
setxattr(struct dentry *d, char *name, void *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);
if (!kvalue)
return -ENOMEM;
if (copy_from_user(kvalue, value, size)) {
kfree(kvalue);
return -EFAULT;
}
}
error = vfs_setxattr(d, kname, kvalue, size, flags);
kfree(kvalue);
return error;
}
asmlinkage long
sys_setxattr(char *path, char *name, void *value, size_t size, int flags)
{
struct nameidata nd;
int error;
error = user_path_walk(path, &nd);
if (error)
return error;
error = setxattr(nd.dentry, name, value, size, flags);
path_release(&nd);
return error;
}
asmlinkage long
sys_lsetxattr(char *path, char *name, void *value, size_t size, int flags)
{
struct nameidata nd;
int error;
error = user_path_walk_link(path, &nd);
if (error)
return error;
error = setxattr(nd.dentry, name, value, size, flags);
path_release(&nd);
return error;
}
asmlinkage long
sys_fsetxattr(int fd, char *name, void *value, size_t size, int flags)
{
struct file *f;
int error = -EBADF;
f = fget(fd);
if (!f)
return error;
error = setxattr(f->f_dentry, name, value, size, flags);
fput(f);
return error;
}
/*
* Extended attribute GET operations
*/
static ssize_t
getxattr(struct dentry *d, char *name, void *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 = kmalloc(size, GFP_KERNEL);
if (!kvalue)
return -ENOMEM;
memset(kvalue, 0, size);
}
error = vfs_getxattr(d, kname, kvalue, size);
if (error > 0) {
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;
}
kfree(kvalue);
return error;
}
asmlinkage ssize_t
sys_getxattr(char *path, char *name, void *value, size_t size)
{
struct nameidata nd;
ssize_t error;
error = user_path_walk(path, &nd);
if (error)
return error;
error = getxattr(nd.dentry, name, value, size);
path_release(&nd);
return error;
}
asmlinkage ssize_t
sys_lgetxattr(char *path, char *name, void *value, size_t size)
{
struct nameidata nd;
ssize_t error;
error = user_path_walk_link(path, &nd);
if (error)
return error;
error = getxattr(nd.dentry, name, value, size);
path_release(&nd);
return error;
}
asmlinkage ssize_t
sys_fgetxattr(int fd, char *name, void *value, size_t size)
{
struct file *f;
ssize_t error = -EBADF;
f = fget(fd);
if (!f)
return error;
error = getxattr(f->f_dentry, name, value, size);
fput(f);
return error;
}
/*
* Extended attribute LIST operations
*/
static ssize_t
listxattr(struct dentry *d, char *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_KERNEL);
if (!klist)
return -ENOMEM;
}
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
lock_kernel();
down(&d->d_inode->i_sem);
error = d->d_inode->i_op->listxattr(d, klist, size);
unlock_kernel();
up(&d->d_inode->i_sem);
}
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;
}
kfree(klist);
return error;
}
asmlinkage ssize_t
sys_listxattr(char *path, char *list, size_t size)
{
struct nameidata nd;
ssize_t error;
error = user_path_walk(path, &nd);
if (error)
return error;
error = listxattr(nd.dentry, list, size);
path_release(&nd);
return error;
}
asmlinkage ssize_t
sys_llistxattr(char *path, char *list, size_t size)
{
struct nameidata nd;
ssize_t error;
error = user_path_walk_link(path, &nd);
if (error)
return error;
error = listxattr(nd.dentry, list, size);
path_release(&nd);
return error;
}
asmlinkage ssize_t
sys_flistxattr(int fd, char *list, size_t size)
{
struct file *f;
ssize_t error = -EBADF;
f = fget(fd);
if (!f)
return error;
error = listxattr(f->f_dentry, list, size);
fput(f);
return error;
}
/*
* Extended attribute REMOVE operations
*/
static long
removexattr(struct dentry *d, char *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);
}
asmlinkage long
sys_removexattr(char *path, char *name)
{
struct nameidata nd;
int error;
error = user_path_walk(path, &nd);
if (error)
return error;
error = removexattr(nd.dentry, name);
path_release(&nd);
return error;
}
asmlinkage long
sys_lremovexattr(char *path, char *name)
{
struct nameidata nd;
int error;
error = user_path_walk_link(path, &nd);
if (error)
return error;
error = removexattr(nd.dentry, name);
path_release(&nd);
return error;
}
asmlinkage long
sys_fremovexattr(int fd, char *name)
{
struct file *f;
int error = -EBADF;
f = fget(fd);
if (!f)
return error;
error = removexattr(f->f_dentry, name);
fput(f);
return error;
}