From: Andreas Gruenbacher Subject: NFSv4 ACLs for ext3 Patch-mainline: Not yet With the acl=nfs4 mount option, ext3 will use NFSv4 ACLs instead of POSIX ACLs. See http://www.suse.de/~agruen/nfs4acl/ for some documentation and examples. Signed-off-by: Andreas Gruenbacher --- fs/ext3/Kconfig | 7 fs/ext3/Makefile | 1 fs/ext3/file.c | 4 fs/ext3/ialloc.c | 6 fs/ext3/inode.c | 73 ++++++++ fs/ext3/namei.c | 15 + fs/ext3/namei.h | 1 fs/ext3/nfs4acl.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ext3/nfs4acl.h | 36 ++++ fs/ext3/super.c | 61 +++++-- fs/ext3/xattr.c | 9 + fs/ext3/xattr.h | 5 include/linux/ext3_fs.h | 1 include/linux/ext3_fs_i.h | 3 14 files changed, 582 insertions(+), 18 deletions(-) --- a/fs/ext3/Kconfig +++ b/fs/ext3/Kconfig @@ -97,6 +97,13 @@ config EXT3_FS_POSIX_ACL If you don't know what Access Control Lists are, say N +config EXT3_FS_NFS4ACL + bool "Native NFSv4 ACLs (EXPERIMENTAL)" + depends on EXT3_FS_XATTR && EXPERIMENTAL + select FS_NFS4ACL + help + Allow to use NFSv4 ACLs instead of POSIX ACLs. + config EXT3_FS_SECURITY bool "Ext3 Security Labels" depends on EXT3_FS_XATTR --- a/fs/ext3/Makefile +++ b/fs/ext3/Makefile @@ -10,3 +10,4 @@ ext3-y := balloc.o bitmap.o dir.o file.o ext3-$(CONFIG_EXT3_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext3-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o ext3-$(CONFIG_EXT3_FS_SECURITY) += xattr_security.o +ext3-$(CONFIG_EXT3_FS_NFS4ACL) += nfs4acl.o --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -24,8 +24,10 @@ #include #include #include +#include "namei.h" #include "xattr.h" #include "acl.h" +#include "nfs4acl.h" /* * Called when an inode is released. Note that this is different @@ -81,5 +83,7 @@ const struct inode_operations ext3_file_ #endif .check_acl = ext3_check_acl, .fiemap = ext3_fiemap, + .may_create = ext3_may_create, + .may_delete = ext3_may_delete, }; --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -28,6 +28,7 @@ #include "xattr.h" #include "acl.h" +#include "nfs4acl.h" /* * ialloc.c contains the inodes allocation and deallocation routines @@ -593,7 +594,10 @@ got: if (err) goto fail_drop; - err = ext3_init_acl(handle, inode, dir); + if (test_opt(sb, NFS4ACL)) + err = ext3_nfs4acl_init(handle, inode, dir); + else + err = ext3_init_acl(handle, inode, dir); if (err) goto fail_free_drop; --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -40,6 +40,7 @@ #include #include "xattr.h" #include "acl.h" +#include "nfs4acl.h" static int ext3_writepage_trans_blocks(struct inode *inode); @@ -2790,6 +2791,9 @@ struct inode *ext3_iget(struct super_blo return inode; ei = EXT3_I(inode); +#ifdef CONFIG_EXT3_FS_NFS4ACL + ei->i_nfs4acl = EXT3_NFS4ACL_NOT_CACHED; +#endif ei->i_block_alloc_info = NULL; ret = __ext3_get_inode_loc(inode, &iloc, 0); @@ -3124,6 +3128,65 @@ int ext3_write_inode(struct inode *inode return ext3_force_commit(inode->i_sb); } +#ifdef CONFIG_EXT3_FS_NFS4ACL +static int ext3_inode_change_ok(struct inode *inode, struct iattr *attr) +{ + unsigned int ia_valid = attr->ia_valid; + + if (!test_opt(inode->i_sb, NFS4ACL)) + return inode_change_ok(inode, attr); + + /* If force is set do it anyway. */ + if (ia_valid & ATTR_FORCE) + return 0; + + /* Make sure a caller can chown. */ + if ((ia_valid & ATTR_UID) && + (current_fsuid() != inode->i_uid || + attr->ia_uid != inode->i_uid) && + (current_fsuid() != attr->ia_uid || + ext3_nfs4acl_permission(inode, ACE4_WRITE_OWNER)) && + !capable(CAP_CHOWN)) + goto error; + + /* Make sure caller can chgrp. */ + if ((ia_valid & ATTR_GID)) { + int in_group = in_group_p(attr->ia_gid); + if ((current_fsuid() != inode->i_uid || + (!in_group && attr->ia_gid != inode->i_gid)) && + (!in_group || + ext3_nfs4acl_permission(inode, ACE4_WRITE_OWNER)) && + !capable(CAP_CHOWN)) + goto error; + } + + /* Make sure a caller can chmod. */ + if (ia_valid & ATTR_MODE) { + if (current_fsuid() != inode->i_uid && + ext3_nfs4acl_permission(inode, ACE4_WRITE_ACL) && + !capable(CAP_FOWNER)) + goto error; + /* Also check the setgid bit! */ + if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : + inode->i_gid) && !capable(CAP_FSETID)) + attr->ia_mode &= ~S_ISGID; + } + + /* Check for setting the inode time. */ + if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { + if (current_fsuid() != inode->i_uid && + ext3_nfs4acl_permission(inode, ACE4_WRITE_ATTRIBUTES) && + !capable(CAP_FOWNER)) + goto error; + } + return 0; +error: + return -EPERM; +} +#else +# define ext3_inode_change_ok inode_change_ok +#endif + /* * ext3_setattr() * @@ -3147,7 +3210,7 @@ int ext3_setattr(struct dentry *dentry, int error, rc = 0; const unsigned int ia_valid = attr->ia_valid; - error = inode_change_ok(inode, attr); + error = ext3_inode_change_ok(inode, attr); if (error) return error; @@ -3200,8 +3263,12 @@ int ext3_setattr(struct dentry *dentry, rc = inode_setattr(inode, attr); - if (!rc && (ia_valid & ATTR_MODE)) - rc = ext3_acl_chmod(inode); + if (!rc && (ia_valid & ATTR_MODE)) { + if (test_opt(inode->i_sb, NFS4ACL)) + rc = ext3_nfs4acl_chmod(inode); + else + rc = ext3_acl_chmod(inode); + } err_out: ext3_std_error(inode->i_sb, error); --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -40,6 +40,7 @@ #include "namei.h" #include "xattr.h" #include "acl.h" +#include "nfs4acl.h" /* * define how far ahead to read directories while searching them. @@ -2445,6 +2446,16 @@ end_rename: return retval; } +int ext3_permission(struct inode *inode, int mask) +{ +#ifdef CONFIG_EXT3_FS_NFS4ACL + if (test_opt(inode->i_sb, NFS4ACL)) + return ext3_nfs4acl_permission(inode, nfs4acl_want_to_mask(mask)); + else +#endif + return generic_permission(inode, mask, ext3_check_acl); +} + /* * directories can handle most operations... */ @@ -2466,6 +2477,8 @@ const struct inode_operations ext3_dir_i .removexattr = generic_removexattr, #endif .check_acl = ext3_check_acl, + .may_create = ext3_may_create, + .may_delete = ext3_may_delete, }; const struct inode_operations ext3_special_inode_operations = { @@ -2477,4 +2490,6 @@ const struct inode_operations ext3_speci .removexattr = generic_removexattr, #endif .check_acl = ext3_check_acl, + .may_create = ext3_may_create, + .may_delete = ext3_may_delete, }; --- a/fs/ext3/namei.h +++ b/fs/ext3/namei.h @@ -5,4 +5,5 @@ * */ +extern int ext3_permission (struct inode *, int); extern struct dentry *ext3_get_parent(struct dentry *child); --- /dev/null +++ b/fs/ext3/nfs4acl.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2006 Andreas Gruenbacher + * + * 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, 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. + */ + +#include +#include +#include +#include +#include +#include +#include "namei.h" +#include "xattr.h" +#include "nfs4acl.h" + +static inline struct nfs4acl * +ext3_iget_nfs4acl(struct inode *inode) +{ + struct nfs4acl *acl = EXT3_NFS4ACL_NOT_CACHED; + struct ext3_inode_info *ei = EXT3_I(inode); + + spin_lock(&inode->i_lock); + if (ei->i_nfs4acl != EXT3_NFS4ACL_NOT_CACHED) + acl = nfs4acl_get(ei->i_nfs4acl); + spin_unlock(&inode->i_lock); + + return acl; +} + +static inline void +ext3_iset_nfs4acl(struct inode *inode, struct nfs4acl *acl) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + + spin_lock(&inode->i_lock); + if (ei->i_nfs4acl != EXT3_NFS4ACL_NOT_CACHED) + nfs4acl_put(ei->i_nfs4acl); + ei->i_nfs4acl = nfs4acl_get(acl); + spin_unlock(&inode->i_lock); +} + +static struct nfs4acl * +ext3_get_nfs4acl(struct inode *inode) +{ + const int name_index = EXT3_XATTR_INDEX_NFS4ACL; + void *value = NULL; + struct nfs4acl *acl; + int retval; + + if (!test_opt(inode->i_sb, NFS4ACL)) + return NULL; + + acl = ext3_iget_nfs4acl(inode); + if (acl != EXT3_NFS4ACL_NOT_CACHED) + return acl; + 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 = nfs4acl_from_xattr(value, retval); + if (acl == ERR_PTR(-EINVAL)) + acl = ERR_PTR(-EIO); + } else if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + else + acl = ERR_PTR(retval); + kfree(value); + + if (!IS_ERR(acl)) + ext3_iset_nfs4acl(inode, acl); + + return acl; +} + +static int +ext3_set_nfs4acl(handle_t *handle, struct inode *inode, struct nfs4acl *acl) +{ + const int name_index = EXT3_XATTR_INDEX_NFS4ACL; + size_t size = 0; + void *value = NULL; + int retval; + + if (acl) { + size = nfs4acl_xattr_size(acl); + value = kmalloc(size, GFP_KERNEL); + if (!value) + return -ENOMEM; + nfs4acl_to_xattr(acl, value); + } + if (handle) + retval = ext3_xattr_set_handle(handle, inode, name_index, "", + value, size, 0); + else + retval = ext3_xattr_set(inode, name_index, "", value, size, 0); + if (value) + kfree(value); + if (!retval) + ext3_iset_nfs4acl(inode, acl); + + return retval; +} + +int +ext3_nfs4acl_permission(struct inode *inode, unsigned int mask) +{ + struct nfs4acl *acl; + int retval; + + BUG_ON(!test_opt(inode->i_sb, NFS4ACL)); + + acl = ext3_get_nfs4acl(inode); + if (!acl) + retval = nfs4acl_generic_permission(inode, mask); + else if (IS_ERR(acl)) + retval = PTR_ERR(acl); + else { + retval = nfs4acl_permission(inode, acl, mask); + nfs4acl_put(acl); + } + + return retval; +} + +int ext3_may_create(struct inode *dir, int isdir) +{ + int error; + + if (test_opt(dir->i_sb, NFS4ACL)) { + unsigned int mask = (isdir ? ACE4_ADD_SUBDIRECTORY : ACE4_ADD_FILE) | + ACE4_EXECUTE; + + error = ext3_nfs4acl_permission(dir, mask); + } else + error = ext3_permission(dir, MAY_WRITE | MAY_EXEC); + + return error; +} + +static int check_sticky(struct inode *dir, struct inode *inode) +{ + if (!(dir->i_mode & S_ISVTX)) + return 0; + if (inode->i_uid == current_fsuid()) + return 0; + if (dir->i_uid == current_fsuid()) + return 0; + return !capable(CAP_FOWNER); +} + +int ext3_may_delete(struct inode *dir, struct inode *inode) +{ + int error; + + if (test_opt(inode->i_sb, NFS4ACL)) { + error = ext3_nfs4acl_permission(dir, ACE4_DELETE_CHILD | ACE4_EXECUTE); + if (!error && check_sticky(dir, inode)) + error = -EPERM; + if (error && !ext3_nfs4acl_permission(inode, ACE4_DELETE)) + error = 0; + } else { + error = ext3_permission(dir, MAY_WRITE | MAY_EXEC); + if (!error && check_sticky(dir, inode)) + error = -EPERM; + } + + return error; +} + +int +ext3_nfs4acl_init(handle_t *handle, struct inode *inode, struct inode *dir) +{ + struct nfs4acl *dir_acl = NULL, *acl; + int retval; + + if (!S_ISLNK(inode->i_mode)) + dir_acl = ext3_get_nfs4acl(dir); + if (!dir_acl || IS_ERR(dir_acl)) { + inode->i_mode &= ~current->fs->umask; + return PTR_ERR(dir_acl); + } + acl = nfs4acl_inherit(dir_acl, inode->i_mode); + nfs4acl_put(dir_acl); + + retval = PTR_ERR(acl); + if (acl && !IS_ERR(acl)) { + retval = ext3_set_nfs4acl(handle, inode, acl); + inode->i_mode = (inode->i_mode & ~S_IRWXUGO) | + nfs4acl_masks_to_mode(acl); + nfs4acl_put(acl); + } + return retval; +} + +int +ext3_nfs4acl_chmod(struct inode *inode) +{ + struct nfs4acl *acl; + int retval; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = ext3_get_nfs4acl(inode); + if (!acl || IS_ERR(acl)) + return PTR_ERR(acl); + acl = nfs4acl_chmod(acl, inode->i_mode); + if (IS_ERR(acl)) + return PTR_ERR(acl); + retval = ext3_set_nfs4acl(NULL, inode, acl); + nfs4acl_put(acl); + + return retval; +} + +static size_t +ext3_xattr_list_nfs4acl(struct dentry *dentry, char *list, size_t list_len, + const char *name, size_t name_len, int handler_flags) +{ + struct inode *inode = dentry->d_inode; + const size_t size = sizeof(NFS4ACL_XATTR); + + if (!test_opt(inode->i_sb, NFS4ACL)) + return 0; + if (list && size <= list_len) + memcpy(list, NFS4ACL_XATTR, size); + return size; +} + +static int +ext3_xattr_get_nfs4acl(struct dentry *dentry, const char *name, void *buffer, + size_t buffer_size, int handler_flags) +{ + struct inode *inode = dentry->d_inode; + struct nfs4acl *acl; + size_t size; + + if (!test_opt(inode->i_sb, NFS4ACL)) + return -EOPNOTSUPP; + if (strcmp(name, "") != 0) + return -EINVAL; + + acl = ext3_get_nfs4acl(inode); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + size = nfs4acl_xattr_size(acl); + if (buffer) { + if (size > buffer_size) + return -ERANGE; + nfs4acl_to_xattr(acl, buffer); + } + nfs4acl_put(acl); + + return size; +} + +#ifdef NFS4ACL_DEBUG +static size_t +ext3_xattr_list_masked_nfs4acl(struct dentry *dentry, char *list, + size_t list_len, const char *name, + size_t name_len, int handler_flags) +{ + return 0; +} + +static int +ext3_xattr_get_masked_nfs4acl(struct dentry *dentry, const char *name, + void *buffer, size_t buffer_size, + int handler_flags) +{ + struct inode *inode = dentry->d_inode; + const int name_index = EXT3_XATTR_INDEX_NFS4ACL; + struct nfs4acl *acl; + void *xattr; + size_t size; + int retval; + + if (!test_opt(inode->i_sb, NFS4ACL)) + return -EOPNOTSUPP; + if (strcmp(name, "") != 0) + return -EINVAL; + retval = ext3_xattr_get(inode, name_index, "", NULL, 0); + if (retval <= 0) + return retval; + xattr = kmalloc(retval, GFP_KERNEL); + if (!xattr) + return -ENOMEM; + retval = ext3_xattr_get(inode, name_index, "", xattr, retval); + if (retval <= 0) + return retval; + acl = nfs4acl_from_xattr(xattr, retval); + kfree(xattr); + if (IS_ERR(acl)) + return PTR_ERR(acl); + retval = nfs4acl_apply_masks(&acl); + if (retval) { + nfs4acl_put(acl); + return retval; + } + size = nfs4acl_xattr_size(acl); + if (buffer) { + if (size > buffer_size) + return -ERANGE; + nfs4acl_to_xattr(acl, buffer); + } + nfs4acl_put(acl); + return size; +} +#endif + +static int +ext3_xattr_set_nfs4acl(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, + int handler_flags) +{ + struct inode *inode = dentry->d_inode; + handle_t *handle; + struct nfs4acl *acl = NULL; + int retval, retries = 0; + + if (S_ISLNK(inode->i_mode) || !test_opt(inode->i_sb, NFS4ACL)) + return -EOPNOTSUPP; + if (strcmp(name, "") != 0) + return -EINVAL; + if (current_fsuid() != inode->i_uid && + ext3_nfs4acl_permission(inode, ACE4_WRITE_ACL) && + !capable(CAP_FOWNER)) + return -EPERM; + if (value) { + acl = nfs4acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + inode->i_mode &= ~S_IRWXUGO; + inode->i_mode |= nfs4acl_masks_to_mode(acl); + } + +retry: + handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb)); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ext3_mark_inode_dirty(handle, inode); + retval = ext3_set_nfs4acl(handle, inode, acl); + ext3_journal_stop(handle); + if (retval == ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) + goto retry; + nfs4acl_put(acl); + return retval; +} + +struct xattr_handler ext3_nfs4acl_xattr_handler = { + .prefix = NFS4ACL_XATTR, + .list = ext3_xattr_list_nfs4acl, + .get = ext3_xattr_get_nfs4acl, + .set = ext3_xattr_set_nfs4acl, +}; + +#ifdef NFS4ACL_DEBUG +struct xattr_handler ext3_masked_nfs4acl_xattr_handler = { + .prefix = "system.masked-nfs4acl", + .list = ext3_xattr_list_masked_nfs4acl, + .get = ext3_xattr_get_masked_nfs4acl, + .set = ext3_xattr_set_nfs4acl, +}; +#endif --- /dev/null +++ b/fs/ext3/nfs4acl.h @@ -0,0 +1,36 @@ +#ifndef __FS_EXT3_NFS4ACL_H +#define __FS_EXT3_NFS4ACL_H + +#ifdef CONFIG_EXT3_FS_NFS4ACL + +#include + +/* Value for i_nfs4acl if NFS4ACL has not been cached */ +#define EXT3_NFS4ACL_NOT_CACHED ((void *)-1) + +extern int ext3_nfs4acl_permission(struct inode *, unsigned int); +extern int ext3_may_create(struct inode *, int); +extern int ext3_may_delete(struct inode *, struct inode *); +extern int ext3_nfs4acl_init(handle_t *, struct inode *, struct inode *); +extern int ext3_nfs4acl_chmod(struct inode *); + +#else /* CONFIG_FS_EXT3_NFS4ACL */ + +#define ext3_may_create NULL +#define ext3_may_delete NULL + +static inline int +ext3_nfs4acl_init(handle_t *handle, struct inode *inode, struct inode *dir) +{ + return 0; +} + +static inline int +ext3_nfs4acl_chmod(struct inode *inode) +{ + return 0; +} + +#endif /* CONFIG_FS_EXT3_NFS4ACL */ + +#endif /* __FS_EXT3_NFS4ACL_H */ --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -36,12 +36,14 @@ #include #include #include +#include #include #include #include "xattr.h" #include "acl.h" +#include "nfs4acl.h" #include "namei.h" #ifdef CONFIG_EXT3_DEFAULTS_TO_ORDERED @@ -476,6 +478,9 @@ static struct inode *ext3_alloc_inode(st ei = kmem_cache_alloc(ext3_inode_cachep, GFP_NOFS); if (!ei) return NULL; +#ifdef CONFIG_EXT3_FS_NFS4ACL + ei->i_nfs4acl = EXT3_NFS4ACL_NOT_CACHED; +#endif ei->i_block_alloc_info = NULL; ei->vfs_inode.i_version = 1; atomic_set(&ei->i_datasync_tid, 0); @@ -529,6 +534,13 @@ static void ext3_clear_inode(struct inod { struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info; +#ifdef CONFIG_EXT3_FS_NFS4ACL + if (EXT3_I(inode)->i_nfs4acl && + EXT3_I(inode)->i_nfs4acl != EXT3_NFS4ACL_NOT_CACHED) { + nfs4acl_put(EXT3_I(inode)->i_nfs4acl); + EXT3_I(inode)->i_nfs4acl = EXT3_NFS4ACL_NOT_CACHED; + } +#endif dquot_drop(inode); ext3_discard_reservation(inode); EXT3_I(inode)->i_block_alloc_info = NULL; @@ -803,7 +815,7 @@ enum { Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, - Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, + Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_acl_flavor, Opt_noacl, Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, Opt_bh, Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, @@ -836,6 +848,7 @@ static const match_table_t tokens = { {Opt_user_xattr, "user_xattr"}, {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, + {Opt_acl_flavor, "acl=%s"}, {Opt_noacl, "noacl"}, {Opt_reservation, "reservation"}, {Opt_noreservation, "noreservation"}, @@ -1040,20 +1053,33 @@ static int parse_options (char *options, "(no)user_xattr options not supported"); break; #endif -#ifdef CONFIG_EXT3_FS_POSIX_ACL case Opt_acl: - set_opt(sbi->s_mount_opt, POSIX_ACL); + args[0].to = args[0].from; + /* fall through */ + case Opt_acl_flavor: +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (match_string(&args[0], "") || + match_string(&args[0], "posix")) { + set_opt(sbi->s_mount_opt, POSIX_ACL); + clear_opt(sbi->s_mount_opt, NFS4ACL); + } else +#endif +#ifdef CONFIG_EXT3_FS_NFS4ACL + if (match_string(&args[0], "nfs4")) { + clear_opt(sbi->s_mount_opt, POSIX_ACL); + set_opt(sbi->s_mount_opt, NFS4ACL); + } else +#endif + { + ext3_msg(sb, KERN_ERR, + "unsupported acl flavor"); + return 0; + } break; case Opt_noacl: clear_opt(sbi->s_mount_opt, POSIX_ACL); + clear_opt(sbi->s_mount_opt, NFS4ACL); break; -#else - case Opt_acl: - case Opt_noacl: - ext3_msg(sb, KERN_INFO, - "(no)acl options not supported"); - break; -#endif case Opt_reservation: set_opt(sbi->s_mount_opt, RESERVATION); break; @@ -1698,8 +1724,11 @@ static int ext3_fill_super (struct super NULL, 0)) goto failed_mount; - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | - (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); + sb->s_flags = (sb->s_flags & ~MS_POSIXACL); + if (test_opt(sb, POSIX_ACL)) + sb->s_flags |= MS_POSIXACL; + if (test_opt(sb, NFS4ACL)) + sb->s_flags |= MS_POSIXACL | MS_WITHAPPEND; if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV && (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) || @@ -2576,8 +2605,12 @@ static int ext3_remount (struct super_bl if (test_opt(sb, ABORT)) ext3_abort(sb, __func__, "Abort forced by user"); - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | - (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); + sb->s_flags = (sb->s_flags & ~MS_POSIXACL); + if (test_opt(sb, POSIX_ACL)) + sb->s_flags |= MS_POSIXACL; + if (test_opt(sb, NFS4ACL)) + sb->s_flags |= MS_POSIXACL; + es = sbi->s_es; --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -114,6 +114,9 @@ static struct xattr_handler *ext3_xattr_ #ifdef CONFIG_EXT3_FS_SECURITY [EXT3_XATTR_INDEX_SECURITY] = &ext3_xattr_security_handler, #endif +#ifdef CONFIG_EXT3_FS_NFS4ACL + [EXT3_XATTR_INDEX_NFS4ACL] = &ext3_nfs4acl_xattr_handler, +#endif }; struct xattr_handler *ext3_xattr_handlers[] = { @@ -126,6 +129,12 @@ struct xattr_handler *ext3_xattr_handler #ifdef CONFIG_EXT3_FS_SECURITY &ext3_xattr_security_handler, #endif +#ifdef CONFIG_EXT3_FS_NFS4ACL + &ext3_nfs4acl_xattr_handler, +#ifdef NFS4ACL_DEBUG + &ext3_masked_nfs4acl_xattr_handler, +#endif +#endif NULL }; --- a/fs/ext3/xattr.h +++ b/fs/ext3/xattr.h @@ -21,6 +21,7 @@ #define EXT3_XATTR_INDEX_TRUSTED 4 #define EXT3_XATTR_INDEX_LUSTRE 5 #define EXT3_XATTR_INDEX_SECURITY 6 +#define EXT3_XATTR_INDEX_NFS4ACL 7 struct ext3_xattr_header { __le32 h_magic; /* magic number for identification */ @@ -63,6 +64,10 @@ extern struct xattr_handler ext3_xattr_t extern struct xattr_handler ext3_xattr_acl_access_handler; extern struct xattr_handler ext3_xattr_acl_default_handler; extern struct xattr_handler ext3_xattr_security_handler; +extern struct xattr_handler ext3_nfs4acl_xattr_handler; +#ifdef NFS4ACL_DEBUG +extern struct xattr_handler ext3_masked_nfs4acl_xattr_handler; +#endif extern ssize_t ext3_listxattr(struct dentry *, char *, size_t); --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -406,6 +406,7 @@ struct ext3_inode { #define EXT3_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */ #define EXT3_MOUNT_DATA_ERR_ABORT 0x400000 /* Abort on file data write * error in ordered mode */ +#define EXT3_MOUNT_NFS4ACL 0x800000 /* NFS version 4 ACLs */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H --- a/include/linux/ext3_fs_i.h +++ b/include/linux/ext3_fs_i.h @@ -103,6 +103,9 @@ struct ext3_inode_info { */ struct rw_semaphore xattr_sem; #endif +#ifdef CONFIG_EXT3_FS_NFS4ACL + struct nfs4acl *i_nfs4acl; +#endif struct list_head i_orphan; /* unlinked but open inodes */