diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 02:20:36 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 02:20:36 +0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/xattr.c | |
download | linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/xattr.c')
-rw-r--r-- | fs/xattr.c | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/fs/xattr.c b/fs/xattr.c new file mode 100644 index 000000000000..93dee70a1dbe --- /dev/null +++ b/fs/xattr.c @@ -0,0 +1,480 @@ +/* + 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/smp_lock.h> +#include <linux/file.h> +#include <linux/xattr.h> +#include <linux/namei.h> +#include <linux/security.h> +#include <linux/syscalls.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +/* + * Extended attribute SET operations + */ +static long +setxattr(struct dentry *d, char __user *name, 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); + if (!kvalue) + return -ENOMEM; + if (copy_from_user(kvalue, value, size)) { + kfree(kvalue); + return -EFAULT; + } + } + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->setxattr) { + down(&d->d_inode->i_sem); + error = security_inode_setxattr(d, kname, kvalue, size, flags); + if (error) + goto out; + error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags); + if (!error) + security_inode_post_setxattr(d, kname, kvalue, size, flags); +out: + up(&d->d_inode->i_sem); + } + if (kvalue) + kfree(kvalue); + return error; +} + +asmlinkage long +sys_setxattr(char __user *path, char __user *name, void __user *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 __user *path, char __user *name, void __user *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 __user *name, void __user *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 __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 = kmalloc(size, GFP_KERNEL); + if (!kvalue) + return -ENOMEM; + } + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->getxattr) { + error = security_inode_getxattr(d, kname); + if (error) + goto out; + error = d->d_inode->i_op->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; + } + } +out: + if (kvalue) + kfree(kvalue); + return error; +} + +asmlinkage ssize_t +sys_getxattr(char __user *path, char __user *name, void __user *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 __user *path, char __user *name, void __user *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 __user *name, void __user *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 __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_KERNEL); + if (!klist) + return -ENOMEM; + } + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { + error = security_inode_listxattr(d); + if (error) + goto out; + error = d->d_inode->i_op->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; + } + } +out: + if (klist) + kfree(klist); + return error; +} + +asmlinkage ssize_t +sys_listxattr(char __user *path, char __user *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 __user *path, char __user *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 __user *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 __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; + + error = -EOPNOTSUPP; + if (d->d_inode->i_op && d->d_inode->i_op->removexattr) { + error = security_inode_removexattr(d, kname); + if (error) + goto out; + down(&d->d_inode->i_sem); + error = d->d_inode->i_op->removexattr(d, kname); + up(&d->d_inode->i_sem); + } +out: + return error; +} + +asmlinkage long +sys_removexattr(char __user *path, char __user *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 __user *path, char __user *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 __user *name) +{ + struct file *f; + int error = -EBADF; + + f = fget(fd); + if (!f) + return error; + error = removexattr(f->f_dentry, name); + fput(f); + return error; +} + + +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; +} + +/* + * In order to implement different sets of xattr operations for each xattr + * prefix with the generic xattr API, 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. + * + * The generic_fooxattr() functions will use this list to dispatch xattr + * operations to the correct xattr_handler. + */ +#define for_each_xattr_handler(handlers, handler) \ + for ((handler) = *(handlers)++; \ + (handler) != NULL; \ + (handler) = *(handlers)++) + +/* + * Find the xattr_handler with the matching prefix. + */ +static struct xattr_handler * +xattr_resolve_name(struct xattr_handler **handlers, const char **name) +{ + struct xattr_handler *handler; + + if (!*name) + return NULL; + + for_each_xattr_handler(handlers, handler) { + const char *n = strcmp_prefix(*name, handler->prefix); + if (n) { + *name = n; + break; + } + } + return handler; +} + +/* + * Find the handler for the prefix and dispatch its get() operation. + */ +ssize_t +generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) +{ + struct xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); + if (!handler) + return -EOPNOTSUPP; + return handler->get(inode, name, buffer, size); +} + +/* + * 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) +{ + struct inode *inode = dentry->d_inode; + struct xattr_handler *handler, **handlers = inode->i_sb->s_xattr; + unsigned int size = 0; + + if (!buffer) { + for_each_xattr_handler(handlers, handler) + size += handler->list(inode, NULL, 0, NULL, 0); + } else { + char *buf = buffer; + + for_each_xattr_handler(handlers, handler) { + size = handler->list(inode, buf, buffer_size, NULL, 0); + if (size > buffer_size) + return -ERANGE; + buf += size; + buffer_size -= size; + } + size = buf - buffer; + } + return size; +} + +/* + * Find the handler for the prefix and dispatch its set() operation. + */ +int +generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) +{ + struct xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + if (size == 0) + value = ""; /* empty EA, do not remove */ + handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, value, size, flags); +} + +/* + * Find the handler for the prefix and dispatch its set() operation to remove + * any associated extended attribute. + */ +int +generic_removexattr(struct dentry *dentry, const char *name) +{ + struct xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, NULL, 0, XATTR_REPLACE); +} + +EXPORT_SYMBOL(generic_getxattr); +EXPORT_SYMBOL(generic_listxattr); +EXPORT_SYMBOL(generic_setxattr); +EXPORT_SYMBOL(generic_removexattr); |