873 lines
23 KiB
Diff
873 lines
23 KiB
Diff
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 */
|
|
|