2011-02-23 13:51:47

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

Hi,

The following set of patches implements VFS and ext4 changes needed to implement
a new acl model for linux. Rich ACLs are an implementation of NFSv4 ACLs,
extended by file masks to fit into the standard POSIX file permission model.
They are designed to work seamlessly locally as well as across the NFSv4 and
CIFS/SMB2 network file system protocols.

Full patch series including NFS server and client changes can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/agruen/linux-2.6-richacl.git richacl
or
git://git.kernel.org/pub/scm/linux/kernel/git/kvaneesh/linux-richacl.git richacl

A user-space utility for displaying and changing richacls is available at [4]
(a number of examples can be found at http://acl.bestbits.at/richacl/examples.html).

[4] git://git.kernel.org/pub/scm/linux/kernel/git/agruen/richacl.git master

To test richacl on ext4 use -o richacl mount option. This mount option may later be
dropped in favour of a feature flag.

More details regarding richacl can be found at
http://acl.bestbits.at/richacl/

Changes from v4:
a) rebase to 2.6.38-rc6
b) Fixes for MAY_DELETE_CHILD and MAY_DELETE_SELF when directory have sticky bits
c) Fixes for NFsv4 permission checking to make it work better with new MAY_* flags


-aneesh



2011-02-23 13:52:55

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 01/24] vfs: Indicate that the permission functions take all the MAY_* flags

From: Andreas Gruenbacher <[email protected]>

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/namei.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 0087cf9..b01eab8 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -200,7 +200,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag
/**
* generic_permission - check for access rights on a Posix-like filesystem
* @inode: inode to check access rights for
- * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...)
* @check_acl: optional callback to check for Posix ACLs
* @flags: IPERM_FLAG_ flags.
*
@@ -247,7 +247,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,
/**
* inode_permission - check for access rights to a given inode
* @inode: inode to check permission on
- * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...)
*
* Used to check for read/write/execute permissions on an inode.
* We use "fsuid" for this, letting us set arbitrary permissions
@@ -294,7 +294,7 @@ int inode_permission(struct inode *inode, int mask)
/**
* file_permission - check for additional access rights to a given file
* @file: file to check access rights for
- * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...)
*
* Used to check for read/write/execute permissions on an already opened
* file.
--
1.7.1


2011-02-23 13:51:55

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 08/24] vfs: Add delete child and delete self permission flags

From: Andreas Gruenbacher <[email protected]>

Normally, deleting a file requires write access to the parent directory.
Some permission models use a different permission on the parent
directory to indicate delete access. In addition, a process can have
per-file delete access even without delete access on the parent
directory.

Introduce two new inode_permission() mask flags and use them in
may_delete()

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/namei.c | 40 ++++++++++++++++++++++++++--------------
include/linux/fs.h | 2 ++
2 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index b4f7198..1b6dbc9 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -253,7 +253,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,
* are used for other things.
*
* When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR,
- * MAY_WRITE must also be set in @mask.
+ * MAY_DELETE_CHILD, MAY_DELETE_SELF, MAY_WRITE must also be set in @mask.
*/
int inode_permission(struct inode *inode, int mask)
{
@@ -1944,7 +1944,7 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
return 0;
if (dir->i_uid == fsuid)
return 0;
- return !capable(CAP_FOWNER);
+ return 1;
}

/*
@@ -1966,30 +1966,42 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static int may_delete(struct inode *dir, struct dentry *victim,
+ int isdir, int replace)
{
- int error;
+ int mask, error, is_sticky;
+ struct inode *inode = victim->d_inode;

- if (!victim->d_inode)
+ if (!inode)
return -ENOENT;

BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(victim, dir);

- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ mask = MAY_WRITE | MAY_EXEC | MAY_DELETE_CHILD;
+ if (replace)
+ mask |= S_ISDIR(inode->i_mode) ?
+ MAY_CREATE_DIR : MAY_CREATE_FILE;
+ is_sticky = check_sticky(dir, inode);
+ error = inode_permission(dir, mask);
+ if ((error || is_sticky) && IS_RICHACL(inode) &&
+ !inode_permission(dir, mask & ~(MAY_WRITE | MAY_DELETE_CHILD)) &&
+ !inode_permission(inode, MAY_DELETE_SELF))
+ error = 0;
+ else if (!error && is_sticky && !capable(CAP_FOWNER))
+ error = -EPERM;
if (error)
return error;
if (IS_APPEND(dir))
return -EPERM;
- if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
- IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode) || IS_SWAPFILE(inode))
return -EPERM;
if (isdir) {
- if (!S_ISDIR(victim->d_inode->i_mode))
+ if (!S_ISDIR(inode->i_mode))
return -ENOTDIR;
if (IS_ROOT(victim))
return -EBUSY;
- } else if (S_ISDIR(victim->d_inode->i_mode))
+ } else if (S_ISDIR(inode->i_mode))
return -EISDIR;
if (IS_DEADDIR(dir))
return -ENOENT;
@@ -2824,7 +2836,7 @@ void dentry_unhash(struct dentry *dentry)

int vfs_rmdir(struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(dir, dentry, 1, 0);

if (error)
return error;
@@ -2911,7 +2923,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)

int vfs_unlink(struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(dir, dentry, 0, 0);

if (error)
return error;
@@ -3304,14 +3316,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;

- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(old_dir, old_dentry, is_dir, 0);
if (error)
return error;

if (!new_dentry->d_inode)
error = may_create(new_dir, new_dentry, is_dir);
else
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(new_dir, new_dentry, is_dir, 1);
if (error)
return error;

diff --git a/include/linux/fs.h b/include/linux/fs.h
index a86afc0..6a6e017 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -64,6 +64,8 @@ struct inodes_stat_t {
#define MAY_CHDIR 64
#define MAY_CREATE_FILE 128
#define MAY_CREATE_DIR 256
+#define MAY_DELETE_CHILD 512
+#define MAY_DELETE_SELF 1024

/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
--
1.7.1

2011-02-23 13:51:54

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 07/24] vfs: Add new file and directory create permission flags

From: Andreas Gruenbacher <[email protected]>

Some permission models distinguish between the permission to create a
non-directory and a directory. Pass this information down to
inode_permission() as mask flags

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/namei.c | 26 +++++++++++++++-----------
include/linux/fs.h | 2 ++
2 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index b1e5104..b4f7198 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -252,7 +252,8 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,
* for filesystem access without changing the "normal" uids which
* are used for other things.
*
- * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
+ * When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR,
+ * MAY_WRITE must also be set in @mask.
*/
int inode_permission(struct inode *inode, int mask)
{
@@ -2005,13 +2006,15 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct inode *dir, struct dentry *child, int isdir)
{
+ int mask = isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission(dir, MAY_WRITE | MAY_EXEC | mask);
}

/*
@@ -2059,7 +2062,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, 0);

if (error)
return error;
@@ -2629,7 +2632,7 @@ EXPORT_SYMBOL_GPL(lookup_create);

int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, 0);

if (error)
return error;
@@ -2733,7 +2736,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, int, mode, unsigned, dev)

int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, 1);

if (error)
return error;
@@ -3016,7 +3019,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)

int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, 0);

if (error)
return error;
@@ -3089,7 +3092,10 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode)
return -ENOENT;

- error = may_create(dir, new_dentry);
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ error = may_create(dir, new_dentry, 0);
if (error)
return error;

@@ -3103,8 +3109,6 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
return -EPERM;
if (!dir->i_op->link)
return -EPERM;
- if (S_ISDIR(inode->i_mode))
- return -EPERM;

error = security_inode_link(old_dentry, dir, new_dentry);
if (error)
@@ -3305,7 +3309,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error;

if (!new_dentry->d_inode)
- error = may_create(new_dir, new_dentry);
+ error = may_create(new_dir, new_dentry, is_dir);
else
error = may_delete(new_dir, new_dentry, is_dir);
if (error)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1dc6e72..a86afc0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -62,6 +62,8 @@ struct inodes_stat_t {
#define MAY_ACCESS 16
#define MAY_OPEN 32
#define MAY_CHDIR 64
+#define MAY_CREATE_FILE 128
+#define MAY_CREATE_DIR 256

/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
--
1.7.1

2011-02-23 13:51:58

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 11/24] vfs: Make acl_permission_check() work for richacls

From: Andreas Gruenbacher <[email protected]>

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/namei.c | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 1b6dbc9..d7ff419 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -174,6 +174,20 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag
{
umode_t mode = inode->i_mode;

+ if (IS_RICHACL(inode)) {
+ int error = check_acl(inode, mask, flags);
+ if (error != -EAGAIN)
+ return error;
+ if (mask & (MAY_DELETE_SELF | MAY_TAKE_OWNERSHIP |
+ MAY_CHMOD | MAY_SET_TIMES)) {
+ /*
+ * The file permission bit cannot grant these
+ * permissions.
+ */
+ return -EACCES;
+ }
+ }
+
if (current_fsuid() == inode->i_uid)
mode >>= 6;
else {
--
1.7.1

2011-02-23 13:51:59

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 12/24] richacl: In-memory representation and helper functions

From: Andreas Gruenbacher <[email protected]>

A richacl consists of an NFSv4 acl and an owner, group, and other mask.
These three masks correspond to the owner, group, and other file
permission bits, but they contain NFSv4 permissions instead of POSIX
permissions.

Each entry in the NFSv4 acl applies to the file owner (OWNER@), the
owning group (GROUP@), literally everyone (EVERYONE@), or to a specific
uid or gid.

As in the standard POSIX file permission model, each process is the
owner, group, or other file class. A richacl grants a requested access
only if the NFSv4 acl in the richacl grants the access (according to the
NFSv4 permission check algorithm), and the file mask that applies to the
process includes the requested permissions.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/Makefile | 3 +
fs/richacl_base.c | 109 +++++++++++++++++++++
include/linux/richacl.h | 245 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 357 insertions(+), 0 deletions(-)
create mode 100644 fs/richacl_base.c
create mode 100644 include/linux/richacl.h

diff --git a/fs/Makefile b/fs/Makefile
index a7f7cef..ed79438 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -48,6 +48,9 @@ obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o

+obj-$(CONFIG_FS_RICHACL) += richacl.o
+richacl-y := richacl_base.o
+
obj-y += quota/

