Commit 0acef032 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] reiserfs: ACL support

From: Chris Mason <mason@suse.com>

From: jeffm@suse.com

reiserfs acl support
parent 0b1a6a8c
......@@ -254,6 +254,18 @@ config REISERFS_FS_XATTR
If unsure, say N.
config REISERFS_FS_POSIX_ACL
bool "ReiserFS POSIX Access Control Lists"
depends on REISERFS_FS_XATTR
help
Posix Access Control Lists (ACLs) support permissions for users and
groups beyond the owner/group/world scheme.
To learn more about Access Control Lists, visit the Posix ACLs for
Linux website <http://acl.bestbits.at/>.
If you don't know what Access Control Lists are, say N
config JFS_FS
tristate "JFS filesystem support"
select NLS
......@@ -292,13 +304,13 @@ config JFS_STATISTICS
to be made available to the user in the /proc/fs/jfs/ directory.
config FS_POSIX_ACL
# Posix ACL utility routines (for now, only ext2/ext3/jfs)
# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs)
#
# NOTE: you can implement Posix ACLs without these helpers (XFS does).
# Never use this symbol for ifdefs.
#
bool
depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL
depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || REISERFS_FS_POSIX_ACL
default y
config XFS_FS
......
......@@ -13,6 +13,10 @@ ifeq ($(CONFIG_REISERFS_FS_XATTR),y)
reiserfs-objs += xattr.o xattr_user.o
endif
ifeq ($(CONFIG_REISERFS_FS_POSIX_ACL),y)
reiserfs-objs += xattr_acl.o
endif
# gcc -O2 (the kernel default) is overaggressive on ppc32 when many inline
# functions are used. This causes the compiler to advance the stack
# pointer out of the available stack space, corrupting kernel space,
......
......@@ -5,6 +5,7 @@
#include <linux/time.h>
#include <linux/reiserfs_fs.h>
#include <linux/reiserfs_acl.h>
#include <linux/reiserfs_xattr.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
......
......@@ -5,6 +5,7 @@
#include <linux/config.h>
#include <linux/time.h>
#include <linux/reiserfs_fs.h>
#include <linux/reiserfs_acl.h>
#include <linux/reiserfs_xattr.h>
#include <linux/smp_lock.h>
#include <linux/pagemap.h>
......@@ -976,6 +977,8 @@ static void init_inode (struct inode * inode, struct path * path)
REISERFS_I(inode)->i_prealloc_count = 0;
REISERFS_I(inode)->i_trans_id = 0;
REISERFS_I(inode)->i_jl = NULL;
REISERFS_I(inode)->i_acl_access = NULL;
REISERFS_I(inode)->i_acl_default = NULL;
if (stat_data_v1 (ih)) {
struct stat_data_v1 * sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih);
......@@ -1637,6 +1640,8 @@ int reiserfs_new_inode (struct reiserfs_transaction_handle *th,
REISERFS_I(inode)->i_attrs =
REISERFS_I(dir)->i_attrs & REISERFS_INHERIT_MASK;
sd_attrs_to_i_attrs( REISERFS_I(inode) -> i_attrs, inode );
REISERFS_I(inode)->i_acl_access = NULL;
REISERFS_I(inode)->i_acl_default = NULL;
if (old_format_only (sb))
make_le_item_head (&ih, 0, KEY_FORMAT_3_5, SD_OFFSET, TYPE_STAT_DATA, SD_V1_SIZE, MAX_US_INT);
......@@ -1721,6 +1726,19 @@ int reiserfs_new_inode (struct reiserfs_transaction_handle *th,
goto out_inserted_sd;
}
/* XXX CHECK THIS */
if (reiserfs_posixacl (inode->i_sb)) {
retval = reiserfs_inherit_default_acl (dir, dentry, inode);
if (retval) {
err = retval;
reiserfs_check_path(&path_to_key) ;
journal_end(th, th->t_super, th->t_blocks_allocated);
goto out_inserted_sd;
}
} else if (inode->i_sb->s_flags & MS_POSIXACL) {
reiserfs_warning ("ACLs aren't enabled in the fs, but vfs thinks they are!\n");
}
insert_inode_hash (inode);
reiserfs_update_sd(th, inode);
reiserfs_check_path(&path_to_key) ;
......@@ -2565,6 +2583,11 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) {
}
if (!error && reiserfs_posixacl (inode->i_sb)) {
if (attr->ia_valid & ATTR_MODE)
error = reiserfs_acl_chmod (inode);
}
out:
reiserfs_write_unlock(inode->i_sb);
return error ;
......
......@@ -15,6 +15,7 @@
#include <linux/time.h>
#include <linux/bitops.h>
#include <linux/reiserfs_fs.h>
#include <linux/reiserfs_acl.h>
#include <linux/reiserfs_xattr.h>
#include <linux/smp_lock.h>
......@@ -579,6 +580,7 @@ static int reiserfs_create (struct inode * dir, struct dentry *dentry, int mode,
struct inode * inode;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ;
struct reiserfs_transaction_handle th ;
int locked;
if (!(inode = new_inode(dir->i_sb))) {
return -ENOMEM ;
......@@ -587,9 +589,19 @@ static int reiserfs_create (struct inode * dir, struct dentry *dentry, int mode,
if (retval)
return retval;
locked = reiserfs_cache_default_acl (dir);
reiserfs_write_lock(dir->i_sb);
if (locked)
reiserfs_write_lock_xattrs (dir->i_sb);
journal_begin(&th, dir->i_sb, jbegin_count) ;
retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode);
if (locked)
reiserfs_write_unlock_xattrs (dir->i_sb);
if (retval) {
goto out_failed;
}
......@@ -625,6 +637,7 @@ static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode,
struct inode * inode;
struct reiserfs_transaction_handle th ;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
int locked;
if (!new_valid_dev(rdev))
return -EINVAL;
......@@ -636,10 +649,20 @@ static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode,
if (retval)
return retval;
locked = reiserfs_cache_default_acl (dir);
reiserfs_write_lock(dir->i_sb);
if (locked)
reiserfs_write_lock_xattrs (dir->i_sb);
journal_begin(&th, dir->i_sb, jbegin_count) ;
retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode);
if (locked)
reiserfs_write_unlock_xattrs (dir->i_sb);
if (retval) {
goto out_failed;
}
......@@ -678,6 +701,7 @@ static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode)
struct inode * inode;
struct reiserfs_transaction_handle th ;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
int locked;
#ifdef DISPLACE_NEW_PACKING_LOCALITIES
/* set flag that new packing locality created and new blocks for the content * of that directory are not displaced yet */
......@@ -691,7 +715,11 @@ static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode)
if (retval)
return retval;
locked = reiserfs_cache_default_acl (dir);
reiserfs_write_lock(dir->i_sb);
if (locked)
reiserfs_write_lock_xattrs (dir->i_sb);
journal_begin(&th, dir->i_sb, jbegin_count) ;
/* inc the link count now, so another writer doesn't overflow it while
......@@ -703,6 +731,9 @@ static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode)
old_format_only (dir->i_sb) ?
EMPTY_DIR_SIZE_V1 : EMPTY_DIR_SIZE,
dentry, inode);
if (locked)
reiserfs_write_unlock_xattrs (dir->i_sb);
if (retval) {
dir->i_nlink-- ;
goto out_failed;
......@@ -945,6 +976,8 @@ static int reiserfs_symlink (struct inode * parent_dir,
memcpy (name, symname, strlen (symname));
padd_item (name, item_len, strlen (symname));
/* We would inherit the default ACL here, but symlinks don't get ACLs */
journal_begin(&th, parent_dir->i_sb, jbegin_count) ;
retval = reiserfs_new_inode (&th, parent_dir, mode, name, strlen (symname),
......
......@@ -17,6 +17,7 @@
#include <linux/time.h>
#include <asm/uaccess.h>
#include <linux/reiserfs_fs.h>
#include <linux/reiserfs_acl.h>
#include <linux/reiserfs_xattr.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
......@@ -433,6 +434,8 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
SLAB_CTOR_CONSTRUCTOR) {
INIT_LIST_HEAD(&ei->i_prealloc_list) ;
inode_init_once(&ei->vfs_inode);
ei->i_acl_access = NULL;
ei->i_acl_default = NULL;
}
}
......@@ -473,6 +476,22 @@ static void reiserfs_dirty_inode (struct inode * inode) {
reiserfs_write_unlock(inode->i_sb);
}
static void reiserfs_clear_inode (struct inode *inode)
{
struct posix_acl *acl;
acl = REISERFS_I(inode)->i_acl_access;
if (acl && !IS_ERR (acl))
posix_acl_release (acl);
REISERFS_I(inode)->i_acl_access = NULL;
acl = REISERFS_I(inode)->i_acl_default;
if (acl && !IS_ERR (acl))
posix_acl_release (acl);
REISERFS_I(inode)->i_acl_default = NULL;
}
struct super_operations reiserfs_sops =
{
.alloc_inode = reiserfs_alloc_inode,
......@@ -480,6 +499,7 @@ struct super_operations reiserfs_sops =
.write_inode = reiserfs_write_inode,
.dirty_inode = reiserfs_dirty_inode,
.delete_inode = reiserfs_delete_inode,
.clear_inode = reiserfs_clear_inode,
.put_super = reiserfs_put_super,
.write_super = reiserfs_write_super,
.write_super_lockfs = reiserfs_write_super_lockfs,
......@@ -682,6 +702,10 @@ static int reiserfs_parse_options (struct super_block * s, char * options, /* st
{"noattrs", 0, 0, 0, 1<<REISERFS_ATTRS},
{"user_xattr", 0, 0, 1<<REISERFS_XATTRS_USER, 0},
{"nouser_xattr", 0, 0, 0, 1<<REISERFS_XATTRS_USER},
#ifdef CONFIG_REISERFS_FS_POSIX_ACL
{"acl", 0, 0, 1<<REISERFS_POSIXACL, 0},
{"noacl", 0, 0, 0, 1<<REISERFS_POSIXACL},
#endif
{"nolog", 0, 0, 0, 0}, /* This is unsupported */
{"replayonly", 0, 0, 1<<REPLAYONLY, 0},
{"block-allocator", 'a', balloc, 0, 0},
......@@ -827,6 +851,7 @@ static int reiserfs_remount (struct super_block * s, int * mount_flags, char * a
safe_mask |= 1 << REISERFS_TEST4;
safe_mask |= 1 << REISERFS_ATTRS;
safe_mask |= 1 << REISERFS_XATTRS_USER;
safe_mask |= 1 << REISERFS_POSIXACL;
/* Update the bitmask, taking care to keep
* the bits we're not allowed to change here */
......
......@@ -6,7 +6,7 @@
*/
/*
* In order to implement EAs in a clean, backwards compatible manner,
* In order to implement EA/ACLs in a clean, backwards compatible manner,
* they are implemented as files in a "private" directory.
* Each EA is in it's own file, with the directory layout like so (/ is assumed
* to be relative to fs root). Inside the /.reiserfs_priv/xattrs directory,
......@@ -15,12 +15,18 @@
* named with the name of the extended attribute.
*
* So, for objectid 12648430, we could have:
* /.reiserfs_priv/xattrs/C0FFEE.0/system.posix_acl_access
* /.reiserfs_priv/xattrs/C0FFEE.0/system.posix_acl_default
* /.reiserfs_priv/xattrs/C0FFEE.0/user.Content-Type
* .. or similar.
*
* The file contents are the text of the EA. The size is known based on the
* stat data describing the file.
*
* In the case of system.posix_acl_access and system.posix_acl_default, since
* these are special cases for filesystem ACLs, they are interpreted by the
* kernel, in addition, they are negatively and positively cached and attached
* to the inode so that unnecessary lookups are avoided.
*/
#include <linux/reiserfs_fs.h>
......@@ -32,6 +38,7 @@
#include <linux/pagemap.h>
#include <linux/xattr.h>
#include <linux/reiserfs_xattr.h>
#include <linux/reiserfs_acl.h>
#include <linux/mbcache.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
......@@ -1169,6 +1176,10 @@ reiserfs_xattr_register_handlers (void)
/* Add the handlers */
list_add_tail (&user_handler.handlers, &xattr_handlers);
#ifdef CONFIG_REISERFS_FS_POSIX_ACL
list_add_tail (&posix_acl_access_handler.handlers, &xattr_handlers);
list_add_tail (&posix_acl_default_handler.handlers, &xattr_handlers);
#endif
/* Run initializers, if available */
list_for_each (p, &xattr_handlers) {
......@@ -1231,7 +1242,7 @@ reiserfs_xattr_init (struct super_block *s, int mount_flags)
} else if (reiserfs_xattrs_optional (s)) {
/* Old format filesystem, but optional xattrs have been enabled
* at mount time. Error out. */
reiserfs_warning ("reiserfs: xattrs not supported on pre v3.6 "
reiserfs_warning ("reiserfs: xattrs/ACLs not supported on pre v3.6 "
"format filesystem. Failing mount.\n");
err = -EOPNOTSUPP;
goto error;
......@@ -1276,7 +1287,7 @@ reiserfs_xattr_init (struct super_block *s, int mount_flags)
/* If we're read-only it just means that the dir hasn't been
* created. Not an error -- just no xattrs on the fs. We'll
* check again if we go read-write */
reiserfs_warning ("reiserfs: xattrs enabled and couldn't "
reiserfs_warning ("reiserfs: xattrs/ACLs enabled and couldn't "
"find/create .reiserfs_priv. Failing mount.\n");
err = -EOPNOTSUPP;
}
......@@ -1288,12 +1299,20 @@ reiserfs_xattr_init (struct super_block *s, int mount_flags)
if (err) {
clear_bit (REISERFS_XATTRS, &(REISERFS_SB(s)->s_mount_opt));
clear_bit (REISERFS_XATTRS_USER, &(REISERFS_SB(s)->s_mount_opt));
clear_bit (REISERFS_POSIXACL, &(REISERFS_SB(s)->s_mount_opt));
}
/* The super_block MS_POSIXACL must mirror the (no)acl mount option. */
s->s_flags = s->s_flags & ~MS_POSIXACL;
if (reiserfs_posixacl (s))
s->s_flags |= MS_POSIXACL;
return err;
}
int
reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
static int
__reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd,
int need_lock)
{
umode_t mode = inode->i_mode;
......@@ -1317,10 +1336,45 @@ reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
if (is_reiserfs_priv_object (inode))
return 0;
if (current->fsuid == inode->i_uid)
if (current->fsuid == inode->i_uid) {
mode >>= 6;
else if (in_group_p(inode->i_gid))
mode >>= 3;
#ifdef CONFIG_REISERFS_FS_POSIX_ACL
} else if (reiserfs_posixacl(inode->i_sb) &&
get_inode_sd_version (inode) != STAT_DATA_V1) {
struct posix_acl *acl;
/* ACL can't contain additional permissions if
the ACL_MASK entry is 0 */
if (!(mode & S_IRWXG))
goto check_groups;
if (need_lock)
reiserfs_read_lock_xattrs (inode->i_sb);
acl = reiserfs_get_acl (inode, ACL_TYPE_ACCESS);
if (need_lock)
reiserfs_read_unlock_xattrs (inode->i_sb);
if (IS_ERR (acl)) {
if (PTR_ERR (acl) == -ENODATA)
goto check_groups;
return PTR_ERR (acl);
}
if (acl) {
int err = posix_acl_permission (inode, acl, mask);
posix_acl_release (acl);
if (err == -EACCES) {
goto check_capabilities;
}
return err;
} else {
goto check_groups;
}
#endif
} else {
check_groups:
if (in_group_p(inode->i_gid))
mode >>= 3;
}
/*
* If the DACs are ok we don't need any capability check.
......@@ -1328,6 +1382,7 @@ reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
return 0;
check_capabilities:
/*
* Read/write DACs are always overridable.
* Executable DACs are overridable if at least one exec bit is set.
......@@ -1344,5 +1399,16 @@ reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
return 0;
return -EACCES;
}
int
reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
{
return __reiserfs_permission (inode, mask, nd, 1);
}
int
reiserfs_permission_locked (struct inode *inode, int mask, struct nameidata *nd)
{
return __reiserfs_permission (inode, mask, nd, 0);
}
#include <linux/fs.h>
#include <linux/posix_acl.h>
#include <linux/reiserfs_fs.h>
#include <linux/errno.h>
#include <linux/pagemap.h>
#include <linux/xattr.h>
#include <linux/xattr_acl.h>
#include <linux/reiserfs_xattr.h>
#include <linux/reiserfs_acl.h>
#include <asm/uaccess.h>
static int
xattr_set_acl(struct inode *inode, int type, const void *value, size_t size)
{
struct posix_acl *acl;
int error;
if (!reiserfs_posixacl(inode->i_sb))
return -EOPNOTSUPP;
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EPERM;
if (value) {
acl = posix_acl_from_xattr(value, size);
if (IS_ERR(acl)) {
return PTR_ERR(acl);
} else if (acl) {
error = posix_acl_valid(acl);
if (error)
goto release_and_out;
}
} else
acl = NULL;
error = reiserfs_set_acl (inode, type, acl);
release_and_out:
posix_acl_release(acl);
return error;
}
static int
xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
{
struct posix_acl *acl;
int error;
if (!reiserfs_posixacl(inode->i_sb))
return -EOPNOTSUPP;
acl = reiserfs_get_acl (inode, type);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl == NULL)
return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size);
posix_acl_release(acl);
return error;
}
/*
* Convert from filesystem to in-memory representation.
*/
static struct posix_acl *
posix_acl_from_disk(const void *value, size_t size)
{
const char *end = (char *)value + size;
int n, count;
struct posix_acl *acl;
if (!value)
return NULL;
if (size < sizeof(reiserfs_acl_header))
return ERR_PTR(-EINVAL);
if (((reiserfs_acl_header *)value)->a_version !=
cpu_to_le32(REISERFS_ACL_VERSION))
return ERR_PTR(-EINVAL);
value = (char *)value + sizeof(reiserfs_acl_header);
count = reiserfs_acl_count(size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
return NULL;
acl = posix_acl_alloc(count, GFP_NOFS);
if (!acl)
return ERR_PTR(-ENOMEM);
for (n=0; n < count; n++) {
reiserfs_acl_entry *entry =
(reiserfs_acl_entry *)value;
if ((char *)value + sizeof(reiserfs_acl_entry_short) > end)
goto fail;
acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
switch(acl->a_entries[n].e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
value = (char *)value +
sizeof(reiserfs_acl_entry_short);
acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
case ACL_GROUP:
value = (char *)value + sizeof(reiserfs_acl_entry);
if ((char *)value > end)
goto fail;
acl->a_entries[n].e_id =
le32_to_cpu(entry->e_id);
break;
default:
goto fail;
}
}
if (value != end)
goto fail;
return acl;
fail:
posix_acl_release(acl);
return ERR_PTR(-EINVAL);
}
/*
* Convert from in-memory to filesystem representation.
*/
static void *
posix_acl_to_disk(const struct posix_acl *acl, size_t *size)
{
reiserfs_acl_header *ext_acl;
char *e;
int n;
*size = reiserfs_acl_size(acl->a_count);
ext_acl = (reiserfs_acl_header *)kmalloc(sizeof(reiserfs_acl_header) +
acl->a_count * sizeof(reiserfs_acl_entry), GFP_NOFS);
if (!ext_acl)
return ERR_PTR(-ENOMEM);
ext_acl->a_version = cpu_to_le32(REISERFS_ACL_VERSION);
e = (char *)ext_acl + sizeof(reiserfs_acl_header);
for (n=0; n < acl->a_count; n++) {
reiserfs_acl_entry *entry = (reiserfs_acl_entry *)e;
entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
switch(acl->a_entries[n].e_tag) {
case ACL_USER:
case ACL_GROUP:
entry->e_id =
cpu_to_le32(acl->a_entries[n].e_id);
e += sizeof(reiserfs_acl_entry);
break;
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
e += sizeof(reiserfs_acl_entry_short);
break;
default:
goto fail;
}
}
return (char *)ext_acl;
fail:
kfree(ext_acl);
return ERR_PTR(-EINVAL);
}
/*
* Inode operation get_posix_acl().
*
* inode->i_sem: down
* BKL held [before 2.5.x]
*/
struct posix_acl *
reiserfs_get_acl(struct inode *inode, int type)
{
char *name, *value;
struct posix_acl *acl, **p_acl;
size_t size;
int retval;
struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
switch (type) {
case ACL_TYPE_ACCESS:
name = XATTR_NAME_ACL_ACCESS;
p_acl = &reiserfs_i->i_acl_access;
break;
case ACL_TYPE_DEFAULT:
name = XATTR_NAME_ACL_DEFAULT;
p_acl = &reiserfs_i->i_acl_default;
break;
default:
return ERR_PTR (-EINVAL);
}
if (IS_ERR (*p_acl)) {
if (PTR_ERR (*p_acl) == -ENODATA)
return NULL;
} else if (*p_acl != NULL)
return posix_acl_dup (*p_acl);
size = reiserfs_xattr_get (inode, name, NULL, 0);
if ((int)size < 0) {
if (size == -ENODATA || size == -ENOSYS) {
*p_acl = ERR_PTR (-ENODATA);
return NULL;
}
return ERR_PTR (size);
}
value = kmalloc (size, GFP_NOFS);
if (!value)
return ERR_PTR (-ENOMEM);
retval = reiserfs_xattr_get(inode, name, value, size);
if (retval == -ENODATA || retval == -ENOSYS) {
/* This shouldn't actually happen as it should have
been caught above.. but just in case */
acl = NULL;
*p_acl = ERR_PTR (-ENODATA);
} else if (retval < 0) {
acl = ERR_PTR(retval);
} else {
acl = posix_acl_from_disk(value, retval);
*p_acl = posix_acl_dup (acl);
}
kfree(value);
return acl;
}
/*
* Inode operation set_posix_acl().
*
* inode->i_sem: down
* BKL held [before 2.5.x]
*/
int
reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
{
char *name;
void *value = NULL;
struct posix_acl **p_acl;
size_t size;
int error;
struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
switch (type) {
case ACL_TYPE_ACCESS:
name = XATTR_NAME_ACL_ACCESS;
p_acl = &reiserfs_i->i_acl_access;
if (acl) {
mode_t mode = inode->i_mode;
error = posix_acl_equiv_mode (acl, &mode);
if (error < 0)
return error;
else {
inode->i_mode = mode;
if (error == 0)
acl = NULL;
}
}
break;
case ACL_TYPE_DEFAULT:
name = XATTR_NAME_ACL_DEFAULT;
p_acl = &reiserfs_i->i_acl_default;
if (!S_ISDIR (inode->i_mode))
return acl ? -EACCES : 0;
break;
default:
return -EINVAL;
}
if (acl) {
value = posix_acl_to_disk(acl, &size);
if (IS_ERR(value))
return (int)PTR_ERR(value);
error = reiserfs_xattr_set(inode, name, value, size, 0);
} else {
error = reiserfs_xattr_del (inode, name);
if (error == -ENODATA)
error = 0;
}
if (value)
kfree(value);
if (!error) {
/* Release the old one */
if (!IS_ERR (*p_acl) && *p_acl)
posix_acl_release (*p_acl);
if (acl == NULL)
*p_acl = ERR_PTR (-ENODATA);
else
*p_acl = posix_acl_dup (acl);
}
return error;
}
/* dir->i_sem: down,
* inode is new and not released into the wild yet */
int
reiserfs_inherit_default_acl (struct inode *dir, struct dentry *dentry, struct inode *inode)
{
struct posix_acl *acl;
int err = 0;
/* ACLs only get applied to files and directories */
if (S_ISLNK (inode->i_mode))
return 0;
/* ACLs can only be used on "new" objects, so if it's an old object
* there is nothing to inherit from */
if (get_inode_sd_version (dir) == STAT_DATA_V1)
goto apply_umask;
/* Don't apply ACLs to objects in the .reiserfs_priv tree.. This
* would be useless since permissions are ignored, and a pain because
* it introduces locking cycles */
if (is_reiserfs_priv_object (dir)) {
REISERFS_I(inode)->i_flags |= i_priv_object;
goto apply_umask;
}
acl = reiserfs_get_acl (dir, ACL_TYPE_DEFAULT);
if (IS_ERR (acl)) {
if (PTR_ERR (acl) == -ENODATA)
goto apply_umask;
return PTR_ERR (acl);
}
if (acl) {
struct posix_acl *acl_copy;
mode_t mode = inode->i_mode;
int need_acl;
/* Copy the default ACL to the default ACL of a new directory */
if (S_ISDIR (inode->i_mode)) {
err = reiserfs_set_acl (inode, ACL_TYPE_DEFAULT, acl);
if (err)
goto cleanup;
}
/* Now we reconcile the new ACL and the mode,
potentially modifying both */
acl_copy = posix_acl_clone (acl, GFP_NOFS);
if (!acl_copy) {
err = -ENOMEM;
goto cleanup;
}
need_acl = posix_acl_create_masq (acl_copy, &mode);
if (need_acl >= 0) {
if (mode != inode->i_mode) {
inode->i_mode = mode;
}
/* If we need an ACL.. */
if (need_acl > 0) {
err = reiserfs_set_acl (inode, ACL_TYPE_ACCESS, acl_copy);
if (err)
goto cleanup_copy;
}
}
cleanup_copy:
posix_acl_release (acl_copy);
cleanup:
posix_acl_release (acl);
} else {
apply_umask:
/* no ACL, apply umask */
inode->i_mode &= ~current->fs->umask;
}
return err;
}
/* Looks up and caches the result of the default ACL.
* We do this so that we don't need to carry the xattr_sem into
* reiserfs_new_inode if we don't need to */
int
reiserfs_cache_default_acl (struct inode *inode)
{
int ret = 0;
if (reiserfs_posixacl (inode->i_sb) &&
!is_reiserfs_priv_object (inode)) {
struct posix_acl *acl;
reiserfs_read_lock_xattrs (inode->i_sb);
acl = reiserfs_get_acl (inode, ACL_TYPE_DEFAULT);
reiserfs_read_unlock_xattrs (inode->i_sb);
ret = acl ? 1 : 0;
posix_acl_release (acl);
}
return ret;
}
int
reiserfs_acl_chmod (struct inode *inode)
{
struct posix_acl *acl, *clone;
int error;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
if (get_inode_sd_version (inode) == STAT_DATA_V1 ||
!reiserfs_posixacl(inode->i_sb))
{
return 0;
}
reiserfs_read_lock_xattrs (inode->i_sb);
acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
reiserfs_read_unlock_xattrs (inode->i_sb);
if (!acl)
return 0;
if (IS_ERR(acl))
return PTR_ERR(acl);
clone = posix_acl_clone(acl, GFP_NOFS);
posix_acl_release(acl);
if (!clone)
return -ENOMEM;
error = posix_acl_chmod_masq(clone, inode->i_mode);
if (!error) {
reiserfs_write_lock_xattrs (inode->i_sb);
error = reiserfs_set_acl(inode, ACL_TYPE_ACCESS, clone);
reiserfs_write_unlock_xattrs (inode->i_sb);
}
posix_acl_release(clone);
return error;
}
static int
posix_acl_access_get(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1)
return -EINVAL;
return xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
}
static int
posix_acl_access_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1)
return -EINVAL;
return xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
}
static int
posix_acl_access_del (struct inode *inode, const char *name)
{
struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
struct posix_acl **acl = &reiserfs_i->i_acl_access;
if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1)
return -EINVAL;
if (!IS_ERR (*acl) && *acl) {
posix_acl_release (*acl);
*acl = ERR_PTR (-ENODATA);
}
return 0;
}
static int
posix_acl_access_list (struct inode *inode, const char *name, int namelen, char *out)
{
int len = namelen;
if (!reiserfs_posixacl (inode->i_sb))
return 0;
if (out)
memcpy (out, name, len);
return len;
}
struct reiserfs_xattr_handler posix_acl_access_handler = {
prefix: XATTR_NAME_ACL_ACCESS,
get: posix_acl_access_get,
set: posix_acl_access_set,
del: posix_acl_access_del,
list: posix_acl_access_list,
};
static int
posix_acl_default_get (struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1)
return -EINVAL;
return xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
}
static int
posix_acl_default_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1)
return -EINVAL;
return xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
}
static int
posix_acl_default_del (struct inode *inode, const char *name)
{
struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
struct posix_acl **acl = &reiserfs_i->i_acl_default;
if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1)
return -EINVAL;
if (!IS_ERR (*acl) && *acl) {
posix_acl_release (*acl);
*acl = ERR_PTR (-ENODATA);
}
return 0;
}
static int
posix_acl_default_list (struct inode *inode, const char *name, int namelen, char *out)
{
int len = namelen;
if (!reiserfs_posixacl (inode->i_sb))
return 0;
if (out)
memcpy (out, name, len);
return len;
}
struct reiserfs_xattr_handler posix_acl_default_handler = {
prefix: XATTR_NAME_ACL_DEFAULT,
get: posix_acl_default_get,
set: posix_acl_default_set,
del: posix_acl_default_del,
list: posix_acl_default_list,
};
......@@ -6,6 +6,10 @@
#include <linux/reiserfs_xattr.h>
#include <asm/uaccess.h>
#ifdef CONFIG_REISERFS_FS_POSIX_ACL
# include <linux/reiserfs_acl.h>
#endif
#define XATTR_USER_PREFIX "user."
static int
......@@ -20,7 +24,7 @@ user_get (struct inode *inode, const char *name, void *buffer, size_t size)
if (!reiserfs_xattrs_user (inode->i_sb))
return -EOPNOTSUPP;
error = permission (inode, MAY_READ, NULL);
error = reiserfs_permission_locked (inode, MAY_READ, NULL);
if (error)
return error;
......@@ -44,7 +48,7 @@ user_set (struct inode *inode, const char *name, const void *buffer,
(!S_ISDIR (inode->i_mode) || inode->i_mode & S_ISVTX))
return -EPERM;
error = permission (inode, MAY_WRITE, NULL);
error = reiserfs_permission_locked (inode, MAY_WRITE, NULL);
if (error)
return error;
......@@ -66,7 +70,7 @@ user_del (struct inode *inode, const char *name)
(!S_ISDIR (inode->i_mode) || inode->i_mode & S_ISVTX))
return -EPERM;
error = permission (inode, MAY_WRITE, NULL);
error = reiserfs_permission_locked (inode, MAY_WRITE, NULL);
if (error)
return error;
......
#include <linux/init.h>
#include <linux/posix_acl.h>
#include <linux/xattr_acl.h>
#define REISERFS_ACL_VERSION 0x0001
typedef struct {
__u16 e_tag;
__u16 e_perm;
__u32 e_id;
} reiserfs_acl_entry;
typedef struct {
__u16 e_tag;
__u16 e_perm;
} reiserfs_acl_entry_short;
typedef struct {
__u32 a_version;
} reiserfs_acl_header;
static inline size_t reiserfs_acl_size(int count)
{
if (count <= 4) {
return sizeof(reiserfs_acl_header) +
count * sizeof(reiserfs_acl_entry_short);
} else {
return sizeof(reiserfs_acl_header) +
4 * sizeof(reiserfs_acl_entry_short) +
(count - 4) * sizeof(reiserfs_acl_entry);
}
}
static inline int reiserfs_acl_count(size_t size)
{
ssize_t s;
size -= sizeof(reiserfs_acl_header);
s = size - 4 * sizeof(reiserfs_acl_entry_short);
if (s < 0) {
if (size % sizeof(reiserfs_acl_entry_short))
return -1;
return size / sizeof(reiserfs_acl_entry_short);
} else {
if (s % sizeof(reiserfs_acl_entry))
return -1;
return s / sizeof(reiserfs_acl_entry) + 4;
}
}
#ifdef CONFIG_REISERFS_FS_POSIX_ACL
struct posix_acl * reiserfs_get_acl(struct inode *inode, int type);
int reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl);
int reiserfs_acl_chmod (struct inode *inode);
int reiserfs_inherit_default_acl (struct inode *dir, struct dentry *dentry, struct inode *inode);
int reiserfs_cache_default_acl (struct inode *dir);
extern int reiserfs_xattr_posix_acl_init (void) __init;
extern int reiserfs_xattr_posix_acl_exit (void);
extern struct reiserfs_xattr_handler posix_acl_default_handler;
extern struct reiserfs_xattr_handler posix_acl_access_handler;
#else
#define reiserfs_set_acl NULL
#define reiserfs_get_acl NULL
#define reiserfs_cache_default_acl(inode) 0
static inline int
reiserfs_xattr_posix_acl_init (void)
{
return 0;
}
static inline int
reiserfs_xattr_posix_acl_exit (void)
{
return 0;
}
static inline int
reiserfs_acl_chmod (struct inode *inode)
{
return 0;
}
static inline int
reiserfs_inherit_default_acl (const struct inode *dir, struct dentry *dentry, struct inode *inode)
{
return 0;
}
#endif
......@@ -52,6 +52,9 @@ struct reiserfs_inode_info {
** flushed */
unsigned long i_trans_id ;
struct reiserfs_journal_list *i_jl;
struct posix_acl *i_acl_access;
struct posix_acl *i_acl_default;
struct inode vfs_inode;
};
......
......@@ -443,6 +443,7 @@ enum reiserfs_mount_options {
REISERFS_ATTRS,
REISERFS_XATTRS,
REISERFS_XATTRS_USER,
REISERFS_POSIXACL,
REISERFS_TEST1,
REISERFS_TEST2,
......@@ -470,7 +471,8 @@ enum reiserfs_mount_options {
#define reiserfs_data_writeback(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_DATA_WRITEBACK))
#define reiserfs_xattrs(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_XATTRS))
#define reiserfs_xattrs_user(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_XATTRS_USER))
#define reiserfs_xattrs_optional(s) reiserfs_xattrs_user(s)
#define reiserfs_posixacl(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_POSIXACL))
#define reiserfs_xattrs_optional(s) (reiserfs_xattrs_user(s) || reiserfs_posixacl(s))
void reiserfs_file_buffer (struct buffer_head * bh, int list);
extern struct file_system_type reiserfs_fs_type;
......
......@@ -42,6 +42,7 @@ int reiserfs_delete_xattrs (struct inode *inode);
int reiserfs_chown_xattrs (struct inode *inode, struct iattr *attrs);
int reiserfs_xattr_init (struct super_block *sb, int mount_flags);
int reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd);
int reiserfs_permission_locked (struct inode *inode, int mask, struct nameidata *nd);
int reiserfs_xattr_del (struct inode *, const char *);
int reiserfs_xattr_get (const struct inode *, const char *, void *, size_t);
......@@ -94,7 +95,11 @@ reiserfs_read_unlock_xattrs(struct super_block *sb)
static inline int reiserfs_delete_xattrs (struct inode *inode) { return 0; };
static inline int reiserfs_chown_xattrs (struct inode *inode, struct iattr *attrs) { return 0; };
static inline int reiserfs_xattr_init (struct super_block *sb, int mount_flags) { return 0; };
static inline int reiserfs_xattr_init (struct super_block *sb, int mount_flags)
{
sb->s_flags = (sb->s_flags & ~MS_POSIXACL); /* to be sure */
return 0;
};
#endif
#endif /* __KERNEL__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment