Index: linux-2.4.29/Documentation/Configure.help =================================================================== --- linux-2.4.29.orig/Documentation/Configure.help +++ linux-2.4.29/Documentation/Configure.help @@ -13900,6 +13900,20 @@ CONFIG_ISP16_CDI 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 @@ -16685,6 +16699,8 @@ CONFIG_EXT2_FS_XATTR 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 @@ -16703,6 +16719,17 @@ CONFIG_EXT2_FS_XATTR_USER 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 @@ -16741,6 +16768,8 @@ CONFIG_EXT3_FS_XATTR 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 @@ -16759,6 +16788,17 @@ CONFIG_EXT3_FS_XATTR_USER 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 Index: linux-2.4.29/arch/alpha/defconfig =================================================================== --- linux-2.4.29.orig/arch/alpha/defconfig +++ linux-2.4.29/arch/alpha/defconfig @@ -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_ALPHA=y # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set Index: linux-2.4.29/arch/arm/defconfig =================================================================== --- linux-2.4.29.orig/arch/arm/defconfig +++ linux-2.4.29/arch/arm/defconfig @@ -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_ARM=y # CONFIG_EISA is not set # CONFIG_SBUS is not set Index: linux-2.4.29/arch/i386/defconfig =================================================================== --- linux-2.4.29.orig/arch/i386/defconfig +++ linux-2.4.29/arch/i386/defconfig @@ -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_X86=y # CONFIG_SBUS is not set CONFIG_UID16=y Index: linux-2.4.29/arch/ia64/defconfig =================================================================== --- linux-2.4.29.orig/arch/ia64/defconfig +++ linux-2.4.29/arch/ia64/defconfig @@ -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 # # Code maturity level options Index: linux-2.4.29/arch/m68k/defconfig =================================================================== --- linux-2.4.29.orig/arch/m68k/defconfig +++ linux-2.4.29/arch/m68k/defconfig @@ -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_UID16=y # Index: linux-2.4.29/arch/mips/defconfig =================================================================== --- linux-2.4.29.orig/arch/mips/defconfig +++ linux-2.4.29/arch/mips/defconfig @@ -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_MIPS=y CONFIG_MIPS32=y # CONFIG_MIPS64 is not set Index: linux-2.4.29/arch/mips64/defconfig =================================================================== --- linux-2.4.29.orig/arch/mips64/defconfig +++ linux-2.4.29/arch/mips64/defconfig @@ -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_MIPS=y # CONFIG_MIPS32 is not set CONFIG_MIPS64=y Index: linux-2.4.29/arch/ppc/defconfig =================================================================== --- linux-2.4.29.orig/arch/ppc/defconfig +++ linux-2.4.29/arch/ppc/defconfig @@ -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_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y Index: linux-2.4.29/arch/ppc64/defconfig =================================================================== --- linux-2.4.29.orig/arch/ppc64/defconfig +++ linux-2.4.29/arch/ppc64/defconfig @@ -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_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y Index: linux-2.4.29/arch/s390/defconfig =================================================================== --- linux-2.4.29.orig/arch/s390/defconfig +++ linux-2.4.29/arch/s390/defconfig @@ -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_ISA is not set # CONFIG_EISA is not set # CONFIG_MCA is not set Index: linux-2.4.29/arch/s390x/defconfig =================================================================== --- linux-2.4.29.orig/arch/s390x/defconfig +++ linux-2.4.29/arch/s390x/defconfig @@ -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_ISA is not set # CONFIG_EISA is not set # CONFIG_MCA is not set Index: linux-2.4.29/arch/sparc/defconfig =================================================================== --- linux-2.4.29.orig/arch/sparc/defconfig +++ linux-2.4.29/arch/sparc/defconfig @@ -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_UID16=y CONFIG_HIGHMEM=y Index: linux-2.4.29/arch/sparc64/defconfig =================================================================== --- linux-2.4.29.orig/arch/sparc64/defconfig +++ linux-2.4.29/arch/sparc64/defconfig @@ -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 # Index: linux-2.4.29/fs/Config.in =================================================================== --- linux-2.4.29.orig/fs/Config.in +++ linux-2.4.29/fs/Config.in @@ -36,6 +36,8 @@ dep_bool ' Ext3 extended user attribu 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 @@ dep_bool ' Ext2 extended user attribu 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 @@ -189,6 +193,15 @@ fi #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' source fs/partitions/Config.in Index: linux-2.4.29/fs/Makefile =================================================================== --- linux-2.4.29.orig/fs/Makefile +++ linux-2.4.29/fs/Makefile @@ -77,8 +77,9 @@ obj-y += binfmt_script.o 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)) Index: linux-2.4.29/fs/ext2/Makefile =================================================================== --- linux-2.4.29.orig/fs/ext2/Makefile +++ linux-2.4.29/fs/ext2/Makefile @@ -17,5 +17,6 @@ export-objs += xattr.o 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 Index: linux-2.4.29/fs/ext2/acl.c =================================================================== --- /dev/null +++ linux-2.4.29/fs/ext2/acl.c @@ -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) +{ + struct ext2_inode_info *ei = EXT2_I(inode); + int name_index; + char *value; + struct posix_acl *acl = NULL; + 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); + } + retval = ext2_xattr_get(inode, name_index, "", NULL, 0); + if (retval > 0) { + value = kmalloc(retval, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + retval = ext2_xattr_get(inode, name_index, "", value, retval); + if (retval > 0) + acl = ext2_acl_from_disk(value, retval); + kfree(value); + } + if (retval <= 0) { + acl = ERR_PTR(retval); + if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + } + 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) { + 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; +} Index: linux-2.4.29/fs/ext2/file.c =================================================================== --- linux-2.4.29.orig/fs/ext2/file.c +++ linux-2.4.29/fs/ext2/file.c @@ -21,6 +21,7 @@ #include #include #include +#include #include /* @@ -56,4 +57,6 @@ struct inode_operations ext2_file_inode_ getxattr: ext2_getxattr, listxattr: ext2_listxattr, removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; Index: linux-2.4.29/fs/ext2/ialloc.c =================================================================== --- linux-2.4.29.orig/fs/ext2/ialloc.c +++ linux-2.4.29/fs/ext2/ialloc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -313,7 +314,7 @@ found: 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,14 +405,24 @@ repeat: 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); desc->bg_free_inodes_count = Index: linux-2.4.29/fs/ext2/inode.c =================================================================== --- linux-2.4.29.orig/fs/ext2/inode.c +++ linux-2.4.29/fs/ext2/inode.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -1027,6 +1028,15 @@ void ext2_read_inode (struct inode * ino #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 @@ int ext2_sync_inode (struct inode *inode { 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; +} + Index: linux-2.4.29/fs/ext2/namei.c =================================================================== --- linux-2.4.29.orig/fs/ext2/namei.c +++ linux-2.4.29/fs/ext2/namei.c @@ -32,6 +32,7 @@ #include #include #include +#include #include /* @@ -112,7 +113,10 @@ static int ext2_mknod (struct inode * di 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 @@ struct inode_operations ext2_dir_inode_o 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 @@ struct inode_operations ext2_special_ino getxattr: ext2_getxattr, listxattr: ext2_listxattr, removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; Index: linux-2.4.29/fs/ext2/super.c =================================================================== --- linux-2.4.29.orig/fs/ext2/super.c +++ linux-2.4.29/fs/ext2/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -149,6 +150,26 @@ void ext2_put_super (struct super_block 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 @@ static struct super_operations ext2_sops write_super: ext2_write_super, statfs: ext2_statfs, remount_fs: ext2_remount, + clear_inode: ext2_clear_inode, }; /* @@ -165,7 +187,8 @@ static struct super_operations ext2_sops */ 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 @@ static int parse_options (char * options 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 @@ struct super_block * ext2_read_super (st #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 @@ int ext2_remount (struct super_block * s 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,6 +896,9 @@ static int __init init_ext2_fs(void) error = init_ext2_xattr_trusted(); if (error) return error; + error = init_ext2_acl(); + if (error) + return error; return register_filesystem(&ext2_fs_type); } Index: linux-2.4.29/fs/ext3/Makefile =================================================================== --- linux-2.4.29.orig/fs/ext3/Makefile +++ linux-2.4.29/fs/ext3/Makefile @@ -17,5 +17,6 @@ export-objs += xattr.o 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 Index: linux-2.4.29/fs/ext3/acl.c =================================================================== --- /dev/null +++ linux-2.4.29/fs/ext3/acl.c @@ -0,0 +1,572 @@ +/* + * 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) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + int name_index; + char *value; + struct posix_acl *acl = NULL; + 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); + } + retval = ext3_xattr_get(inode, name_index, "", NULL, 0); + if (retval > 0) { + value = kmalloc(retval, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + retval = ext3_xattr_get(inode, name_index, "", value, retval); + if (retval > 0) + acl = ext3_acl_from_disk(value, retval); + kfree(value); + } + if (retval <= 0) { + acl = ERR_PTR(retval); + if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + } + 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) { + 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, +}; + +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) + return error; + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); + if (error) + return error; + return 0; +} Index: linux-2.4.29/fs/ext3/file.c =================================================================== --- linux-2.4.29.orig/fs/ext3/file.c +++ linux-2.4.29/fs/ext3/file.c @@ -21,9 +21,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -129,5 +131,6 @@ struct inode_operations ext3_file_inode_ getxattr: ext3_getxattr, /* BKL held */ listxattr: ext3_listxattr, /* BKL held */ removexattr: ext3_removexattr, /* BKL held */ + permission: ext3_permission, /* BKL held */ }; Index: linux-2.4.29/fs/ext3/ialloc.c =================================================================== --- linux-2.4.29.orig/fs/ext3/ialloc.c +++ linux-2.4.29/fs/ext3/ialloc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -298,8 +299,7 @@ error_return: * 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 @@ repeat: 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; @@ -533,6 +536,12 @@ 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); } /* Verify that we are loading a valid orphan from disk */ Index: linux-2.4.29/fs/ext3/inode.c =================================================================== --- linux-2.4.29.orig/fs/ext3/inode.c +++ linux-2.4.29/fs/ext3/inode.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -2194,6 +2196,15 @@ void ext3_read_inode(struct inode * inod #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 @@ void ext3_write_inode(struct inode *inod * 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 @@ int ext3_setattr(struct dentry *dentry, 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,9 +2432,27 @@ int ext3_setattr(struct dentry *dentry, /* 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); if (!error) Index: linux-2.4.29/fs/ext3/namei.c =================================================================== --- linux-2.4.29.orig/fs/ext3/namei.c +++ linux-2.4.29/fs/ext3/namei.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -491,7 +492,10 @@ static int ext3_mknod (struct inode * di 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 @@ struct inode_operations ext3_dir_inode_o 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 */ }; Index: linux-2.4.29/fs/ext3/super.c =================================================================== --- linux-2.4.29.orig/fs/ext3/super.c +++ linux-2.4.29/fs/ext3/super.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -452,6 +453,26 @@ void ext3_put_super (struct super_block 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 @@ static struct super_operations ext3_sops 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 want_numeric(char *value, cha */ 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 @@ static int parse_options (char * options 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 @@ struct super_block * ext3_read_super (st #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; } @@ -1679,19 +1713,21 @@ int ext3_remount (struct super_block * s { 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); @@ -1864,6 +1900,9 @@ static int __init init_ext3_fs(void) error = init_ext3_xattr_trusted(); if (error) return error; + error = init_ext3_acl(); + if (error) + return error; return register_filesystem(&ext3_fs_type); } Index: linux-2.4.29/fs/intermezzo/vfs.c =================================================================== --- linux-2.4.29.orig/fs/intermezzo/vfs.c +++ linux-2.4.29/fs/intermezzo/vfs.c @@ -74,7 +74,7 @@ #ifdef CONFIG_FS_EXT_ATTR # include -# ifdef CONFIG_FS_POSIX_ACL +#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL # include # endif #endif @@ -466,7 +466,7 @@ int lento_setattr(const char *name, stru struct dentry *dentry; struct presto_file_set *fset; int error; -#ifdef CONFIG_FS_POSIX_ACL +#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL int (*set_posix_acl)(struct inode *, int type, posix_acl_t *)=NULL; #endif @@ -507,7 +507,7 @@ int lento_setattr(const char *name, stru (dentry->d_inode->i_mode & ~S_IALLUGO); CDEBUG(D_PIOCTL, "chmod: orig %#o, set %#o, result %#o\n", dentry->d_inode->i_mode, set_mode, iattr->ia_mode); -#ifdef CONFIG_FS_POSIX_ACL +#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL /* ACl code interacts badly with setattr * since it tries to modify the ACL using * set_ext_attr which recurses back into presto. @@ -535,7 +535,7 @@ int lento_setattr(const char *name, stru } } -#ifdef CONFIG_FS_POSIX_ACL +#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL /* restore the inode_operations if we changed them*/ if (iattr->ia_valid & ATTR_MODE) dentry->d_inode->i_op->set_posix_acl=set_posix_acl; @@ -2267,7 +2267,7 @@ exit: #ifdef CONFIG_FS_EXT_ATTR -#ifdef CONFIG_FS_POSIX_ACL +#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL /* Posix ACL code changes i_mode without using a notify_change (or * a mark_inode_dirty!). We need to duplicate this at the reintegrator * which is done by this function. This function also takes care of @@ -2410,7 +2410,7 @@ int presto_do_set_ext_attr(struct presto goto exit; } -#ifdef CONFIG_FS_POSIX_ACL +#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL /* Reset mode if specified*/ /* XXX: when we do native acl support, move this code out! */ if (mode != NULL) { Index: linux-2.4.29/fs/namei.c =================================================================== --- linux-2.4.29.orig/fs/namei.c +++ linux-2.4.29/fs/namei.c @@ -1048,8 +1048,9 @@ do_last: /* 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 @@ asmlinkage long sys_mknod(const char * f 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 @@ asmlinkage long sys_mkdir(const char * p 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); Index: linux-2.4.29/fs/nfs/dir.c =================================================================== --- linux-2.4.29.orig/fs/nfs/dir.c +++ linux-2.4.29/fs/nfs/dir.c @@ -1090,7 +1090,8 @@ nfs_permission(struct inode *inode, int { 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) Index: linux-2.4.29/fs/nfs/inode.c =================================================================== --- linux-2.4.29.orig/fs/nfs/inode.c +++ linux-2.4.29/fs/nfs/inode.c @@ -568,6 +568,7 @@ static int nfs_show_options(struct seq_f { 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; Index: linux-2.4.29/fs/nfsd/export.c =================================================================== --- linux-2.4.29.orig/fs/nfsd/export.c +++ linux-2.4.29/fs/nfsd/export.c @@ -643,6 +643,7 @@ struct flags { { NFSEXP_NOHIDE, {"nohide", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, + { NFSEXP_NOACL, {"no_acl", ""}}, #ifdef MSNFS { NFSEXP_MSNFS, {"msnfs", ""}}, #endif Index: linux-2.4.29/fs/nfsd/nfs3xdr.c =================================================================== --- linux-2.4.29.orig/fs/nfsd/nfs3xdr.c +++ linux-2.4.29/fs/nfsd/nfs3xdr.c @@ -157,9 +157,19 @@ static inline u32 * 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)); Index: linux-2.4.29/fs/nfsd/nfssvc.c =================================================================== --- linux-2.4.29.orig/fs/nfsd/nfssvc.c +++ linux-2.4.29/fs/nfsd/nfssvc.c @@ -155,6 +155,7 @@ static void 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 @@ nfsd(struct svc_rqst *rqstp) 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 @@ nfsd(struct svc_rqst *rqstp) list_del(&me.list); nfsdstats.th_cnt --; +out: /* Release the thread */ svc_exit_thread(rqstp); Index: linux-2.4.29/fs/nfsd/nfsxdr.c =================================================================== --- linux-2.4.29.orig/fs/nfsd/nfsxdr.c +++ linux-2.4.29/fs/nfsd/nfsxdr.c @@ -146,10 +146,20 @@ static inline u32 * 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)); Index: linux-2.4.29/fs/nfsd/vfs.c =================================================================== --- linux-2.4.29.orig/fs/nfsd/vfs.c +++ linux-2.4.29/fs/nfsd/vfs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1611,3 +1612,99 @@ nfsd_racache_init(int cache_size) 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 Index: linux-2.4.29/fs/posix_acl.c =================================================================== --- /dev/null +++ linux-2.4.29/fs/posix_acl.c @@ -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; +} Index: linux-2.4.29/fs/xattr_acl.c =================================================================== --- /dev/null +++ linux-2.4.29/fs/xattr_acl.c @@ -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(-EOPNOTSUPP); + + 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); Index: linux-2.4.29/include/linux/ext2_acl.h =================================================================== --- /dev/null +++ linux-2.4.29/include/linux/ext2_acl.h @@ -0,0 +1,100 @@ +/* + File: linux/ext2_acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +#define EXT2_ACL_VERSION 0x0001 + +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 Index: linux-2.4.29/include/linux/ext2_fs.h =================================================================== --- linux-2.4.29.orig/include/linux/ext2_fs.h +++ linux-2.4.29/include/linux/ext2_fs.h @@ -576,7 +576,7 @@ extern int ext2_sync_file (struct file * 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 int ext2_sync_inode (struct inode 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, Index: linux-2.4.29/include/linux/ext2_fs_i.h =================================================================== --- linux-2.4.29.orig/include/linux/ext2_fs_i.h +++ linux-2.4.29/include/linux/ext2_fs_i.h @@ -45,6 +45,10 @@ struct ext2_inode_info { */ struct rw_semaphore xattr_sem; #endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif }; /* Index: linux-2.4.29/include/linux/ext3_acl.h =================================================================== --- /dev/null +++ linux-2.4.29/include/linux/ext3_acl.h @@ -0,0 +1,107 @@ +/* + File: linux/ext3_acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +#define EXT3_ACL_VERSION 0x0001 + +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__ */ Index: linux-2.4.29/include/linux/ext3_fs.h =================================================================== --- linux-2.4.29.orig/include/linux/ext3_fs.h +++ linux-2.4.29/include/linux/ext3_fs.h @@ -315,6 +315,7 @@ struct ext3_inode { #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_check_dir_entry(const ch 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 *); Index: linux-2.4.29/include/linux/ext3_fs_i.h =================================================================== --- linux-2.4.29.orig/include/linux/ext3_fs_i.h +++ linux-2.4.29/include/linux/ext3_fs_i.h @@ -52,6 +52,10 @@ struct ext3_inode_info { */ 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 */ Index: linux-2.4.29/include/linux/fs.h =================================================================== --- linux-2.4.29.orig/include/linux/fs.h +++ linux-2.4.29/include/linux/fs.h @@ -111,6 +111,7 @@ extern int leases_enable, dir_notify_ena #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 @@ extern int leases_enable, dir_notify_ena #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) Index: linux-2.4.29/include/linux/nfs_mount.h =================================================================== --- linux-2.4.29.orig/include/linux/nfs_mount.h +++ linux-2.4.29/include/linux/nfs_mount.h @@ -53,6 +53,7 @@ struct nfs_mount_data { #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 Index: linux-2.4.29/include/linux/nfsd/export.h =================================================================== --- linux-2.4.29.orig/include/linux/nfsd/export.h +++ linux-2.4.29/include/linux/nfsd/export.h @@ -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 @@ struct svc_export { #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) /* Index: linux-2.4.29/include/linux/nfsd/nfsd.h =================================================================== --- linux-2.4.29.orig/include/linux/nfsd/nfsd.h +++ linux-2.4.29/include/linux/nfsd/nfsd.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -127,6 +128,22 @@ int nfsd_statfs(struct svc_rqst *, stru 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 + /* * lockd binding Index: linux-2.4.29/include/linux/posix_acl.h =================================================================== --- /dev/null +++ linux-2.4.29/include/linux/posix_acl.h @@ -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 */ Index: linux-2.4.29/include/linux/posix_acl_xattr.h =================================================================== --- /dev/null +++ linux-2.4.29/include/linux/posix_acl_xattr.h @@ -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 */ Index: linux-2.4.29/include/linux/xattr_acl.h =================================================================== --- /dev/null +++ linux-2.4.29/include/linux/xattr_acl.h @@ -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 */ Index: linux-2.4.29/kernel/Makefile =================================================================== --- linux-2.4.29.orig/kernel/Makefile +++ linux-2.4.29/kernel/Makefile @@ -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 \ Index: linux-2.4.29/kernel/fork.c =================================================================== --- linux-2.4.29.orig/kernel/fork.c +++ linux-2.4.29/kernel/fork.c @@ -407,6 +407,7 @@ struct fs_struct *copy_fs_struct(struct { return __copy_fs_struct(old); } +EXPORT_SYMBOL(copy_fs_struct); static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) {