From: Chris Mason From: jeffm@suse.com reiserfs acl support --- 25-akpm/fs/Kconfig | 16 25-akpm/fs/reiserfs/Makefile | 4 25-akpm/fs/reiserfs/file.c | 1 25-akpm/fs/reiserfs/inode.c | 23 + 25-akpm/fs/reiserfs/namei.c | 33 + 25-akpm/fs/reiserfs/super.c | 25 + 25-akpm/fs/reiserfs/xattr.c | 82 ++++ 25-akpm/fs/reiserfs/xattr_acl.c | 552 +++++++++++++++++++++++++++++++++ 25-akpm/fs/reiserfs/xattr_user.c | 10 25-akpm/include/linux/reiserfs_acl.h | 91 +++++ 25-akpm/include/linux/reiserfs_fs_i.h | 3 25-akpm/include/linux/reiserfs_fs_sb.h | 4 25-akpm/include/linux/reiserfs_xattr.h | 7 13 files changed, 836 insertions(+), 15 deletions(-) diff -puN fs/Kconfig~reiserfs-acl-02 fs/Kconfig --- 25/fs/Kconfig~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/Kconfig Fri Apr 23 14:36:49 2004 @@ -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 . + + 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 diff -puN fs/reiserfs/file.c~reiserfs-acl-02 fs/reiserfs/file.c --- 25/fs/reiserfs/file.c~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/file.c Fri Apr 23 14:36:49 2004 @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff -puN fs/reiserfs/inode.c~reiserfs-acl-02 fs/reiserfs/inode.c --- 25/fs/reiserfs/inode.c~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/inode.c Fri Apr 23 14:36:49 2004 @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -976,6 +977,8 @@ static void init_inode (struct inode * i 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_ 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_ 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 *dent } + 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 ; diff -puN fs/reiserfs/Makefile~reiserfs-acl-02 fs/reiserfs/Makefile --- 25/fs/reiserfs/Makefile~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/Makefile Fri Apr 23 14:36:49 2004 @@ -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, diff -puN fs/reiserfs/namei.c~reiserfs-acl-02 fs/reiserfs/namei.c --- 25/fs/reiserfs/namei.c~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/namei.c Fri Apr 23 14:36:49 2004 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -579,6 +580,7 @@ static int reiserfs_create (struct inode 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 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 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 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 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 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 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 inod 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), diff -puN fs/reiserfs/super.c~reiserfs-acl-02 fs/reiserfs/super.c --- 25/fs/reiserfs/super.c~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/super.c Fri Apr 23 14:36:49 2004 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -433,6 +434,8 @@ static void init_once(void * foo, kmem_c 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 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 (struc {"noattrs", 0, 0, 0, 1< +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, +}; diff -puN fs/reiserfs/xattr.c~reiserfs-acl-02 fs/reiserfs/xattr.c --- 25/fs/reiserfs/xattr.c~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/xattr.c Fri Apr 23 14:36:49 2004 @@ -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 @@ -32,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -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 } 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 /* 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 @@ error: 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 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 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 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); } diff -puN fs/reiserfs/xattr_user.c~reiserfs-acl-02 fs/reiserfs/xattr_user.c --- 25/fs/reiserfs/xattr_user.c~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/fs/reiserfs/xattr_user.c Fri Apr 23 14:36:49 2004 @@ -6,6 +6,10 @@ #include #include +#ifdef CONFIG_REISERFS_FS_POSIX_ACL +# include +#endif + #define XATTR_USER_PREFIX "user." static int @@ -20,7 +24,7 @@ user_get (struct inode *inode, const cha 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 cha (!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 cha (!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; diff -puN /dev/null include/linux/reiserfs_acl.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/include/linux/reiserfs_acl.h Fri Apr 23 14:36:49 2004 @@ -0,0 +1,91 @@ +#include +#include +#include + +#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 diff -puN include/linux/reiserfs_fs_i.h~reiserfs-acl-02 include/linux/reiserfs_fs_i.h --- 25/include/linux/reiserfs_fs_i.h~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/include/linux/reiserfs_fs_i.h Fri Apr 23 14:36:49 2004 @@ -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; }; diff -puN include/linux/reiserfs_fs_sb.h~reiserfs-acl-02 include/linux/reiserfs_fs_sb.h --- 25/include/linux/reiserfs_fs_sb.h~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/include/linux/reiserfs_fs_sb.h Fri Apr 23 14:36:49 2004 @@ -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; diff -puN include/linux/reiserfs_xattr.h~reiserfs-acl-02 include/linux/reiserfs_xattr.h --- 25/include/linux/reiserfs_xattr.h~reiserfs-acl-02 Fri Apr 23 14:36:49 2004 +++ 25-akpm/include/linux/reiserfs_xattr.h Fri Apr 23 14:36:49 2004 @@ -42,6 +42,7 @@ int reiserfs_delete_xattrs (struct 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 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__ */ _