Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 128850
b: refs/heads/master
c: 33268ea
h: refs/heads/master
v: v3
  • Loading branch information
Josef Bacik authored and Chris Mason committed Sep 25, 2008
1 parent 47b8c69 commit 61231fd
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 49 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6099afe88fe64b2f47c43a8a71c13be3a416bbf7
refs/heads/master: 33268eaf0b3db5e2bd12c0ada81a8e8f87a46d68
296 changes: 261 additions & 35 deletions trunk/fs/btrfs/acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,76 +20,302 @@
#include <linux/string.h>
#include <linux/xattr.h>
#include <linux/posix_acl_xattr.h>
#include <linux/posix_acl.h>
#include <linux/sched.h>

#include "ctree.h"
#include "btrfs_inode.h"
#include "xattr.h"
#ifndef is_owner_or_cap
#define is_owner_or_cap(inode) \
((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER))
#endif

static void btrfs_update_cached_acl(struct inode *inode,
struct posix_acl **p_acl,
struct posix_acl *acl)
{
spin_lock(&inode->i_lock);
if (*p_acl && *p_acl != BTRFS_ACL_NOT_CACHED)
posix_acl_release(*p_acl);
*p_acl = posix_acl_dup(acl);
spin_unlock(&inode->i_lock);
}

static struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
{
int size, name_index;
char *value = NULL;
struct posix_acl *acl = NULL, **p_acl;

switch (type) {
case ACL_TYPE_ACCESS:
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS;
p_acl = &BTRFS_I(inode)->i_acl;
break;
case ACL_TYPE_DEFAULT:
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT;
p_acl = &BTRFS_I(inode)->i_default_acl;
break;
default:
return ERR_PTR(-EINVAL);
}

spin_lock(&inode->i_lock);
if (*p_acl != BTRFS_ACL_NOT_CACHED)
acl = posix_acl_dup(*p_acl);
spin_unlock(&inode->i_lock);

if (acl)
return acl;


size = btrfs_xattr_get(inode, name_index, "", NULL, 0);
if (size > 0) {
value = kzalloc(size, GFP_NOFS);
if (!value)
return ERR_PTR(-ENOMEM);
size = btrfs_xattr_get(inode, name_index, "", value, size);
if (size > 0) {
acl = posix_acl_from_xattr(value, size);
btrfs_update_cached_acl(inode, p_acl, acl);
}
kfree(value);
} else if (size == -ENOENT) {
acl = NULL;
btrfs_update_cached_acl(inode, p_acl, acl);
}

return acl;
}

static int btrfs_xattr_get_acl(struct inode *inode, int type,
void *value, size_t size)
{
struct posix_acl *acl;
int ret = 0;

acl = btrfs_get_acl(inode, type);

if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl == NULL)
return -ENODATA;
ret = posix_acl_to_xattr(acl, value, size);
posix_acl_release(acl);

return ret;
}

/*
* Needs to be called with fs_mutex held
*/
static int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int ret, name_index = 0, size = 0;
struct posix_acl **p_acl;
char *value = NULL;
mode_t mode;

if (acl) {
ret = posix_acl_valid(acl);
if (ret < 0)
return ret;
ret = 0;
}

switch (type) {
case ACL_TYPE_ACCESS:
mode = inode->i_mode;
ret = posix_acl_equiv_mode(acl, &mode);
if (ret < 0)
return ret;
ret = 0;
inode->i_mode = mode;
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS;
p_acl = &BTRFS_I(inode)->i_acl;
break;
case ACL_TYPE_DEFAULT:
if (!S_ISDIR(inode->i_mode))
return acl ? -EINVAL : 0;
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT;
p_acl = &BTRFS_I(inode)->i_default_acl;
break;
default:
return -EINVAL;
}

if (acl) {
size = posix_acl_xattr_size(acl->a_count);
value = kmalloc(size, GFP_NOFS);
if (!value) {
ret = -ENOMEM;
goto out;
}

ret = posix_acl_to_xattr(acl, value, size);
if (ret < 0)
goto out;
}

ret = btrfs_xattr_set(inode, name_index, "", value, size, 0);

out:
if (value)
kfree(value);

if (!ret)
btrfs_update_cached_acl(inode, p_acl, acl);

return ret;
}

