[Btrfs-devel][PATCH] The second patch of acl supporting for btrfs(patch for btrfs-unstable)

Liu Hui onlyflyer at gmail.com
Sun Jan 27 06:50:34 PST 2008


Hi:
  This is second patch for btrfs acl supporting. This patch complete
acl supporting for btrfs-unstable:
1,Enable posix acl support by default, unless someone mounts with -o noacl
2,acl check support
3,acl init support
4,acl chmod support
5,Avoid a performance hit related to always checking acls by mark
inode's flag with BTRFS_INODE_NO_DEFAULT_ACL and
BTRFS_INODE_NO_ACCESS_ACL
6,Remove fs lock operation from btrfs_xattr_get and btrfs_xattr_set
because the lock operation confict with higher caller such as
btrfs_mkfs. These lock operation should be move to higher caller, e.g.
btrfs_xattr_acl_access_get.

--
diff -r 21e9b461f802 acl.c
--- a/acl.c	Thu Jan 24 16:13:14 2008 -0500
+++ b/acl.c	Sun Jan 27 22:44:59 2008 +0800
@@ -23,19 +23,83 @@
 #include <linux/sched.h>
 #include "ctree.h"
 #include "xattr.h"
+#include "btrfs_inode.h"
+
 #ifndef is_owner_or_cap
-#define is_owner_or_cap(inode)	\
+	#define is_owner_or_cap(inode)	\
 	((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER))
 #endif

