Add access control lists for tmpfs. There is more code in tmpfs itself than in the generic acl layer, but at least the acl handling complexities are hidden from the filesystem.
Signed-off-by: Andreas Gruenbacher <[EMAIL PROTECTED]> Index: linux-2.6.11-rc2-mm2/mm/shmem.c =================================================================== --- linux-2.6.11-rc2-mm2.orig/mm/shmem.c +++ linux-2.6.11-rc2-mm2/mm/shmem.c @@ -1617,6 +1617,8 @@ static int shmem_statfs(struct super_blo return 0; } +static void shmem_destroy_inode(struct inode *inode); + /* * File creation. Allocate an inode, and we're done.. */ @@ -1627,6 +1629,11 @@ shmem_mknod(struct inode *dir, struct de int error = -ENOSPC; if (inode) { + error = shmem_acl_init(inode, dir); + if (error) { + shmem_destroy_inode(inode); + return error; + } if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) @@ -1993,6 +2000,9 @@ static int shmem_fill_super(struct super sbinfo->free_inodes = inodes; } sb->s_xattr = shmem_xattr_handlers; +#ifdef CONFIG_TMPFS_POSIX_ACL + sb->s_flags |= MS_POSIXACL; +#endif #else sb->s_flags |= MS_NOUSER; #endif @@ -2037,6 +2047,7 @@ static void shmem_destroy_inode(struct i /* only struct inode is valid if it's an inline symlink */ mpol_free_shared_policy(&SHMEM_I(inode)->policy); } + shmem_acl_destroy_inode(inode); kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); } @@ -2047,6 +2058,10 @@ static void init_once(void *foo, kmem_ca if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { inode_init_once(&p->vfs_inode); +#ifdef CONFIG_TMPFS_POSIX_ACL + p->i_acl = NULL; + p->i_default_acl = NULL; +#endif } } @@ -2095,6 +2110,8 @@ static struct inode_operations shmem_ino .listxattr = generic_listxattr, .removexattr = generic_removexattr, #endif + .setattr = shmem_setattr, + .permission = shmem_permission, }; static struct inode_operations shmem_dir_inode_operations = { @@ -2114,6 +2131,8 @@ static struct inode_operations shmem_dir .listxattr = generic_listxattr, .removexattr = generic_removexattr, #endif + .setattr = shmem_setattr, + .permission = shmem_permission, #endif }; @@ -2124,6 +2143,8 @@ static struct inode_operations shmem_spe .listxattr = generic_listxattr, .removexattr = generic_removexattr, #endif + .setattr = shmem_setattr, + .permission = shmem_permission, }; static struct super_operations shmem_ops = { @@ -2182,6 +2203,10 @@ static struct xattr_handler shmem_xattr_ #ifdef CONFIG_TMPFS_XATTR static struct xattr_handler *shmem_xattr_handlers[] = { +#ifdef CONFIG_TMPFS_POSIX_ACL + &shmem_xattr_acl_access_handler, + &shmem_xattr_acl_default_handler, +#endif #ifdef CONFIG_TMPFS_SECURITY &shmem_xattr_security_handler, #endif Index: linux-2.6.11-rc2-mm2/include/linux/shmem_fs.h =================================================================== --- linux-2.6.11-rc2-mm2.orig/include/linux/shmem_fs.h +++ linux-2.6.11-rc2-mm2/include/linux/shmem_fs.h @@ -19,6 +19,10 @@ struct shmem_inode_info { swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */ struct list_head swaplist; /* chain of maybes on swap */ struct inode vfs_inode; +#ifdef CONFIG_TMPFS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif }; struct shmem_sb_info { @@ -34,4 +38,25 @@ static inline struct shmem_inode_info *S return container_of(inode, struct shmem_inode_info, vfs_inode); } +#ifdef CONFIG_TMPFS_POSIX_ACL +int shmem_setattr(struct dentry *, struct iattr *); +int shmem_permission(struct inode *, int, struct nameidata *); +int shmem_acl_init(struct inode *, struct inode *); +void shmem_acl_destroy_inode(struct inode *); + +extern struct xattr_handler shmem_xattr_acl_access_handler; +extern struct xattr_handler shmem_xattr_acl_default_handler; +#else +#define shmem_setattr NULL +#define shmem_permission NULL + +static inline int shmem_acl_init(struct inode *inode, struct inode *dir) +{ + return 0; +} +void shmem_acl_destroy_inode(struct inode *inode) +{ +} +#endif /* CONFIG_TMPFS_POSIX_ACL */ + #endif Index: linux-2.6.11-rc2-mm2/fs/Kconfig =================================================================== --- linux-2.6.11-rc2-mm2.orig/fs/Kconfig +++ linux-2.6.11-rc2-mm2/fs/Kconfig @@ -884,6 +884,19 @@ config TMPFS_XATTR If unsure, say N. +config TMPFS_POSIX_ACL + bool "tmpfs POSIX Access Control Lists" + depends on TMPFS_XATTR + select GENERIC_ACL + 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 TMPFS_SECURITY bool "tmpfs Security Labels" depends on TMPFS_XATTR Index: linux-2.6.11-rc2-mm2/mm/shmem_acl.c =================================================================== --- /dev/null +++ linux-2.6.11-rc2-mm2/mm/shmem_acl.c @@ -0,0 +1,178 @@ +/* + * mm/shmem_acl.c + * + * (C) 2005 Andreas Gruenbacher <[EMAIL PROTECTED]> + * + * This file is released under the GPL. + */ + +#include <linux/fs.h> +#include <linux/shmem_fs.h> +#include <linux/xattr.h> +#include <linux/generic_acl.h> + +static struct posix_acl * +shmem_get_acl(struct inode *inode, int type) +{ + struct posix_acl *acl = NULL; + + spin_lock(&inode->i_lock); + switch(type) { + case ACL_TYPE_ACCESS: + acl = posix_acl_dup(SHMEM_I(inode)->i_acl); + break; + + case ACL_TYPE_DEFAULT: + acl = posix_acl_dup(SHMEM_I(inode)->i_default_acl); + break; + } + spin_unlock(&inode->i_lock); + + return acl; +} + +static void +shmem_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + spin_lock(&inode->i_lock); + switch(type) { + case ACL_TYPE_ACCESS: + if (SHMEM_I(inode)->i_acl) + posix_acl_release(SHMEM_I(inode)->i_acl); + SHMEM_I(inode)->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + if (SHMEM_I(inode)->i_default_acl) + posix_acl_release(SHMEM_I(inode)->i_default_acl); + SHMEM_I(inode)->i_default_acl = posix_acl_dup(acl); + break; + } + spin_unlock(&inode->i_lock); +} + +struct generic_acl_operations shmem_acl_ops = { + .getacl = shmem_get_acl, + .setacl = shmem_set_acl, +}; + +static size_t +shmem_list_acl_access(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + return generic_acl_list(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, + list, list_size); +} + +static size_t +shmem_list_acl_default(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + return generic_acl_list(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, + list, list_size); +} + +static int +shmem_get_acl_access(struct inode *inode, const char *name, void *buffer, + size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return generic_acl_get(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, buffer, + size); +} + +static int +shmem_get_acl_default(struct inode *inode, const char *name, void *buffer, + size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return generic_acl_get(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, buffer, + size); +} + +static int +shmem_set_acl_access(struct inode *inode, const char *name, const void *value, + size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return generic_acl_set(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, value, + size); +} + +static int +shmem_set_acl_default(struct inode *inode, const char *name, const void *value, + size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return generic_acl_set(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, value, + size); +} + +struct xattr_handler shmem_xattr_acl_access_handler = { + .prefix = XATTR_NAME_ACL_ACCESS, + .list = shmem_list_acl_access, + .get = shmem_get_acl_access, + .set = shmem_set_acl_access, +}; + +struct xattr_handler shmem_xattr_acl_default_handler = { + .prefix = XATTR_NAME_ACL_DEFAULT, + .list = shmem_list_acl_default, + .get = shmem_get_acl_default, + .set = shmem_set_acl_default, +}; + +int +shmem_acl_init(struct inode *inode, struct inode *dir) +{ + return generic_acl_init(inode, dir, &shmem_acl_ops); +} + +void +shmem_acl_destroy_inode(struct inode *inode) +{ + if (SHMEM_I(inode)->i_acl) + posix_acl_release(SHMEM_I(inode)->i_acl); + if (SHMEM_I(inode)->i_default_acl) + posix_acl_release(SHMEM_I(inode)->i_default_acl); + SHMEM_I(inode)->i_acl = NULL; + SHMEM_I(inode)->i_default_acl = NULL; +} + +int +shmem_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, iattr); + if (error) + return error; + error = inode_setattr(inode, iattr); + if (!error && (iattr->ia_valid & ATTR_MODE)) + error = generic_acl_chmod(inode, &shmem_acl_ops); + return error; +} + +static int +shmem_check_acl(struct inode *inode, int mask) +{ + struct posix_acl *acl = shmem_get_acl(inode, ACL_TYPE_ACCESS); + + if (acl) { + int error = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return error; + } + return -EAGAIN; +} + +int +shmem_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, shmem_check_acl); +} Index: linux-2.6.11-rc2-mm2/mm/Makefile =================================================================== --- linux-2.6.11-rc2-mm2.orig/mm/Makefile +++ linux-2.6.11-rc2-mm2/mm/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_HUGETLBFS) += hugetlb.o obj-$(CONFIG_NUMA) += mempolicy.o obj-$(CONFIG_SHMEM) += shmem.o obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o +obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o -- Andreas Gruenbacher <[EMAIL PROTECTED]> SUSE Labs, SUSE LINUX PRODUCTS GMBH - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/