obj-$(CONFIG_PROC_FS) += proc/
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
new file mode 100644
index 0000000..99ed0ca
--- /dev/null
+++ b/fs/richacl_base.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006, 2010 Novell, Inc.
+ * Written by Andreas Gruenbacher <[email protected]>
+ *
+ * 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/sched.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/richacl.h>
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Special e_who identifiers: ACEs which have ACE4_SPECIAL_WHO set in
+ * ace->e_flags use these constants in ace->u.e_who.
+ *
+ * For efficiency, we compare pointers instead of comparing strings.
+ */
+const char richace_owner_who[] = "OWNER@";
+EXPORT_SYMBOL_GPL(richace_owner_who);
+const char richace_group_who[] = "GROUP@";
+EXPORT_SYMBOL_GPL(richace_group_who);
+const char richace_everyone_who[] = "EVERYONE@";
+EXPORT_SYMBOL_GPL(richace_everyone_who);
+
+/**
+ * richacl_alloc - allocate a richacl
+ * @count: number of entries
+ */
+struct richacl *
+richacl_alloc(int count)
+{
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ struct richacl *acl = kzalloc(size, GFP_KERNEL);
+
+ if (acl) {
+ atomic_set(&acl->a_refcount, 1);
+ acl->a_count = count;
+ }
+ return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_alloc);
+
+/**
+ * richacl_clone - create a copy of a richacl
+ */
+static struct richacl *
+richacl_clone(const struct richacl *acl)
+{
+ int count = acl->a_count;
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ struct richacl *dup = kmalloc(size, GFP_KERNEL);
+
+ if (dup) {
+ memcpy(dup, acl, size);
+ atomic_set(&dup->a_refcount, 1);
+ }
+ return dup;
+}
+
+/**
+ * richace_is_same_identifier - are both identifiers the same?
+ */
+int
+richace_is_same_identifier(const struct richace *a, const struct richace *b)
+{
+#define WHO_FLAGS (ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP)
+ if ((a->e_flags & WHO_FLAGS) != (b->e_flags & WHO_FLAGS))
+ return 0;
+ if (a->e_flags & ACE4_SPECIAL_WHO)
+ return a->u.e_who == b->u.e_who;
+ else
+ return a->u.e_id == b->u.e_id;
+#undef WHO_FLAGS
+}
+
+/**
+ * richacl_set_who - set a special who value
+ * @ace: acl entry
+ * @who: who value to use
+ */
+int
+richace_set_who(struct richace *ace, const char *who)
+{
+ if (!strcmp(who, richace_owner_who))
+ who = richace_owner_who;
+ else if (!strcmp(who, richace_group_who))
+ who = richace_group_who;
+ else if (!strcmp(who, richace_everyone_who))
+ who = richace_everyone_who;
+ else
+ return -EINVAL;
+
+ ace->u.e_who = who;
+ ace->e_flags |= ACE4_SPECIAL_WHO;
+ ace->e_flags &= ~ACE4_IDENTIFIER_GROUP;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(richace_set_who);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
new file mode 100644
index 0000000..b0472f1
--- /dev/null
+++ b/include/linux/richacl.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2006, 2010 Novell, Inc.
+ * Written by Andreas Gruenbacher <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __RICHACL_H
+#define __RICHACL_H
+#include <linux/slab.h>
+
+struct richace {
+ unsigned short e_type;
+ unsigned short e_flags;
+ unsigned int e_mask;
+ union {
+ unsigned int e_id;
+ const char *e_who;
+ } u;
+};
+
+struct richacl {
+ atomic_t a_refcount;
+ unsigned int a_owner_mask;
+ unsigned int a_group_mask;
+ unsigned int a_other_mask;
+ unsigned short a_count;
+ unsigned short a_flags;
+ struct richace a_entries[0];
+};
+
+#define richacl_for_each_entry(_ace, _acl) \
+ for (_ace = _acl->a_entries; \
+ _ace != _acl->a_entries + _acl->a_count; \
+ _ace++)
+
+#define richacl_for_each_entry_reverse(_ace, _acl) \
+ for (_ace = _acl->a_entries + _acl->a_count - 1; \
+ _ace != _acl->a_entries - 1; \
+ _ace--)
+
+/* Flag values defined by rich-acl */
+#define ACL4_MASKED 0x80
+
+#define ACL4_VALID_FLAGS ( \
+ ACL4_MASKED)
+
+/* e_type values */
+#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000
+#define ACE4_ACCESS_DENIED_ACE_TYPE 0x0001
+/*#define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x0002*/
+/*#define ACE4_SYSTEM_ALARM_ACE_TYPE 0x0003*/
+
+/* e_flags bitflags */
+#define ACE4_FILE_INHERIT_ACE 0x0001
+#define ACE4_DIRECTORY_INHERIT_ACE 0x0002
+#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x0004
+#define ACE4_INHERIT_ONLY_ACE 0x0008
+/*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/
+/*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/
+#define ACE4_IDENTIFIER_GROUP 0x0040
+/* in-memory representation only */
+#define ACE4_SPECIAL_WHO 0x4000
+
+#define ACE4_VALID_FLAGS ( \
+ ACE4_FILE_INHERIT_ACE | \
+ ACE4_DIRECTORY_INHERIT_ACE | \
+ ACE4_NO_PROPAGATE_INHERIT_ACE | \
+ ACE4_INHERIT_ONLY_ACE | \
+ ACE4_IDENTIFIER_GROUP)
+
+/* e_mask bitflags */
+#define ACE4_READ_DATA 0x00000001
+#define ACE4_LIST_DIRECTORY 0x00000001
+#define ACE4_WRITE_DATA 0x00000002
+#define ACE4_ADD_FILE 0x00000002
+#define ACE4_APPEND_DATA 0x00000004
+#define ACE4_ADD_SUBDIRECTORY 0x00000004
+#define ACE4_READ_NAMED_ATTRS 0x00000008
+#define ACE4_WRITE_NAMED_ATTRS 0x00000010
+#define ACE4_EXECUTE 0x00000020
+#define ACE4_DELETE_CHILD 0x00000040
+#define ACE4_READ_ATTRIBUTES 0x00000080
+#define ACE4_WRITE_ATTRIBUTES 0x00000100
+#define ACE4_WRITE_RETENTION 0x00000200
+#define ACE4_WRITE_RETENTION_HOLD 0x00000400
+#define ACE4_DELETE 0x00010000
+#define ACE4_READ_ACL 0x00020000
+#define ACE4_WRITE_ACL 0x00040000
+#define ACE4_WRITE_OWNER 0x00080000
+#define ACE4_SYNCHRONIZE 0x00100000
+
+/* Valid ACE4_* flags for directories and non-directories */
+#define ACE4_VALID_MASK ( \
+ ACE4_READ_DATA | ACE4_LIST_DIRECTORY | \
+ ACE4_WRITE_DATA | ACE4_ADD_FILE | \
+ ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \
+ ACE4_READ_NAMED_ATTRS | \
+ ACE4_WRITE_NAMED_ATTRS | \
+ ACE4_EXECUTE | \
+ ACE4_DELETE_CHILD | \
+ ACE4_READ_ATTRIBUTES | \
+ ACE4_WRITE_ATTRIBUTES | \
+ ACE4_WRITE_RETENTION | \
+ ACE4_WRITE_RETENTION_HOLD | \
+ ACE4_DELETE | \
+ ACE4_READ_ACL | \
+ ACE4_WRITE_ACL | \
+ ACE4_WRITE_OWNER | \
+ ACE4_SYNCHRONIZE)
+
+/**
+ * richacl_get - grab another reference to a richacl handle
+ */
+static inline struct richacl *
+richacl_get(struct richacl *acl)
+{
+ if (acl)
+ atomic_inc(&acl->a_refcount);
+ return acl;
+}
+
+/**
+ * richacl_put - free a richacl handle
+ */
+static inline void
+richacl_put(struct richacl *acl)
+{
+ if (acl && atomic_dec_and_test(&acl->a_refcount))
+ kfree(acl);
+}
+
+/*
+ * Special e_who identifiers: we use these pointer values in comparisons
+ * instead of doing a strcmp.
+ */
+extern const char richace_owner_who[];
+extern const char richace_group_who[];
+extern const char richace_everyone_who[];
+
+/**
+ * richace_is_owner - check if @ace is an OWNER@ entry
+ */
+static inline int
+richace_is_owner(const struct richace *ace)
+{
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
+ ace->u.e_who == richace_owner_who;
+}
+
+/**
+ * richace_is_group - check if @ace is a GROUP@ entry
+ */
+static inline int
+richace_is_group(const struct richace *ace)
+{
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
+ ace->u.e_who == richace_group_who;
+}
+
+/**
+ * richace_is_everyone - check if @ace is an EVERYONE@ entry
+ */
+static inline int
+richace_is_everyone(const struct richace *ace)
+{
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
+ ace->u.e_who == richace_everyone_who;
+}
+
+/**
+ * richace_is_unix_id - check if @ace applies to a specific uid or gid
+ */
+static inline int
+richace_is_unix_id(const struct richace *ace)
+{
+ return !(ace->e_flags & ACE4_SPECIAL_WHO);
+}
+
+/**
+ * richace_is_inherit_only - check if @ace is for inheritance only
+ *
+ * ACEs with the %ACE4_INHERIT_ONLY_ACE flag set have no effect during
+ * permission checking.
+ */
+static inline int
+richace_is_inherit_only(const struct richace *ace)
+{
+ return ace->e_flags & ACE4_INHERIT_ONLY_ACE;
+}
+
+/**
+ * richace_is_inheritable - check if @ace is inheritable
+ */
+static inline int
+richace_is_inheritable(const struct richace *ace)
+{
+ return ace->e_flags & (ACE4_FILE_INHERIT_ACE |
+ ACE4_DIRECTORY_INHERIT_ACE);
+}
+
+/**
+ * richace_clear_inheritance_flags - clear all inheritance flags in @ace
+ */
+static inline void
+richace_clear_inheritance_flags(struct richace *ace)
+{
+ ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE |
+ ACE4_DIRECTORY_INHERIT_ACE |
+ ACE4_NO_PROPAGATE_INHERIT_ACE |
+ ACE4_INHERIT_ONLY_ACE);
+}
+
+/**
+ * richace_is_allow - check if @ace is an %ALLOW type entry
+ */
+static inline int
+richace_is_allow(const struct richace *ace)
+{
+ return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE;
+}
+
+/**
+ * richace_is_deny - check if @ace is a %DENY type entry
+ */
+static inline int
+richace_is_deny(const struct richace *ace)
+{
+ return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE;
+}
+
+extern struct richacl *richacl_alloc(int);
+extern int richace_is_same_identifier(const struct richace *,
+ const struct richace *);
+extern int richace_set_who(struct richace *, const char *);
+
+#endif /* __RICHACL_H */
--
1.7.1

2011-02-23 13:52:00

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 13/24] richacl: Permission mapping functions

From: Andreas Gruenbacher <[email protected]>

We need to map from POSIX permissions to NFSv4 permissions when a
chmod() is done, from NFSv4 permissions to POSIX permissions when an acl
is set (which implicitly sets the file permission bits), and from the
MAY_READ/MAY_WRITE/MAY_EXEC/MAY_APPEND flags to NFSv4 permissions when
doing an access check in a richacl.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/richacl_base.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 46 ++++++++++++++++++
2 files changed, 164 insertions(+), 0 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 99ed0ca..50ba645 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -69,6 +69,124 @@ richacl_clone(const struct richacl *acl)
}

/**
+ * richacl_mask_to_mode - compute the file permission bits which correspond to @mask
+ * @mask: %ACE4_* permission mask
+ *
+ * See richacl_masks_to_mode().
+ */
+static int
+richacl_mask_to_mode(unsigned int mask)
+{
+ int mode = 0;
+
+ if (mask & ACE4_POSIX_MODE_READ)
+ mode |= MAY_READ;
+ if (mask & ACE4_POSIX_MODE_WRITE)
+ mode |= MAY_WRITE;
+ if (mask & ACE4_POSIX_MODE_EXEC)
+ mode |= MAY_EXEC;
+
+ return mode;
+}
+
+/**
+ * richacl_masks_to_mode - compute the file permission bits from the file masks
+ *
+ * When setting a richacl, we set the file permission bits to indicate maximum
+ * permissions: for example, we set the Write permission when a mask contains
+ * ACE4_APPEND_DATA even if it does not also contain ACE4_WRITE_DATA.
+ *
+ * Permissions which are not in ACE4_POSIX_MODE_READ, ACE4_POSIX_MODE_WRITE, or
+ * ACE4_POSIX_MODE_EXEC cannot be represented in the file permission bits.
+ * Such permissions can still be effective, but not for new files or after a
+ * chmod(), and only if they were set explicitly, for example, by setting a
+ * richacl.
+ */
+int
+richacl_masks_to_mode(const struct richacl *acl)
+{
+ return richacl_mask_to_mode(acl->a_owner_mask) << 6 |
+ richacl_mask_to_mode(acl->a_group_mask) << 3 |
+ richacl_mask_to_mode(acl->a_other_mask);
+}
+EXPORT_SYMBOL_GPL(richacl_masks_to_mode);
+
+/**
+ * richacl_mode_to_mask - compute a file mask from the lowest three mode bits
+ *
+ * When the file permission bits of a file are set with chmod(), this specifies
+ * the maximum permissions that processes will get. All permissions beyond
+ * that will be removed from the file masks, and become ineffective.
+ *
+ * We also add in the permissions which are always allowed no matter what the
+ * acl says.
+ */
+unsigned int
+richacl_mode_to_mask(mode_t mode)
+{
+ unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED;
+
+ if (mode & MAY_READ)
+ mask |= ACE4_POSIX_MODE_READ;
+ if (mode & MAY_WRITE)
+ mask |= ACE4_POSIX_MODE_WRITE;
+ if (mode & MAY_EXEC)
+ mask |= ACE4_POSIX_MODE_EXEC;
+
+ return mask;
+}
+
+/**
+ * richacl_want_to_mask - convert the iop->permission want argument to a mask
+ * @want: @want argument of the permission inode operation
+ *
+ * When checking for append, @want is (MAY_WRITE | MAY_APPEND).
+ *
+ * Richacls use the iop->may_create and iop->may_delete hooks which are
+ * used for checking if creating and deleting files is allowed. These hooks do
+ * not use richacl_want_to_mask(), so we do not have to deal with mapping
+ * MAY_WRITE to ACE4_ADD_FILE, ACE4_ADD_SUBDIRECTORY, and ACE4_DELETE_CHILD
+ * here.
+ */
+unsigned int
+richacl_want_to_mask(int want)
+{
+ unsigned int mask = 0;
+
+ if (want & MAY_READ)
+ mask |= ACE4_READ_DATA;
+ if (want & (MAY_APPEND |
+ MAY_CREATE_FILE | MAY_CREATE_DIR |
+ MAY_DELETE_CHILD | MAY_DELETE_SELF |
+ MAY_TAKE_OWNERSHIP | MAY_CHMOD | MAY_SET_TIMES)) {
+ if (want & MAY_APPEND)
+ mask |= ACE4_APPEND_DATA;
+ else if (want & MAY_DELETE_SELF)
+ mask |= ACE4_DELETE;
+ else if (want & MAY_TAKE_OWNERSHIP)
+ mask |= ACE4_WRITE_OWNER;
+ else if (want & MAY_CHMOD)
+ mask |= ACE4_WRITE_ACL;
+ else if (want & MAY_SET_TIMES)
+ mask |= ACE4_WRITE_ATTRIBUTES;
+ else {
+ if (want & MAY_CREATE_FILE)
+ mask |= ACE4_ADD_FILE;
+ if (want & MAY_CREATE_DIR)
+ mask |= ACE4_ADD_SUBDIRECTORY;
+ if (want & MAY_DELETE_CHILD)
+ mask |= ACE4_DELETE_CHILD;
+ }
+ } else if (want & MAY_WRITE)
+ mask |= ACE4_WRITE_DATA;
+ if (want & MAY_EXEC)
+ mask |= ACE4_EXECUTE;
+
+ return mask;
+}
+EXPORT_SYMBOL_GPL(richacl_want_to_mask);
+
+/**
* richace_is_same_identifier - are both identifiers the same?
*/
int
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index b0472f1..399dbc9 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -117,6 +117,49 @@ struct richacl {
ACE4_WRITE_OWNER | \
ACE4_SYNCHRONIZE)

