diff options
Diffstat (limited to 'fs/xattr.c')
-rw-r--r-- | fs/xattr.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/fs/xattr.c b/fs/xattr.c new file mode 100644 index 000000000..68578c641 --- /dev/null +++ b/fs/xattr.c @@ -0,0 +1,341 @@ +/* + 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> + +/* + * Extended attribute memory allocation wrappers, originally + * based on the Intermezzo PRESTO_ALLOC/PRESTO_FREE macros. + * The vmalloc use here is very uncommon - extended attributes + * are supposed to be small chunks of metadata, and it is quite + * unusual to have very many extended attributes, so lists tend + * to be quite short as well. The 64K upper limit is derived + * from the extended attribute size limit used by XFS. + * Intentionally allow zero @size for value/list size requests. + */ +static void * +xattr_alloc(size_t size, size_t limit) +{ + void *ptr; + + if (size > limit) + return ERR_PTR(-E2BIG); + + if (!size) /* size request, no buffer is needed */ + return NULL; + else if (size <= PAGE_SIZE) + ptr = kmalloc((unsigned long) size, GFP_KERNEL); + else + ptr = vmalloc((unsigned long) size); + if (!ptr) + return ERR_PTR(-ENOMEM); + return ptr; +} + +static void +xattr_free(void *ptr, size_t size) +{ + if (!size) /* size request, no buffer was needed */ + return; + else if (size <= PAGE_SIZE) + kfree(ptr); + else + vfree(ptr); +} + +/* + * Extended attribute SET operations + */ +static long +setxattr(struct dentry *d, char *name, void *value, size_t size, int flags) +{ + int error; + void *kvalue; + char kname[XATTR_NAME_MAX + 1]; + + if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) + return -EINVAL; + + if (copy_from_user(kname, name, XATTR_NAME_MAX)) + return -EFAULT; + kname[XATTR_NAME_MAX] = '\0'; + + kvalue = xattr_alloc(size, XATTR_SIZE_MAX); + if (IS_ERR(kvalue)) + return PTR_ERR(kvalue); + + if (size > 0 && copy_from_user(kvalue, value, size)) { + xattr_free(kvalue, size); + return -EFAULT; + } + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->setxattr) { + lock_kernel(); + error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags); + unlock_kernel(); + } + + xattr_free(kvalue, size); + 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 long +getxattr(struct dentry *d, char *name, void *value, size_t size) +{ + int error; + void *kvalue; + char kname[XATTR_NAME_MAX + 1]; + + if (copy_from_user(kname, name, XATTR_NAME_MAX)) + return -EFAULT; + kname[XATTR_NAME_MAX] = '\0'; + + kvalue = xattr_alloc(size, XATTR_SIZE_MAX); + if (IS_ERR(kvalue)) + return PTR_ERR(kvalue); + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->getxattr) { + lock_kernel(); + error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); + unlock_kernel(); + } + + if (kvalue && error > 0) + if (copy_to_user(value, kvalue, size)) + error = -EFAULT; + xattr_free(kvalue, size); + return error; +} + +asmlinkage long +sys_getxattr(char *path, char *name, void *value, size_t size) +{ + struct nameidata nd; + int error; + + error = user_path_walk(path, &nd); + if (error) + return error; + error = getxattr(nd.dentry, name, value, size); + path_release(&nd); + return error; +} + +asmlinkage long +sys_lgetxattr(char *path, char *name, void *value, size_t size) +{ + struct nameidata nd; + int 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 long +sys_fgetxattr(int fd, char *name, void *value, size_t size) +{ + struct file *f; + int 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 long +listxattr(struct dentry *d, char *list, size_t size) +{ + int error; + char *klist; + + klist = (char *)xattr_alloc(size, XATTR_LIST_MAX); + if (IS_ERR(klist)) + return PTR_ERR(klist); + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { + lock_kernel(); + error = d->d_inode->i_op->listxattr(d, klist, size); + unlock_kernel(); + } + + if (klist && error > 0) + if (copy_to_user(list, klist, size)) + error = -EFAULT; + xattr_free(klist, size); + return error; +} + +asmlinkage long +sys_listxattr(char *path, char *list, size_t size) +{ + struct nameidata nd; + int error; + + error = user_path_walk(path, &nd); + if (error) + return error; + error = listxattr(nd.dentry, list, size); + path_release(&nd); + return error; +} + +asmlinkage long +sys_llistxattr(char *path, char *list, size_t size) +{ + struct nameidata nd; + int error; + + error = user_path_walk_link(path, &nd); + if (error) + return error; + error = listxattr(nd.dentry, list, size); + path_release(&nd); + return error; +} + +asmlinkage long +sys_flistxattr(int fd, char *list, size_t size) +{ + struct file *f; + int 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]; + + if (copy_from_user(kname, name, XATTR_NAME_MAX)) + return -EFAULT; + kname[XATTR_NAME_MAX] = '\0'; + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->removexattr) { + lock_kernel(); + error = d->d_inode->i_op->removexattr(d, kname); + unlock_kernel(); + } + return error; +} + +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; +} |