--- /dev/null
+From: Andreas Gruenbacher <agruen@suse.de>
+Subject: NFSv4 ACLs for ext3
+
+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 <agruen@suse.de>
+
+---
+ fs/Kconfig | 7
+ fs/ext3/Makefile | 1
+ fs/ext3/acl.c | 8
+ fs/ext3/acl.h | 4
+ 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 | 370 ++++++++++++++++++++++++++++++++++++++++++++++
+ fs/ext3/nfs4acl.h | 36 ++++
+ fs/ext3/super.c | 60 +++++--
+ fs/ext3/xattr.c | 9 +
+ fs/ext3/xattr.h | 5
+ include/linux/ext3_fs.h | 1
+ include/linux/ext3_fs_i.h | 3
+ 16 files changed, 577 insertions(+), 26 deletions(-)
+
+--- a/fs/Kconfig
++++ b/fs/Kconfig
+@@ -124,6 +124,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/acl.c
++++ b/fs/ext3/acl.c
+@@ -282,7 +282,7 @@ ext3_set_acl(handle_t *handle, struct in
+ return error;
+ }
+
+-static int
++int
+ ext3_check_acl(struct inode *inode, int mask)
+ {
+ struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
+@@ -298,12 +298,6 @@ ext3_check_acl(struct inode *inode, int
+ return -EAGAIN;
+ }
+
+-int
+-ext3_permission(struct inode *inode, int mask)
+-{
+- return generic_permission(inode, mask, ext3_check_acl);
+-}
+-
+ /*
+ * Initialize the ACLs of a new inode. Called from ext3_new_inode.
+ *
+--- a/fs/ext3/acl.h
++++ b/fs/ext3/acl.h
+@@ -58,13 +58,13 @@ static inline int ext3_acl_count(size_t
+ #define EXT3_ACL_NOT_CACHED ((void *)-1)
+
+ /* acl.c */
+-extern int ext3_permission (struct inode *, int);
++extern int ext3_check_acl (struct inode *, int);
+ extern int ext3_acl_chmod (struct inode *);
+ extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
+
+ #else /* CONFIG_EXT3_FS_POSIX_ACL */
+ #include <linux/sched.h>
+-#define ext3_permission NULL
++#define ext3_check_acl NULL
+
+ static inline int
+ ext3_acl_chmod(struct inode *inode)
+--- a/fs/ext3/file.c
++++ b/fs/ext3/file.c
+@@ -23,8 +23,10 @@
+ #include <linux/jbd.h>
+ #include <linux/ext3_fs.h>
+ #include <linux/ext3_jbd.h>
++#include "namei.h"
+ #include "xattr.h"
+ #include "acl.h"
++#include "nfs4acl.h"
+
+ /*
+ * Called when an inode is released. Note that this is different
+@@ -134,5 +136,7 @@ const struct inode_operations ext3_file_
+ .removexattr = generic_removexattr,
+ #endif
+ .permission = ext3_permission,
++ .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
+@@ -595,7 +596,10 @@ got:
+ 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
+@@ -38,6 +38,7 @@
+ #include <linux/bio.h>
+ #include "xattr.h"
+ #include "acl.h"
++#include "nfs4acl.h"
+
+ static int ext3_writepage_trans_blocks(struct inode *inode);
+
+@@ -2684,6 +2685,9 @@ struct inode *ext3_iget(struct super_blo
+ ei->i_acl = EXT3_ACL_NOT_CACHED;
+ ei->i_default_acl = EXT3_ACL_NOT_CACHED;
+ #endif
++#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);
+@@ -2983,6 +2987,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()
+ *
+@@ -3006,7 +3069,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;
+
+@@ -3063,8 +3126,12 @@ int ext3_setattr(struct dentry *dentry,
+ if (inode->i_nlink)
+ ext3_orphan_del(NULL, inode);
+
+- 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.
+@@ -2412,6 +2413,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...
+ */
+@@ -2433,6 +2444,8 @@ const struct inode_operations ext3_dir_i
+ .removexattr = generic_removexattr,
+ #endif
+ .permission = ext3_permission,
++ .may_create = ext3_may_create,
++ .may_delete = ext3_may_delete,
+ };
+
+ const struct inode_operations ext3_special_inode_operations = {
+@@ -2444,4 +2457,6 @@ const struct inode_operations ext3_speci
+ .removexattr = generic_removexattr,
+ #endif
+ .permission = ext3_permission,
++ .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,370 @@
++/*
++ * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@computer.org>
++ *
++ * 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 <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/ext3_jbd.h>
++#include <linux/ext3_fs.h>
++#include <linux/nfs4acl_xattr.h>
++#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 inode *inode, char *list, size_t list_len,
++ const char *name, size_t name_len)
++{
++ 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 inode *inode, const char *name, void *buffer,
++ size_t buffer_size)
++{
++ 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 inode *inode, char *list, size_t list_len,
++ const char *name, size_t name_len)
++{
++ return 0;
++}
++
++static int
++ext3_xattr_get_masked_nfs4acl(struct inode *inode, const char *name,
++ void *buffer, size_t buffer_size)
++{
++ 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 inode *inode, const char *name,
++ const void *value, size_t size, int flags)
++{
++ 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 <linux/nfs4acl.h>
++
++/* 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 <linux/namei.h>
+ #include <linux/quotaops.h>
+ #include <linux/seq_file.h>
++#include <linux/nfs4acl.h>
+ #include <linux/log2.h>
+
+ #include <asm/uaccess.h>
+
+ #include "xattr.h"
+ #include "acl.h"
++#include "nfs4acl.h"
+ #include "namei.h"
+
+ static int ext3_load_journal(struct super_block *, struct ext3_super_block *,
+@@ -454,6 +456,9 @@ static struct inode *ext3_alloc_inode(st
+ ei->i_acl = EXT3_ACL_NOT_CACHED;
+ ei->i_default_acl = EXT3_ACL_NOT_CACHED;
+ #endif
++#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;
+ return &ei->vfs_inode;
+@@ -516,6 +521,13 @@ static void ext3_clear_inode(struct inod
+ EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED;
+ }
+ #endif
++#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
+ ext3_discard_reservation(inode);
+ EXT3_I(inode)->i_block_alloc_info = NULL;
+ if (unlikely(rsv))
+@@ -750,7 +762,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,
+@@ -782,6 +794,7 @@ static 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"},
+@@ -925,19 +938,33 @@ static int parse_options (char *options,
+ printk("EXT3 (no)user_xattr options not supported\n");
+ 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
++ {
++ printk(KERN_ERR "EXT3-fs: unsupported acl "
++ "flavor\n");
++ 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:
+- printk("EXT3 (no)acl options not supported\n");
+- break;
+-#endif
+ case Opt_reservation:
+ set_opt(sbi->s_mount_opt, RESERVATION);
+ break;
+@@ -1607,8 +1634,11 @@ static int ext3_fill_super (struct super
+ NULL, 0))
+ goto failed_mount;
+
+- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
+- ((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
++ sb->s_flags = (sb->s_flags & ~MS_POSIXACL);
++ if (sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL)
++ sb->s_flags |= MS_POSIXACL;
++ if (sbi->s_mount_opt & EXT3_MOUNT_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) ||
+@@ -2451,8 +2481,12 @@ static int ext3_remount (struct super_bl
+ if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
+ ext3_abort(sb, __func__, "Abort forced by user");
+
+- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
+- ((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
++ sb->s_flags = (sb->s_flags & ~MS_POSIXACL);
++ if (sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL)
++ sb->s_flags |= MS_POSIXACL;
++ if (sbi->s_mount_opt & EXT3_MOUNT_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
+@@ -380,6 +380,7 @@ struct ext3_inode {
+ #define EXT3_MOUNT_QUOTA 0x80000 /* Some quota option set */
+ #define EXT3_MOUNT_USRQUOTA 0x100000 /* "old" user quota */
+ #define EXT3_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */
++#define EXT3_MOUNT_NFS4ACL 0x400000 /* 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
+@@ -107,6 +107,9 @@ struct ext3_inode_info {
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+ #endif
++#ifdef CONFIG_EXT3_FS_NFS4ACL
++ struct nfs4acl *i_nfs4acl;
++#endif
+
+ struct list_head i_orphan; /* unlinked but open inodes */
+