+/*
+ * The POSIX permissions are supersets of the following NFSv4 permissions:
+ *
+ * - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type
+ * of the file system object.
+ *
+ * - MAY_WRITE maps to WRITE_DATA or ACE4_APPEND_DATA for files, and to
+ * ADD_FILE, ACE4_ADD_SUBDIRECTORY, or ACE4_DELETE_CHILD for directories.
+ *
+ * - MAY_EXECUTE maps to ACE4_EXECUTE.
+ *
+ * (Some of these NFSv4 permissions have the same bit values.)
+ */
+#define ACE4_POSIX_MODE_READ ( \
+ ACE4_READ_DATA | \
+ ACE4_LIST_DIRECTORY)
+#define ACE4_POSIX_MODE_WRITE ( \
+ ACE4_WRITE_DATA | \
+ ACE4_ADD_FILE | \
+ ACE4_APPEND_DATA | \
+ ACE4_ADD_SUBDIRECTORY | \
+ ACE4_DELETE_CHILD)
+#define ACE4_POSIX_MODE_EXEC ACE4_EXECUTE
+#define ACE4_POSIX_MODE_ALL ( \
+ ACE4_POSIX_MODE_READ | \
+ ACE4_POSIX_MODE_WRITE | \
+ ACE4_POSIX_MODE_EXEC)
+/*
+ * These permissions are always allowed
+ * no matter what the acl says.
+ */
+#define ACE4_POSIX_ALWAYS_ALLOWED ( \
+ ACE4_SYNCHRONIZE | \
+ ACE4_READ_ATTRIBUTES | \
+ ACE4_READ_ACL)
+/*
+ * The owner is implicitly granted
+ * these permissions under POSIX.
+ */
+#define ACE4_POSIX_OWNER_ALLOWED ( \
+ ACE4_WRITE_ATTRIBUTES | \
+ ACE4_WRITE_OWNER | \
+ ACE4_WRITE_ACL)
/**
* richacl_get - grab another reference to a richacl handle
*/
@@ -241,5 +284,8 @@ extern struct richacl *richacl_alloc(int);
extern int richace_is_same_identifier(const struct richace *,
const struct richace *);
extern int richace_set_who(struct richace *, const char *);
+extern int richacl_masks_to_mode(const struct richacl *);
+extern unsigned int richacl_mode_to_mask(mode_t);
+extern unsigned int richacl_want_to_mask(int);

#endif /* __RICHACL_H */
--
1.7.1

2011-02-23 13:52:05

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 18/24] richacl: Check if an acl is equivalent to a file mode

From: Andreas Gruenbacher <[email protected]>

This function is used to avoid storing richacls on disk if the acl can
be computed from the file permission bits.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/richacl_base.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 55 insertions(+), 0 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 5e926ad..e60440b 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -561,3 +561,57 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)

return acl;
}
+
+/**
+ * richacl_equiv_mode - check if @acl is equivalent to file permission bits
+ * @mode_p: the file mode (including the file type)
+ *
+ * If @acl can be fully represented by file permission bits, this function
+ * returns 0, and the file permission bits in @mode_p are set to the equivalent
+ * of @acl.
+ *
+ * This function is used to avoid storing richacls on disk if the acl can be
+ * computed from the file permission bits. It allows user-space to make sure
+ * that a file has no explicit richacl set.
+ */
+int
+richacl_equiv_mode(const struct richacl *acl, mode_t *mode_p)
+{
+ const struct richace *ace = acl->a_entries;
+ unsigned int x;
+ mode_t mode;
+
+ if (acl->a_count != 1 ||
+ acl->a_flags != ACL4_MASKED ||
+ !richace_is_everyone(ace) ||
+ !richace_is_allow(ace) ||
+ ace->e_flags & ~ACE4_SPECIAL_WHO)
+ return -1;
+
+ /*
+ * Figure out the permissions we care about: ACE4_DELETE_CHILD is
+ * meaningless for non-directories, so we ignore it.
+ */
+ x = ~ACE4_POSIX_ALWAYS_ALLOWED;
+ if (!S_ISDIR(*mode_p))
+ x &= ~ACE4_DELETE_CHILD;
+
+ mode = richacl_masks_to_mode(acl);
+ if ((acl->a_group_mask & x) != (richacl_mode_to_mask(mode >> 3) & x) ||
+ (acl->a_other_mask & x) != (richacl_mode_to_mask(mode) & x))
+ return -1;
+
+ /*
+ * Ignore permissions which the owner is always allowed.
+ */
+ x &= ~ACE4_POSIX_OWNER_ALLOWED;
+ if ((acl->a_owner_mask & x) != (richacl_mode_to_mask(mode >> 6) & x))
+ return -1;
+
+ if ((ace->e_mask & x) != (ACE4_POSIX_MODE_ALL & x))
+ return -1;
+
+ *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(richacl_equiv_mode);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 630bf86..12a79f1 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -292,6 +292,7 @@ extern struct richacl *richacl_chmod(struct richacl *, mode_t);
extern int richacl_permission(struct inode *, const struct richacl *,
unsigned int);
extern struct richacl *richacl_inherit(const struct richacl *, int);
+extern int richacl_equiv_mode(const struct richacl *, mode_t *);

/* richacl_inode.c */
extern struct richacl *richacl_inherit_inode(const struct richacl *,
--
1.7.1

2011-02-23 13:52:03

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 16/24] richacl: Permission check algorithm

From: Andreas Gruenbacher <[email protected]>

As in the standard POSIX file permission model, each process is the
owner, group, or other file class. A process is

- in the owner file class if it owns the file,
- in the group file class if it is in the file's owning group or it
matches any of the user or group entries, and
- in the other file class otherwise.

Each file class is associated with a file mask.

A richacl grants a requested access if the NFSv4 acl in the richacl
grants the requested permissions (according to the NFSv4 permission
check algorithm) and the file mask that applies to the process includes
the requested permissions.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/richacl_base.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 2 +
2 files changed, 101 insertions(+), 0 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index b59c75c..a6e3199 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -393,3 +393,102 @@ richacl_chmod(struct richacl *acl, mode_t mode)
return clone;
}
EXPORT_SYMBOL_GPL(richacl_chmod);
+
+/**
+ * richacl_permission - richacl permission check algorithm
+ * @inode: inode to check
+ * @acl: rich acl of the inode
+ * @mask: requested access (ACE4_* bitmask)
+ *
+ * Checks if the current process is granted @mask flags in @acl.
+ */
+int
+richacl_permission(struct inode *inode, const struct richacl *acl,
+ unsigned int mask)
+{
+ const struct richace *ace;
+ unsigned int requested = mask, denied = 0;
+ int in_owning_group = in_group_p(inode->i_gid);
+ int in_owner_or_group_class = in_owning_group;
+
+ /*
+ * We don't need to know which class the process is in when the acl is
+ * not masked.
+ */
+ if (!(acl->a_flags & ACL4_MASKED))
+ in_owner_or_group_class = 1;
+
+ /*
+ * A process is
+ * - in the owner file class if it owns the file,
+ * - in the group file class if it is in the file's owning group or
+ * it matches any of the user or group entries, and
+ * - in the other file class otherwise.
+ */
+
+ /*
+ * Check if the acl grants the requested access and determine which
+ * file class the process is in.
+ */
+ richacl_for_each_entry(ace, acl) {
+ unsigned int ace_mask = ace->e_mask;
+
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_owner(ace)) {
+ if (current_fsuid() != inode->i_uid)
+ continue;
+ goto is_owner;
+ } else if (richace_is_group(ace)) {
+ if (!in_owning_group)
+ continue;
+ } else if (richace_is_unix_id(ace)) {
+ if (ace->e_flags & ACE4_IDENTIFIER_GROUP) {
+ if (!in_group_p(ace->u.e_id))
+ continue;
+ } else {
+ if (current_fsuid() != ace->u.e_id)
+ continue;
+ }
+ } else
+ goto is_everyone;
+
+is_owner:
+ /* The process is in the owner or group file class. */
+ in_owner_or_group_class = 1;
+
+is_everyone:
+ /* Check which mask flags the ACE allows or denies. */
+ if (richace_is_deny(ace))
+ denied |= ace_mask & mask;
+ mask &= ~ace_mask;
+
+ /*
+ * Keep going until we know which file class
+ * the process is in.
+ */
+ if (!mask && in_owner_or_group_class)
+ break;
+ }
+ denied |= mask;
+
+ if (acl->a_flags & ACL4_MASKED) {
+ unsigned int file_mask;
+
+ /*
+ * The file class a process is in determines which file mask
+ * applies. Check if that file mask also grants the requested
+ * access.
+ */
+ if (current_fsuid() == inode->i_uid)
+ file_mask = acl->a_owner_mask;
+ else if (in_owner_or_group_class)
+ file_mask = acl->a_group_mask;
+ else
+ file_mask = acl->a_other_mask;
+ denied |= requested & ~file_mask;
+ }
+
+ return denied ? -EACCES : 0;
+}
+EXPORT_SYMBOL_GPL(richacl_permission);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 3aa165b..c9d2761 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -289,5 +289,7 @@ extern unsigned int richacl_mode_to_mask(mode_t);
extern unsigned int richacl_want_to_mask(int);
extern void richacl_compute_max_masks(struct richacl *);
extern struct richacl *richacl_chmod(struct richacl *, mode_t);
+extern int richacl_permission(struct inode *, const struct richacl *,
+ unsigned int);

#endif /* __RICHACL_H */
--
1.7.1

2011-02-23 13:52:08

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 21/24] ext4: Use IS_POSIXACL() to check for POSIX ACL support

Use IS_POSIXACL() instead of a file system specific mount flag since we
have IS_POSIXACL() in the vfs already, anyway.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/ext4/acl.c | 16 ++++++++--------
fs/ext4/ext4.h | 1 -
fs/ext4/super.c | 16 +++++-----------
3 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index e0270d1..364514c 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -139,7 +139,7 @@ ext4_get_acl(struct inode *inode, int type)
struct posix_acl *acl;
int retval;

- if (!test_opt(inode->i_sb, POSIX_ACL))
+ if (!IS_POSIXACL(inode))
return NULL;

acl = get_cached_acl(inode, type);
@@ -273,7 +273,7 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
int error = 0;

