diff options
Diffstat (limited to 'fs/xattr.c')
-rw-r--r-- | fs/xattr.c | 203 |
1 files changed, 96 insertions, 107 deletions
diff --git a/fs/xattr.c b/fs/xattr.c index 9b932b95d74e..d5dd6c8b82a7 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -208,25 +208,6 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, return error; } -/* Compare an extended attribute value with the given value */ -int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, - const char *value, size_t size, gfp_t flags) -{ - char *xattr_value = NULL; - int rc; - - rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags); - if (rc < 0) - return rc; - - if ((rc != size) || (memcmp(xattr_value, value, rc) != 0)) - rc = -EINVAL; - else - rc = 0; - kfree(xattr_value); - return rc; -} - ssize_t vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { @@ -324,7 +305,6 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, { int error; void *kvalue = NULL; - void *vvalue = NULL; /* If non-NULL, we used vmalloc() */ char kname[XATTR_NAME_MAX + 1]; if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) @@ -341,10 +321,9 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, return -E2BIG; kvalue = kmalloc(size, GFP_KERNEL | __GFP_NOWARN); if (!kvalue) { - vvalue = vmalloc(size); - if (!vvalue) + kvalue = vmalloc(size); + if (!kvalue) return -ENOMEM; - kvalue = vvalue; } if (copy_from_user(kvalue, value, size)) { error = -EFAULT; @@ -357,10 +336,8 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, error = vfs_setxattr(d, kname, kvalue, size, flags); out: - if (vvalue) - vfree(vvalue); - else - kfree(kvalue); + kvfree(kvalue); + return error; } @@ -428,7 +405,6 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, { ssize_t error; void *kvalue = NULL; - void *vvalue = NULL; char kname[XATTR_NAME_MAX + 1]; error = strncpy_from_user(kname, name, sizeof(kname)); @@ -442,10 +418,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, size = XATTR_SIZE_MAX; kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); if (!kvalue) { - vvalue = vmalloc(size); - if (!vvalue) + kvalue = vmalloc(size); + if (!kvalue) return -ENOMEM; - kvalue = vvalue; } } @@ -461,10 +436,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, than XATTR_SIZE_MAX bytes. Not possible. */ error = -E2BIG; } - if (vvalue) - vfree(vvalue); - else - kfree(kvalue); + + kvfree(kvalue); + return error; } @@ -521,17 +495,15 @@ listxattr(struct dentry *d, char __user *list, size_t size) { ssize_t error; char *klist = NULL; - char *vlist = NULL; /* If non-NULL, we used vmalloc() */ if (size) { if (size > XATTR_LIST_MAX) size = XATTR_LIST_MAX; klist = kmalloc(size, __GFP_NOWARN | GFP_KERNEL); if (!klist) { - vlist = vmalloc(size); - if (!vlist) + klist = vmalloc(size); + if (!klist) return -ENOMEM; - klist = vlist; } } @@ -544,10 +516,9 @@ listxattr(struct dentry *d, char __user *list, size_t size) than XATTR_LIST_MAX bytes. Not possible. */ error = -E2BIG; } - if (vlist) - vfree(vlist); - else - kfree(klist); + + kvfree(klist); + return error; } @@ -700,13 +671,20 @@ xattr_resolve_name(const struct xattr_handler **handlers, const char **name) return NULL; for_each_xattr_handler(handlers, handler) { - const char *n = strcmp_prefix(*name, handler->prefix); + 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; - break; + return handler; } } - return handler; + return ERR_PTR(-EOPNOTSUPP); } /* @@ -718,8 +696,8 @@ generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t s const struct xattr_handler *handler; handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); - if (!handler) - return -EOPNOTSUPP; + if (IS_ERR(handler)) + return PTR_ERR(handler); return handler->get(handler, dentry, name, buffer, size); } @@ -735,19 +713,25 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) if (!buffer) { for_each_xattr_handler(handlers, handler) { - size += handler->list(handler, dentry, NULL, 0, - NULL, 0); + 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) { - size = handler->list(handler, dentry, buf, buffer_size, - NULL, 0); - if (size > buffer_size) + if (!handler->name || + (handler->list && !handler->list(dentry))) + continue; + len = strlen(handler->name); + if (len + 1 > buffer_size) return -ERANGE; - buf += size; - buffer_size -= size; + memcpy(buf, handler->name, len + 1); + buf += len + 1; + buffer_size -= len + 1; } size = buf - buffer; } @@ -765,8 +749,8 @@ generic_setxattr(struct dentry *dentry, const char *name, const void *value, siz if (size == 0) value = ""; /* empty EA, do not remove */ handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); - if (!handler) - return -EOPNOTSUPP; + if (IS_ERR(handler)) + return PTR_ERR(handler); return handler->set(handler, dentry, name, value, size, flags); } @@ -780,8 +764,8 @@ generic_removexattr(struct dentry *dentry, const char *name) const struct xattr_handler *handler; handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); - if (!handler) - return -EOPNOTSUPP; + if (IS_ERR(handler)) + return PTR_ERR(handler); return handler->set(handler, dentry, name, NULL, 0, XATTR_REPLACE); } @@ -808,7 +792,7 @@ EXPORT_SYMBOL(generic_removexattr); const char *xattr_full_name(const struct xattr_handler *handler, const char *name) { - size_t prefix_len = strlen(handler->prefix); + size_t prefix_len = strlen(xattr_prefix(handler)); return name - prefix_len; } @@ -863,8 +847,22 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, return ret; } -static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name, - const void *value, size_t size, int flags) +/** + * 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; @@ -914,73 +912,64 @@ out: } -/** - * simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems - * @xattrs: target simple_xattr list - * @name: name of the new extended attribute - * @value: value of the new 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) -{ - if (size == 0) - value = ""; /* empty EA, do not remove */ - return __simple_xattr_set(xattrs, name, value, size, flags); -} - -/* - * xattr REMOVE operation for in-memory/pseudo filesystems - */ -int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name) +static bool xattr_is_trusted(const char *name) { - return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE); + return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); } -static bool xattr_is_trusted(const char *name) +static int xattr_list_one(char **buffer, ssize_t *remaining_size, + const char *name) { - return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); + 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; } /* * xattr LIST operation for in-memory/pseudo filesystems */ -ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer, - size_t size) +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; - size_t used = 0; + ssize_t remaining_size = size; + int err; + +#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) { - size_t len; - /* skip "trusted." attributes for unprivileged callers */ if (!trusted && xattr_is_trusted(xattr->name)) continue; - len = strlen(xattr->name) + 1; - used += len; - if (buffer) { - if (size < used) { - used = -ERANGE; - break; - } - memcpy(buffer, xattr->name, len); - buffer += len; - } + err = xattr_list_one(&buffer, &remaining_size, xattr->name); + if (err) + return err; } spin_unlock(&xattrs->lock); - return used; + return size - remaining_size; } /* |