+static void *btrfs_acl_to_xattr(struct posix_acl *acl, size_t *size)
+{
+	size_t real_size;
+	void *buf = NULL;
+	int error;
+
+	real_size = posix_acl_xattr_size(acl->a_count);
+	if (real_size <= 0)
+		return NULL;
+
+	*size = real_size;
+	buf = kmalloc(real_size, GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	error = posix_acl_to_xattr(acl, buf, real_size);
+	if (error < 0) {
+		kfree(buf);
+		return NULL;
+	}
+	return buf;
+}
+
+static int btrfs_xattr_get_acl(struct inode *inode, int type,
+			       void *value, size_t size)
+{
+	if (btrfs_test_opt(BTRFS_I(inode)->root, NOACL))
+		return -EOPNOTSUPP;
+	return btrfs_xattr_get(inode, type, "", value, size);
+}
+
+static struct posix_acl *btrfs_get_acl(struct inode *inode, int type) {
+	int ret, len;
+	char *value = NULL;
+	struct posix_acl *acl;
+
+	len = btrfs_xattr_get(inode, type, "", NULL, 0);
+	if (IS_ERR((const void *)len))
+		return ERR_PTR(len);
+	if (len > 0) {
+		value = kmalloc(len, GFP_KERNEL);
+		if (!value)
+			return ERR_PTR(-ENOMEM);
+
+	} else
+		return NULL;
+
+	ret = btrfs_xattr_get_acl(inode, type, value, len);
+	if (ret > 0)
+		acl = posix_acl_from_xattr(value, len);
+	else if (-ENODATA == ret)
+		acl = NULL;
+	else
+		acl = ERR_PTR(ret);
+
+	kfree(value);
+	return acl;
+}
+
 static int btrfs_xattr_set_acl(struct inode *inode, int type,
 			       const void *value, size_t size)
 {
 	int ret = 0;
 	struct posix_acl *acl;

+	if (btrfs_test_opt(BTRFS_I(inode)->root, NOACL))
+		return -EOPNOTSUPP;
 	if (!is_owner_or_cap(inode))
 		return -EPERM;
+
 	if (value) {
 		acl = posix_acl_from_xattr(value, size);
 		if (acl == NULL) {
@@ -53,53 +117,229 @@ static int btrfs_xattr_set_acl(struct in
 	return btrfs_xattr_set(inode, type, "", value, size, 0);
 }

-static int btrfs_xattr_get_acl(struct inode *inode, int type,
-			       void *value, size_t size)
-{
-	return btrfs_xattr_get(inode, type, "", value, size);
-}
+static int btrfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+	void *value = NULL;
+	size_t size = 0;
+	int err;
+
+	if (!acl)
+		return -ENODATA;
+
+	value = btrfs_acl_to_xattr(acl, &size);
+	if (!value)
+		return -ENODATA;
+
+	err = btrfs_xattr_set_acl(inode, type, value, size);
+	kfree(value);
+	return err;
+}
+
 static int btrfs_xattr_acl_access_get(struct inode *inode, const char *name,
 				      void *value, size_t size)
 {
-	if (*name != '\0')
-	       return -EINVAL;
-	return btrfs_xattr_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
-				   value, size);
-}
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	int err;
+
+	if (*name != '\0')
+		return -EINVAL;
+	if (btrfs_test_flag(inode, NO_ACCESS_ACL))
+		return -ENODATA;
+	mutex_lock(&root->fs_info->fs_mutex);
+	err = btrfs_xattr_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
+				  value, size);
+	mutex_unlock(&root->fs_info->fs_mutex);
+	return err;
+}
+
 static int btrfs_xattr_acl_access_set(struct inode *inode, const char *name,
 				      const void *value, size_t size, int flags)
 {
-	if (*name != '\0')
-	       return -EINVAL;
-	return btrfs_xattr_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
-				   value, size);
-}
+	int err;
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+
+	if (*name != '\0')
+		return -EINVAL;
+	mutex_lock(&root->fs_info->fs_mutex);
+	err = btrfs_xattr_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
+				  value, size);
+	mutex_unlock(&root->fs_info->fs_mutex);
+	if (!err)
+		btrfs_clear_flag(inode, NO_ACCESS_ACL);
+	return err;
+}
+
+struct xattr_handler btrfs_xattr_acl_access_handler = {
+	.prefix = POSIX_ACL_XATTR_ACCESS,
+	.list   = btrfs_xattr_generic_list,
+	.get    = btrfs_xattr_acl_access_get,
+	.set    = btrfs_xattr_acl_access_set,
+};
+
 static int btrfs_xattr_acl_default_get(struct inode *inode, const char *name,
 				       void *value, size_t size)
 {
-	if (*name != '\0')
-	       return -EINVAL;
-	return btrfs_xattr_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
-				   value, size);
-}
+	int err;
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+
+	if (*name != '\0')
+		return -EINVAL;
+	if (btrfs_test_flag(inode, NO_DEFAULT_ACL))
+		return -ENODATA;
+
+	mutex_lock(&root->fs_info->fs_mutex);
+	err = btrfs_xattr_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
+				  value, size);
+	mutex_unlock(&root->fs_info->fs_mutex);
+	return err;
+}
+
 static int btrfs_xattr_acl_default_set(struct inode *inode, const char *name,
 				       const void *value, size_t size, int flags)
 {
-	if (*name != '\0')
-	       return -EINVAL;
-	return btrfs_xattr_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
-				   value, size);
+	int err;
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+
+	if (*name != '\0')
+		return -EINVAL;
+	mutex_lock(&root->fs_info->fs_mutex);
+	err = btrfs_xattr_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
+				  value, size);
+	mutex_unlock(&root->fs_info->fs_mutex);
+	if (!err)
+		btrfs_clear_flag(inode, NO_DEFAULT_ACL);
+	return err;
 }
 struct xattr_handler btrfs_xattr_acl_default_handler = {
 	.prefix = POSIX_ACL_XATTR_DEFAULT,
-	.list	= btrfs_xattr_generic_list,
-	.get	= btrfs_xattr_acl_default_get,
-	.set	= btrfs_xattr_acl_default_set,
+	.list   = btrfs_xattr_generic_list,
+	.get    = btrfs_xattr_acl_default_get,
+	.set    = btrfs_xattr_acl_default_set,
 };