if (!S_ISLNK(inode->i_mode)) {
- if (test_opt(dir->i_sb, POSIX_ACL)) {
+ if (IS_POSIXACL(inode)) {
acl = ext4_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
@@ -281,7 +281,7 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
if (!acl)
inode->i_mode &= ~current_umask();
}
- if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
+ if (IS_POSIXACL(inode) && acl) {
struct posix_acl *clone;
mode_t mode;

@@ -335,7 +335,7 @@ ext4_acl_chmod(struct inode *inode)

if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
- if (!test_opt(inode->i_sb, POSIX_ACL))
+ if (!IS_POSIXACL(inode))
return 0;
acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl)
@@ -377,7 +377,7 @@ ext4_xattr_list_acl_access(struct dentry *dentry, char *list, size_t list_len,
{
const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);

- if (!test_opt(dentry->d_sb, POSIX_ACL))
+ if (!IS_POSIXACL(dentry->d_inode))
return 0;
if (list && size <= list_len)
memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
@@ -390,7 +390,7 @@ ext4_xattr_list_acl_default(struct dentry *dentry, char *list, size_t list_len,
{
const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);

- if (!test_opt(dentry->d_sb, POSIX_ACL))
+ if (!IS_POSIXACL(dentry->d_inode))
return 0;
if (list && size <= list_len)
memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
@@ -406,7 +406,7 @@ ext4_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,

if (strcmp(name, "") != 0)
return -EINVAL;
- if (!test_opt(dentry->d_sb, POSIX_ACL))
+ if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;

acl = ext4_get_acl(dentry->d_inode, type);
@@ -431,7 +431,7 @@ ext4_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,

if (strcmp(name, "") != 0)
return -EINVAL;
- if (!test_opt(inode->i_sb, POSIX_ACL))
+ if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;
if (!is_owner_or_cap(inode))
return -EPERM;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 3aa0b72..5d1f56a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -892,7 +892,6 @@ struct ext4_inode_info {
#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000 /* Update the journal format */
#define EXT4_MOUNT_NO_UID32 0x02000 /* Disable 32-bit UIDs */
#define EXT4_MOUNT_XATTR_USER 0x04000 /* Extended user attributes */
-#define EXT4_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */
#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */
#define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */
#define EXT4_MOUNT_QUOTA 0x80000 /* Some quota option set */
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index f6a318f..adc97b7 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1006,9 +1006,9 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
}
#endif
#ifdef CONFIG_EXT4_FS_POSIX_ACL
- if (test_opt(sb, POSIX_ACL) && !(def_mount_opts & EXT4_DEFM_ACL))
+ if ((sb->s_flags & MS_POSIXACL) && !(def_mount_opts & EXT4_DEFM_ACL))
seq_puts(seq, ",acl");
- if (!test_opt(sb, POSIX_ACL) && (def_mount_opts & EXT4_DEFM_ACL))
+ if (!(sb->s_flags & MS_POSIXACL) && (def_mount_opts & EXT4_DEFM_ACL))
seq_puts(seq, ",noacl");
#endif
if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {
@@ -1529,10 +1529,10 @@ static int parse_options(char *options, struct super_block *sb,
#endif
#ifdef CONFIG_EXT4_FS_POSIX_ACL
case Opt_acl:
- set_opt(sb, POSIX_ACL);
+ sb->s_flags |= MS_POSIXACL;
break;
case Opt_noacl:
- clear_opt(sb, POSIX_ACL);
+ sb->s_flags &= ~MS_POSIXACL;
break;
#else
case Opt_acl:
@@ -3101,7 +3101,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
#endif
#ifdef CONFIG_EXT4_FS_POSIX_ACL
if (def_mount_opts & EXT4_DEFM_ACL)
- set_opt(sb, POSIX_ACL);
+ sb->s_flags |= MS_POSIXACL;
#endif
if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)
set_opt(sb, JOURNAL_DATA);
@@ -3148,9 +3148,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
&journal_ioprio, NULL, 0))
goto failed_mount;

- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
-
if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&
(EXT4_HAS_COMPAT_FEATURE(sb, ~0U) ||
EXT4_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
@@ -4235,9 +4232,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
ext4_abort(sb, "Abort forced by user");

- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
-
es = sbi->s_es;

if (sbi->s_journal) {
--
1.7.1

2011-02-23 13:52:07

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 20/24] richacl: xattr mapping functions

From: Andreas Gruenbacher <[email protected]>

Map between "system.richacl" xattrs and the in-kernel representation.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/Makefile | 2 +-
fs/richacl_xattr.c | 156 +++++++++++++++++++++++++++++++++++++++++
include/linux/richacl_xattr.h | 47 ++++++++++++
3 files changed, 204 insertions(+), 1 deletions(-)
create mode 100644 fs/richacl_xattr.c
create mode 100644 include/linux/richacl_xattr.h

diff --git a/fs/Makefile b/fs/Makefile
index e313219..4e52702 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o

obj-$(CONFIG_FS_RICHACL) += richacl.o
-richacl-y := richacl_base.o richacl_inode.o
+richacl-y := richacl_base.o richacl_inode.o richacl_xattr.o

obj-y += quota/

diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
new file mode 100644
index 0000000..51d1c3c
--- /dev/null
+++ b/fs/richacl_xattr.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2006, 2010 Novell, Inc.
+ * Written by Andreas Gruenbacher <[email protected]>
+ *
+ * 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/slab.h>
+#include <linux/module.h>
+#include <linux/richacl_xattr.h>
+
+MODULE_LICENSE("GPL");
+
+/**
+ * richacl_from_xattr - convert a richacl xattr into the in-memory representation
+ */
+struct richacl *
+richacl_from_xattr(const void *value, size_t size)
+{
+ const struct richacl_xattr *xattr_acl = value;
+ const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
+ struct richacl *acl;
+ struct richace *ace;
+ int count;
+
+ if (size < sizeof(struct richacl_xattr) ||
+ xattr_acl->a_version != ACL4_XATTR_VERSION ||
+ (xattr_acl->a_flags & ~ACL4_VALID_FLAGS))
+ return ERR_PTR(-EINVAL);
+
+ count = le16_to_cpu(xattr_acl->a_count);
+ if (count > ACL4_XATTR_MAX_COUNT)
+ return ERR_PTR(-EINVAL);
+
+ acl = richacl_alloc(count);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ acl->a_flags = xattr_acl->a_flags;
+ acl->a_owner_mask = le32_to_cpu(xattr_acl->a_owner_mask);
+ if (acl->a_owner_mask & ~ACE4_VALID_MASK)
+ goto fail_einval;
+ acl->a_group_mask = le32_to_cpu(xattr_acl->a_group_mask);
+ if (acl->a_group_mask & ~ACE4_VALID_MASK)
+ goto fail_einval;
+ acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
+ if (acl->a_other_mask & ~ACE4_VALID_MASK)
+ goto fail_einval;
+
+ richacl_for_each_entry(ace, acl) {
+ const char *who = (void *)(xattr_ace + 1), *end;
+ ssize_t used = (void *)who - value;
+
+ if (used > size)
+ goto fail_einval;
+ end = memchr(who, 0, size - used);
+ if (!end)
+ goto fail_einval;
+
+ ace->e_type = le16_to_cpu(xattr_ace->e_type);
+ ace->e_flags = le16_to_cpu(xattr_ace->e_flags);
+ ace->e_mask = le32_to_cpu(xattr_ace->e_mask);
+ ace->u.e_id = le32_to_cpu(xattr_ace->e_id);
+
+ if (ace->e_flags & ~ACE4_VALID_FLAGS)
+ goto fail_einval;
+ if (ace->e_type > ACE4_ACCESS_DENIED_ACE_TYPE ||
+ (ace->e_mask & ~ACE4_VALID_MASK))
+ goto fail_einval;
+
+ if (who == end) {
+ if (ace->u.e_id == -1)
+ goto fail_einval; /* uid/gid needed */
+ } else if (richace_set_who(ace, who))
+ goto fail_einval;
+
+ xattr_ace = (void *)who + ALIGN(end - who + 1, 4);
+ }
+
+ return acl;
+
+fail_einval:
+ richacl_put(acl);
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(richacl_from_xattr);
+
+/**
+ * richacl_xattr_size - compute the size of the xattr representation of @acl
+ */
+size_t
+richacl_xattr_size(const struct richacl *acl)
+{
+ size_t size = sizeof(struct richacl_xattr);
+ const struct richace *ace;
+
+ richacl_for_each_entry(ace, acl) {
+ size += sizeof(struct richace_xattr) +
+ (richace_is_unix_id(ace) ? 4 :
+ ALIGN(strlen(ace->u.e_who) + 1, 4));
+ }
+ return size;
+}
+EXPORT_SYMBOL_GPL(richacl_xattr_size);
+
+/**
+ * richacl_to_xattr - convert @acl into its xattr representation
+ * @acl: the richacl to convert
+ * @buffer: buffer of size richacl_xattr_size(@acl) for the result
+ */
+void
+richacl_to_xattr(const struct richacl *acl, void *buffer)
+{
+ struct richacl_xattr *xattr_acl = buffer;
+ struct richace_xattr *xattr_ace;
+ const struct richace *ace;
+
+ xattr_acl->a_version = ACL4_XATTR_VERSION;
+ xattr_acl->a_flags = acl->a_flags;
+ xattr_acl->a_count = cpu_to_le16(acl->a_count);
+
+ xattr_acl->a_owner_mask = cpu_to_le32(acl->a_owner_mask);
+ xattr_acl->a_group_mask = cpu_to_le32(acl->a_group_mask);
+ xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
+
+ xattr_ace = (void *)(xattr_acl + 1);
+ richacl_for_each_entry(ace, acl) {
+ xattr_ace->e_type = cpu_to_le16(ace->e_type);
+ xattr_ace->e_flags = cpu_to_le16(ace->e_flags &
+ ACE4_VALID_FLAGS);
+ xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
+ if (richace_is_unix_id(ace)) {
+ xattr_ace->e_id = cpu_to_le32(ace->u.e_id);
+ memset(xattr_ace->e_who, 0, 4);
+ xattr_ace = (void *)xattr_ace->e_who + 4;
+ } else {
+ int sz = ALIGN(strlen(ace->u.e_who) + 1, 4);
+
+ xattr_ace->e_id = cpu_to_le32(-1);
+ memset(xattr_ace->e_who + sz - 4, 0, 4);
+ strcpy(xattr_ace->e_who, ace->u.e_who);
+ xattr_ace = (void *)xattr_ace->e_who + sz;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(richacl_to_xattr);
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
new file mode 100644
index 0000000..e038a7c
--- /dev/null
+++ b/include/linux/richacl_xattr.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006, 2010 Novell, Inc.
+ * Written by Andreas Gruenbacher <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __RICHACL_XATTR_H
+#define __RICHACL_XATTR_H
+
+#include <linux/richacl.h>
+
+#define RICHACL_XATTR "system.richacl"
+
+struct richace_xattr {
+ __le16 e_type;
+ __le16 e_flags;
+ __le32 e_mask;
+ __le32 e_id;
+ char e_who[0];
+};
+
+struct richacl_xattr {
+ unsigned char a_version;
+ unsigned char a_flags;
+ __le16 a_count;
+ __le32 a_owner_mask;
+ __le32 a_group_mask;
+ __le32 a_other_mask;
+};
+
+#define ACL4_XATTR_VERSION 0
+#define ACL4_XATTR_MAX_COUNT 1024
+
+extern struct richacl *richacl_from_xattr(const void *, size_t);
+extern size_t richacl_xattr_size(const struct richacl *acl);
+extern void richacl_to_xattr(const struct richacl *, void *);
+
+#endif /* __RICHACL_XATTR_H */
--
1.7.1

2011-02-23 13:52:10

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 23/24] ext4: Implement rich acl for ext4

Support the richacl permission model in ext4. The richacls are stored
in "system.richacl" xattrs.This need to be enabled by tune2fs or during
mkfs.ext4

Signed-off-by: Aneesh Kumar K.V <[email protected]>
Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/ext4/Kconfig | 15 +++
fs/ext4/Makefile | 3 +-
fs/ext4/acl.c | 2 +-
fs/ext4/acl.h | 3 +-
fs/ext4/acls.c | 20 ++++
fs/ext4/ext4.h | 6 ++
fs/ext4/file.c | 1 +
fs/ext4/ialloc.c | 7 ++-
fs/ext4/inode.c | 10 ++-
fs/ext4/richacl.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/richacl.h | 45 ++++++++++
fs/ext4/xattr.c | 6 ++
fs/ext4/xattr.h | 2 +
13 files changed, 367 insertions(+), 8 deletions(-)
create mode 100644 fs/ext4/acls.c
create mode 100644 fs/ext4/richacl.c
create mode 100644 fs/ext4/richacl.h

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 9ed1bb1..a22b8f1 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -83,3 +83,18 @@ config EXT4_DEBUG

If you select Y here, then you will be able to turn on debugging
with a command such as "echo 1 > /sys/kernel/debug/ext4/mballoc-debug"
+
+config EXT4_FS_RICHACL
+ bool "Ext4 Rich Access Control Lists (EXPERIMENTAL)"
+ depends on EXT4_FS_XATTR && EXPERIMENTAL
+ select FS_RICHACL
+ help
+ Rich ACLs are an implementation of NFSv4 ACLs, extended by file masks
+ to fit into the standard POSIX file permission model. They are
+ designed to work seamlessly locally as well as across the NFSv4 and
+ CIFS/SMB2 network file system protocols.
+
+ To learn more about Rich ACL, visit
+ http://acl.bestbits.at/richacl/
+
+ If you don't know what Rich ACLs are, say N
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index c947e36..a37e439 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -9,5 +9,6 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o

ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
-ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
+ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acls.o acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
+ext4-$(CONFIG_EXT4_FS_RICHACL) += acls.o richacl.o
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 364514c..aa9257e 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -238,7 +238,7 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
}

int
-ext4_check_acl(struct inode *inode, int mask, unsigned int flags)
+ext4_check_posix_acl(struct inode *inode, int mask, unsigned int flags)
{
struct posix_acl *acl;

diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index dec8211..296ee5b 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -54,13 +54,12 @@ static inline int ext4_acl_count(size_t size)
#ifdef CONFIG_EXT4_FS_POSIX_ACL

/* acl.c */
-extern int ext4_check_acl(struct inode *, int, unsigned int);
+extern int ext4_check_posix_acl(struct inode *, int, unsigned int);
extern int ext4_acl_chmod(struct inode *);
extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);

#else /* CONFIG_EXT4_FS_POSIX_ACL */
#include <linux/sched.h>
-#define ext4_check_acl NULL

static inline int
ext4_acl_chmod(struct inode *inode)
diff --git a/fs/ext4/acls.c b/fs/ext4/acls.c
new file mode 100644
index 0000000..c23fcef
--- /dev/null
+++ b/fs/ext4/acls.c
@@ -0,0 +1,20 @@
+#include <linux/fs.h>
+#include "ext4.h"
+#include "acl.h"
+#include "richacl.h"
+
+int
+ext4_check_acl(struct inode *inode, int mask, unsigned int flags)
+{
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
+ if (IS_POSIXACL(inode))
+ return ext4_check_posix_acl(inode, mask, flags);
+ else
+#endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+ if (IS_RICHACL(inode))
+ return ext4_check_richacl(inode, mask, flags);
+ else
+#endif
+ return -EAGAIN;
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5d1f56a..f0e09ba 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1754,6 +1754,12 @@ extern int ext4_orphan_del(handle_t *, struct inode *);
extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
__u32 start_minor_hash, __u32 *next_hash);

+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+extern int ext4_check_acl(struct inode *, int, unsigned int);
+#else
+# define ext4_check_acl NULL
+#endif
+
/* resize.c */
extern int ext4_group_add(struct super_block *sb,
struct ext4_new_group_data *input);
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 7b80d54..0df1da6 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -28,6 +28,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+#include "richacl.h"

/*
* Called when an inode is released. Note that this is different
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index eb9097a..245e22f 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -28,6 +28,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+#include "richacl.h"

#include <trace/events/ext4.h>

@@ -1038,7 +1039,11 @@ got:
if (err)
goto fail_drop;

- err = ext4_init_acl(handle, inode, dir);
+ if (EXT4_IS_RICHACL(dir))
+ err = ext4_init_richacl(handle, inode, dir);
+ else
+ err = ext4_init_acl(handle, inode, dir);
+
if (err)
goto fail_free_drop;

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 9f7f9e4..7fb0920 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -47,6 +47,7 @@
#include "xattr.h"
#include "acl.h"
#include "ext4_extents.h"
+#include "richacl.h"

#include <trace/events/ext4.h>

@@ -5417,9 +5418,12 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
if (orphan && inode->i_nlink)
ext4_orphan_del(NULL, inode);

- if (!rc && (ia_valid & ATTR_MODE))
- rc = ext4_acl_chmod(inode);
-
+ if (!rc && (ia_valid & ATTR_MODE)) {
+ if (EXT4_IS_RICHACL(inode))
+ rc = ext4_richacl_chmod(inode);
+ else
+ rc = ext4_acl_chmod(inode);
+ }
err_out:
ext4_std_error(inode->i_sb, error);
if (!error)
diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
new file mode 100644
index 0000000..5547fa5
--- /dev/null
+++ b/fs/ext4/richacl.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Author Aneesh Kumar K.V <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/richacl_xattr.h>
+
+#include "ext4.h"
+#include "ext4_jbd2.h"
+#include "xattr.h"
+#include "acl.h"
+#include "richacl.h"
+
+static struct richacl *
+ext4_get_richacl(struct inode *inode)
+{
+ const int name_index = EXT4_XATTR_INDEX_RICHACL;
+ void *value = NULL;
+ struct richacl *acl;
+ int retval;
+
+ if (!IS_RICHACL(inode))
+ return ERR_PTR(-EOPNOTSUPP);
+ acl = get_cached_richacl(inode);
+ if (acl != ACL_NOT_CACHED)
+ return acl;
+ retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
+ if (retval > 0) {
+ value = kmalloc(retval, GFP_KERNEL);
+ if (!value)
+ return ERR_PTR(-ENOMEM);
+ retval = ext4_xattr_get(inode, name_index, "", value, retval);
+ }
+ if (retval > 0) {
+ acl = richacl_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_OR_NULL(acl))
+ set_cached_richacl(inode, acl);
+
+ return acl;
+}
+
+static int
+ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl)
+{
+ const int name_index = EXT4_XATTR_INDEX_RICHACL;
+ size_t size = 0;
+ void *value = NULL;
+ int retval;
+
+ if (acl) {
+ mode_t mode = inode->i_mode;
+ if (richacl_equiv_mode(acl, &mode) == 0) {
+ inode->i_mode = mode;
+ ext4_mark_inode_dirty(handle, inode);
+ acl = NULL;
+ }
+ }
+ if (acl) {
+ size = richacl_xattr_size(acl);
+ value = kmalloc(size, GFP_KERNEL);
+ if (!value)
+ return -ENOMEM;
+ richacl_to_xattr(acl, value);
+ }
+ if (handle)
+ retval = ext4_xattr_set_handle(handle, inode, name_index, "",
+ value, size, 0);
+ else
+ retval = ext4_xattr_set(inode, name_index, "", value, size, 0);
+ kfree(value);
+ if (!retval)
+ set_cached_richacl(inode, acl);
+
+ return retval;
+}
+
+static int
+__ext4_check_richacl(struct inode *inode, unsigned int mask)
+{
+ struct richacl *acl = ext4_get_richacl(inode);
+
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl) {
+ int error = richacl_permission(inode, acl, mask);
+ richacl_put(acl);
+ return error;
+ }
+
+ return -EAGAIN;
+}
+
+int
+ext4_check_richacl(struct inode *inode, int want, unsigned int flags)
+{
+ if (flags & IPERM_FLAG_RCU) {
+ if (!negative_cached_richacl(inode))
+ return -ECHILD;
+ return -EAGAIN;
+ }
+ return __ext4_check_richacl(inode, richacl_want_to_mask(want));
+}
+
+int
+ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ struct richacl *dir_acl = NULL;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ dir_acl = ext4_get_richacl(dir);
+ if (IS_ERR(dir_acl))
+ return PTR_ERR(dir_acl);
+ }
+ if (dir_acl) {
+ struct richacl *acl;
+ int retval;
+
+ acl = richacl_inherit_inode(dir_acl, inode);
+ richacl_put(dir_acl);
+
+ retval = PTR_ERR(acl);
+ if (acl && !IS_ERR(acl)) {
+ retval = ext4_set_richacl(handle, inode, acl);
+ richacl_put(acl);
+ }
+ return retval;
+ } else {
+ inode->i_mode &= ~current_umask();
+ return 0;
+ }
+}
+
+int
+ext4_richacl_chmod(struct inode *inode)
+{
+ struct richacl *acl;
+ int retval;
+
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ acl = ext4_get_richacl(inode);
+ if (IS_ERR_OR_NULL(acl))
+ return PTR_ERR(acl);
+ acl = richacl_chmod(acl, inode->i_mode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ retval = ext4_set_richacl(NULL, inode, acl);
+ richacl_put(acl);
+
+ return retval;
+}
+
+static size_t
+ext4_xattr_list_richacl(struct dentry *dentry, char *list, size_t list_len,
+ const char *name, size_t name_len, int type)
+{
+ const size_t size = sizeof(RICHACL_XATTR);
+ if (!IS_RICHACL(dentry->d_inode))
+ return 0;
+ if (list && size <= list_len)
+ memcpy(list, RICHACL_XATTR, size);
+ return size;
+}
+
+static int
+ext4_xattr_get_richacl(struct dentry *dentry, const char *name, void *buffer,
+ size_t buffer_size, int type)
+{
+ struct richacl *acl;
+ size_t size;
+
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ acl = ext4_get_richacl(dentry->d_inode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl == NULL)
+ return -ENODATA;
+ size = richacl_xattr_size(acl);
+ if (buffer) {
+ if (size > buffer_size)
+ return -ERANGE;
+ richacl_to_xattr(acl, buffer);
+ }
+ richacl_put(acl);
+
+ return size;
+}
+
+static int
+ext4_xattr_set_richacl(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags, int type)
+{
+ handle_t *handle;
+ struct richacl *acl = NULL;
+ int retval, retries = 0;
+ struct inode *inode = dentry->d_inode;
+
+ if (!IS_RICHACL(dentry->d_inode))
+ return -EOPNOTSUPP;
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ if (current_fsuid() != inode->i_uid &&
+ __ext4_check_richacl(inode, ACE4_WRITE_ACL) &&
+ !capable(CAP_FOWNER))
+ return -EPERM;
+ if (value) {
+ acl = richacl_from_xattr(value, size);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+
+ inode->i_mode &= ~S_IRWXUGO;
+ inode->i_mode |= richacl_masks_to_mode(acl);
+ }
+
+retry:
+ handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb));
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ ext4_mark_inode_dirty(handle, inode);
+ retval = ext4_set_richacl(handle, inode, acl);
+ ext4_journal_stop(handle);
+ if (retval == ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+ goto retry;
+ richacl_put(acl);
+ return retval;
+}
+
+const struct xattr_handler ext4_richacl_xattr_handler = {
+ .prefix = RICHACL_XATTR,
+ .list = ext4_xattr_list_richacl,
+ .get = ext4_xattr_get_richacl,
+ .set = ext4_xattr_set_richacl,
+};
diff --git a/fs/ext4/richacl.h b/fs/ext4/richacl.h
new file mode 100644
index 0000000..c71a02d
--- /dev/null
+++ b/fs/ext4/richacl.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Author Aneesh Kumar K.V <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __FS_EXT4_RICHACL_H
+#define __FS_EXT4_RICHACL_H
+
+#include <linux/richacl.h>
+
+#ifdef CONFIG_EXT4_FS_RICHACL
+
+# define EXT4_IS_RICHACL(inode) IS_RICHACL(inode)
+
+extern int ext4_check_richacl(struct inode *, int, unsigned int);
+extern int ext4_init_richacl(handle_t *, struct inode *, struct inode *);
+extern int ext4_richacl_chmod(struct inode *);
+
+#else /* CONFIG_FS_EXT4_RICHACL */
+
+# define EXT4_IS_RICHACL(inode) (0)
+
+static inline int
+ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ return 0;
+}
+
+static inline int
+ext4_richacl_chmod(struct inode *inode)
+{
+ return 0;
+}
+
+#endif /* CONFIG_FS_EXT4_RICHACL */
+#endif /* __FS_EXT4_RICHACL_H */
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index fc32176..1984bb9 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -107,6 +107,9 @@ static const struct xattr_handler *ext4_xattr_handler_map[] = {
#ifdef CONFIG_EXT4_FS_SECURITY
[EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler,
#endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+ [EXT4_XATTR_INDEX_RICHACL] = &ext4_richacl_xattr_handler,
+#endif
};

const struct xattr_handler *ext4_xattr_handlers[] = {
@@ -119,6 +122,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
#ifdef CONFIG_EXT4_FS_SECURITY
&ext4_xattr_security_handler,
#endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+ &ext4_richacl_xattr_handler,
+#endif
NULL
};

diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 1ef1652..5cede1a 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -21,6 +21,7 @@
#define EXT4_XATTR_INDEX_TRUSTED 4
#define EXT4_XATTR_INDEX_LUSTRE 5
#define EXT4_XATTR_INDEX_SECURITY 6
+#define EXT4_XATTR_INDEX_RICHACL 7

struct ext4_xattr_header {
__le32 h_magic; /* magic number for identification */
@@ -70,6 +71,7 @@ extern const struct xattr_handler ext4_xattr_trusted_handler;
extern const struct xattr_handler ext4_xattr_acl_access_handler;
extern const struct xattr_handler ext4_xattr_acl_default_handler;
extern const struct xattr_handler ext4_xattr_security_handler;
+extern const struct xattr_handler ext4_richacl_xattr_handler;

extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);

--
1.7.1

2011-02-23 13:52:11

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 24/24] ext4: Add temporary richacl mount option for ext4

This helps in easy testing of the patchset. The mount
option will be later removed in favour of a feature flag.

***Should be folded before merging***

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/super.c | 52 +++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index adc97b7..9f3fe61 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1011,6 +1011,10 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (!(sb->s_flags & MS_POSIXACL) && (def_mount_opts & EXT4_DEFM_ACL))
seq_puts(seq, ",noacl");
#endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+ if (sb->s_flags & MS_RICHACL)
+ seq_puts(seq, ",richacl");
+#endif
if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {
seq_printf(seq, ",commit=%u",
(unsigned) (sbi->s_commit_interval / HZ));
@@ -1262,6 +1266,7 @@ enum {
Opt_dioread_nolock, Opt_dioread_lock,
Opt_discard, Opt_nodiscard,
Opt_init_inode_table, Opt_noinit_inode_table,
+ Opt_richacl,
};

static const match_table_t tokens = {
@@ -1337,6 +1342,7 @@ static const match_table_t tokens = {
{Opt_init_inode_table, "init_itable=%u"},
{Opt_init_inode_table, "init_itable"},
{Opt_noinit_inode_table, "noinit_itable"},
+ {Opt_richacl, "richacl"},
{Opt_err, NULL},
};

@@ -1363,6 +1369,35 @@ static ext4_fsblk_t get_sb_block(void **data)
return sb_block;
}

+static void enable_acl(struct super_block *sb)
+{
+#if !defined(CONFIG_EXT4_FS_POSIX_ACL)
+ ext4_msg(sb, KERN_ERR, "acl options not supported");
+ return;
+#endif
+ sb->s_flags |= MS_POSIXACL;
+}
+
+static void disable_acl(struct super_block *sb)
+{
+#if !defined(CONFIG_EXT4_FS_POSIX_ACL)
+ ext4_msg(sb, KERN_ERR, "acl options not supported");
+ return;
+#endif
+ sb->s_flags &= ~MS_POSIXACL;
+ return;
+}
+
+static void enable_richacl(struct super_block *sb)
+{
+#if !defined(CONFIG_EXT4_FS_RICHACL)
+ ext4_msg(sb, KERN_ERR, "richacl options not supported");
+ return;
+#endif
+ sb->s_flags |= MS_RICHACL;
+ return;
+}
+
#define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
static char deprecated_msg[] = "Mount option \"%s\" will be removed by %s\n"
"Contact [email protected] if you think we should keep it.\n";
@@ -1527,19 +1562,12 @@ static int parse_options(char *options, struct super_block *sb,
ext4_msg(sb, KERN_ERR, "(no)user_xattr options not supported");
break;
#endif
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
case Opt_acl:
- sb->s_flags |= MS_POSIXACL;
+ enable_acl(sb);
break;
case Opt_noacl:
- sb->s_flags &= ~MS_POSIXACL;
+ disable_acl(sb);
break;
-#else
- case Opt_acl:
- case Opt_noacl:
- ext4_msg(sb, KERN_ERR, "(no)acl options not supported");
- break;
-#endif
case Opt_journal_update:
/* @@@ FIXME */
/* Eventually we will want to be able to create
@@ -1826,6 +1854,8 @@ set_qf_format:
break;
case Opt_noinit_inode_table:
clear_opt(sb, INIT_INODE_TABLE);
+ case Opt_richacl:
+ enable_richacl(sb);
break;
default:
ext4_msg(sb, KERN_ERR,
@@ -3099,9 +3129,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (def_mount_opts & EXT4_DEFM_XATTR_USER)
set_opt(sb, XATTR_USER);
#endif
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
+#if defined(CONFIG_EXT4_FS_POSIX_ACL)
if (def_mount_opts & EXT4_DEFM_ACL)
- sb->s_flags |= MS_POSIXACL;
+ enable_acl(sb);
#endif
if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)
set_opt(sb, JOURNAL_DATA);
--
1.7.1

2011-02-23 13:51:52

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 05/24] vfs: Add IS_RICHACL() test for richacl support

From: Andreas Gruenbacher <[email protected]>

Introduce a new MS_RICHACL super-block flag and a new IS_RICHACL() test
which file systems like nfs can use. IS_ACL() is true if IS_POSIXACL()
or IS_RICHACL() is true.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
include/linux/fs.h | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 70c7779..e542157 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -214,6 +214,7 @@ struct inodes_stat_t {
#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
+#define MS_RICHACL (1<<25) /* Supports richacls */
#define MS_BORN (1<<29)
#define MS_ACTIVE (1<<30)
#define MS_NOUSER (1<<31)
@@ -272,6 +273,7 @@ struct inodes_stat_t {
#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL)
+#define IS_RICHACL(inode) __IS_FLG(inode, MS_RICHACL)

#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
@@ -284,7 +286,7 @@ struct inodes_stat_t {
* IS_ACL() tells the VFS to not apply the umask
* and use iop->check_acl for acl permission checks when defined.
*/
-#define IS_ACL(inode) __IS_FLG(inode, MS_POSIXACL)
+#define IS_ACL(inode) __IS_FLG(inode, MS_POSIXACL | MS_RICHACL)

/* the read-only stuff doesn't really belong here, but any other place is
probably as bad and I don't want to create yet another include file. */
--
1.7.1

2011-02-23 13:52:09

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 22/24] vfs: Cache richacl in struct inode

From: Andreas Gruenbacher <[email protected]>

Cache richacls in struct inode so that this doesn't have to be done
individually in each filesystem.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/inode.c | 24 ++++++++++++++++----
include/linux/fs.h | 12 ++++++++-
include/linux/richacl.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index da85e56..381edca 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -25,6 +25,7 @@
#include <linux/async.h>
#include <linux/posix_acl.h>
#include <linux/ima.h>
+#include <linux/richacl.h>

/*
* This is needed for the following functions:
@@ -219,7 +220,12 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
inode->i_private = NULL;
inode->i_mapping = mapping;
#ifdef CONFIG_FS_POSIX_ACL
- inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
+ if (IS_POSIXACL(inode))
+ inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
+#endif
+#ifdef CONFIG_FS_RICHACL
+ if (IS_RICHACL(inode))
+ inode->i_richacl = ACL_NOT_CACHED;
#endif

#ifdef CONFIG_FSNOTIFY
@@ -269,10 +275,18 @@ void __destroy_inode(struct inode *inode)
security_inode_free(inode);
fsnotify_inode_delete(inode);
#ifdef CONFIG_FS_POSIX_ACL
- if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
- posix_acl_release(inode->i_acl);
- if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
- posix_acl_release(inode->i_default_acl);
+ if (IS_POSIXACL(inode)) {
+ if (inode->i_acl != ACL_NOT_CACHED)
+ posix_acl_release(inode->i_acl);
+ if (inode->i_default_acl != ACL_NOT_CACHED)
+ posix_acl_release(inode->i_default_acl);
+ }
+#endif
+#ifdef CONFIG_FS_RICHACL
+ if (IS_RICHACL(inode)) {
+ if (inode->i_richacl != ACL_NOT_CACHED)
+ richacl_put(inode->i_richacl);
+ }
#endif
this_cpu_dec(nr_inodes);
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1627592..77ce324 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -754,6 +754,7 @@ static inline int mapping_writably_mapped(struct address_space *mapping)
#endif

struct posix_acl;
+struct richacl;
#define ACL_NOT_CACHED ((void *)(-1))

struct inode {
@@ -824,10 +825,17 @@ struct inode {
#ifdef CONFIG_SECURITY
void *i_security;
#endif
+ union {
#ifdef CONFIG_FS_POSIX_ACL
- struct posix_acl *i_acl;
- struct posix_acl *i_default_acl;
+ struct {
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+ };
#endif
+#ifdef CONFIG_FS_RICHACL
+ struct richacl *i_richacl;
+#endif
+ };
void *i_private; /* fs or device private pointer */
};

diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 23befbc..c788a91 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -189,6 +189,59 @@ richacl_put(struct richacl *acl)
kfree(acl);
}

+#ifdef CONFIG_FS_RICHACL
+static inline struct richacl *get_cached_richacl(struct inode *inode)
+{
+ struct richacl **p, *acl;
+
+ p = &inode->i_richacl;
+ acl = ACCESS_ONCE(*p);
+ if (acl) {
+ spin_lock(&inode->i_lock);
+ acl = *p;
+ if (acl != ACL_NOT_CACHED)
+ acl = richacl_get(acl);
+ spin_unlock(&inode->i_lock);
+ }
+ return acl;
+}
+
+static inline void set_cached_richacl(struct inode *inode,
+ struct richacl *acl)
+{
+ struct richacl *old = NULL;
+ spin_lock(&inode->i_lock);
+ old = inode->i_richacl;
+ inode->i_richacl = richacl_get(acl);
+ spin_unlock(&inode->i_lock);
+ if (old != ACL_NOT_CACHED)
+ richacl_put(old);
+}
+
+static inline void forget_cached_richacl(struct inode *inode)
+{
+ struct richacl *old = NULL;
+ spin_lock(&inode->i_lock);
+ old = inode->i_richacl;
+ inode->i_richacl = ACL_NOT_CACHED;
+ spin_unlock(&inode->i_lock);
+ if (old != ACL_NOT_CACHED)
+ richacl_put(old);
+}
+
+static inline int negative_cached_richacl(struct inode *inode)
+{
+ struct richacl **p, *acl;
+
+ p = &inode->i_richacl;
+ acl = ACCESS_ONCE(*p);
+ if (acl)
+ return 0;
+ return 1;
+}
+
+#endif
+
static inline int
richacl_is_auto_inherit(const struct richacl *acl)
{
--
1.7.1

2011-02-23 13:52:06

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 19/24] richacl: Automatic Inheritance

From: Andreas Gruenbacher <[email protected]>

Automatic Inheritance (AI) allows changes to the acl of a directory to
recursively propagate down to files and directories in the directory.

To implement this, the kernel keeps track of which permissions have been
inherited, and makes sure that permission propagation is turned off when
the file permission bits of a file are changed (upon create or chmod).

The actual permission propagation is implemented in user space.

AI works as follows:

- When the ACL4_AUTO_INHERIT flag in the acl of a file is cleared, the
file is not affected by AI.

- When the ACL4_AUTO_INHERIT flag in the acl of a directory is set and
a file or subdirectory is created in that directory, files created in
the directory will have the ACL4_AUTO_INHERIT flag set, and all
inherited aces will have the ACE4_INHERITED_ACE flag set. This
allows user space to distinguish between aces which have been
inherited, and aces which have been explicitly added.

- When the ACL4_PROTECTED acl flag in the acl of a file is set, AI will
not modify the acl of the file. This does not affect propagation of
permissions from the file to its children (if the file is a
directory).

Linux does not have a way of creating files without setting the file
permission bits, so all files created inside a directory with
ACL4_AUTO_INHERIT set will also have the ACL4_PROTECTED flag set. This
effectively disables AI.

Protocols which support creating files without specifying permissions
can explicitly clear the ACL4_PROTECTED flag after creating a file (and
reset the file masks to "undo" applying the create mode; see
richacl_compute_max_masks()). This is a workaround; a per-create or
per-process flag indicating to ignore the create mode when AI is in
effect would fix this problem.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/richacl_base.c | 10 +++++++++-
fs/richacl_inode.c | 7 ++++++-
include/linux/richacl.h | 25 +++++++++++++++++++++++--
3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index e60440b..c064c90 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -377,7 +377,8 @@ richacl_chmod(struct richacl *acl, mode_t mode)
if (acl->a_owner_mask == owner_mask &&
acl->a_group_mask == group_mask &&
acl->a_other_mask == other_mask &&
- (acl->a_flags & ACL4_MASKED))
+ (acl->a_flags & ACL4_MASKED) &&
+ (!richacl_is_auto_inherit(acl) || richacl_is_protected(acl)))
return acl;

clone = richacl_clone(acl);
@@ -389,6 +390,8 @@ richacl_chmod(struct richacl *acl, mode_t mode)
clone->a_owner_mask = owner_mask;
clone->a_group_mask = group_mask;
clone->a_other_mask = other_mask;
+ if (richacl_is_auto_inherit(clone))
+ clone->a_flags |= ACL4_PROTECTED;

return clone;
}
@@ -558,6 +561,11 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
ace++;
}
}
+ if (richacl_is_auto_inherit(dir_acl)) {
+ acl->a_flags = ACL4_AUTO_INHERIT;
+ richacl_for_each_entry(ace, acl)
+ ace->e_flags |= ACE4_INHERITED_ACE;
+ }

return acl;
}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index 31e6925..5ae6326 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -37,9 +37,14 @@ richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)

acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode));
if (acl) {
+ /*
+ * We need to set ACL4_PROTECTED because we are
+ * doing an implicit chmod
+ */
+ if (richacl_is_auto_inherit(acl))
+ acl->a_flags |= ACL4_PROTECTED;

richacl_compute_max_masks(acl);
-
/*
* Ensure that the acl will not grant any permissions beyond
* the create mode.
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 12a79f1..23befbc 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -47,10 +47,16 @@ struct richacl {
_ace != _acl->a_entries - 1; \
_ace--)

+/* a_flags values */
+#define ACL4_AUTO_INHERIT 0x01
+#define ACL4_PROTECTED 0x02
+/* #define ACL4_DEFAULTED 0x04 */
/* Flag values defined by rich-acl */
#define ACL4_MASKED 0x80

#define ACL4_VALID_FLAGS ( \
+ ACL4_AUTO_INHERIT | \
+ ACL4_PROTECTED | \
ACL4_MASKED)

/* e_type values */
@@ -67,6 +73,7 @@ struct richacl {
/*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/
/*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/
#define ACE4_IDENTIFIER_GROUP 0x0040
+#define ACE4_INHERITED_ACE 0x0080
/* in-memory representation only */
#define ACE4_SPECIAL_WHO 0x4000

@@ -75,7 +82,8 @@ struct richacl {
ACE4_DIRECTORY_INHERIT_ACE | \
ACE4_NO_PROPAGATE_INHERIT_ACE | \
ACE4_INHERIT_ONLY_ACE | \
- ACE4_IDENTIFIER_GROUP)
+ ACE4_IDENTIFIER_GROUP | \
+ ACE4_INHERITED_ACE)

