|
@@ -24,6 +24,59 @@
|
|
|
|
|
|
#include <asm/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;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Check permissions for extended attribute access. This is a bit complicated
|
|
|
* because different namespaces have very different rules.
|
|
@@ -80,6 +133,23 @@ xattr_permission(struct inode *inode, const char *name, int mask)
|
|
|
return inode_permission(inode, mask);
|
|
|
}
|
|
|
|
|
|
+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);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(__vfs_setxattr);
|
|
|
+
|
|
|
/**
|
|
|
* __vfs_setxattr_noperm - perform setxattr operation without performing
|
|
|
* permission checks.
|
|
@@ -106,8 +176,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
|
|
|
|
|
|
if (issec)
|
|
|
inode->i_flags &= ~S_NOSEC;
|
|
|
- if (inode->i_op->setxattr) {
|
|
|
- error = inode->i_op->setxattr(dentry, inode, name, value, size, flags);
|
|
|
+ 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,
|
|
@@ -115,6 +185,9 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
|
|
|
}
|
|
|
} else if (issec) {
|
|
|
const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
|
|
|
+
|
|
|
+ if (unlikely(is_bad_inode(inode)))
|
|
|
+ return -EIO;
|
|
|
error = security_inode_setsecurity(inode, suffix, value,
|
|
|
size, flags);
|
|
|
if (!error)
|
|
@@ -188,6 +261,7 @@ 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;
|
|
@@ -196,10 +270,12 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
|
|
|
if (error)
|
|
|
return error;
|
|
|
|
|
|
- if (!inode->i_op->getxattr)
|
|
|
+ handler = xattr_resolve_name(inode, &name);
|
|
|
+ if (IS_ERR(handler))
|
|
|
+ return PTR_ERR(handler);
|
|
|
+ if (!handler->get)
|
|
|
return -EOPNOTSUPP;
|
|
|
-
|
|
|
- error = inode->i_op->getxattr(dentry, inode, name, NULL, 0);
|
|
|
+ error = handler->get(handler, dentry, inode, name, NULL, 0);
|
|
|
if (error < 0)
|
|
|
return error;
|
|
|
|
|
@@ -210,11 +286,26 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
|
|
|
memset(value, 0, error + 1);
|
|
|
}
|
|
|
|
|
|
- error = inode->i_op->getxattr(dentry, inode, name, value, error);
|
|
|
+ error = handler->get(handler, dentry, inode, name, value, error);
|
|
|
*xattr_value = value;
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+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);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(__vfs_getxattr);
|
|
|
+
|
|
|
ssize_t
|
|
|
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
|
|
|
{
|
|
@@ -242,28 +333,24 @@ vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
|
|
|
return ret;
|
|
|
}
|
|
|
nolsm:
|
|
|
- if (inode->i_op->getxattr)
|
|
|
- error = inode->i_op->getxattr(dentry, inode, name, value, size);
|
|
|
- else
|
|
|
- error = -EOPNOTSUPP;
|
|
|
-
|
|
|
- return error;
|
|
|
+ return __vfs_getxattr(dentry, inode, name, value, size);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(vfs_getxattr);
|
|
|
|
|
|
ssize_t
|
|
|
-vfs_listxattr(struct dentry *d, char *list, size_t size)
|
|
|
+vfs_listxattr(struct dentry *dentry, char *list, size_t size)
|
|
|
{
|
|
|
+ struct inode *inode = d_inode(dentry);
|
|
|
ssize_t error;
|
|
|
|
|
|
- error = security_inode_listxattr(d);
|
|
|
+ error = security_inode_listxattr(dentry);
|
|
|
if (error)
|
|
|
return error;
|
|
|
- error = -EOPNOTSUPP;
|
|
|
- if (d->d_inode->i_op->listxattr) {
|
|
|
- error = d->d_inode->i_op->listxattr(d, list, size);
|
|
|
+ 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(d->d_inode, list, size);
|
|
|
+ error = security_inode_listsecurity(inode, list, size);
|
|
|
if (size && error > size)
|
|
|
error = -ERANGE;
|
|
|
}
|
|
@@ -271,15 +358,27 @@ vfs_listxattr(struct dentry *d, char *list, size_t size)
|
|
|
}
|
|
|
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);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(__vfs_removexattr);
|
|
|
+
|
|
|
int
|
|
|
vfs_removexattr(struct dentry *dentry, const 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;
|
|
@@ -289,7 +388,7 @@ vfs_removexattr(struct dentry *dentry, const char *name)
|
|
|
if (error)
|
|
|
goto out;
|
|
|
|
|
|
- error = inode->i_op->removexattr(dentry, name);
|
|
|
+ error = __vfs_removexattr(dentry, name);
|
|
|
|
|
|
if (!error) {
|
|
|
fsnotify_xattr(dentry);
|
|
@@ -641,76 +740,6 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
|
|
|
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) \
|
|
|
- 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(const struct xattr_handler **handlers, const char **name)
|
|
|
-{
|
|
|
- const struct xattr_handler *handler;
|
|
|
-
|
|
|
- if (!*name)
|
|
|
- return ERR_PTR(-EINVAL);
|
|
|
-
|
|
|
- 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);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Find the handler for the prefix and dispatch its get() operation.
|
|
|
- */
|
|
|
-ssize_t
|
|
|
-generic_getxattr(struct dentry *dentry, struct inode *inode,
|
|
|
- const char *name, void *buffer, size_t size)
|
|
|
-{
|
|
|
- const struct xattr_handler *handler;
|
|
|
-
|
|
|
- handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
|
|
|
- if (IS_ERR(handler))
|
|
|
- return PTR_ERR(handler);
|
|
|
- return handler->get(handler, dentry, inode,
|
|
|
- name, buffer, size);
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Combine the results of the list() operation from every xattr_handler in the
|
|
|
* list.
|
|
@@ -747,44 +776,7 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|
|
}
|
|
|
return size;
|
|
|
}
|
|
|
-
|
|
|
-/*
|
|
|
- * Find the handler for the prefix and dispatch its set() operation.
|
|
|
- */
|
|
|
-int
|
|
|
-generic_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
|
|
|
- const void *value, size_t size, int flags)
|
|
|
-{
|
|
|
- const struct xattr_handler *handler;
|
|
|
-
|
|
|
- if (size == 0)
|
|
|
- value = ""; /* empty EA, do not remove */
|
|
|
- handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
|
|
|
- if (IS_ERR(handler))
|
|
|
- return PTR_ERR(handler);
|
|
|
- return handler->set(handler, dentry, 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)
|
|
|
-{
|
|
|
- const struct xattr_handler *handler;
|
|
|
-
|
|
|
- handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
|
|
|
- if (IS_ERR(handler))
|
|
|
- return PTR_ERR(handler);
|
|
|
- return handler->set(handler, dentry, d_inode(dentry), name, NULL,
|
|
|
- 0, XATTR_REPLACE);
|
|
|
-}
|
|
|
-
|
|
|
-EXPORT_SYMBOL(generic_getxattr);
|
|
|
EXPORT_SYMBOL(generic_listxattr);
|
|
|
-EXPORT_SYMBOL(generic_setxattr);
|
|
|
-EXPORT_SYMBOL(generic_removexattr);
|
|
|
|
|
|
/**
|
|
|
* xattr_full_name - Compute full attribute name from suffix
|