Liu Hui
2008-Jan-27 06:50 UTC
[Btrfs-devel][PATCH] The second patch of acl supporting for btrfs(patch for btrfs-unstable)
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