/* e_mask bitflags */
#define ACE4_READ_DATA 0x00000001
@@ -181,6 +189,18 @@ richacl_put(struct richacl *acl)
kfree(acl);
}

+static inline int
+richacl_is_auto_inherit(const struct richacl *acl)
+{
+ return acl->a_flags & ACL4_AUTO_INHERIT;
+}
+
+static inline int
+richacl_is_protected(const struct richacl *acl)
+{
+ return acl->a_flags & ACL4_PROTECTED;
+}
+
/*
* Special e_who identifiers: we use these pointer values in comparisons
* instead of doing a strcmp.
@@ -259,7 +279,8 @@ richace_clear_inheritance_flags(struct richace *ace)
ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE |
ACE4_DIRECTORY_INHERIT_ACE |
ACE4_NO_PROPAGATE_INHERIT_ACE |
- ACE4_INHERIT_ONLY_ACE);
+ ACE4_INHERIT_ONLY_ACE |
+ ACE4_INHERITED_ACE);
}

/**
--
1.7.1

2011-02-23 13:52:04

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 17/24] richacl: Create-time inheritance

When a new file is created, it can inherit an acl from its parent
directory; this is similar to how default acls work in POSIX (draft)
ACLs.

As with POSIX ACLs, if a file inherits an acl from its parent directory,
the intersection between the create mode and the permissions granted by
the inherited acl determines the file masks and file permission bits,
and the umask is ignored.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/Makefile | 2 +-
fs/richacl_base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++
fs/richacl_inode.c | 59 ++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 4 +++
4 files changed, 133 insertions(+), 1 deletions(-)
create mode 100644 fs/richacl_inode.c

diff --git a/fs/Makefile b/fs/Makefile
index ed79438..e313219 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o

obj-$(CONFIG_FS_RICHACL) += richacl.o
-richacl-y := richacl_base.o
+richacl-y := richacl_base.o richacl_inode.o

obj-y += quota/

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index a6e3199..5e926ad 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -492,3 +492,72 @@ is_everyone:
return denied ? -EACCES : 0;
}
EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * richacl_inherit - compute the inherited acl of a new file
+ * @dir_acl: acl of the containing direcory
+ * @isdir: inherit by a directory or non-directory?
+ *
+ * A directory can have acl entries which files and/or directories created
+ * inside the directory will inherit. This function computes the acl for such
+ * a new file. If there is no inheritable acl, it will return %NULL.
+ */
+struct richacl *
+richacl_inherit(const struct richacl *dir_acl, int isdir)
+{
+ const struct richace *dir_ace;
+ struct richacl *acl = NULL;
+ struct richace *ace;
+ int count = 0;
+
+ if (isdir) {
+ richacl_for_each_entry(dir_ace, dir_acl) {
+ if (!richace_is_inheritable(dir_ace))
+ continue;
+ count++;
+ }
+ if (!count)
+ return NULL;
+ acl = richacl_alloc(count);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ ace = acl->a_entries;
+ richacl_for_each_entry(dir_ace, dir_acl) {
+ if (!richace_is_inheritable(dir_ace))
+ continue;
+ memcpy(ace, dir_ace, sizeof(struct richace));
+ if (dir_ace->e_flags & ACE4_NO_PROPAGATE_INHERIT_ACE)
+ richace_clear_inheritance_flags(ace);
+ if ((dir_ace->e_flags & ACE4_FILE_INHERIT_ACE) &&
+ !(dir_ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE))
+ ace->e_flags |= ACE4_INHERIT_ONLY_ACE;
+ ace++;
+ }
+ } else {
+ richacl_for_each_entry(dir_ace, dir_acl) {
+ if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE))
+ continue;
+ count++;
+ }
+ if (!count)
+ return NULL;
+ acl = richacl_alloc(count);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ ace = acl->a_entries;
+ richacl_for_each_entry(dir_ace, dir_acl) {
+ if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE))
+ continue;
+ memcpy(ace, dir_ace, sizeof(struct richace));
+ richace_clear_inheritance_flags(ace);
+ /*
+ * ACE4_DELETE_CHILD is meaningless for
+ * non-directories, so clear it.
+ */
+ ace->e_mask &= ~ACE4_DELETE_CHILD;
+ ace++;
+ }
+ }
+
+ return acl;
+}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
new file mode 100644
index 0000000..31e6925
--- /dev/null
+++ b/fs/richacl_inode.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 Novell, Inc.
+ * Written by Andreas Gruenbacher <[email protected]>
+ *
+ * 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/sched.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/richacl.h>
+
+/**
+ * richacl_inherit_inode - compute inherited acl and file mode
+ * @dir_acl: acl of the containing direcory
+ * @inode: inode of the new file (create mode in i_mode)
+ *
+ * The file permission bits in inode->i_mode must be set to the create mode.
+ * If there is an inheritable acl, the maximum permissions that the acl grants
+ * will be computed and permissions not granted by the acl will be removed from
+ * inode->i_mode. If there is no inheritable acl, the umask will be applied
+ * instead.
+ */
+struct richacl *
+richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)
+{
+ struct richacl *acl;
+ mode_t mask;
+
+ acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode));
+ if (acl) {
+
+ richacl_compute_max_masks(acl);
+
+ /*
+ * Ensure that the acl will not grant any permissions beyond
+ * the create mode.
+ */
+ acl->a_flags |= ACL4_MASKED;
+ acl->a_owner_mask &= richacl_mode_to_mask(inode->i_mode >> 6) |
+ ACE4_POSIX_OWNER_ALLOWED;
+ acl->a_group_mask &= richacl_mode_to_mask(inode->i_mode >> 3);
+ acl->a_other_mask &= richacl_mode_to_mask(inode->i_mode);
+ mask = ~S_IRWXUGO | richacl_masks_to_mode(acl);
+ } else
+ mask = ~current_umask();
+
+ inode->i_mode &= mask;
+ return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_inherit_inode);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index c9d2761..630bf86 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -291,5 +291,9 @@ extern void richacl_compute_max_masks(struct richacl *);
extern struct richacl *richacl_chmod(struct richacl *, mode_t);
extern int richacl_permission(struct inode *, const struct richacl *,
unsigned int);
+extern struct richacl *richacl_inherit(const struct richacl *, int);

