-------------------------------------------------------------------- This patch adds ACL support for JFS 1.1.2. This patch was built against linux-2.4.20 + ea+acl+nfsacl-2.4.20-0.8.57 + JFS 1.1.2 + the JFS extended attribute patch. ea+acl+nfsacl-2.4.20-08.57 is available from http://acl.bestbits.at/ -------------------------------------------------------------------- diff -Nur linux-ea/Documentation/Configure.help linux-acl/Documentation/Configure.help --- linux-ea/Documentation/Configure.help 2003-05-08 14:07:13.000000000 -0500 +++ linux-acl/Documentation/Configure.help 2003-05-08 14:41:19.000000000 -0500 @@ -16232,6 +16232,10 @@ If you do not intend to use the JFS filesystem, say N. +JFS Posix ACL support +CONFIG_JFS_POSIX_ACL + Say Y here for Posix ACL support on JFS. + JFS Debugging CONFIG_JFS_DEBUG If you are experiencing any problems with the JFS filesystem, say diff -Nur linux-ea/fs/Config.in linux-acl/fs/Config.in --- linux-ea/fs/Config.in 2003-05-08 14:07:12.000000000 -0500 +++ linux-acl/fs/Config.in 2003-05-08 14:41:19.000000000 -0500 @@ -64,6 +64,7 @@ dep_mbool ' Transparent decompression extension' CONFIG_ZISOFS $CONFIG_ISO9660_FS tristate 'JFS filesystem support' CONFIG_JFS_FS +dep_mbool ' JFS Posix ACL support' CONFIG_JFS_POSIX_ACL $CONFIG_JFS_FS dep_mbool ' JFS debugging' CONFIG_JFS_DEBUG $CONFIG_JFS_FS dep_mbool ' JFS statistics' CONFIG_JFS_STATISTICS $CONFIG_JFS_FS @@ -182,7 +183,8 @@ # POSIX ACL helper functions if [ "$CONFIG_EXT2_FS_POSIX_ACL" = "y" -o \ - "$CONFIG_EXT3_FS_POSIX_ACL" = "y" ]; then + "$CONFIG_EXT3_FS_POSIX_ACL" = "y" -o \ + "$CONFIG_JFS_POSIX_ACL" = "y" ]; then define_bool CONFIG_FS_POSIX_ACL y else bool "POSIX ACL helper functions" CONFIG_FS_POSIX_ACL diff -Nur linux-ea/fs/jfs/Makefile linux-acl/fs/jfs/Makefile --- linux-ea/fs/jfs/Makefile 2003-05-08 14:13:00.000000000 -0500 +++ linux-acl/fs/jfs/Makefile 2003-05-08 14:41:19.000000000 -0500 @@ -14,6 +14,7 @@ jfs_extent.o symlink.o jfs_metapage.o \ jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o resize.o xattr.o obj-m := $(O_TARGET) +obj-$(CONFIG_JFS_POSIX_ACL) += acl.o EXTRA_CFLAGS += -D_JFS_4K diff -Nur linux-ea/fs/jfs/acl.c linux-acl/fs/jfs/acl.c --- linux-ea/fs/jfs/acl.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-acl/fs/jfs/acl.c 2003-05-08 14:41:19.000000000 -0500 @@ -0,0 +1,308 @@ +/* + * Copyright (c) International Business Machines Corp., 2002-2003 + * Copyright (c) Andreas Gruenbacher, 2001 + * Copyright (c) Linus Torvalds, 1991, 1992 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "jfs_incore.h" +#include "jfs_xattr.h" +#include "jfs_acl.h" + +static struct posix_acl *jfs_get_acl(struct inode *inode, int type) +{ + struct posix_acl *acl; + char *ea_name; + struct jfs_inode_info *ji = JFS_IP(inode); + struct posix_acl **p_acl; + int size; + char *value = NULL; + + switch(type) { + case ACL_TYPE_ACCESS: + ea_name = XATTR_NAME_ACL_ACCESS; + p_acl = &ji->i_acl; + break; + case ACL_TYPE_DEFAULT: + ea_name = XATTR_NAME_ACL_DEFAULT; + p_acl = &ji->i_default_acl; + break; + default: + return ERR_PTR(-EINVAL); + } + + if (*p_acl != JFS_ACL_NOT_CACHED) + return posix_acl_dup(*p_acl); + + size = __jfs_getxattr(inode, ea_name, NULL, 0); + + if (size > 0) { + value = kmalloc(size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + size = __jfs_getxattr(inode, ea_name, value, size); + } + + if (size < 0) { + if (size == -ENODATA) { + *p_acl = NULL; + acl = NULL; + } else + acl = ERR_PTR(size); + } else { + acl = posix_acl_from_xattr(value, size); + if (!IS_ERR(acl)) + *p_acl = posix_acl_dup(acl); + } + if (value) + kfree(value); + return acl; +} + +static int jfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + char *ea_name; + struct jfs_inode_info *ji = JFS_IP(inode); + struct posix_acl **p_acl; + int rc; + int size = 0; + char *value = NULL; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch(type) { + case ACL_TYPE_ACCESS: + ea_name = XATTR_NAME_ACL_ACCESS; + p_acl = &ji->i_acl; + break; + case ACL_TYPE_DEFAULT: + ea_name = XATTR_NAME_ACL_DEFAULT; + p_acl = &ji->i_default_acl; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + default: + return -EINVAL; + } + if (acl) { + size = xattr_acl_size(acl->a_count); + value = kmalloc(size, GFP_KERNEL); + if (!value) + return -ENOMEM; + rc = posix_acl_to_xattr(acl, value, size); + if (rc < 0) + goto out; + } + rc = __jfs_setxattr(inode, ea_name, value, size, 0); +out: + if (value) + kfree(value); + + if (!rc) { + if (*p_acl && (*p_acl != JFS_ACL_NOT_CACHED)) + posix_acl_release(*p_acl); + *p_acl = posix_acl_dup(acl); + } + return rc; +} + +/* + * __jfs_permission() + * + * modified vfs_permission to check posix acl + */ +static int __jfs_permission(struct inode * inode, int mask, int have_sem) +{ + umode_t mode = inode->i_mode; + struct jfs_inode_info *ji = JFS_IP(inode); + + if (mask & MAY_WRITE) { + /* + * Nobody gets write access to a read-only fs. + */ + if (IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + + /* + * Nobody gets write access to an immutable file. + */ + if (IS_IMMUTABLE(inode)) + return -EACCES; + } + + if (current->fsuid == inode->i_uid) { + mode >>= 6; + goto check_mode; + } + /* + * ACL can't contain additional permissions if the ACL_MASK entry + * is zero. + */ + if (!(mode & S_IRWXG)) + goto check_groups; + + if (ji->i_acl == JFS_ACL_NOT_CACHED) { + struct posix_acl *acl; + + if (!have_sem) + down(&inode->i_sem); + acl = jfs_get_acl(inode, ACL_TYPE_ACCESS); + if (!have_sem) + up(&inode->i_sem); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + } + + if (ji->i_acl) { + int rc = posix_acl_permission(inode, ji->i_acl, mask); + if (rc == -EACCES) + goto check_capabilities; + return rc; + } + +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + +check_mode: + /* + * If the DACs are ok we don't need any capability check. + */ + if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)) + return 0; + +check_capabilities: + /* + * Read/write DACs are always overridable. + * Executable DACs are overridable if at least one exec bit is set. + */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + + /* + * Searching includes executable on directories, else just read. + */ + if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) + if (capable(CAP_DAC_READ_SEARCH)) + return 0; + + return -EACCES; +} +int jfs_permission(struct inode * inode, int mask) +{ + return __jfs_permission(inode, mask, 0); +} +int jfs_permission_have_sem(struct inode * inode, int mask) +{ + return __jfs_permission(inode, mask, 1); +} + +int jfs_init_acl(struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + struct posix_acl *clone; + mode_t mode; + int rc = 0; + + if (S_ISLNK(inode->i_mode)) + return 0; + + acl = jfs_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + if (acl) { + if (S_ISDIR(inode->i_mode)) { + rc = jfs_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (rc) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + if (!clone) { + rc = -ENOMEM; + goto cleanup; + } + mode = inode->i_mode; + rc = posix_acl_create_masq(clone, &mode); + if (rc >= 0) { + inode->i_mode = mode; + if (rc > 0) + rc = jfs_set_acl(inode, ACL_TYPE_ACCESS, clone); + } + posix_acl_release(clone); +cleanup: + posix_acl_release(acl); + } else + inode->i_mode &= ~current->fs->umask; + + return rc; +} + +int jfs_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int rc; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + acl = jfs_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; + + rc = posix_acl_chmod_masq(clone, inode->i_mode); + if (!rc) + rc = jfs_set_acl(inode, ACL_TYPE_ACCESS, clone); + + posix_acl_release(clone); + return rc; +} + +int jfs_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode = dentry->d_inode; + int rc; + + rc = inode_change_ok(inode, iattr); + if (rc) + return rc; + + inode_setattr(inode, iattr); + + if (iattr->ia_valid & ATTR_MODE) { + /* + * Mode change may affect acl. i_sem already held by + * truncate (ia_valid & ATTR_SIZE) + */ + if (!(iattr->ia_valid & ATTR_SIZE)) + down(&inode->i_sem); + rc = jfs_acl_chmod(inode); + if (!(iattr->ia_valid & ATTR_SIZE)) + up(&inode->i_sem); + } + return rc; +} diff -Nur linux-ea/fs/jfs/file.c linux-acl/fs/jfs/file.c --- linux-ea/fs/jfs/file.c 2003-05-08 14:13:00.000000000 -0500 +++ linux-acl/fs/jfs/file.c 2003-05-08 14:41:19.000000000 -0500 @@ -23,6 +23,7 @@ #include "jfs_dmap.h" #include "jfs_txnmgr.h" #include "jfs_xattr.h" +#include "jfs_acl.h" #include "jfs_debug.h" @@ -96,6 +97,10 @@ .getxattr = jfs_getxattr, .listxattr = jfs_listxattr, .removexattr = jfs_removexattr, +#ifdef CONFIG_JFS_POSIX_ACL + .setattr = jfs_setattr, + .permission = jfs_permission, +#endif }; struct file_operations jfs_file_operations = { diff -Nur linux-ea/fs/jfs/inode.c linux-acl/fs/jfs/inode.c --- linux-ea/fs/jfs/inode.c 2003-05-08 14:13:00.000000000 -0500 +++ linux-acl/fs/jfs/inode.c 2003-05-08 14:41:19.000000000 -0500 @@ -24,6 +24,7 @@ #include "jfs_dmap.h" #include "jfs_imap.h" #include "jfs_extent.h" +#include "jfs_acl.h" #include "jfs_unicode.h" #include "jfs_debug.h" @@ -57,6 +58,17 @@ ASSERT(list_empty(&ji->anon_inode_list)); +#ifdef CONFIG_JFS_POSIX_ACL + if (ji->i_acl != JFS_ACL_NOT_CACHED) { + posix_acl_release(ji->i_acl); + ji->i_acl = JFS_ACL_NOT_CACHED; + } + if (ji->i_default_acl != JFS_ACL_NOT_CACHED) { + posix_acl_release(ji->i_default_acl); + ji->i_default_acl = JFS_ACL_NOT_CACHED; + } +#endif + if (ji->atlhead) { jfs_err("jfs_clear_inode: inode %p has anonymous tlocks", inode); diff -Nur linux-ea/fs/jfs/jfs_acl.h linux-acl/fs/jfs/jfs_acl.h --- linux-ea/fs/jfs/jfs_acl.h 1969-12-31 18:00:00.000000000 -0600 +++ linux-acl/fs/jfs/jfs_acl.h 2003-05-08 14:41:19.000000000 -0500 @@ -0,0 +1,32 @@ +/* + * Copyright (c) International Business Machines Corp., 2002-2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _H_JFS_ACL +#define _H_JFS_ACL + +#ifdef CONFIG_JFS_POSIX_ACL + +#include + +int jfs_permission_have_sem(struct inode *, int); +int jfs_permission(struct inode *, int); +int jfs_init_acl(struct inode *, struct inode *); +int jfs_setattr(struct dentry *, struct iattr *); + +#endif /* CONFIG_JFS_POSIX_ACL */ + +#endif /* _H_JFS_ACL */ diff -Nur linux-ea/fs/jfs/jfs_incore.h linux-acl/fs/jfs/jfs_incore.h --- linux-ea/fs/jfs/jfs_incore.h 2003-05-08 14:13:01.000000000 -0500 +++ linux-acl/fs/jfs/jfs_incore.h 2003-05-08 14:41:19.000000000 -0500 @@ -69,6 +69,10 @@ */ struct semaphore commit_sem; lid_t xtlid; /* lid of xtree lock on directory */ +#ifdef CONFIG_JFS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif union { struct { xtpage_t _xtroot; /* 288: xtree root */ @@ -96,6 +100,7 @@ #define i_inline u.link._inline #define i_inline_ea u.link._inline_ea +#define JFS_ACL_NOT_CACHED ((void *)-1) #define IREAD_LOCK(ip) down_read(&JFS_IP(ip)->rdwrlock) #define IREAD_UNLOCK(ip) up_read(&JFS_IP(ip)->rdwrlock) diff -Nur linux-ea/fs/jfs/namei.c linux-acl/fs/jfs/namei.c --- linux-ea/fs/jfs/namei.c 2003-05-08 14:13:01.000000000 -0500 +++ linux-acl/fs/jfs/namei.c 2003-05-08 14:41:19.000000000 -0500 @@ -25,6 +25,7 @@ #include "jfs_unicode.h" #include "jfs_metapage.h" #include "jfs_xattr.h" +#include "jfs_acl.h" #include "jfs_debug.h" extern struct inode_operations jfs_file_inode_operations; @@ -35,6 +36,7 @@ extern int jfs_fsync(struct file *, struct dentry *, int); extern void jfs_truncate_nolock(struct inode *, loff_t); +extern int jfs_init_acl(struct inode *, struct inode *); /* * forward references @@ -150,6 +152,11 @@ out2: free_UCSname(&dname); +#ifdef CONFIG_JFS_POSIX_ACL + if (rc == 0) + jfs_init_acl(ip, dip); +#endif + out1: jfs_info("jfs_create: rc:%d", -rc); @@ -275,6 +282,11 @@ out2: free_UCSname(&dname); +#ifdef CONFIG_JFS_POSIX_ACL + if (rc == 0) + jfs_init_acl(ip, dip); +#endif + out1: jfs_info("jfs_mkdir: rc:%d", -rc); @@ -1009,6 +1021,11 @@ out2: free_UCSname(&dname); +#ifdef CONFIG_JFS_POSIX_ACL + if (rc == 0) + jfs_init_acl(ip, dip); +#endif + out1: jfs_info("jfs_symlink: rc:%d", -rc); return -rc; @@ -1345,6 +1362,11 @@ out1: free_UCSname(&dname); +#ifdef CONFIG_JFS_POSIX_ACL + if (rc == 0) + jfs_init_acl(ip, dir); +#endif + out: jfs_info("jfs_mknod: returning %d", rc); return -rc; @@ -1407,6 +1429,10 @@ .getxattr = jfs_getxattr, .listxattr = jfs_listxattr, .removexattr = jfs_removexattr, +#ifdef CONFIG_JFS_POSIX_ACL + .setattr = jfs_setattr, + .permission = jfs_permission, +#endif }; struct file_operations jfs_dir_operations = { diff -Nur linux-ea/fs/jfs/super.c linux-acl/fs/jfs/super.c --- linux-ea/fs/jfs/super.c 2003-05-08 14:10:45.000000000 -0500 +++ linux-acl/fs/jfs/super.c 2003-05-08 14:41:19.000000000 -0500 @@ -432,6 +432,10 @@ init_MUTEX(&jfs_ip->commit_sem); jfs_ip->atlhead = 0; jfs_ip->active_ag = -1; +#ifdef CONFIG_JFS_POSIX_ACL + jfs_ip->i_acl = JFS_ACL_NOT_CACHED; + jfs_ip->i_default_acl = JFS_ACL_NOT_CACHED; +#endif } } diff -Nur linux-ea/fs/jfs/xattr.c linux-acl/fs/jfs/xattr.c --- linux-ea/fs/jfs/xattr.c 2003-05-08 14:30:04.000000000 -0500 +++ linux-acl/fs/jfs/xattr.c 2003-05-08 14:41:19.000000000 -0500 @@ -26,6 +26,7 @@ #include "jfs_extent.h" #include "jfs_metapage.h" #include "jfs_xattr.h" +#include "jfs_acl.h" /* * jfs_xattr.c: extended attribute service @@ -201,7 +202,6 @@ ji->mode2 |= INLINEEA; } - mark_inode_dirty(ip); return 0; } @@ -640,6 +640,71 @@ return rc; } +/* + * can_set_system_xattr + * + * This code is specific to the system.* namespace. It contains policy + * which doesn't belong in the main xattr codepath. + */ +static int can_set_system_xattr(struct inode *inode, const char *name, + const void *value, size_t value_len) +{ +#ifdef CONFIG_JFS_POSIX_ACL + struct posix_acl *acl; + int rc; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + /* + * XATTR_NAME_ACL_ACCESS is tied to i_mode + */ + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) { + acl = posix_acl_from_xattr(value, value_len); + if (acl < 0) { + printk(KERN_ERR "posix_acl_from_xattr returned %d\n", + rc); + return rc; + } + if (acl > 0) { + mode_t mode = inode->i_mode; + rc = posix_acl_equiv_mode(acl, &mode); + posix_acl_release(acl); + if (rc < 0) { + printk(KERN_ERR + "posix_acl_equiv_mode returned %d\n", + rc); + return rc; + } + inode->i_mode = mode; + mark_inode_dirty(inode); + if (rc == 0) + value = NULL; + } + /* + * We're changing the ACL. Get rid of the cached one + */ + acl =JFS_IP(inode)->i_acl; + if (acl && (acl != JFS_ACL_NOT_CACHED)) + posix_acl_release(acl); + JFS_IP(inode)->i_acl = JFS_ACL_NOT_CACHED; + } else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) { + /* + * We're changing the default ACL. Get rid of the cached one + */ + acl =JFS_IP(inode)->i_default_acl; + if (acl && (acl != JFS_ACL_NOT_CACHED)) + posix_acl_release(acl); + JFS_IP(inode)->i_default_acl = JFS_ACL_NOT_CACHED; + } else + /* Invalid xattr name */ + return -EINVAL; + return 0; +#else /* CONFIG_JFS_POSIX_ACL */ + return -EOPNOTSUPP; +#endif /* CONFIG_JFS_POSIX_ACL */ +} + static int can_set_xattr(struct inode *inode, const char *name, const void *value, size_t value_len) { @@ -649,6 +714,12 @@ if (IS_IMMUTABLE(inode) || IS_APPEND(inode) || S_ISLNK(inode->i_mode)) return -EPERM; + if(strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) == 0) + /* + * "system.*" + */ + return can_set_system_xattr(inode, name, value, value_len); + if((strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) != 0) && (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) != 0)) return -EOPNOTSUPP; @@ -657,7 +728,11 @@ (!S_ISDIR(inode->i_mode) || inode->i_mode &S_ISVTX)) return -EPERM; +#ifdef CONFIG_JFS_POSIX_ACL + return jfs_permission_have_sem(inode, MAY_WRITE); +#else return permission(inode, MAY_WRITE); +#endif } int __jfs_setxattr(struct inode *inode, const char *name, const void *value, @@ -810,9 +885,16 @@ return __jfs_setxattr(dentry->d_inode, name, value, value_len, flags); } -static inline int can_get_xattr(struct inode *inode, const char *name) +static int can_get_xattr(struct inode *inode, const char *name) { +#ifdef CONFIG_JFS_POSIX_ACL + if(strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) == 0) + return 0; + else + return jfs_permission_have_sem(inode, MAY_READ); +#else return permission(inode, MAY_READ); +#endif } ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data,