diff -urN linux-2.4.27-20040814-ea-0.8.71/Documentation/Configure.help linux-2.4.27-20040814-acl-0.8.71/Documentation/Configure.help --- linux-2.4.27-20040814-ea-0.8.71/Documentation/Configure.help Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/Documentation/Configure.help Sun Aug 15 23:53:23 2004 @@ -13923,6 +13923,20 @@ The module will be called isp16.o. If you want to compile it as a module, say M here and read . +Posix Access Control Lists +CONFIG_FS_POSIX_ACL + 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 plan to use Access Control Lists, you may also need the + getfacl and setfacl utilities, along with some additional patches + from the website. + + If you don't know what Access Control Lists are, say N. + iSeries Virtual I/O CD Support CONFIG_VIOCD If you are running Linux on an IBM iSeries system and you want to @@ -16708,6 +16722,8 @@ the kernel or by users (see the attr(5) manual page, or visit for details). + You need this for POSIX ACL support on ext2. + If unsure, say N. Ext2 extended attribute block sharing @@ -16726,6 +16742,17 @@ If unsure, say N. +Ext2 POSIX Access Control Lists +CONFIG_EXT2_FS_POSIX_ACL + POSIX Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. This option enables + POSIX ACLs on ext2. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If unsure, say N. + Ext3 journalling file system support (EXPERIMENTAL) CONFIG_EXT3_FS This is the journalling version of the Second extended file system @@ -16764,6 +16791,8 @@ the kernel or by users (see the attr(5) manual page, or visit for details). + You need this for POSIX ACL support on ext3. + If unsure, say N. Ext3 extended attribute block sharing @@ -16782,6 +16811,17 @@ If unsure, say N. +Ext3 POSIX Access Control Lists +CONFIG_EXT3_FS_POSIX_ACL + POSIX Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. This option enables + POSIX ACLs on ext3. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If unsure, say N. + Journal Block Device support (JBD for ext3) (EXPERIMENTAL) CONFIG_JBD This is a generic journalling layer for block devices. It is @@ -17687,6 +17727,16 @@ functional, and may cause serious problems. If unsure, say N. + +POSIX ACL support +CONFIG_XFS_POSIX_ACL + 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. Tracing support (EXPERIMENTAL) CONFIG_XFS_TRACE diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/alpha/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/alpha/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/alpha/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/alpha/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/arm/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/arm/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/arm/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/arm/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/i386/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/i386/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/i386/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/i386/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/ia64/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/ia64/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/ia64/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/ia64/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/m68k/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/m68k/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/m68k/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/m68k/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/mips/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/mips/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/mips/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/mips/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/mips64/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/mips64/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/mips64/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/mips64/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/ppc/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/ppc/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/ppc/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/ppc/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/ppc64/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/ppc64/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/ppc64/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/ppc64/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/s390/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/s390/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/s390/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/s390/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/s390x/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/s390x/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/s390x/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/s390x/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/sparc/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/sparc/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/sparc/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/sparc/defconfig Sun Aug 15 23:53:23 2004 @@ -1,6 +1,9 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT3_FS_XATTR_SHARING is not set # CONFIG_EXT3_FS_XATTR_USER is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/arch/sparc64/defconfig linux-2.4.27-20040814-acl-0.8.71/arch/sparc64/defconfig --- linux-2.4.27-20040814-ea-0.8.71/arch/sparc64/defconfig Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/arch/sparc64/defconfig Sun Aug 15 23:53:23 2004 @@ -1,4 +1,7 @@ # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # Automatically generated make config: don't edit # # CONFIG_EXT3_FS_XATTR is not set diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/Config.in linux-2.4.27-20040814-acl-0.8.71/fs/Config.in --- linux-2.4.27-20040814-ea-0.8.71/fs/Config.in Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/Config.in Sun Aug 15 23:53:23 2004 @@ -36,6 +36,8 @@ CONFIG_EXT3_FS_XATTR_USER $CONFIG_EXT3_FS_XATTR dep_bool ' Ext3 trusted extended attributes' \ CONFIG_EXT3_FS_XATTR_TRUSTED $CONFIG_EXT3_FS_XATTR +dep_bool ' Ext3 POSIX Access Control Lists' \ + CONFIG_EXT3_FS_POSIX_ACL $CONFIG_EXT3_FS_XATTR # CONFIG_JBD could be its own option (even modular), but until there are # other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS # dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS @@ -106,6 +108,8 @@ CONFIG_EXT2_FS_XATTR_USER $CONFIG_EXT2_FS_XATTR dep_bool ' Ext2 trusted extended attributes' \ CONFIG_EXT2_FS_XATTR_TRUSTED $CONFIG_EXT2_FS_XATTR +dep_bool ' Ext2 POSIX Access Control Lists' \ + CONFIG_EXT2_FS_POSIX_ACL $CONFIG_EXT2_FS_XATTR tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS @@ -117,6 +121,7 @@ tristate 'XFS filesystem support' CONFIG_XFS_FS dep_mbool ' Quota support' CONFIG_XFS_QUOTA $CONFIG_XFS_FS +dep_mbool ' POSIX ACL support' CONFIG_XFS_POSIX_ACL $CONFIG_XFS_FS dep_mbool ' Realtime support (EXPERIMENTAL)' CONFIG_XFS_RT $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' Tracing support (EXPERIMENTAL)' CONFIG_XFS_TRACE $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' Debugging support (EXPERIMENTAL)' CONFIG_XFS_DEBUG $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL @@ -188,6 +193,15 @@ # Meta block cache for Extended Attributes (ext2/ext3) #tristate 'Meta block cache' CONFIG_FS_MBCACHE define_tristate CONFIG_FS_MBCACHE y + +# POSIX ACL helper functions + +if [ "$CONFIG_EXT2_FS_POSIX_ACL" = "y" -o \ + "$CONFIG_EXT3_FS_POSIX_ACL" = "y" ]; then + define_bool CONFIG_FS_POSIX_ACL y +else + bool "POSIX ACL helper functions" CONFIG_FS_POSIX_ACL +fi mainmenu_option next_comment comment 'Partition Types' diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/Makefile linux-2.4.27-20040814-acl-0.8.71/fs/Makefile --- linux-2.4.27-20040814-ea-0.8.71/fs/Makefile Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/Makefile Sun Aug 15 23:53:23 2004 @@ -77,8 +77,9 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o -export-objs += mbcache.o +export-objs += mbcache.o posix_acl.o xattr_acl.o obj-$(CONFIG_FS_MBCACHE) += mbcache.o +obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o # persistent filesystems obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/Makefile linux-2.4.27-20040814-acl-0.8.71/fs/ext2/Makefile --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/Makefile Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/Makefile Sun Aug 15 23:53:23 2004 @@ -17,5 +17,6 @@ obj-$(CONFIG_EXT2_FS_XATTR) += xattr.o obj-$(CONFIG_EXT2_FS_XATTR_USER) += xattr_user.o obj-$(CONFIG_EXT2_FS_XATTR_TRUSTED) += xattr_trusted.o +obj-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o include $(TOPDIR)/Rules.make diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/acl.c linux-2.4.27-20040814-acl-0.8.71/fs/ext2/acl.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/acl.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/acl.c Sun Aug 15 23:53:23 2004 @@ -0,0 +1,575 @@ +/* + * linux/fs/ext2/acl.c + * + * Copyright (C) 2001-2003 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +ext2_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(ext2_acl_header)) + return ERR_PTR(-EINVAL); + if (((ext2_acl_header *)value)->a_version != + cpu_to_le32(EXT2_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(ext2_acl_header); + count = ext2_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + ext2_acl_entry *entry = + (ext2_acl_entry *)value; + if ((char *)value + sizeof(ext2_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(ext2_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(ext2_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 * +ext2_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + ext2_acl_header *ext_acl; + char *e; + size_t n; + + *size = ext2_acl_size(acl->a_count); + ext_acl = (ext2_acl_header *)kmalloc(sizeof(ext2_acl_header) + + acl->a_count * sizeof(ext2_acl_entry), GFP_KERNEL); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION); + e = (char *)ext_acl + sizeof(ext2_acl_header); + for (n=0; n < acl->a_count; n++) { + ext2_acl_entry *entry = (ext2_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(ext2_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(ext2_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)ext_acl; + +fail: + kfree(ext_acl); + return ERR_PTR(-EINVAL); +} + +/* + * inode->i_sem: don't care + * BKL: held + */ +static struct posix_acl * +ext2_get_acl(struct inode *inode, int type) +{ + const size_t max_size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES); + struct ext2_inode_info *ei = EXT2_I(inode); + int name_index; + char *value; + struct posix_acl *acl; + int retval; + + if (!IS_POSIXACL(inode)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT2_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_acl); + name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT2_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_default_acl); + name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + + default: + return ERR_PTR(-EINVAL); + } + value = kmalloc(max_size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + + retval = ext2_xattr_get(inode, name_index, "", value, max_size); + acl = ERR_PTR(retval); + if (retval >= 0) + acl = ext2_acl_from_disk(value, retval); + else if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + kfree(value); + + if (!IS_ERR(acl)) { + switch(type) { + case ACL_TYPE_ACCESS: + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return acl; +} + +/* + * inode->i_sem: down, or inode is just being initialized + * BKL: held + */ +static int +ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + struct ext2_inode_info *ei = EXT2_I(inode); + int name_index; + void *value = NULL; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (!IS_POSIXACL(inode)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + name_index = EXT2_XATTR_INDEX_POSIX_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; + mark_inode_dirty(inode); + if (error == 0) + acl = NULL; + } + } + break; + + case ACL_TYPE_DEFAULT: + name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + if (acl) { + if (acl->a_count > EXT2_ACL_MAX_ENTRIES) + return -EINVAL; + value = ext2_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + } + + error = ext2_xattr_set(inode, name_index, "", value, size, 0); + + if (value) + kfree(value); + if (!error) { + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT2_ACL_NOT_CACHED) + posix_acl_release(ei->i_acl); + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT2_ACL_NOT_CACHED) + posix_acl_release(ei->i_default_acl); + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return error; +} + +/* + * Inode operation permission(). + * + * inode->i_sem: don't care + * BKL: held + */ +int +ext2_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (IS_POSIXACL(inode)) { + struct ext2_inode_info *ei = EXT2_I(inode); + + /* The access ACL cannot grant access if the group class + permission bits don't contain all requested permissions. */ + if (((mode >> 3) & mask & S_IRWXO) != mask) + goto check_groups; + if (ei->i_acl == EXT2_ACL_NOT_CACHED) { + struct posix_acl *acl = + ext2_get_acl(inode, ACL_TYPE_ACCESS); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + if (ei->i_acl == EXT2_ACL_NOT_CACHED) + return -EIO; + } + if (ei->i_acl) { + int error = posix_acl_permission(inode, ei->i_acl,mask); + if (error == -EACCES) + goto check_capabilities; + return error; + } else + goto check_groups; + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +/* + * Initialize the ACLs of a new inode. Called from ext2_new_inode. + * + * dir->i_sem: don't care + * inode->i_sem: up (access to inode is still exclusive) + * BKL: held + */ +int +ext2_init_acl(struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (IS_POSIXACL(dir)) { + acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) { + inode->i_mode &= ~current->fs->umask; + mark_inode_dirty(inode); + } + } + if (IS_POSIXACL(inode) && acl) { + struct posix_acl *clone; + mode_t mode; + + if (S_ISDIR(inode->i_mode)) { + error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (error) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + error = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + error = posix_acl_create_masq(clone, &mode); + if (error >= 0) { + inode->i_mode = mode; + mark_inode_dirty(inode); + if (error > 0) { + /* This is an extended ACL */ + error = ext2_set_acl(inode, + ACL_TYPE_ACCESS, clone); + } + } + posix_acl_release(clone); + } +cleanup: + posix_acl_release(acl); + return error; +} + +/* + * Does chmod for an inode that may have an Access Control List. The + * inode->i_mode field must be updated to the desired value by the caller + * before calling this function. + * Returns 0 on success, or a negative error number. + * + * We change the ACL rather than storing some ACL entries in the file + * mode permission bits (which would be more efficient), because that + * would break once additional permissions (like ACL_APPEND, ACL_DELETE + * for directories) are added. There are no more bits available in the + * file mode. + * + * inode->i_sem: down + * BKL: held + */ +int +ext2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (!IS_POSIXACL(inode)) + return 0; + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = ext2_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; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) + error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return error; +} + +/* + * Extended attribut handlers + */ +static size_t +ext2_xattr_list_acl_access(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_ACCESS, size); + return size; +} + +static size_t +ext2_xattr_list_acl_default(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_DEFAULT, size); + return size; +} + +static int +ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + return -EOPNOTSUPP; + + acl = ext2_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; +} + +static int +ext2_xattr_get_acl_access(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +ext2_xattr_get_acl_default(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +ext2_xattr_set_acl(struct inode *inode, int type, const void *value, + size_t size) +{ + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + 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 = ext2_set_acl(inode, type, acl); + +release_and_out: + posix_acl_release(acl); + return error; +} + +static int +ext2_xattr_set_acl_access(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +ext2_xattr_set_acl_default(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +struct ext2_xattr_handler ext2_xattr_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + list: ext2_xattr_list_acl_access, + get: ext2_xattr_get_acl_access, + set: ext2_xattr_set_acl_access, +}; + +struct ext2_xattr_handler ext2_xattr_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + list: ext2_xattr_list_acl_default, + get: ext2_xattr_get_acl_default, + set: ext2_xattr_set_acl_default, +}; + +void +exit_ext2_acl(void) +{ + ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext2_xattr_acl_access_handler); + ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext2_xattr_acl_default_handler); +} + +int __init +init_ext2_acl(void) +{ + int error; + + error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext2_xattr_acl_access_handler); + if (error) + goto fail; + error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext2_xattr_acl_default_handler); + if (error) + goto fail; + return 0; + +fail: + exit_ext2_acl(); + return error; +} diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/file.c linux-2.4.27-20040814-acl-0.8.71/fs/ext2/file.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/file.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/file.c Sun Aug 15 23:53:23 2004 @@ -21,6 +21,7 @@ #include #include #include +#include #include /* @@ -56,4 +57,6 @@ getxattr: ext2_getxattr, listxattr: ext2_listxattr, removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/ialloc.c linux-2.4.27-20040814-acl-0.8.71/fs/ext2/ialloc.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/ialloc.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/ialloc.c Sun Aug 15 23:53:23 2004 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -313,7 +314,7 @@ return group; } -struct inode * ext2_new_inode (const struct inode * dir, int mode) +struct inode * ext2_new_inode (struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; @@ -404,13 +405,23 @@ unlock_super (sb); if(DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - return ERR_PTR(-EDQUOT); + err = -EDQUOT; + goto fail3; } + err = ext2_init_acl(inode, dir); + if (err) { + DQUOT_FREE_INODE(inode); + goto fail3; + } + ext2_debug ("allocating inode %lu\n", inode->i_ino); return inode; + +fail3: + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return ERR_PTR(err); fail2: desc = ext2_get_group_desc (sb, group, &bh2); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/inode.c linux-2.4.27-20040814-acl-0.8.71/fs/ext2/inode.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/inode.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/inode.c Sun Aug 15 23:53:23 2004 @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -1027,6 +1028,15 @@ #ifdef CONFIG_EXT2_FS_XATTR init_rwsem(&inode->u.ext2_i.xattr_sem); #endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + if (inode->u.ext2_i.i_file_acl) { + /* The filesystem is mounted with ACL support, and there + are extended attributes for this inode. However we do + not yet know whether there are actually any ACLs. */ + inode->u.ext2_i.i_acl = EXT2_ACL_NOT_CACHED; + inode->u.ext2_i.i_default_acl = EXT2_ACL_NOT_CACHED; + } +#endif return; bad_inode: @@ -1177,3 +1187,23 @@ { return ext2_update_inode (inode, 1); } + +int ext2_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; + inode_setattr(inode, iattr); + if (iattr->ia_valid & ATTR_MODE) { + if (!(iattr->ia_valid & ATTR_SIZE)) + down(&inode->i_sem); + error = ext2_acl_chmod(inode); + if (!(iattr->ia_valid & ATTR_SIZE)) + up(&inode->i_sem); + } + return error; +} + diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/namei.c linux-2.4.27-20040814-acl-0.8.71/fs/ext2/namei.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/namei.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/namei.c Sun Aug 15 23:53:23 2004 @@ -32,6 +32,7 @@ #include #include #include +#include #include /* @@ -112,7 +113,10 @@ struct inode * inode = ext2_new_inode (dir, mode); int err = PTR_ERR(inode); if (!IS_ERR(inode)) { - init_special_inode(inode, mode, rdev); + init_special_inode(inode, inode->i_mode, rdev); +#ifdef CONFIG_EXT2_FS_POSIX_ACL + inode->i_op = &ext2_special_inode_operations; +#endif mark_inode_dirty(inode); err = ext2_add_nondir(dentry, inode); } @@ -350,6 +354,8 @@ getxattr: ext2_getxattr, listxattr: ext2_listxattr, removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; struct inode_operations ext2_special_inode_operations = { @@ -357,4 +363,6 @@ getxattr: ext2_getxattr, listxattr: ext2_listxattr, removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext2/super.c linux-2.4.27-20040814-acl-0.8.71/fs/ext2/super.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext2/super.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext2/super.c Sun Aug 15 23:53:23 2004 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -149,6 +150,26 @@ return; } +#ifdef CONFIG_EXT2_FS_POSIX_ACL + +static void ext2_clear_inode(struct inode *inode) +{ + if (inode->u.ext2_i.i_acl && + inode->u.ext2_i.i_acl != EXT2_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext2_i.i_acl); + inode->u.ext2_i.i_acl = EXT2_ACL_NOT_CACHED; + } + if (inode->u.ext2_i.i_default_acl && + inode->u.ext2_i.i_default_acl != EXT2_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext2_i.i_default_acl); + inode->u.ext2_i.i_default_acl = EXT2_ACL_NOT_CACHED; + } +} + +#else +# define ext2_clear_inode NULL +#endif + static struct super_operations ext2_sops = { read_inode: ext2_read_inode, write_inode: ext2_write_inode, @@ -158,6 +179,7 @@ write_super: ext2_write_super, statfs: ext2_statfs, remount_fs: ext2_remount, + clear_inode: ext2_clear_inode, }; /* @@ -165,7 +187,8 @@ */ static int parse_options (char * options, unsigned long * sb_block, unsigned short *resuid, unsigned short * resgid, - unsigned long * mount_options) + unsigned long * mount_options, + unsigned long * mount_flags) { char * this_char; char * value; @@ -184,6 +207,13 @@ clear_opt (*mount_options, XATTR_USER); else #endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + if (!strcmp(this_char, "acl")) + *mount_flags |= MS_POSIXACL; + else if (!strcmp(this_char, "noacl")) + *mount_flags &= ~MS_POSIXACL; + else +#endif if (!strcmp (this_char, "bsddf")) clear_opt (*mount_options, MINIX_DF); else if (!strcmp (this_char, "nouid32")) { @@ -458,8 +488,11 @@ #ifdef CONFIG_EXT2_FS_XATTR_USER /* set_opt (sb->u.ext2_sb.s_mount_opt, XATTR_USER); */ #endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + /* sb->s_flags |= MS_POSIXACL; */ +#endif if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, - &sb->u.ext2_sb.s_mount_opt)) { + &sb->u.ext2_sb.s_mount_opt, &sb->s_flags)) { return NULL; } @@ -751,17 +784,19 @@ struct ext2_super_block * es; unsigned short resuid = sb->u.ext2_sb.s_resuid; unsigned short resgid = sb->u.ext2_sb.s_resgid; - unsigned long new_mount_opt; + unsigned long new_mount_opt, new_mount_flags; unsigned long tmp; /* * Allow the "check" option to be passed as a remount option. */ new_mount_opt = sb->u.ext2_sb.s_mount_opt; + new_mount_flags = sb->s_flags; if (!parse_options (data, &tmp, &resuid, &resgid, - &new_mount_opt)) + &new_mount_opt, &new_mount_flags)) return -EINVAL; + sb->s_flags = new_mount_flags; sb->u.ext2_sb.s_mount_opt = new_mount_opt; sb->u.ext2_sb.s_resuid = resuid; sb->u.ext2_sb.s_resgid = resgid; @@ -861,10 +896,15 @@ error = init_ext2_xattr_trusted(); if (error) goto fail2; + error = init_ext2_acl(); + if (error) + goto fail3; error = register_filesystem(&ext2_fs_type); if (!error) return 0; + exit_ext2_acl(); +fail3: exit_ext2_xattr_trusted(); fail2: exit_ext2_xattr_user(); @@ -876,6 +916,7 @@ static void __exit exit_ext2_fs(void) { unregister_filesystem(&ext2_fs_type); + exit_ext2_acl(); exit_ext2_xattr_trusted(); exit_ext2_xattr_user(); exit_ext2_xattr(); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/Makefile linux-2.4.27-20040814-acl-0.8.71/fs/ext3/Makefile --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/Makefile Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/Makefile Sun Aug 15 23:53:23 2004 @@ -17,5 +17,6 @@ obj-$(CONFIG_EXT3_FS_XATTR) += xattr.o obj-$(CONFIG_EXT3_FS_XATTR_USER) += xattr_user.o obj-$(CONFIG_EXT3_FS_XATTR_TRUSTED) += xattr_trusted.o +obj-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o include $(TOPDIR)/Rules.make diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/acl.c linux-2.4.27-20040814-acl-0.8.71/fs/ext3/acl.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/acl.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/acl.c Sun Aug 15 23:53:23 2004 @@ -0,0 +1,585 @@ +/* + * linux/fs/ext3/acl.c + * + * Copyright (C) 2001-2003 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +ext3_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + size_t n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(ext3_acl_header)) + return ERR_PTR(-EINVAL); + if (((ext3_acl_header *)value)->a_version != + cpu_to_le32(EXT3_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(ext3_acl_header); + count = ext3_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + ext3_acl_entry *entry = + (ext3_acl_entry *)value; + if ((char *)value + sizeof(ext3_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(ext3_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(ext3_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 * +ext3_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + ext3_acl_header *ext_acl; + char *e; + size_t n; + + *size = ext3_acl_size(acl->a_count); + ext_acl = (ext3_acl_header *)kmalloc(sizeof(ext3_acl_header) + + acl->a_count * sizeof(ext3_acl_entry), GFP_KERNEL); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION); + e = (char *)ext_acl + sizeof(ext3_acl_header); + for (n=0; n < acl->a_count; n++) { + ext3_acl_entry *entry = (ext3_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(ext3_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(ext3_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: don't care + * BKL: held + */ +struct posix_acl * +ext3_get_acl(struct inode *inode, int type) +{ + const size_t max_size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES); + struct ext3_inode_info *ei = EXT3_I(inode); + int name_index; + char *value; + struct posix_acl *acl; + int retval; + + if (!IS_POSIXACL(inode)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT3_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_acl); + name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT3_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_default_acl); + name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + + default: + return ERR_PTR(-EINVAL); + } + value = kmalloc(max_size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + + retval = ext3_xattr_get(inode, name_index, "", value, max_size); + acl = ERR_PTR(retval); + if (retval > 0) + acl = ext3_acl_from_disk(value, retval); + else if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + kfree(value); + + if (!IS_ERR(acl)) { + switch(type) { + case ACL_TYPE_ACCESS: + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return acl; +} + +/* + * inode->i_sem: down, or inode is just being initialized + * BKL: held + */ +static int +ext3_do_set_acl(handle_t *handle, struct inode *inode, int type, + struct posix_acl *acl) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + int name_index; + void *value = NULL; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -ENODATA; + + switch(type) { + case ACL_TYPE_ACCESS: + name_index = EXT3_XATTR_INDEX_POSIX_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; + ext3_mark_inode_dirty(handle, inode); + if (error == 0) + acl = NULL; + } + } + break; + + case ACL_TYPE_DEFAULT: + name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + if (acl) { + if (acl->a_count > EXT3_ACL_MAX_ENTRIES) + return -EINVAL; + value = ext3_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + } + + error = ext3_xattr_set_handle(handle, inode, name_index, "", + value, size, 0); + + if (value) + kfree(value); + if (!error) { + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT3_ACL_NOT_CACHED) + posix_acl_release(ei->i_acl); + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT3_ACL_NOT_CACHED) + posix_acl_release(ei->i_default_acl); + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return error; +} + +/* + * Inode operation permission(). + * + * inode->i_sem: don't care + * BKL: held + */ +int +ext3_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (IS_POSIXACL(inode)) { + struct ext3_inode_info *ei = EXT3_I(inode); + + /* The access ACL cannot grant access if the group class + permission bits don't contain all requested permissions. */ + if (((mode >> 3) & mask & S_IRWXO) != mask) + goto check_groups; + if (ei->i_acl == EXT3_ACL_NOT_CACHED) { + struct posix_acl *acl = + ext3_get_acl(inode, ACL_TYPE_ACCESS); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + if (ei->i_acl == EXT3_ACL_NOT_CACHED) + return -EIO; + } + if (ei->i_acl) { + int error = posix_acl_permission(inode, ei->i_acl,mask); + if (error == -EACCES) + goto check_capabilities; + return error; + } else + goto check_groups; + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +/* + * Initialize the ACLs of a new inode. Called from ext3_new_inode. + * + * dir->i_sem: don't care + * inode->i_sem: up (access to inode is still exclusive) + * BKL: held + */ +int +ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (IS_POSIXACL(dir)) { + acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) { + inode->i_mode &= ~current->fs->umask; + ext3_mark_inode_dirty(handle, inode); + } + } + if (IS_POSIXACL(inode) && acl) { + struct posix_acl *clone; + mode_t mode; + + if (S_ISDIR(inode->i_mode)) { + error = ext3_do_set_acl(handle, inode, + ACL_TYPE_DEFAULT, acl); + if (error) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + error = -ENOMEM; + if (!clone) + goto cleanup; + + mode = inode->i_mode; + error = posix_acl_create_masq(clone, &mode); + if (error >= 0) { + inode->i_mode = mode; + ext3_mark_inode_dirty(handle, inode); + if (error > 0) { + /* This is an extended ACL */ + error = ext3_do_set_acl(handle, inode, + ACL_TYPE_ACCESS, clone); + } + } + posix_acl_release(clone); + } +cleanup: + posix_acl_release(acl); + return error; +} + +/* + * Does chmod for an inode that may have an Access Control List. The + * inode->i_mode field must be updated to the desired value by the caller + * before calling this function. + * Returns 0 on success, or a negative error number. + * + * We change the ACL rather than storing some ACL entries in the file + * mode permission bits (which would be more efficient), because that + * would break once additional permissions (like ACL_APPEND, ACL_DELETE + * for directories) are added. There are no more bits available in the + * file mode. + * + * inode->i_sem: down + * BKL: held + */ +int +ext3_acl_chmod(handle_t *handle, struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (!IS_POSIXACL(inode)) + return 0; + acl = ext3_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; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) + error = ext3_do_set_acl(handle, inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return error; +} + +/* + * Extended attribut handlers + */ +static size_t +ext3_xattr_list_acl_access(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_ACCESS, size); + return size; +} + +static size_t +ext3_xattr_list_acl_default(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_DEFAULT, size); + return size; +} + +static int +ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + return -EOPNOTSUPP; + + acl = ext3_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; +} + +static int +ext3_xattr_get_acl_access(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +ext3_xattr_get_acl_default(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +ext3_xattr_set_acl(struct inode *inode, int type, const void *value, + size_t size) +{ + handle_t *handle; + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + 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; + + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + error = ext3_do_set_acl(handle, inode, type, acl); + ext3_journal_stop(handle, inode); + +release_and_out: + posix_acl_release(acl); + return error; +} + +static int +ext3_xattr_set_acl_access(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +ext3_xattr_set_acl_default(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +struct ext3_xattr_handler ext3_xattr_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + list: ext3_xattr_list_acl_access, + get: ext3_xattr_get_acl_access, + set: ext3_xattr_set_acl_access, +}; + +struct ext3_xattr_handler ext3_xattr_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + list: ext3_xattr_list_acl_default, + get: ext3_xattr_get_acl_default, + set: ext3_xattr_set_acl_default, +}; + +void +exit_ext3_acl(void) +{ + ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext3_xattr_acl_access_handler); + ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); +} + +int __init +init_ext3_acl(void) +{ + int error; + + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext3_xattr_acl_access_handler); + if (error) + goto fail; + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); + if (error) + goto fail; + return 0; + +fail: + exit_ext3_acl(); + return error; +} diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/file.c linux-2.4.27-20040814-acl-0.8.71/fs/ext3/file.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/file.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/file.c Sun Aug 15 23:53:23 2004 @@ -21,9 +21,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -129,5 +131,6 @@ getxattr: ext3_getxattr, /* BKL held */ listxattr: ext3_listxattr, /* BKL held */ removexattr: ext3_removexattr, /* BKL held */ + permission: ext3_permission, /* BKL held */ }; diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/ialloc.c linux-2.4.27-20040814-acl-0.8.71/fs/ext3/ialloc.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/ialloc.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/ialloc.c Sun Aug 15 23:53:23 2004 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -298,8 +299,7 @@ * For other inodes, search forward from the parent directory's block * group to find a free inode. */ -struct inode * ext3_new_inode (handle_t *handle, - const struct inode * dir, int mode) +struct inode * ext3_new_inode (handle_t *handle, struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; @@ -519,10 +519,13 @@ unlock_super (sb); if(DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - return ERR_PTR(-EDQUOT); + err = -EDQUOT; + goto fail2; + } + err = ext3_init_acl(handle, inode, dir); + if (err) { + DQUOT_FREE_INODE(inode); + goto fail2; } ext3_debug ("allocating inode %lu\n", inode->i_ino); return inode; @@ -531,6 +534,12 @@ ext3_std_error(sb, err); out: unlock_super(sb); + iput(inode); + return ERR_PTR(err); + +fail2: + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; iput(inode); return ERR_PTR(err); } diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/inode.c linux-2.4.27-20040814-acl-0.8.71/fs/ext3/inode.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/inode.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/inode.c Sun Aug 15 23:53:23 2004 @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -2194,6 +2196,15 @@ #ifdef CONFIG_EXT3_FS_XATTR init_rwsem(&inode->u.ext3_i.xattr_sem); #endif +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (inode->u.ext3_i.i_file_acl) { + /* The filesystem is mounted with ACL support, and there + are extended attributes for this inode. However we do + not yet know whether there are actually any ACLs. */ + inode->u.ext3_i.i_acl = EXT3_ACL_NOT_CACHED; + inode->u.ext3_i.i_default_acl = EXT3_ACL_NOT_CACHED; + } +#endif return; bad_inode: @@ -2378,10 +2389,6 @@ * be freed, so we have a strong guarantee that no future commit will * leave these blocks visible to the user.) * - * This is only needed for regular files. rmdir() has its own path, and - * we can never truncate a direcory except on final unlink (at which - * point i_nlink is zero so recovery is easy.) - * * Called with the BKL. */ @@ -2402,7 +2409,8 @@ return error; } - if (attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { + if (S_ISREG(inode->i_mode) && + attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { handle_t *handle; handle = ext3_journal_start(inode, 3); @@ -2424,8 +2432,26 @@ /* If inode_setattr's call to ext3_truncate failed to get a * transaction handle at all, we need to clean up the in-core * orphan list manually. */ - if (inode->i_nlink) + if (S_ISREG(inode->i_mode) && inode->i_nlink) ext3_orphan_del(NULL, inode); + +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (!rc && (ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) { + handle_t *handle; + + if (!(ia_valid & ATTR_SIZE)) + down(&inode->i_sem); + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) + error = PTR_ERR(handle); + else { + rc = ext3_acl_chmod(handle, inode); + ext3_journal_stop(handle, inode); + } + if (!(ia_valid & ATTR_SIZE)) + up(&inode->i_sem); + } +#endif err_out: ext3_std_error(inode->i_sb, error); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/namei.c linux-2.4.27-20040814-acl-0.8.71/fs/ext3/namei.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/namei.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/namei.c Sun Aug 15 23:53:23 2004 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -491,7 +492,10 @@ inode = ext3_new_inode (handle, dir, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { - init_special_inode(inode, mode, rdev); + init_special_inode(inode, inode->i_mode, rdev); +#ifdef CONFIG_EXT3_FS_XATTR + inode->i_op = &ext3_special_inode_operations; +#endif err = ext3_add_nondir(handle, dentry, inode); } ext3_journal_stop(handle, dir); @@ -1118,16 +1122,20 @@ rmdir: ext3_rmdir, /* BKL held */ mknod: ext3_mknod, /* BKL held */ rename: ext3_rename, /* BKL held */ + setattr: ext3_setattr, /* BKL held */ setxattr: ext3_setxattr, /* BKL held */ getxattr: ext3_getxattr, /* BKL held */ listxattr: ext3_listxattr, /* BKL held */ removexattr: ext3_removexattr, /* BKL held */ + permission: ext3_permission, /* BKL held */ }; struct inode_operations ext3_special_inode_operations = { + setattr: ext3_setattr, /* BKL held */ setxattr: ext3_setxattr, /* BKL held */ getxattr: ext3_getxattr, /* BKL held */ listxattr: ext3_listxattr, /* BKL held */ removexattr: ext3_removexattr, /* BKL held */ + permission: ext3_permission, /* BKL held */ }; diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/ext3/super.c linux-2.4.27-20040814-acl-0.8.71/fs/ext3/super.c --- linux-2.4.27-20040814-ea-0.8.71/fs/ext3/super.c Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/ext3/super.c Sun Aug 15 23:53:23 2004 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -452,6 +453,26 @@ static struct dquot_operations ext3_qops; +#ifdef CONFIG_EXT3_FS_POSIX_ACL + +static void ext3_clear_inode(struct inode *inode) +{ + if (inode->u.ext3_i.i_acl && + inode->u.ext3_i.i_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext3_i.i_acl); + inode->u.ext3_i.i_acl = EXT3_ACL_NOT_CACHED; + } + if (inode->u.ext3_i.i_default_acl && + inode->u.ext3_i.i_default_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext3_i.i_default_acl); + inode->u.ext3_i.i_default_acl = EXT3_ACL_NOT_CACHED; + } +} + +#else +# define ext3_clear_inode NULL +#endif + static struct super_operations ext3_sops = { read_inode: ext3_read_inode, /* BKL held */ write_inode: ext3_write_inode, /* BKL not held. Don't need */ @@ -465,6 +486,7 @@ unlockfs: ext3_unlockfs, /* BKL not held. We take it */ statfs: ext3_statfs, /* BKL held */ remount_fs: ext3_remount, /* BKL held */ + clear_inode: ext3_clear_inode, /* BKL not needed. */ }; static int want_value(char *value, char *option) @@ -502,6 +524,7 @@ */ static int parse_options (char * options, unsigned long * sb_block, struct ext3_sb_info *sbi, + unsigned long *mount_flags, unsigned long * inum, int is_remount) { @@ -526,6 +549,13 @@ clear_opt (*mount_options, XATTR_USER); else #endif +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (!strcmp(this_char, "acl")) + *mount_flags |= MS_POSIXACL; + else if (!strcmp(this_char, "noacl")) + *mount_flags &= ~MS_POSIXACL; + else +#endif if (!strcmp (this_char, "bsddf")) clear_opt (*mount_options, MINIX_DF); else if (!strcmp (this_char, "nouid32")) { @@ -966,8 +996,12 @@ #ifdef CONFIG_EXT3_FS_XATTR_USER /* set_opt(sbi->s_mount_opt, XATTR_USER); */ #endif +#ifdef CONFIG_EXT3_FS_POSIX_ACL + /* sb->s_flags |= MS_POSIXACL; */ +#endif - if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) { + if (!parse_options ((char *) data, &sb_block, sbi, &sb->s_flags, + &journal_inum, 0)) { sb->s_dev = 0; goto out_fail; } @@ -1678,19 +1712,21 @@ { struct ext3_super_block * es; struct ext3_sb_info *sbi = EXT3_SB(sb); - unsigned long tmp; + unsigned long mount_flags = sb->s_flags, tmp; clear_ro_after(sb); /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, &tmp, sbi, &tmp, 1)) + if (!parse_options(data, &tmp, sbi, &mount_flags, &tmp, 1)) return -EINVAL; if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) ext3_abort(sb, __FUNCTION__, "Abort forced by user"); + sb->s_flags = mount_flags; + es = sbi->s_es; ext3_init_journal_params(sbi, sbi->s_journal); @@ -1863,10 +1899,15 @@ error = init_ext3_xattr_trusted(); if (error) goto fail2; + error = init_ext3_acl(); + if (error) + goto fail3; error = register_filesystem(&ext3_fs_type); if (!error) return 0; + exit_ext3_acl(); +fail3: exit_ext3_xattr_trusted(); fail2: exit_ext3_xattr_user(); @@ -1877,6 +1918,7 @@ static void __exit exit_ext3_fs(void) { + exit_ext3_acl(); exit_ext3_xattr_trusted(); exit_ext3_xattr_user(); exit_ext3_xattr(); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/namei.c linux-2.4.27-20040814-acl-0.8.71/fs/namei.c --- linux-2.4.27-20040814-ea-0.8.71/fs/namei.c Wed Jul 14 19:01:06 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/namei.c Sun Aug 15 23:53:23 2004 @@ -1048,8 +1048,9 @@ /* Negative dentry, just create the file */ if (!dentry->d_inode) { - error = vfs_create(dir->d_inode, dentry, - mode & ~current->fs->umask); + if (!IS_POSIXACL(dir->d_inode)) + mode &= ~current->fs->umask; + error = vfs_create(dir->d_inode, dentry, mode); up(&dir->d_inode->i_sem); dput(nd->dentry); nd->dentry = dentry; @@ -1280,7 +1281,8 @@ dentry = lookup_create(&nd, 0); error = PTR_ERR(dentry); - mode &= ~current->fs->umask; + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; if (!IS_ERR(dentry)) { switch (mode & S_IFMT) { case 0: case S_IFREG: @@ -1348,8 +1350,9 @@ dentry = lookup_create(&nd, 1); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { - error = vfs_mkdir(nd.dentry->d_inode, dentry, - mode & ~current->fs->umask); + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; + error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); dput(dentry); } up(&nd.dentry->d_inode->i_sem); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfs/dir.c linux-2.4.27-20040814-acl-0.8.71/fs/nfs/dir.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfs/dir.c Thu Jun 3 09:44:53 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfs/dir.c Sun Aug 15 23:53:23 2004 @@ -1096,7 +1096,8 @@ { int error = vfs_permission(inode, mask); - if (!NFS_PROTO(inode)->access) + if ((NFS_SERVER(inode)->flags & NFS_MOUNT_NOACL) || + !NFS_PROTO(inode)->access) goto out; if (error == -EROFS) diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfs/inode.c linux-2.4.27-20040814-acl-0.8.71/fs/nfs/inode.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfs/inode.c Sat Apr 17 23:05:49 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfs/inode.c Sun Aug 15 23:53:23 2004 @@ -568,6 +568,7 @@ { NFS_MOUNT_NOAC, ",noac", "" }, { NFS_MOUNT_NONLM, ",nolock", ",lock" }, { NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" }, + { NFS_MOUNT_NOACL, ",noacl", "" }, { 0, NULL, NULL } }; struct proc_nfs_info *nfs_infop; diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/export.c linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/export.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/export.c Tue Oct 7 13:31:41 2003 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/export.c Sun Aug 15 23:53:23 2004 @@ -643,6 +643,7 @@ { NFSEXP_NOHIDE, {"nohide", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, + { NFSEXP_NOACL, {"no_acl", ""}}, #ifdef MSNFS { NFSEXP_MSNFS, {"msnfs", ""}}, #endif diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/nfs3xdr.c linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/nfs3xdr.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/nfs3xdr.c Tue Apr 22 03:05:00 2003 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/nfs3xdr.c Sun Aug 15 23:53:23 2004 @@ -157,9 +157,19 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) { struct inode *inode = fhp->fh_dentry->d_inode; + mode_t mode = inode->i_mode; + if (IS_POSIXACL(inode) && EX_NOACL(fhp->fh_export)) { + struct posix_acl *acl = nfsd_get_posix_acl(fhp,ACL_TYPE_ACCESS); + + if (!IS_ERR(acl) && acl) { + posix_acl_masq_nfs_mode(acl, &mode); + posix_acl_release(acl); + } + } + *p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]); - *p++ = htonl((u32) inode->i_mode); + *p++ = htonl((u32) mode); *p++ = htonl((u32) inode->i_nlink); *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/nfssvc.c linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/nfssvc.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/nfssvc.c Tue Jan 7 15:50:41 2003 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/nfssvc.c Sun Aug 15 23:53:23 2004 @@ -155,6 +155,7 @@ nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; + struct fs_struct *fsp; int err; struct nfsd_list me; @@ -165,6 +166,19 @@ sprintf(current->comm, "nfsd"); current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + /* Make sure umask is 0. + * This is required by the new ACL code which does the umask + * munging below vfs_create() level. + */ + fsp = copy_fs_struct(current->fs); + if (fsp == NULL) { + printk("Unable to start nfsd thread: out of memory\n"); + goto out; + } + exit_fs(current); + current->fs = fsp; + fsp->umask = 0; + nfsdstats.th_cnt++; /* Let svc_process check client's authentication. */ rqstp->rq_auth = 1; @@ -246,6 +260,7 @@ list_del(&me.list); nfsdstats.th_cnt --; +out: /* Release the thread */ svc_exit_thread(rqstp); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/nfsxdr.c linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/nfsxdr.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/nfsxdr.c Fri Jan 16 11:26:19 2004 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/nfsxdr.c Sun Aug 15 23:53:23 2004 @@ -146,10 +146,20 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) { struct inode *inode = fhp->fh_dentry->d_inode; + mode_t mode = inode->i_mode; int type = (inode->i_mode & S_IFMT); + + if (IS_POSIXACL(inode) && EX_NOACL(fhp->fh_export)) { + struct posix_acl *acl = nfsd_get_posix_acl(fhp,ACL_TYPE_ACCESS); + + if (!IS_ERR(acl) && acl) { + posix_acl_masq_nfs_mode(acl, &mode); + posix_acl_release(acl); + } + } *p++ = htonl(nfs_ftypes[type >> 12]); - *p++ = htonl((u32) inode->i_mode); + *p++ = htonl((u32) mode); *p++ = htonl((u32) inode->i_nlink); *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/vfs.c linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/vfs.c --- linux-2.4.27-20040814-ea-0.8.71/fs/nfsd/vfs.c Tue Oct 7 13:31:41 2003 +++ linux-2.4.27-20040814-acl-0.8.71/fs/nfsd/vfs.c Sun Aug 15 23:53:23 2004 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1590,3 +1591,99 @@ nfsdstats.ra_size = cache_size; return 0; } + +#ifdef CONFIG_FS_POSIX_ACL +struct posix_acl * +nfsd_get_posix_acl(struct svc_fh *fhp, int type) +{ + struct inode *inode = fhp->fh_dentry->d_inode; + char *name; + void *value = NULL; + ssize_t size; + struct posix_acl *acl; + + if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->getxattr) + return ERR_PTR(-EOPNOTSUPP); + switch(type) { + case ACL_TYPE_ACCESS: + name = XATTR_NAME_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + name = XATTR_NAME_ACL_DEFAULT; + break; + default: + return ERR_PTR(-EOPNOTSUPP); + } + + lock_kernel(); /* goes away in 2.5 */ + size = inode->i_op->getxattr(fhp->fh_dentry, name, NULL, 0); + unlock_kernel(); /* goes away in 2.5 */ + + if (size < 0) { + acl = ERR_PTR(size); + goto getout; + } else if (size > 0) { + value = kmalloc(size, GFP_KERNEL); + if (!value) { + acl = ERR_PTR(-ENOMEM); + goto getout; + } + size = inode->i_op->getxattr(fhp->fh_dentry, name, value, size); + if (size < 0) { + acl = ERR_PTR(size); + goto getout; + } + } + acl = posix_acl_from_xattr(value, size); + +getout: + kfree(value); + return acl; +} + +int +nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl) +{ + struct inode *inode = fhp->fh_dentry->d_inode; + char *name; + void *value = NULL; + size_t size; + int error; + + if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->setxattr) + return -EOPNOTSUPP; + switch(type) { + case ACL_TYPE_ACCESS: + name = XATTR_NAME_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + name = XATTR_NAME_ACL_DEFAULT; + break; + default: + return -EOPNOTSUPP; + } + + if (acl && acl->a_count) { + size = xattr_acl_size(acl->a_count); + value = kmalloc(size, GFP_KERNEL); + if (!value) + return -ENOMEM; + size = posix_acl_to_xattr(acl, value, size); + if (size < 0) { + error = size; + goto getout; + } + } else + size = 0; + + if (!fhp->fh_locked) + fh_lock(fhp); /* unlocking is done automatically */ + lock_kernel(); /* goes away in 2.5 */ + error = inode->i_op->setxattr(fhp->fh_dentry, name, value, size, 0); + unlock_kernel(); /* goes away in 2.5 */ + +getout: + kfree(value); + return error; +} +#endif diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/posix_acl.c linux-2.4.27-20040814-acl-0.8.71/fs/posix_acl.c --- linux-2.4.27-20040814-ea-0.8.71/fs/posix_acl.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/fs/posix_acl.c Sun Aug 15 23:53:23 2004 @@ -0,0 +1,430 @@ +/* + * linux/fs/posix_acl.c + * + * Copyright (C) 2002 by Andreas Gruenbacher + * + * Fixes from William Schumacher incorporated on 15 March 2001. + * (Reported by Charles Bertsch, ). + */ + +/* + * This file contains generic functions for manipulating + * POSIX 1003.1e draft standard 17 ACLs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Andreas Gruenbacher "); +MODULE_DESCRIPTION("Generic Posix Access Control List (ACL) Manipulation"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) +MODULE_LICENSE("GPL"); +#endif + +EXPORT_SYMBOL(posix_acl_alloc); +EXPORT_SYMBOL(posix_acl_clone); +EXPORT_SYMBOL(posix_acl_valid); +EXPORT_SYMBOL(posix_acl_equiv_mode); +EXPORT_SYMBOL(posix_acl_from_mode); +EXPORT_SYMBOL(posix_acl_create_masq); +EXPORT_SYMBOL(posix_acl_chmod_masq); +EXPORT_SYMBOL(posix_acl_masq_nfs_mode); +EXPORT_SYMBOL(posix_acl_permission); + +/* + * Allocate a new ACL with the specified number of entries. + */ +struct posix_acl * +posix_acl_alloc(int count, int flags) +{ + const size_t size = sizeof(struct posix_acl) + + count * sizeof(struct posix_acl_entry); + struct posix_acl *acl = kmalloc(size, flags); + if (acl) { + atomic_set(&acl->a_refcount, 1); + acl->a_count = count; + } + return acl; +} + +/* + * Clone an ACL. + */ +struct posix_acl * +posix_acl_clone(const struct posix_acl *acl, int flags) +{ + struct posix_acl *clone = NULL; + + if (acl) { + int size = sizeof(struct posix_acl) + acl->a_count * + sizeof(struct posix_acl_entry); + clone = kmalloc(size, flags); + if (clone) { + memcpy(clone, acl, size); + atomic_set(&clone->a_refcount, 1); + } + } + return clone; +} + +/* + * Check if an acl is valid. Returns 0 if it is, or -E... otherwise. + */ +int +posix_acl_valid(const struct posix_acl *acl) +{ + const struct posix_acl_entry *pa, *pe; + int state = ACL_USER_OBJ; + unsigned int id = 0; /* keep gcc happy */ + int needs_mask = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) + return -EINVAL; + switch (pa->e_tag) { + case ACL_USER_OBJ: + if (state == ACL_USER_OBJ) { + id = 0; + state = ACL_USER; + break; + } + return -EINVAL; + + case ACL_USER: + if (state != ACL_USER) + return -EINVAL; + if (pa->e_id == ACL_UNDEFINED_ID || + pa->e_id < id) + return -EINVAL; + id = pa->e_id + 1; + needs_mask = 1; + break; + + case ACL_GROUP_OBJ: + if (state == ACL_USER) { + id = 0; + state = ACL_GROUP; + break; + } + return -EINVAL; + + case ACL_GROUP: + if (state != ACL_GROUP) + return -EINVAL; + if (pa->e_id == ACL_UNDEFINED_ID || + pa->e_id < id) + return -EINVAL; + id = pa->e_id + 1; + needs_mask = 1; + break; + + case ACL_MASK: + if (state != ACL_GROUP) + return -EINVAL; + state = ACL_OTHER; + break; + + case ACL_OTHER: + if (state == ACL_OTHER || + (state == ACL_GROUP && !needs_mask)) { + state = 0; + break; + } + return -EINVAL; + + default: + return -EINVAL; + } + } + if (state == 0) + return 0; + return -EINVAL; +} + +/* + * Returns 0 if the acl can be exactly represented in the traditional + * file mode permission bits, or else 1. Returns -E... on error. + */ +int +posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p) +{ + const struct posix_acl_entry *pa, *pe; + mode_t mode = 0; + int not_equiv = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch (pa->e_tag) { + case ACL_USER_OBJ: + mode |= (pa->e_perm & S_IRWXO) << 6; + break; + case ACL_GROUP_OBJ: + mode |= (pa->e_perm & S_IRWXO) << 3; + break; + case ACL_OTHER: + mode |= pa->e_perm & S_IRWXO; + break; + case ACL_MASK: + mode = (mode & ~S_IRWXG) | + ((pa->e_perm & S_IRWXO) << 3); + not_equiv = 1; + break; + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + default: + return -EINVAL; + } + } + if (mode_p) + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +/* + * Create an ACL representing the file mode permission bits of an inode. + */ +struct posix_acl * +posix_acl_from_mode(mode_t mode, int flags) +{ + struct posix_acl *acl = posix_acl_alloc(3, flags); + if (!acl) + return ERR_PTR(-ENOMEM); + + acl->a_entries[0].e_tag = ACL_USER_OBJ; + acl->a_entries[0].e_id = ACL_UNDEFINED_ID; + acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; + + acl->a_entries[1].e_tag = ACL_GROUP_OBJ; + acl->a_entries[1].e_id = ACL_UNDEFINED_ID; + acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; + + acl->a_entries[2].e_tag = ACL_OTHER; + acl->a_entries[2].e_id = ACL_UNDEFINED_ID; + acl->a_entries[2].e_perm = (mode & S_IRWXO); + return acl; +} + +/* + * Return 0 if current is granted want access to the inode + * by the acl. Returns -E... otherwise. + */ +int +posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) +{ + const struct posix_acl_entry *pa, *pe, *mask_obj; + int found = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + /* (May have been checked already) */ + if (inode->i_uid == current->fsuid) + goto check_perm; + break; + case ACL_USER: + if (pa->e_id == current->fsuid) + goto mask; + break; + case ACL_GROUP_OBJ: + if (in_group_p(inode->i_gid)) { + found = 1; + if ((pa->e_perm & want) == want) + goto mask; + } + break; + case ACL_GROUP: + if (in_group_p(pa->e_id)) { + found = 1; + if ((pa->e_perm & want) == want) + goto mask; + } + break; + case ACL_MASK: + break; + case ACL_OTHER: + if (found) + return -EACCES; + else + goto check_perm; + default: + return -EIO; + } + } + return -EIO; + +mask: + for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { + if (mask_obj->e_tag == ACL_MASK) { + if ((pa->e_perm & mask_obj->e_perm & want) == want) + return 0; + return -EACCES; + } + } + +check_perm: + if ((pa->e_perm & want) == want) + return 0; + return -EACCES; +} + +/* + * Modify acl when creating a new inode. The caller must ensure the acl is + * only referenced once. + * + * mode_p initially must contain the mode parameter to the open() / creat() + * system calls. All permissions that are not granted by the acl are removed. + * The permissions in the acl are changed to reflect the mode_p parameter. + */ +int +posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + mode_t mode = *mode_p; + int not_equiv = 0; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm &= (mode >> 6) | ~S_IRWXO; + mode &= (pa->e_perm << 6) | ~S_IRWXU; + break; + + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm &= mode | ~S_IRWXO; + mode &= pa->e_perm | ~S_IRWXO; + break; + + case ACL_MASK: + mask_obj = pa; + not_equiv = 1; + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (group_obj->e_perm << 3) | ~S_IRWXG; + } + + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +/* + * Modify the ACL for the chmod syscall. + */ +int +posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode) +{ + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + struct posix_acl_entry *pa, *pe; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm = (mode & S_IRWXU) >> 6; + break; + + case ACL_USER: + case ACL_GROUP: + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_MASK: + mask_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm = (mode & S_IRWXO); + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm = (mode & S_IRWXG) >> 3; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm = (mode & S_IRWXG) >> 3; + } + + return 0; +} + +/* + * Adjust the mode parameter so that NFSv2 grants nobody permissions + * that may not be granted by the ACL. This is necessary because NFSv2 + * may compute access permissions on the client side, and may serve cached + * data whenever it assumes access would be granted. Since ACLs may also + * be used to deny access to specific users, the minimal permissions + * for secure operation over NFSv2 are very restrictive. Permissions + * granted to users via Access Control Lists will not be effective over + * NFSv2. + * + * Privilege escalation can only happen for read operations, as writes are + * always carried out on the NFS server, where the proper access checks are + * implemented. + */ +int +posix_acl_masq_nfs_mode(struct posix_acl *acl, mode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; int min_perm = S_IRWXO; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + break; + + case ACL_USER: + case ACL_GROUP_OBJ: + case ACL_GROUP: + case ACL_MASK: + case ACL_OTHER: + min_perm &= pa->e_perm; + break; + + default: + return -EIO; + } + } + *mode_p = (*mode_p & ~(S_IRWXG|S_IRWXO)) | (min_perm << 3) | min_perm; + + return 0; +} diff -urN linux-2.4.27-20040814-ea-0.8.71/fs/xattr_acl.c linux-2.4.27-20040814-acl-0.8.71/fs/xattr_acl.c --- linux-2.4.27-20040814-ea-0.8.71/fs/xattr_acl.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/fs/xattr_acl.c Sun Aug 15 23:53:23 2004 @@ -0,0 +1,99 @@ +/* + * linux/fs/xattr_acl.c + * + * Almost all from linux/fs/ext2/acl.c: + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include + + +/* + * Convert from extended attribute to in-memory representation. + */ +struct posix_acl * +posix_acl_from_xattr(const void *value, size_t size) +{ + xattr_acl_header *header = (xattr_acl_header *)value; + xattr_acl_entry *entry = (xattr_acl_entry *)(header+1), *end; + int count; + struct posix_acl *acl; + struct posix_acl_entry *acl_e; + + if (!value) + return NULL; + if (size < sizeof(xattr_acl_header)) + return ERR_PTR(-EINVAL); + if (header->a_version != cpu_to_le32(XATTR_ACL_VERSION)) + return ERR_PTR(-EINVAL); + + count = xattr_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + acl_e = acl->a_entries; + + for (end = entry + count; entry != end; acl_e++, entry++) { + acl_e->e_tag = le16_to_cpu(entry->e_tag); + acl_e->e_perm = le16_to_cpu(entry->e_perm); + + switch(acl_e->e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + acl_e->e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + acl_e->e_id = le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL (posix_acl_from_xattr); + +/* + * Convert from in-memory to extended attribute representation. + */ +int +posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size) +{ + xattr_acl_header *ext_acl = (xattr_acl_header *)buffer; + xattr_acl_entry *ext_entry = ext_acl->a_entries; + int real_size, n; + + real_size = xattr_acl_size(acl->a_count); + if (!buffer) + return real_size; + if (real_size > size) + return -ERANGE; + + ext_acl->a_version = cpu_to_le32(XATTR_ACL_VERSION); + + for (n=0; n < acl->a_count; n++, ext_entry++) { + ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id); + } + return real_size; +} +EXPORT_SYMBOL (posix_acl_to_xattr); diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/ext2_acl.h linux-2.4.27-20040814-acl-0.8.71/include/linux/ext2_acl.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/ext2_acl.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/ext2_acl.h Sun Aug 15 23:53:23 2004 @@ -0,0 +1,101 @@ +/* + File: linux/ext2_acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +#define EXT2_ACL_VERSION 0x0001 +#define EXT2_ACL_MAX_ENTRIES 32 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} ext2_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} ext2_acl_entry_short; + +typedef struct { + __u32 a_version; +} ext2_acl_header; + +static inline size_t ext2_acl_size(int count) +{ + if (count <= 4) { + return sizeof(ext2_acl_header) + + count * sizeof(ext2_acl_entry_short); + } else { + return sizeof(ext2_acl_header) + + 4 * sizeof(ext2_acl_entry_short) + + (count - 4) * sizeof(ext2_acl_entry); + } +} + +static inline int ext2_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext2_acl_header); + s = size - 4 * sizeof(ext2_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext2_acl_entry_short)) + return -1; + return size / sizeof(ext2_acl_entry_short); + } else { + if (s % sizeof(ext2_acl_entry)) + return -1; + return s / sizeof(ext2_acl_entry) + 4; + } +} + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT2_FS_POSIX_ACL + +/* Value for inode->u.ext2_i.i_acl and inode->u.ext2_i.i_default_acl + if the ACL has not been cached */ +# define EXT2_ACL_NOT_CACHED ((void *)-1) + +/* acl.c */ +extern int ext2_permission (struct inode *, int); +extern int ext2_acl_chmod (struct inode *); +extern int ext2_init_acl (struct inode *, struct inode *); + +extern int init_ext2_acl(void) __init; +extern void exit_ext2_acl(void); + +# else +# include +# define ext2_permission NULL + +static inline int +ext2_acl_chmod (struct inode *inode) +{ + return 0; +} + +static inline int ext2_init_acl (struct inode *inode, struct inode *dir) +{ + inode->i_mode &= ~current->fs->umask; + mark_inode_dirty(inode); + return 0; +} + +static inline int +init_ext2_acl(void) +{ + return 0; +} + +static inline void +exit_ext2_acl(void) +{ +} + +# endif +#endif diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/ext2_fs.h linux-2.4.27-20040814-acl-0.8.71/include/linux/ext2_fs.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/ext2_fs.h Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/ext2_fs.h Sun Aug 15 23:53:23 2004 @@ -576,7 +576,7 @@ extern int ext2_fsync_inode (struct inode *, int); /* ialloc.c */ -extern struct inode * ext2_new_inode (const struct inode *, int); +extern struct inode * ext2_new_inode (struct inode *, int); extern void ext2_free_inode (struct inode *); extern unsigned long ext2_count_free_inodes (struct super_block *); extern void ext2_check_inodes_bitmap (struct super_block *); @@ -591,6 +591,7 @@ extern void ext2_discard_prealloc (struct inode *); extern void ext2_truncate (struct inode *); extern void ext2_set_inode_flags(struct inode *inode); +extern int ext2_setattr (struct dentry *, struct iattr *); /* ioctl.c */ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/ext2_fs_i.h linux-2.4.27-20040814-acl-0.8.71/include/linux/ext2_fs_i.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/ext2_fs_i.h Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/ext2_fs_i.h Sun Aug 15 23:53:23 2004 @@ -45,6 +45,10 @@ */ struct rw_semaphore xattr_sem; #endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif }; /* diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/ext3_acl.h linux-2.4.27-20040814-acl-0.8.71/include/linux/ext3_acl.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/ext3_acl.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/ext3_acl.h Sun Aug 15 23:53:23 2004 @@ -0,0 +1,108 @@ +/* + File: linux/ext3_acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +#define EXT3_ACL_VERSION 0x0001 +#define EXT3_ACL_MAX_ENTRIES 32 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} ext3_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} ext3_acl_entry_short; + +typedef struct { + __u32 a_version; +} ext3_acl_header; + +static inline size_t ext3_acl_size(int count) +{ + if (count <= 4) { + return sizeof(ext3_acl_header) + + count * sizeof(ext3_acl_entry_short); + } else { + return sizeof(ext3_acl_header) + + 4 * sizeof(ext3_acl_entry_short) + + (count - 4) * sizeof(ext3_acl_entry); + } +} + +static inline int ext3_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext3_acl_header); + s = size - 4 * sizeof(ext3_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext3_acl_entry_short)) + return -1; + return size / sizeof(ext3_acl_entry_short); + } else { + if (s % sizeof(ext3_acl_entry)) + return -1; + return s / sizeof(ext3_acl_entry) + 4; + } +} + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT3_FS_POSIX_ACL + +/* Value for inode->u.ext3_i.i_acl and inode->u.ext3_i.i_default_acl + if the ACL has not been cached */ +# define EXT3_ACL_NOT_CACHED ((void *)-1) + +/* acl.c */ +extern int ext3_permission (struct inode *, int); +extern struct posix_acl *ext3_get_acl (struct inode *, int); +extern int ext3_set_acl (struct inode *, int, struct posix_acl *); +extern int ext3_acl_chmod (handle_t *, struct inode *); +extern int ext3_init_acl (handle_t *, struct inode *, struct inode *); +extern int ext3_get_acl_xattr (struct inode *, int, void *, size_t); +extern int ext3_set_acl_xattr (struct inode *, int, void *, size_t); + +extern int init_ext3_acl(void) __init; +extern void exit_ext3_acl(void); + +# else /* CONFIG_EXT3_FS_POSIX_ACL */ +# include +# define ext3_permission NULL +# define ext3_get_acl NULL +# define ext3_set_acl NULL + +static inline int +ext3_acl_chmod(handle_t *handle, struct inode *inode) +{ + return 0; +} + +static inline int +ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + inode->i_mode &= ~current->fs->umask; + ext3_mark_inode_dirty(handle, inode); + return 0; +} + +static inline int +init_ext3_acl(void) +{ + return 0; +} + +static inline void +exit_ext3_acl(void) +{ +} + +# endif /* CONFIG_EXT3_FS_POSIX_ACL */ +#endif /* __KERNEL__ */ diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/ext3_fs.h linux-2.4.27-20040814-acl-0.8.71/include/linux/ext3_fs.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/ext3_fs.h Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/ext3_fs.h Sun Aug 15 23:53:23 2004 @@ -315,6 +315,7 @@ #define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */ #define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */ #define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ +#define EXT3_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H @@ -614,7 +615,7 @@ extern int ext3_sync_file (struct file *, struct dentry *, int); /* ialloc.c */ -extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int); +extern struct inode * ext3_new_inode (handle_t *, struct inode *, int); extern void ext3_free_inode (handle_t *, struct inode *); extern struct inode * ext3_orphan_get (struct super_block *, unsigned long); extern unsigned long ext3_count_free_inodes (struct super_block *); diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/ext3_fs_i.h linux-2.4.27-20040814-acl-0.8.71/include/linux/ext3_fs_i.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/ext3_fs_i.h Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/ext3_fs_i.h Sun Aug 15 23:53:23 2004 @@ -52,6 +52,10 @@ */ struct rw_semaphore xattr_sem; #endif +#ifdef CONFIG_EXT3_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif struct list_head i_orphan; /* unlinked but open inodes */ diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/fs.h linux-2.4.27-20040814-acl-0.8.71/include/linux/fs.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/fs.h Sun Aug 15 23:50:43 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/fs.h Sun Aug 15 23:53:23 2004 @@ -111,6 +111,7 @@ #define MS_MOVE 8192 #define MS_REC 16384 #define MS_VERBOSE 32768 +#define MS_POSIXACL 65536 /* VFS does not apply the umask */ #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) @@ -161,6 +162,7 @@ #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME)) #define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME) +#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/nfs_mount.h linux-2.4.27-20040814-acl-0.8.71/include/linux/nfs_mount.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/nfs_mount.h Thu Jul 29 23:22:05 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/nfs_mount.h Sun Aug 15 23:53:23 2004 @@ -53,6 +53,7 @@ #define NFS_MOUNT_KERBEROS 0x0100 /* 3 */ #define NFS_MOUNT_NONLM 0x0200 /* 3 */ #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */ +#define NFS_MOUNT_NOACL 0x0800 /* 4 */ #define NFS_MOUNT_FLAGMASK 0xFFFF #endif diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/nfsd/export.h linux-2.4.27-20040814-acl-0.8.71/include/linux/nfsd/export.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/nfsd/export.h Thu Jul 29 23:21:42 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/nfsd/export.h Sun Aug 15 23:53:23 2004 @@ -40,7 +40,8 @@ #define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */ #define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect */ #define NFSEXP_FSID 0x2000 -#define NFSEXP_ALLFLAGS 0x3FFF +#define NFSEXP_NOACL 0x8000 +#define NFSEXP_ALLFLAGS 0xFFFF #ifdef __KERNEL__ @@ -83,6 +84,19 @@ #define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE) #define EX_SUNSECURE(exp) ((exp)->ex_flags & NFSEXP_SUNSECURE) #define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) +/* + * With Posix Access Control Lists, pre-NFS3 clients and older Linux + * NFSv3 clients in some cases mis-interpret the file mode permission + * bits, and either allow the remote user to read data she is not + * permitted to, or deny the user read access that should be granted. + * (With proper NFSv3, the access RPC is used to check access, and + * access decisions are not implemented on the client.) + * + * The no_acl option should be set in environments with clients that do + * not use the NFSv3 ACCESS RPC. This option should always be set for NFSv2 + * clients. + */ +#define EX_NOACL(exp) ((exp)->ex_flags & NFSEXP_NOACL) /* diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/nfsd/nfsd.h linux-2.4.27-20040814-acl-0.8.71/include/linux/nfsd/nfsd.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/nfsd/nfsd.h Fri Jul 30 23:16:13 2004 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/nfsd/nfsd.h Sun Aug 15 23:53:23 2004 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,22 @@ int nfsd_notify_change(struct inode *, struct iattr *); int nfsd_permission(struct svc_export *, struct dentry *, int); + +#ifdef CONFIG_FS_POSIX_ACL +struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int); +int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *); +#else +static inline struct posix_acl * +nfsd_get_posix_acl(struct svc_fh *fhp, int acl_type) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline int +nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl) +{ + return -EOPNOTSUPP; +} +#endif /* diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/posix_acl.h linux-2.4.27-20040814-acl-0.8.71/include/linux/posix_acl.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/posix_acl.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/posix_acl.h Sun Aug 15 23:53:23 2004 @@ -0,0 +1,87 @@ +/* + File: linux/posix_acl.h + + (C) 2002 Andreas Gruenbacher, +*/ + + +#ifndef __LINUX_POSIX_ACL_H +#define __LINUX_POSIX_ACL_H + +#include + +#define ACL_UNDEFINED_ID (-1) + +/* a_type field in acl_user_posix_entry_t */ +#define ACL_TYPE_ACCESS (0x8000) +#define ACL_TYPE_DEFAULT (0x4000) + +/* e_tag entry in struct posix_acl_entry */ +#define ACL_USER_OBJ (0x01) +#define ACL_USER (0x02) +#define ACL_GROUP_OBJ (0x04) +#define ACL_GROUP (0x08) +#define ACL_MASK (0x10) +#define ACL_OTHER (0x20) + +/* permissions in the e_perm field */ +#define ACL_READ (0x04) +#define ACL_WRITE (0x02) +#define ACL_EXECUTE (0x01) +//#define ACL_ADD (0x08) +//#define ACL_DELETE (0x10) + +struct posix_acl_entry { + short e_tag; + unsigned short e_perm; + unsigned int e_id; +}; + +struct posix_acl { + atomic_t a_refcount; + unsigned int a_count; + struct posix_acl_entry a_entries[0]; +}; + +#define FOREACH_ACL_ENTRY(pa, acl, pe) \ + for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; paa_refcount); + return acl; +} + +/* + * Free an ACL handle. + */ +static inline void +posix_acl_release(struct posix_acl *acl) +{ + if (acl && atomic_dec_and_test(&acl->a_refcount)) + kfree(acl); +} + + +/* posix_acl.c */ + +extern struct posix_acl *posix_acl_alloc(int, int); +extern struct posix_acl *posix_acl_clone(const struct posix_acl *, int); +extern int posix_acl_valid(const struct posix_acl *); +extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); +extern struct posix_acl *posix_acl_from_mode(mode_t, int); +extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *); +extern int posix_acl_create_masq(struct posix_acl *, mode_t *); +extern int posix_acl_chmod_masq(struct posix_acl *, mode_t); +extern int posix_acl_masq_nfs_mode(struct posix_acl *, mode_t *); + +extern struct posix_acl *get_posix_acl(struct inode *, int); +extern int set_posix_acl(struct inode *, int, struct posix_acl *); + +#endif /* __LINUX_POSIX_ACL_H */ diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/posix_acl_xattr.h linux-2.4.27-20040814-acl-0.8.71/include/linux/posix_acl_xattr.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/posix_acl_xattr.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/posix_acl_xattr.h Sun Aug 15 23:53:23 2004 @@ -0,0 +1,66 @@ +/* + File: linux/posix_acl_xattr.h + + Extended attribute system call representation of Access Control Lists. + + Copyright (C) 2000 by Andreas Gruenbacher + */ +#ifndef _POSIX_ACL_XATTR_H +#define _POSIX_ACL_XATTR_H + +/* Extended attribute names */ +#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access" +#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default" + +/* Supported ACL a_version fields */ +#define POSIX_ACL_XATTR_VERSION 0x0002 + + +/* An undefined entry e_id value */ +#define ACL_UNDEFINED_ID (-1) + +/* ACL entry e_tag field values */ +#define ACL_USER_OBJ (0x01) +#define ACL_USER (0x02) +#define ACL_GROUP_OBJ (0x04) +#define ACL_GROUP (0x08) +#define ACL_MASK (0x10) +#define ACL_OTHER (0x20) + +/* ACL entry e_perm bitfield values */ +#define ACL_READ (0x04) +#define ACL_WRITE (0x02) +#define ACL_EXECUTE (0x01) + + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} posix_acl_xattr_entry; + +typedef struct { + __u32 a_version; + posix_acl_xattr_entry a_entries[0]; +} posix_acl_xattr_header; + + +static inline size_t +posix_acl_xattr_size(int count) +{ + return (sizeof(posix_acl_xattr_header) + + (count * sizeof(posix_acl_xattr_entry))); +} + +static inline int +posix_acl_xattr_count(size_t size) +{ + if (size < sizeof(posix_acl_xattr_header)) + return -1; + size -= sizeof(posix_acl_xattr_header); + if (size % sizeof(posix_acl_xattr_entry)) + return -1; + return size / sizeof(posix_acl_xattr_entry); +} + +#endif /* _POSIX_ACL_XATTR_H */ diff -urN linux-2.4.27-20040814-ea-0.8.71/include/linux/xattr_acl.h linux-2.4.27-20040814-acl-0.8.71/include/linux/xattr_acl.h --- linux-2.4.27-20040814-ea-0.8.71/include/linux/xattr_acl.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.27-20040814-acl-0.8.71/include/linux/xattr_acl.h Sun Aug 15 23:53:23 2004 @@ -0,0 +1,50 @@ +/* + File: linux/xattr_acl.h + + (extended attribute representation of access control lists) + + (C) 2000 Andreas Gruenbacher, +*/ + +#ifndef _LINUX_XATTR_ACL_H +#define _LINUX_XATTR_ACL_H + +#include + +#define XATTR_NAME_ACL_ACCESS "system.posix_acl_access" +#define XATTR_NAME_ACL_DEFAULT "system.posix_acl_default" + +#define XATTR_ACL_VERSION 0x0002 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} xattr_acl_entry; + +typedef struct { + __u32 a_version; + xattr_acl_entry a_entries[0]; +} xattr_acl_header; + +static inline size_t xattr_acl_size(int count) +{ + return sizeof(xattr_acl_header) + count * sizeof(xattr_acl_entry); +} + +static inline int xattr_acl_count(size_t size) +{ + if (size < sizeof(xattr_acl_header)) + return -1; + size -= sizeof(xattr_acl_header); + if (size % sizeof(xattr_acl_entry)) + return -1; + return size / sizeof(xattr_acl_entry); +} + +struct posix_acl * posix_acl_from_xattr(const void *value, size_t size); +int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size); + + + +#endif /* _LINUX_XATTR_ACL_H */ diff -urN linux-2.4.27-20040814-ea-0.8.71/kernel/Makefile linux-2.4.27-20040814-acl-0.8.71/kernel/Makefile --- linux-2.4.27-20040814-ea-0.8.71/kernel/Makefile Mon Sep 17 06:22:40 2001 +++ linux-2.4.27-20040814-acl-0.8.71/kernel/Makefile Sun Aug 15 23:53:23 2004 @@ -9,7 +9,7 @@ O_TARGET := kernel.o -export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o +export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o fork.o obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ diff -urN linux-2.4.27-20040814-ea-0.8.71/kernel/fork.c linux-2.4.27-20040814-acl-0.8.71/kernel/fork.c --- linux-2.4.27-20040814-ea-0.8.71/kernel/fork.c Sat Apr 17 23:05:49 2004 +++ linux-2.4.27-20040814-acl-0.8.71/kernel/fork.c Sun Aug 15 23:53:23 2004 @@ -407,6 +407,7 @@ { return __copy_fs_struct(old); } +EXPORT_SYMBOL(copy_fs_struct); static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) {