+/* richacl_inode.c */
+extern struct richacl *richacl_inherit_inode(const struct richacl *,
+ struct inode *);
#endif /* __RICHACL_H */
--
1.7.1

2011-02-23 13:51:57

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 10/24] vfs: Add permission flags for setting file attributes

From: Andreas Gruenbacher <[email protected]>

Some permission models can allow processes to take ownership of a file,
change the file permissions, and set the file timestamps. Introduce new
permission mask flags and check for those permissions in
inode_change_ok().

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/attr.c | 49 ++++++++++++++++++++++++++++++++++++++-----------
include/linux/fs.h | 3 +++
2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index f081b5a..793f34c 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -13,6 +13,20 @@
#include <linux/fsnotify.h>
#include <linux/fcntl.h>
#include <linux/security.h>
+#include <linux/richacl.h>
+
+static int richacl_change_ok(struct inode *inode, int mask)
+{
+ if (!IS_RICHACL(inode))
+ return -EPERM;
+
+ if (inode->i_op->permission)
+ return inode->i_op->permission(inode, mask, 0);
+ else if (inode->i_op->check_acl)
+ return inode->i_op->check_acl(inode, mask, 0);
+ else
+ return -EPERM;
+}

/**
* inode_change_ok - check if attribute changes to an inode are allowed
@@ -47,20 +61,29 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
/* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) &&
(current_fsuid() != inode->i_uid ||
- attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
- return -EPERM;
+ attr->ia_uid != inode->i_uid) &&
+ (current_fsuid() != attr->ia_uid ||
+ richacl_change_ok(inode, MAY_TAKE_OWNERSHIP)) &&
+ !capable(CAP_CHOWN))
+ goto error;

/* Make sure caller can chgrp. */
- if ((ia_valid & ATTR_GID) &&
- (current_fsuid() != inode->i_uid ||
- (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
- !capable(CAP_CHOWN))
- return -EPERM;
+ 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 ||
+ richacl_change_ok(inode, MAY_TAKE_OWNERSHIP)) &&
+ !capable(CAP_CHOWN))
+ goto error;
+ }

/* Make sure a caller can chmod. */
if (ia_valid & ATTR_MODE) {
- if (!is_owner_or_cap(inode))
- return -EPERM;
+ if (current_fsuid() != inode->i_uid &&
+ richacl_change_ok(inode, MAY_CHMOD) &&
+ !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))
@@ -69,11 +92,15 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)

/* Check for setting the inode time. */
if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
- if (!is_owner_or_cap(inode))
- return -EPERM;
+ if (current_fsuid() != inode->i_uid &&
+ richacl_change_ok(inode, MAY_SET_TIMES) &&
+ !capable(CAP_FOWNER))
+ goto error;
}

return 0;
+error:
+ return -EPERM;
}
EXPORT_SYMBOL(inode_change_ok);

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3ae23a2..1627592 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -66,6 +66,9 @@ struct inodes_stat_t {
#define MAY_CREATE_DIR 256
#define MAY_DELETE_CHILD 512
#define MAY_DELETE_SELF 1024
+#define MAY_TAKE_OWNERSHIP 2048
+#define MAY_CHMOD 4096
+#define MAY_SET_TIMES 8192

/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
--
1.7.1


2011-02-23 13:51:56

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 09/24] vfs: Make the inode passed to inode_change_ok non-const

From: Andreas Gruenbacher <[email protected]>

We will need to call iop->permission and iop->change_acl from
inode_change_ok() for additional permission checks, and both take a
non-const inode.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/attr.c | 2 +-
include/linux/fs.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 7ca4181..f081b5a 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -26,7 +26,7 @@
* Should be called as the first thing in ->setattr implementations,
* possibly after taking additional locks.
*/
-int inode_change_ok(const struct inode *inode, struct iattr *attr)
+int inode_change_ok(struct inode *inode, struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6a6e017..3ae23a2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2472,7 +2472,7 @@ extern int buffer_migrate_page(struct address_space *,
#define buffer_migrate_page NULL
#endif

-extern int inode_change_ok(const struct inode *, struct iattr *);
+extern int inode_change_ok(struct inode *, struct iattr *);
extern int inode_newsize_ok(const struct inode *, loff_t offset);
extern void setattr_copy(struct inode *inode, const struct iattr *attr);

--
1.7.1

2011-02-23 13:51:53

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 06/24] vfs: Optimize out IS_RICHACL() if CONFIG_FS_RICHACL is not defined

From: Andreas Gruenbacher <[email protected]>

if CONFIG_FS_RICHACL is not defined optimize out
the ACL check function.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/Kconfig | 3 +++
include/linux/fs.h | 5 +++++
2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 3db9caa..753b194 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -46,6 +46,9 @@ endif # BLOCK
config FS_POSIX_ACL
def_bool n

+config FS_RICHACL
+ def_bool n
+
config EXPORTFS
tristate

diff --git a/include/linux/fs.h b/include/linux/fs.h
index e542157..1dc6e72 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -273,7 +273,12 @@ struct inodes_stat_t {
#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL)
+
+#ifdef CONFIG_FS_RICHACL
#define IS_RICHACL(inode) __IS_FLG(inode, MS_RICHACL)
+#else
+#define IS_RICHACL(inode) 0
+#endif

#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
--
1.7.1

2011-02-23 13:51:51

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH -V5 04/24] vfs: Add generic IS_ACL() test for acl support

From: Andreas Gruenbacher <[email protected]>

When IS_POSIXACL() is true, the vfs does not apply the umask. Other acl
models will need the same exception, so introduce a separate IS_ACL()
test.

The IS_POSIX_ACL() test is still needed so that nfsd can determine when
the underlying file system supports POSIX ACLs (as opposed to some other
kind).

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/namei.c | 6 +++---
include/linux/fs.h | 8 +++++++-
2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 0bfc546..b1e5104 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2161,7 +2161,7 @@ static int __open_namei_create(struct nameidata *nd, struct path *path,
int error;
struct dentry *dir = nd->path.dentry;

- if (!IS_POSIXACL(dir->d_inode))
+ if (!IS_ACL(dir->d_inode))
mode &= ~current_umask();
error = security_path_mknod(&nd->path, path->dentry, mode, 0);
if (error)
@@ -2691,7 +2691,7 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, int, mode,
error = PTR_ERR(dentry);
goto out_unlock;
}
- if (!IS_POSIXACL(nd.path.dentry->d_inode))
+ if (!IS_ACL(nd.path.dentry->d_inode))
mode &= ~current_umask();
error = may_mknod(mode);
if (error)
@@ -2768,7 +2768,7 @@ SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode)
if (IS_ERR(dentry))
goto out_unlock;

- if (!IS_POSIXACL(nd.path.dentry->d_inode))
+ if (!IS_ACL(nd.path.dentry->d_inode))
mode &= ~current_umask();
error = mnt_want_write(nd.path.mnt);
if (error)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index bd32159..70c7779 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -205,7 +205,7 @@ struct inodes_stat_t {
#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
MS_VERBOSE is deprecated. */
#define MS_SILENT 32768
-#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
+#define MS_POSIXACL (1<<16) /* Supports POSIX ACLs */
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
#define MS_PRIVATE (1<<18) /* change to private */
#define MS_SLAVE (1<<19) /* change to slave */
@@ -280,6 +280,12 @@ struct inodes_stat_t {
#define IS_IMA(inode) ((inode)->i_flags & S_IMA)
#define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT)

+/*
+ * IS_ACL() tells the VFS to not apply the umask
+ * and use iop->check_acl for acl permission checks when defined.
+ */
+#define IS_ACL(inode) __IS_FLG(inode, MS_POSIXACL)
+
/* the read-only stuff doesn't really belong here, but any other place is
probably as bad and I don't want to create yet another include file. */

--
1.7.1

2011-02-28 21:11:45

by Theodore Y. Ts'o

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

Hi Aneesh,

What is the current status of this patch series? I seem to remember
that Christoph and Al Viro had some objections; have those been
cleared yet? If not, can you summarize what their objections are?

To be honest I haven't been paying super close attention to this patch
series, and I'm curious what needs to happen with it one way or
another.

- Ted

2011-03-01 06:51:09

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> Hi Aneesh,
>
> What is the current status of this patch series? I seem to remember
> that Christoph and Al Viro had some objections; have those been
> cleared yet? If not, can you summarize what their objections are?

The main objection raised was the use of may_delete and may_create inode
operations callback. They are gone now and we have MAY_* flags as
favoured by Al Viro. The new MAY_* flags added are

#define MAY_CREATE_FILE 128
#define MAY_CREATE_DIR 256
#define MAY_DELETE_CHILD 512
#define MAY_DELETE_SELF 1024
#define MAY_TAKE_OWNERSHIP 2048
#define MAY_CHMOD 4096
#define MAY_SET_TIMES 8192


>
> To be honest I haven't been paying super close attention to this patch
> series, and I'm curious what needs to happen with it one way or
> another.
>

IMHO we are ready to get first 11 patches upstream in the next merge
window. ie the below set of patches.

vfs: Make acl_permission_check() work for richacls
vfs: Add permission flags for setting file attributes
vfs: Make the inode passed to inode_change_ok non-const
vfs: Add delete child and delete self permission flags
vfs: Add new file and directory create permission flags
vfs: Optimize out IS_RICHACL() if CONFIG_FS_RICHACL is not defined
vfs: Add IS_RICHACL() test for richacl support
vfs: Add generic IS_ACL() test for acl support
vfs: Add a comment to inode_permission()
vfs: Pass all mask flags down to iop->check_acl
vfs: Indicate that the permission functions take all the MAY_* flags

-aneesh

2011-03-02 15:49:59

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Tue, Mar 01, 2011 at 12:20:36PM +0530, Aneesh Kumar K. V wrote:
> On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> > Hi Aneesh,
> >
> > What is the current status of this patch series? I seem to remember
> > that Christoph and Al Viro had some objections; have those been
> > cleared yet? If not, can you summarize what their objections are?
>
> The main objection raised was the use of may_delete and may_create inode
> operations callback. They are gone now and we have MAY_* flags as
> favoured by Al Viro. The new MAY_* flags added are
>
> #define MAY_CREATE_FILE 128
> #define MAY_CREATE_DIR 256
> #define MAY_DELETE_CHILD 512
> #define MAY_DELETE_SELF 1024
> #define MAY_TAKE_OWNERSHIP 2048
> #define MAY_CHMOD 4096
> #define MAY_SET_TIMES 8192
>
>
> >
> > To be honest I haven't been paying super close attention to this patch
> > series, and I'm curious what needs to happen with it one way or
> > another.
> >
>
> IMHO we are ready to get first 11 patches upstream in the next merge
> window. ie the below set of patches.

Why aren't all of them ready?

--b.

>
> vfs: Make acl_permission_check() work for richacls
> vfs: Add permission flags for setting file attributes
> vfs: Make the inode passed to inode_change_ok non-const
> vfs: Add delete child and delete self permission flags
> vfs: Add new file and directory create permission flags
> vfs: Optimize out IS_RICHACL() if CONFIG_FS_RICHACL is not defined
> vfs: Add IS_RICHACL() test for richacl support
> vfs: Add generic IS_ACL() test for acl support
> vfs: Add a comment to inode_permission()
> vfs: Pass all mask flags down to iop->check_acl
> vfs: Indicate that the permission functions take all the MAY_* flags
>
> -aneesh

2011-03-02 17:47:56

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Wed, 2 Mar 2011 10:49:43 -0500, "J. Bruce Fields" <[email protected]> wrote:
> On Tue, Mar 01, 2011 at 12:20:36PM +0530, Aneesh Kumar K. V wrote:
> > On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> > > Hi Aneesh,
> > >
> > > What is the current status of this patch series? I seem to remember
> > > that Christoph and Al Viro had some objections; have those been
> > > cleared yet? If not, can you summarize what their objections are?
> >
> > The main objection raised was the use of may_delete and may_create inode
> > operations callback. They are gone now and we have MAY_* flags as
> > favoured by Al Viro. The new MAY_* flags added are
> >
> > #define MAY_CREATE_FILE 128
> > #define MAY_CREATE_DIR 256
> > #define MAY_DELETE_CHILD 512
> > #define MAY_DELETE_SELF 1024
> > #define MAY_TAKE_OWNERSHIP 2048
> > #define MAY_CHMOD 4096
> > #define MAY_SET_TIMES 8192
> >
> >
> > >
> > > To be honest I haven't been paying super close attention to this patch
> > > series, and I'm curious what needs to happen with it one way or
> > > another.
> > >
> >
> > IMHO we are ready to get first 11 patches upstream in the next merge
> > window. ie the below set of patches.
>
> Why aren't all of them ready?
>

All except how to enable richacl in local file system is ready. I
actually floated two ideas in the patch series

1) mount option
2) Ext4 compat flags.

If we can get to decide which one, then the entire set can go in. We also
want others to review the richacl format. If that cannot be completed by
next merge window there is no reason to prevent the vfs changes from
going in. VFS changes are independent of richacl format.

-aneesh