-struct xattr_handler btrfs_xattr_acl_access_handler = {
-	.prefix = POSIX_ACL_XATTR_ACCESS,
-	.list	= btrfs_xattr_generic_list,
-	.get	= btrfs_xattr_acl_access_get,
-	.set	= btrfs_xattr_acl_access_set,
-};
+static int __btrfs_acl_init(struct inode *inode, struct inode *dir)
+{
+	struct posix_acl *acl, *clone;
+	mode_t mode;
+	int err;
+
+	acl = btrfs_get_acl(dir, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT);
+	if (!acl)
+		return -ENODATA;
+
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+
+	if (S_ISDIR(inode->i_mode)) {
+		err = btrfs_set_acl(inode,
+				    BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
+				    acl);
+		if (err)
+			goto cleanup;
+	}
+	clone = posix_acl_clone(acl, GFP_KERNEL);
+	if (!clone) {
+		err = -ENOMEM;
+		goto cleanup;
+	}
+	mode = inode->i_mode;
+	err = posix_acl_create_masq(clone, &mode);
+	if (err >= 0) {
+		inode->i_mode = mode;
+		if (err > 0)
+			btrfs_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
+				      clone);
+	}
+	posix_acl_release(clone);
+	err = 0;
+cleanup:
+	posix_acl_release(acl);
+	return err;
+}
+
+int btrfs_acl_init(struct inode *inode, struct inode *dir)
+{
+	int err;
+
+	inode->i_mode &= ~current->fs->umask;
+	if (S_ISLNK(inode->i_mode))
+		return 0;
+
+	if (btrfs_test_flag(dir, NO_DEFAULT_ACL) ||
+	    btrfs_test_opt(BTRFS_I(inode)->root, NOACL)) {
+
+		btrfs_set_flag(inode, NO_ACCESS_ACL);
+
+		if (S_ISDIR(inode->i_mode))
+			btrfs_set_flag(inode, NO_DEFAULT_ACL);
+		return 0;
+	}
+
+	err = __btrfs_acl_init(inode, dir);
+	if (err) {
+		if (S_ISDIR(inode->i_mode))
+			btrfs_set_flag(inode, NO_DEFAULT_ACL);
+
+		btrfs_set_flag(inode, NO_ACCESS_ACL);
+	} else {
+		if (S_ISDIR(inode->i_mode))
+			btrfs_clear_flag(inode, NO_DEFAULT_ACL);
+
+		btrfs_clear_flag(inode, NO_ACCESS_ACL);
+	}
+	return err;
+}
+
+int btrfs_acl_chmod(struct inode *inode)
+{
+	struct posix_acl *acl, *clone;
+	int err = 0;
+
+	if (btrfs_test_opt(BTRFS_I(inode)->root, NOACL) ||
+	    btrfs_test_flag(inode, NO_ACCESS_ACL))
+		return 0;
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+
+	acl = btrfs_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS);
+	if (IS_ERR(acl) || !acl)
+		return PTR_ERR(acl);
+
+	clone = posix_acl_clone(acl, GFP_KERNEL);
+	posix_acl_release(acl);
+	if (!clone)
+		return -ENOMEM;
+
+	err = posix_acl_chmod_masq(clone, inode->i_mode);
+	if (!err)
+		err = btrfs_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
+				    clone);
+	posix_acl_release(clone);
+
+	return err;
+}
+
+int btrfs_check_acl(struct inode *inode, int mask)
+{
+	struct posix_acl *acl;
+	int error;
+
+	if (btrfs_test_opt(BTRFS_I(inode)->root, NOACL) ||
+	    btrfs_test_flag(inode, NO_ACCESS_ACL))
+		return -EAGAIN;
+
+	mutex_lock(&BTRFS_I(inode)->root->fs_info->fs_mutex);
+	acl = btrfs_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS);
+	mutex_unlock(&BTRFS_I(inode)->root->fs_info->fs_mutex);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+
+	if (acl) {
+		error = posix_acl_permission(inode, acl, mask);
+		posix_acl_release(acl);
+		return error;
+	}
+
+	return -EAGAIN;
+}
diff -r 21e9b461f802 ctree.h
--- a/ctree.h	Thu Jan 24 16:13:14 2008 -0500
+++ b/ctree.h	Sun Jan 27 22:44:59 2008 +0800
@@ -447,6 +447,7 @@ struct btrfs_root {
 #define BTRFS_MOUNT_NODATACOW		(1 << 1)
 #define BTRFS_MOUNT_NOBARRIER		(1 << 2)
 #define BTRFS_MOUNT_SSD			(1 << 3)
+#define BTRFS_MOUNT_NOACL		(1 << 4)

 #define btrfs_clear_opt(o, opt)		((o) &= ~BTRFS_MOUNT_##opt)
 #define btrfs_set_opt(o, opt)		((o) |= BTRFS_MOUNT_##opt)
@@ -458,6 +459,8 @@ struct btrfs_root {
 #define BTRFS_INODE_NODATASUM		(1 << 0)
 #define BTRFS_INODE_NODATACOW		(1 << 1)
 #define BTRFS_INODE_READONLY		(1 << 2)
+#define BTRFS_INODE_NO_ACCESS_ACL	(1 << 3)
+#define BTRFS_INODE_NO_DEFAULT_ACL      (1 << 4)
 #define btrfs_clear_flag(inode, flag)	(BTRFS_I(inode)->flags &= \
 					 ~BTRFS_INODE_##flag)
 #define btrfs_set_flag(inode, flag)	(BTRFS_I(inode)->flags |= \
@@ -1194,4 +1197,9 @@ int btrfs_delete_xattrs(struct btrfs_tra
 			struct btrfs_root *root, struct inode *inode);
 /* super.c */
 u64 btrfs_parse_size(char *str);
+
+/* acl.c */
+int btrfs_acl_init(struct inode *ionde, struct inode *dir);
+int btrfs_acl_chmod(struct inode *inode);
+int btrfs_check_acl(struct inode *inode, int mask);
 #endif
diff -r 21e9b461f802 inode.c
--- a/inode.c	Thu Jan 24 16:13:14 2008 -0500
+++ b/inode.c	Sun Jan 27 22:44:59 2008 +0800
@@ -1020,6 +1020,13 @@ static int btrfs_setattr(struct dentry *
 	}
 out:
 	err = inode_setattr(inode, attr);
+#ifdef CONFIG_FS_POSIX_ACL
+	if (!err && (attr->ia_valid & ATTR_MODE)) {
+		mutex_lock(&BTRFS_I(inode)->root->fs_info->fs_mutex);
+		err = btrfs_acl_chmod(inode);
+		mutex_unlock(&BTRFS_I(inode)->root->fs_info->fs_mutex);
+	}
+#endif
 fail:
 	return err;
 }
@@ -1617,7 +1624,13 @@ static int btrfs_mknod(struct inode *dir
 	err = PTR_ERR(inode);
 	if (IS_ERR(inode))
 		goto out_unlock;
-
+#ifdef CONFIG_FS_POSIX_ACL
+	err = btrfs_acl_init(inode, dir);
+	if (err) {
+		drop_inode = 1;
+		goto out_unlock;
+	}
+#endif
 	btrfs_set_trans_block_group(trans, inode);
 	err = btrfs_add_nondir(trans, dentry, inode);
 	if (err)
@@ -1674,7 +1687,13 @@ static int btrfs_create(struct inode *di
 	err = PTR_ERR(inode);
 	if (IS_ERR(inode))
 		goto out_unlock;
-
+#ifdef CONFIG_FS_POSIX_ACL
+	err = btrfs_acl_init(inode, dir);
+	if (err) {
+		drop_inode = 1;
+		goto out_unlock;
+	}
+#endif
 	btrfs_set_trans_block_group(trans, inode);
 	err = btrfs_add_nondir(trans, dentry, inode);
 	if (err)
@@ -1793,7 +1812,13 @@ static int btrfs_mkdir(struct inode *dir
 		err = PTR_ERR(inode);
 		goto out_fail;
 	}
-
+#ifdef CONFIG_FS_POSIX_ACL
+	err = btrfs_acl_init(inode, dir);
+	if (err) {
+		drop_on_err = 1;
+		goto out_fail;
+	}
+#endif
 	drop_on_err = 1;
 	inode->i_op = &btrfs_dir_inode_operations;
 	inode->i_fop = &btrfs_dir_file_operations;
@@ -2909,7 +2934,12 @@ static int btrfs_permission(struct inode
 {
 	if (btrfs_test_flag(inode, READONLY) && (mask & MAY_WRITE))
 		return -EACCES;
+
+#ifdef CONFIG_FS_POSIX_ACL
+	return generic_permission(inode, mask, btrfs_check_acl);
+#else
 	return generic_permission(inode, mask, NULL);
+#endif
 }

 static struct inode_operations btrfs_dir_inode_operations = {
diff -r 21e9b461f802 super.c
--- a/super.c	Thu Jan 24 16:13:14 2008 -0500
+++ b/super.c	Sun Jan 27 22:44:59 2008 +0800
@@ -64,7 +64,7 @@ static void btrfs_put_super (struct supe

 enum {
 	Opt_subvol, Opt_nodatasum, Opt_nodatacow, Opt_max_extent,
-	Opt_alloc_start, Opt_nobarrier, Opt_ssd, Opt_err,
+	Opt_alloc_start, Opt_nobarrier, Opt_ssd, Opt_noacl, Opt_err,
 };

 static match_table_t tokens = {
@@ -75,6 +75,7 @@ static match_table_t tokens = {
 	{Opt_max_extent, "max_extent=%s"},
 	{Opt_alloc_start, "alloc_start=%s"},
 	{Opt_ssd, "ssd"},
+        {Opt_noacl, "noacl"},
 	{Opt_err, NULL}
 };

@@ -156,6 +157,12 @@ static int parse_options (char * options
 				btrfs_set_opt(info->mount_opt, SSD);
 			}
 			break;
+                case Opt_noacl:
+                        if (info) {
+                                printk("btrfs: disable posix acl\n");
+                                btrfs_set_opt(info->mount_opt, NOACL);
+                        }
+                        break;
 		case Opt_nobarrier:
 			if (info) {
 				printk("btrfs: turning off barriers\n");
@@ -247,6 +254,11 @@ static int btrfs_fill_super(struct super
 	}

 	parse_options((char *)data, tree_root, NULL);
+        if (btrfs_test_opt(tree_root, NOACL)) {
+                sb->s_flags = sb->s_flags & ~MS_POSIXACL;
+        } else {
+                sb->s_flags |= MS_POSIXACL;
+        }

 	/* this does the super kobj at the same time */
 	err = btrfs_sysfs_add_super(tree_root->fs_info);
diff -r 21e9b461f802 xattr.c
--- a/xattr.c	Thu Jan 24 16:13:14 2008 -0500
+++ b/xattr.c	Sun Jan 27 22:44:59 2008 +0800
@@ -129,6 +129,9 @@ size_t btrfs_xattr_generic_list(struct i
 	return name_len+1;
 }

+/*
+ * fs_mutex should be held when we come into here
+ */
 ssize_t btrfs_xattr_get(struct inode *inode, int name_index,
 			const char *attr_name, void *buffer, size_t size)
 {
@@ -153,7 +156,6 @@ ssize_t btrfs_xattr_get(struct inode *in
 		return -ENOMEM;
 	}

-	mutex_lock(&root->fs_info->fs_mutex);
 	/* lookup the xattr by name */
 	di = btrfs_lookup_xattr(NULL, root, path, inode->i_ino, name,
 				strlen(name), 0);
@@ -181,12 +183,15 @@ ssize_t btrfs_xattr_get(struct inode *in
 	ret = btrfs_dir_data_len(leaf, di);

 out:
-	mutex_unlock(&root->fs_info->fs_mutex);
 	kfree(name);
 	btrfs_free_path(path);
 	return ret;
 }

+
+/*
+ * fs_mutex should be held when we come into here
+ */
 int btrfs_xattr_set(struct inode *inode, int name_index,
 		    const char *attr_name, const void *value, size_t size,
 		    int flags)
@@ -210,7 +215,6 @@ int btrfs_xattr_set(struct inode *inode,
 		return -ENOMEM;
 	}

-	mutex_lock(&root->fs_info->fs_mutex);
 	trans = btrfs_start_transaction(root, 1);
 	btrfs_set_trans_block_group(trans, inode);

@@ -260,7 +264,6 @@ out:
 	}

 	btrfs_end_transaction(trans, root);
-	mutex_unlock(&root->fs_info->fs_mutex);
 	kfree(name);
 	btrfs_free_path(path);

@@ -445,17 +448,27 @@ static int btrfs_xattr_##name##_get(stru
 				    const char *name, void *value,	\
 				    size_t size)			\
 {									\
+	int err;							\
+	struct btrfs_root *root = BTRFS_I(inode)->root;			\
 	if (*name == '\0')						\
 		return -EINVAL;						\
-	return btrfs_xattr_get(inode, index, name, value, size);	\
+	mutex_lock(&root->fs_info->fs_mutex);				\
+	err = btrfs_xattr_get(inode, index, name, value, size);		\
+	mutex_unlock(&root->fs_info->fs_mutex);				\
+	return err;							\
 }									\
 static int btrfs_xattr_##name##_set(struct inode *inode,		\
 				    const char *name, const void *value,\
 				    size_t size, int flags)		\
 {									\
+	int err;							\
+	struct btrfs_root *root = BTRFS_I(inode)->root;			\
 	if (*name == '\0')						\
 		return -EINVAL;						\
-	return btrfs_xattr_set(inode, index, name, value, size, flags);	\
+	mutex_lock(&root->fs_info->fs_mutex);				\
+	err = btrfs_xattr_set(inode, index, name, value, size, flags);	\
+	mutex_unlock(&root->fs_info->fs_mutex);				\
+	return err;							\
 }

 BTRFS_XATTR_SETGET_FUNCS(security, BTRFS_XATTR_INDEX_SECURITY);
-- 
Thanks & Best Regards
Liu Hui



More information about the Btrfs-devel mailing list