static int btrfs_xattr_set_acl(struct inode *inode, int type,
const void *value, size_t size)
{
int ret = 0;
struct posix_acl *acl;
struct posix_acl *acl = NULL;

if (!is_owner_or_cap(inode))
return -EPERM;
if (value) {
acl = posix_acl_from_xattr(value, size);
if (acl == NULL) {
value = NULL;
size = 0;
} else if (IS_ERR(acl)) {
ret = PTR_ERR(acl);
} else {
ret = posix_acl_valid(acl);
posix_acl_release(acl);
return PTR_ERR(acl);
}
if (ret)
return ret;
}
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);
ret = btrfs_set_acl(inode, acl, type);

posix_acl_release(acl);

return ret;
}


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);
return btrfs_xattr_get_acl(inode, ACL_TYPE_ACCESS, value, size);
}

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);
return btrfs_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
}

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);
return btrfs_xattr_get_acl(inode, ACL_TYPE_DEFAULT, value, size);
}

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);
return btrfs_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
}

int btrfs_check_acl(struct inode *inode, int mask)
{
struct posix_acl *acl;
int error = -EAGAIN;

acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);

if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
}

return error;
}

/*
* btrfs_init_acl is already generally called under fs_mutex, so the locking
* stuff has been fixed to work with that. If the locking stuff changes, we
* need to re-evaluate the acl locking stuff.
*/
int btrfs_init_acl(struct inode *inode, struct inode *dir)
{
struct posix_acl *acl = NULL;
int ret = 0;

/* this happens with subvols */
if (!dir)
return 0;

if (!S_ISLNK(inode->i_mode)) {
if (IS_POSIXACL(dir)) {
acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
}

if (!acl)
inode->i_mode &= ~current->fs->umask;
}

if (IS_POSIXACL(dir) && acl) {
struct posix_acl *clone;
mode_t mode;

if (S_ISDIR(inode->i_mode)) {
ret = btrfs_set_acl(inode, acl, ACL_TYPE_DEFAULT);
if (ret)
goto failed;
}
clone = posix_acl_clone(acl, GFP_NOFS);
ret = -ENOMEM;
if (!clone)
goto failed;

mode = inode->i_mode;
ret = posix_acl_create_masq(clone, &mode);
if (ret >= 0) {
inode->i_mode = mode;
if (ret > 0) {
/* we need an acl */
ret = btrfs_set_acl(inode, clone,
ACL_TYPE_ACCESS);
}
}
}
failed:
posix_acl_release(acl);

return ret;
}

int btrfs_acl_chmod(struct inode *inode)
{
struct posix_acl *acl, *clone;
int ret = 0;

if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;

if (!IS_POSIXACL(inode))
return 0;

acl = btrfs_get_acl(inode, ACL_TYPE_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;

ret = posix_acl_chmod_masq(clone, inode->i_mode);
if (!ret)
ret = btrfs_set_acl(inode, clone, ACL_TYPE_ACCESS);

posix_acl_release(clone);

return ret;
}

struct xattr_handler btrfs_xattr_acl_default_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.list = btrfs_xattr_generic_list,
Expand Down
3 changes: 3 additions & 0 deletions trunk/fs/btrfs/btrfs_inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ struct btrfs_inode {
struct inode vfs_inode;
struct btrfs_ordered_inode_tree ordered_tree;

struct posix_acl *i_acl;
struct posix_acl *i_default_acl;

/*
* transid of the trans_handle that last modified this inode
*/
Expand Down
7 changes: 7 additions & 0 deletions trunk/fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ struct btrfs_ordered_sum;

#define BTRFS_MAGIC "_B5RfS_M"

#define BTRFS_ACL_NOT_CACHED ((void *)-1)

#ifdef CONFIG_LOCKDEP
# define BTRFS_MAX_LEVEL 7
#else
Expand Down Expand Up @@ -1694,4 +1696,9 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
u64 btrfs_parse_size(char *str);
int btrfs_parse_options(struct btrfs_root *root, char *options);
int btrfs_sync_fs(struct super_block *sb, int wait);

/* acl.c */
int btrfs_check_acl(struct inode *inode, int mask);
int btrfs_init_acl(struct inode *inode, struct inode *dir);
int btrfs_acl_chmod(struct inode *inode);
#endif
Loading

0 comments on commit 61231fd

Please sign in to comment.