aboutsummaryrefslogtreecommitdiff
path: root/fs/btrfs/xattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/xattr.c')
-rw-r--r--fs/btrfs/xattr.c103
1 files changed, 73 insertions, 30 deletions
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index d733b9cfea3..ad8328d797e 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -22,11 +22,13 @@
#include <linux/rwsem.h>
#include <linux/xattr.h>
#include <linux/security.h>
+#include <linux/posix_acl_xattr.h>
#include "ctree.h"
#include "btrfs_inode.h"
#include "transaction.h"
#include "xattr.h"
#include "disk-io.h"
+#include "props.h"
ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
@@ -116,11 +118,38 @@ static int do_setxattr(struct btrfs_trans_handle *trans,
if (ret)
goto out;
btrfs_release_path(path);
+
+ /*
+ * remove the attribute
+ */
+ if (!value)
+ goto out;
+ } else {
+ di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode),
+ name, name_len, 0);
+ if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+ goto out;
+ }
+ if (!di && !value)
+ goto out;
+ btrfs_release_path(path);
}
again:
ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
name, name_len, value, size);
+ /*
+ * If we're setting an xattr to a new value but the new value is say
+ * exactly BTRFS_MAX_XATTR_SIZE, we could end up with EOVERFLOW getting
+ * back from split_leaf. This is because it thinks we'll be extending
+ * the existing item size, but we're asking for enough space to add the
+ * item itself. So if we get EOVERFLOW just set ret to EEXIST and let
+ * the rest of the function figure it out.
+ */
+ if (ret == -EOVERFLOW)
+ ret = -EEXIST;
+
if (ret == -EEXIST) {
if (flags & XATTR_CREATE)
goto out;
@@ -158,6 +187,9 @@ out:
return ret;
}
+/*
+ * @value: "" makes the attribute to empty, NULL removes it
+ */
int __btrfs_setxattr(struct btrfs_trans_handle *trans,
struct inode *inode, const char *name,
const void *value, size_t size, int flags)
@@ -176,11 +208,13 @@ int __btrfs_setxattr(struct btrfs_trans_handle *trans,
if (ret)
goto out;
+ inode_inc_iversion(inode);
inode->i_ctime = CURRENT_TIME;
+ set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
ret = btrfs_update_inode(trans, root, inode);
BUG_ON(ret);
out:
- btrfs_end_transaction_throttle(trans, root);
+ btrfs_end_transaction(trans, root);
return ret;
}
@@ -244,7 +278,7 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
if (verify_dir_item(root, leaf, di))
- continue;
+ goto next;
name_len = btrfs_dir_name_len(leaf, di);
total_size += name_len + 1;
@@ -281,8 +315,8 @@ err:
*/
const struct xattr_handler *btrfs_xattr_handlers[] = {
#ifdef CONFIG_BTRFS_FS_POSIX_ACL
- &btrfs_xattr_acl_access_handler,
- &btrfs_xattr_acl_default_handler,
+ &posix_acl_access_xattr_handler,
+ &posix_acl_default_xattr_handler,
#endif
NULL,
};
@@ -299,7 +333,8 @@ static bool btrfs_is_valid_xattr(const char *name)
XATTR_SECURITY_PREFIX_LEN) ||
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) ||
!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
- !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
+ !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) ||
+ !strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN);
}
ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
@@ -341,6 +376,10 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ value, size, flags);
+
if (size == 0)
value = ""; /* empty EA, do not remove */
@@ -370,40 +409,44 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
if (!btrfs_is_valid_xattr(name))
return -EOPNOTSUPP;
+ if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
+ return btrfs_set_prop(dentry->d_inode, name,
+ NULL, 0, XATTR_REPLACE);
+
return __btrfs_setxattr(NULL, dentry->d_inode, name, NULL, 0,
XATTR_REPLACE);
}
-int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
- struct inode *inode, struct inode *dir,
- const struct qstr *qstr)
+static int btrfs_initxattrs(struct inode *inode,
+ const struct xattr *xattr_array, void *fs_info)
{
- int err;
- size_t len;
- void *value;
- char *suffix;
+ const struct xattr *xattr;
+ struct btrfs_trans_handle *trans = fs_info;
char *name;
+ int err = 0;
- err = security_inode_init_security(inode, dir, qstr, &suffix, &value,
- &len);
- if (err) {
- if (err == -EOPNOTSUPP)
- return 0;
- return err;
- }
-
- name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1,
- GFP_NOFS);
- if (!name) {
- err = -ENOMEM;
- } else {
+ for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+ name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+ strlen(xattr->name) + 1, GFP_NOFS);
+ if (!name) {
+ err = -ENOMEM;
+ break;
+ }
strcpy(name, XATTR_SECURITY_PREFIX);
- strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
- err = __btrfs_setxattr(trans, inode, name, value, len, 0);
+ strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+ err = __btrfs_setxattr(trans, inode, name,
+ xattr->value, xattr->value_len, 0);
kfree(name);
+ if (err < 0)
+ break;
}
-
- kfree(suffix);
- kfree(value);
return err;
}
+
+int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
+ struct inode *inode, struct inode *dir,
+ const struct qstr *qstr)
+{
+ return security_inode_init_security(inode, dir, qstr,
+ &btrfs_initxattrs, trans);
+}