2011-03-02 18:58:47

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Wed, Mar 02, 2011 at 11:17:56PM +0530, Aneesh Kumar K. V wrote:
> On Wed, 2 Mar 2011 10:49:43 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > On Tue, Mar 01, 2011 at 12:20:36PM +0530, Aneesh Kumar K. V wrote:
> > > On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> > > > Hi Aneesh,
> > > >
> > > > What is the current status of this patch series? I seem to remember
> > > > that Christoph and Al Viro had some objections; have those been
> > > > cleared yet? If not, can you summarize what their objections are?
> > >
> > > The main objection raised was the use of may_delete and may_create inode
> > > operations callback. They are gone now and we have MAY_* flags as
> > > favoured by Al Viro. The new MAY_* flags added are
> > >
> > > #define MAY_CREATE_FILE 128
> > > #define MAY_CREATE_DIR 256
> > > #define MAY_DELETE_CHILD 512
> > > #define MAY_DELETE_SELF 1024
> > > #define MAY_TAKE_OWNERSHIP 2048
> > > #define MAY_CHMOD 4096
> > > #define MAY_SET_TIMES 8192
> > >
> > >
> > > >
> > > > To be honest I haven't been paying super close attention to this patch
> > > > series, and I'm curious what needs to happen with it one way or
> > > > another.
> > > >
> > >
> > > IMHO we are ready to get first 11 patches upstream in the next merge
> > > window. ie the below set of patches.
> >
> > Why aren't all of them ready?
> >
>
> All except how to enable richacl in local file system is ready. I
> actually floated two ideas in the patch series
>
> 1) mount option
> 2) Ext4 compat flags.

The choice of ACL format is a persistant property of the filesystem, not
of a single mount of the filesystem: for example, people can't try out
richacls for one mount and then decide to revert bacak to posix acls.

(Right?) So I'm assuming we should use the latter--but I don't
understand what ext4 compat flags are.... Is there some disadvantage to
using them?

--b.

>
> If we can get to decide which one, then the entire set can go in. We also
> want others to review the richacl format. If that cannot be completed by
> next merge window there is no reason to prevent the vfs changes from
> going in. VFS changes are independent of richacl format.

2011-03-04 10:38:15

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Wed, 2 Mar 2011 13:58:47 -0500, "J. Bruce Fields" <[email protected]> wrote:
> On Wed, Mar 02, 2011 at 11:17:56PM +0530, Aneesh Kumar K. V wrote:
> > On Wed, 2 Mar 2011 10:49:43 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > > On Tue, Mar 01, 2011 at 12:20:36PM +0530, Aneesh Kumar K. V wrote:
> > > > On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> > > > > Hi Aneesh,
> > > > >
> > > > > What is the current status of this patch series? I seem to remember
> > > > > that Christoph and Al Viro had some objections; have those been
> > > > > cleared yet? If not, can you summarize what their objections are?
> > > >
> > > > The main objection raised was the use of may_delete and may_create inode
> > > > operations callback. They are gone now and we have MAY_* flags as
> > > > favoured by Al Viro. The new MAY_* flags added are
> > > >
> > > > #define MAY_CREATE_FILE 128
> > > > #define MAY_CREATE_DIR 256
> > > > #define MAY_DELETE_CHILD 512
> > > > #define MAY_DELETE_SELF 1024
> > > > #define MAY_TAKE_OWNERSHIP 2048
> > > > #define MAY_CHMOD 4096
> > > > #define MAY_SET_TIMES 8192
> > > >
> > > >
> > > > >
> > > > > To be honest I haven't been paying super close attention to this patch
> > > > > series, and I'm curious what needs to happen with it one way or
> > > > > another.
> > > > >
> > > >
> > > > IMHO we are ready to get first 11 patches upstream in the next merge
> > > > window. ie the below set of patches.
> > >
> > > Why aren't all of them ready?
> > >
> >
> > All except how to enable richacl in local file system is ready. I
> > actually floated two ideas in the patch series
> >
> > 1) mount option
> > 2) Ext4 compat flags.
>
> The choice of ACL format is a persistant property of the filesystem, not
> of a single mount of the filesystem: for example, people can't try out
> richacls for one mount and then decide to revert bacak to posix acls.
>
> (Right?) So I'm assuming we should use the latter--but I don't
> understand what ext4 compat flags are.... Is there some disadvantage to
> using them?
>

We already have a mount option to enable posix acl (-o acl|noacl). So
along the same line should we have -o richacl|norichacl or should we
have richacl as a ext4 compat flag EXT4_FEATURE_COMPAT_RICHACL. The
compat feature can be enabled via tune2fs for an already created file
system. Once the compat feature is enabled a -o acl mount option cause
the richacl access check to be enabled. That can also result in
mapping the existing posix acl in the file system to richacl and
using mapped richacl for access restriction. With compat flag once set
we will never be able to mount the file system again to use posix acl
access restriction. (We cannot map richacl to posixacl because richacl
support advanced access masks)


With mount option (-o richacl) we can still mount the file system with
-o acl which implies we will have to ignore the richacl associated with
files and only evaluate the posix acl stored.

-aneesh

2011-03-05 00:32:31

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Fri, Mar 04, 2011 at 04:08:15PM +0530, Aneesh Kumar K. V wrote:
> On Wed, 2 Mar 2011 13:58:47 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > On Wed, Mar 02, 2011 at 11:17:56PM +0530, Aneesh Kumar K. V wrote:
> > > On Wed, 2 Mar 2011 10:49:43 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > > > On Tue, Mar 01, 2011 at 12:20:36PM +0530, Aneesh Kumar K. V wrote:
> > > > > On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> > > > > > Hi Aneesh,
> > > > > >
> > > > > > What is the current status of this patch series? I seem to remember
> > > > > > that Christoph and Al Viro had some objections; have those been
> > > > > > cleared yet? If not, can you summarize what their objections are?
> > > > >
> > > > > The main objection raised was the use of may_delete and may_create inode
> > > > > operations callback. They are gone now and we have MAY_* flags as
> > > > > favoured by Al Viro. The new MAY_* flags added are
> > > > >
> > > > > #define MAY_CREATE_FILE 128
> > > > > #define MAY_CREATE_DIR 256
> > > > > #define MAY_DELETE_CHILD 512
> > > > > #define MAY_DELETE_SELF 1024
> > > > > #define MAY_TAKE_OWNERSHIP 2048
> > > > > #define MAY_CHMOD 4096
> > > > > #define MAY_SET_TIMES 8192
> > > > >
> > > > >
> > > > > >
> > > > > > To be honest I haven't been paying super close attention to this patch
> > > > > > series, and I'm curious what needs to happen with it one way or
> > > > > > another.
> > > > > >
> > > > >
> > > > > IMHO we are ready to get first 11 patches upstream in the next merge
> > > > > window. ie the below set of patches.
> > > >
> > > > Why aren't all of them ready?
> > > >
> > >
> > > All except how to enable richacl in local file system is ready. I
> > > actually floated two ideas in the patch series
> > >
> > > 1) mount option
> > > 2) Ext4 compat flags.
> >
> > The choice of ACL format is a persistant property of the filesystem, not
> > of a single mount of the filesystem: for example, people can't try out
> > richacls for one mount and then decide to revert bacak to posix acls.
> >
> > (Right?) So I'm assuming we should use the latter--but I don't
> > understand what ext4 compat flags are.... Is there some disadvantage to
> > using them?
> >
>
> We already have a mount option to enable posix acl (-o acl|noacl). So
> along the same line should we have -o richacl|norichacl or should we
> have richacl as a ext4 compat flag EXT4_FEATURE_COMPAT_RICHACL. The
> compat feature can be enabled via tune2fs for an already created file
> system. Once the compat feature is enabled a -o acl mount option cause
> the richacl access check to be enabled. That can also result in
> mapping the existing posix acl in the file system to richacl and
> using mapped richacl for access restriction. With compat flag once set
> we will never be able to mount the file system again to use posix acl
> access restriction. (We cannot map richacl to posixacl because richacl
> support advanced access masks)

Sounds fine to me. I'm not sure you answered my question. Is there any
disadvantage to doing it this way?

--b.

> With mount option (-o richacl) we can still mount the file system with
> -o acl which implies we will have to ignore the richacl associated with
> files and only evaluate the posix acl stored.

2011-03-05 17:58:12

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Fri, 4 Mar 2011 19:32:15 -0500, "J. Bruce Fields" <[email protected]> wrote:
> On Fri, Mar 04, 2011 at 04:08:15PM +0530, Aneesh Kumar K. V wrote:
> > On Wed, 2 Mar 2011 13:58:47 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > > On Wed, Mar 02, 2011 at 11:17:56PM +0530, Aneesh Kumar K. V wrote:
> > > > On Wed, 2 Mar 2011 10:49:43 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > > > > On Tue, Mar 01, 2011 at 12:20:36PM +0530, Aneesh Kumar K. V wrote:
> > > > > > On Mon, 28 Feb 2011 16:11:45 -0500, "Ted Ts'o" <[email protected]> wrote:
> > > > > > > Hi Aneesh,
> > > > > > >
> > > > > > > What is the current status of this patch series? I seem to remember
> > > > > > > that Christoph and Al Viro had some objections; have those been
> > > > > > > cleared yet? If not, can you summarize what their objections are?
> > > > > >
> > > > > > The main objection raised was the use of may_delete and may_create inode
> > > > > > operations callback. They are gone now and we have MAY_* flags as
> > > > > > favoured by Al Viro. The new MAY_* flags added are
> > > > > >
> > > > > > #define MAY_CREATE_FILE 128
> > > > > > #define MAY_CREATE_DIR 256
> > > > > > #define MAY_DELETE_CHILD 512
> > > > > > #define MAY_DELETE_SELF 1024
> > > > > > #define MAY_TAKE_OWNERSHIP 2048
> > > > > > #define MAY_CHMOD 4096
> > > > > > #define MAY_SET_TIMES 8192
> > > > > >
> > > > > >
> > > > > > >
> > > > > > > To be honest I haven't been paying super close attention to this patch
> > > > > > > series, and I'm curious what needs to happen with it one way or
> > > > > > > another.
> > > > > > >
> > > > > >
> > > > > > IMHO we are ready to get first 11 patches upstream in the next merge
> > > > > > window. ie the below set of patches.
> > > > >
> > > > > Why aren't all of them ready?
> > > > >
> > > >
> > > > All except how to enable richacl in local file system is ready. I
> > > > actually floated two ideas in the patch series
> > > >
> > > > 1) mount option
> > > > 2) Ext4 compat flags.
> > >
> > > The choice of ACL format is a persistant property of the filesystem, not
> > > of a single mount of the filesystem: for example, people can't try out
> > > richacls for one mount and then decide to revert bacak to posix acls.
> > >
> > > (Right?) So I'm assuming we should use the latter--but I don't
> > > understand what ext4 compat flags are.... Is there some disadvantage to
> > > using them?
> > >
> >
> > We already have a mount option to enable posix acl (-o acl|noacl). So
> > along the same line should we have -o richacl|norichacl or should we
> > have richacl as a ext4 compat flag EXT4_FEATURE_COMPAT_RICHACL. The
> > compat feature can be enabled via tune2fs for an already created file
> > system. Once the compat feature is enabled a -o acl mount option cause
> > the richacl access check to be enabled. That can also result in
> > mapping the existing posix acl in the file system to richacl and
> > using mapped richacl for access restriction. With compat flag once set
> > we will never be able to mount the file system again to use posix acl
> > access restriction. (We cannot map richacl to posixacl because richacl
> > support advanced access masks)
>
> Sounds fine to me. I'm not sure you answered my question. Is there any
> disadvantage to doing it this way?
>

Andreas didn't like the compat feature flag patch. I don't remember why
though. Andreas can you comment on why you didn't want the compat
feature flag ?

-aneesh

2011-03-15 08:48:00

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

Aneesh,

On Saturday 05 March 2011 18:58:12 Aneesh Kumar K. V wrote:
> Andreas didn't like the compat feature flag patch. I don't remember why
> though. Andreas can you comment on why you didn't want the compat
> feature flag ?

The mount option made developing and testing of the patch set easier because
it didn't require a modification to e2fsprogs. For upstream, the feature flag
(*) makes a lot more sense, though.

(*) http://git.kernel.org/?p=linux/kernel/git/kvaneesh/linux-richacl.git;h=fb5c519

Thanks,
Andreas

2011-05-11 23:59:56

by Björn JACKE

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

Hi,

On 2011-02-28 at 16:11 -0500 Ted Ts'o sent off:
> What is the current status of this patch series? I seem to remember
> that Christoph and Al Viro had some objections; have those been
> cleared yet? If not, can you summarize what their objections are?
>
> To be honest I haven't been paying super close attention to this patch
> series, and I'm curious what needs to happen with it one way or
> another.

after the discussion of this path submission thread are there any major or
minor issues left that prevent these to go upstream? I'd really love to see
NFSv4 ACLs being available on Linux, too.

Cheers
Bj?rn


Attachments:
(No filename) (620.00 B)
(No filename) (198.00 B)
Download all attachments

2011-05-13 15:40:27

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH -V5 00/24] New ACL format for better NFSv4 acl interoperability

On Thu, 12 May 2011 00:16:13 +0200, Björn JACKE <[email protected]> wrote:
> Hi,
>
> On 2011-02-28 at 16:11 -0500 Ted Ts'o sent off:
> > What is the current status of this patch series? I seem to remember
> > that Christoph and Al Viro had some objections; have those been
> > cleared yet? If not, can you summarize what their objections are?
> >
> > To be honest I haven't been paying super close attention to this patch
> > series, and I'm curious what needs to happen with it one way or
> > another.
>
> after the discussion of this path submission thread are there any major or
> minor issues left that prevent these to go upstream? I'd really love to see
> NFSv4 ACLs being available on Linux, too.
>

I updated richaclv23 branch at

http://git.kernel.org/?p=linux/kernel/git/kvaneesh/linux-richacl.git;a=summary

which is rebased against latest linus tree. The changes pass richacl
test against local file system. I am yet to test richacl on NFS
client. Once i get the results i will repost the series again.

-aneesh