qubes-linux-kernel/patches.suse/nfs4acl-ext3.diff

873 lines
23 KiB
Diff
Raw Normal View History

From: Andreas Gruenbacher <agruen@suse.de>
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 <agruen@suse.de>
---
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 <linux/quotaops.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
@@ -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 <linux/namei.h>
#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 <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/fs_struct.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 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 <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"
#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 */