2010-02-01 05:35:21

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC PATCH] New ACL format for better NFSv4 acl interoperability

Hi,
**** RFC patch. Not for inclusion ****

The following set of patches implements a new acl format
for linux. Rich-acl format is proposed so that we can have
better acl interoperability with CIFS and NFSv4 acl.

Posix acl should still be considered as the default acl on
any file system because of its simplicity. File systems
should provide a migration mechanism to rich-acl format if
they ever want to export these file systems via CIFS or NFSv4.

Some of the patches in the series are earlier published as
NFSv4 acl patches at http://www.suse.de/~agruen/nfs4acl/
and http://oss.sgi.com/projects/nfs/nfs4acl/. Linux kernel
already supports "system.nfs4_acl" via NFSv4 client. NFSv4
client provides byte sequence representation of the acl value
to the userspace. The userspace then use this array and build
the acl structure. Linux native NFSv4acl work done by
Andreas Gruenbacher on the other hand provided an acl struct
to the userspace. Since both the implementation used NFSv4
acl format it was decided to rename non upstream implementation as
rich-acl even though the acl model is NFSv4.

Rich-acl also adds some exception to the NFSv4 specified
access rules. The access rules are modified in a way that
make sense in posix environment. For example with patches applied

a) we always allow read attributes
b) we always allow read acl.
c) execute doesn't imply read.

That implies even if the file system object have acl values that
deny read attribute access, local file system still allows read
attributes access to make sure we don't break posix semantics. This
gives an opportunity for nfsd to use the attribute value and
deny read attribute access as per NFSv4 RFC

Patches are done in way that changes done by me are kept
as separate patches. This make sure I don't end up breaking
the access check algorithm done by Andreas Gruenbacher. This also
helps in collecting review feedback on some of the changes done
by me in the access check algorithm. Before merging this upstream
most of these patches have to folded back into relevant patches.

Userspace changes needed for acl tools can be found at
http://git.kernel.org/?p=fs/acl/kvaneesh/acl.git;a=summary
I have updated getfacl to print rich-acl format if rich-acl is
enabled. Only Ext4 is updated to support the new acl format. To
enable rich-acl format one should use tune2fs to enabled the
richacl file system feature. The relevant patches can be found
at http://www.kernel.org/pub/linux/kernel/people/kvaneesh/richaclv2/e2fsprogs/

NFSD is also updated to save and read richacl format if the local
file system supports the new acl format.

git repo for the kernel change is at
http://git.kernel.org/?p=linux/kernel/git/kvaneesh/linux-richacl.git;a=summary

git://git.kernel.org/pub/scm/linux/kernel/git/kvaneesh/linux-richacl.git for-upstream

-aneesh



2010-02-01 05:34:54

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 12/23] richacl: Use directory specific mask values for operation on directories.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/namei.c | 4 ++--
fs/richacl_base.c | 24 ++++++++++++++++--------
include/linux/richacl.h | 2 +-
3 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 48ee6a3..a7c87c8 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2514,11 +2514,11 @@ end_rename:

int ext4_permission(struct inode *inode, int mask)
{
-
#ifdef CONFIG_EXT4_FS_RICHACL
if (richacl_enabled(inode->i_sb))
return ext4_richacl_permission(inode,
- richacl_want_to_mask(mask));
+ richacl_want_to_mask(mask,
+ S_ISDIR(inode->i_mode)));
else
#endif
return generic_permission(inode, mask, ext4_check_acl);
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 0d8953c..b5c28cf 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -188,16 +188,24 @@ EXPORT_SYMBOL_GPL(richacl_chmod);
* When checking for append, @want is (MAY_WRITE | MAY_APPEND).
*/
unsigned int
-richacl_want_to_mask(int want)
+richacl_want_to_mask(int want, int is_dir)
{
unsigned int mask = 0;
-
- if (want & MAY_READ)
- mask |= ACE4_READ_DATA;
- if (want & MAY_APPEND)
- mask |= ACE4_APPEND_DATA;
- else if (want & MAY_WRITE)
- mask |= ACE4_WRITE_DATA;
+ if (is_dir) {
+ if (want & MAY_READ)
+ mask |= ACE4_LIST_DIRECTORY;
+ if (want & MAY_APPEND)
+ mask |= ACE4_ADD_FILE | ACE4_ADD_SUBDIRECTORY;
+ else if (want & MAY_WRITE)
+ mask |= ACE4_ADD_FILE | ACE4_ADD_SUBDIRECTORY;
+ } else {
+ if (want & MAY_READ)
+ mask |= ACE4_READ_DATA;
+ if (want & MAY_APPEND)
+ mask |= ACE4_APPEND_DATA;
+ else if (want & MAY_WRITE)
+ mask |= ACE4_WRITE_DATA;
+ }
if (want & MAY_EXEC)
mask |= ACE4_EXECUTE;

diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index b0df740..de71ca5 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -222,7 +222,7 @@ richace_is_deny(const struct richace *ace)
extern struct richacl *richacl_alloc(int count);
extern struct richacl *richacl_clone(const struct richacl *acl);

-extern unsigned int richacl_want_to_mask(int want);
+extern unsigned int richacl_want_to_mask(int want, int is_dir);
extern int richacl_permission(struct inode *,
const struct richacl *, unsigned int);
extern int richacl_generic_permission(struct inode *, unsigned int);
--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:48

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 06/23] vfs: Implement those parts of Automatic Inheritance (AI) which are safe under POSIX

From: Andreas Gruenbacher <[email protected]>

If AI is disabled for a directory (ACL4_AUTO_INHERIT
not set), nothing changes. If AI is enabled for a directory, the
create-time inheritance algorithm changes as follows:

* All inherited ACEs will have the ACE4_INHERITED_ACE flag set.

* The create mode is applied to the ACL (by setting the file masks),
which means that the ACL must no longer be subject to AI permission
propagation, and so the ACL4_PROTECTED is set.

By itelf, this is relatively useless because it will not allow
permissions to propagate, but AI aware applications can clear the
ACL4_PROTECTED flag when they know what they are doing, and this will
enable AI permission propagation.

It would be nice if AI aware applications could indicate this fact to
the kernel so that the kernel can avoid setting the ACL4_PROTECTED flag
in the first place, but there is no such user-space interface at this
point.

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

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index de99340..e75f713 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -150,7 +150,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_other_mask == other_mask &&
+ (!richacl_is_auto_inherit(acl) || richacl_is_protected(acl)))
return acl;

clone = richacl_clone(acl);
@@ -161,6 +162,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;

if (richacl_write_through(&clone)) {
richacl_put(clone);
@@ -561,7 +564,12 @@ richacl_inherit(const struct richacl *dir_acl, mode_t mode)
return ERR_PTR(-ENOMEM);
}

- acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH);
+ acl->a_flags = (dir_acl->a_flags & ~ACL4_PROTECTED);
+ if (richacl_is_auto_inherit(acl)) {
+ richacl_for_each_entry(ace, acl)
+ ace->e_flags |= ACE4_INHERITED_ACE;
+ acl->a_flags |= ACL4_PROTECTED;
+ }

return acl;
}
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index c56f152..f9089dc 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -33,10 +33,16 @@ struct richacl {
_ace--)

/* a_flags values */
+#define ACL4_AUTO_INHERIT 0x01
+#define ACL4_PROTECTED 0x02
+#define ACL4_DEFAULTED 0x04
#define ACL4_WRITE_THROUGH 0x40

-#define ACL4_VALID_FLAGS \
- ACL4_WRITE_THROUGH
+#define ACL4_VALID_FLAGS ( \
+ ACL4_AUTO_INHERIT | \
+ ACL4_PROTECTED | \
+ ACL4_DEFAULTED | \
+ ACL4_WRITE_THROUGH)

/* e_type values */
#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000
@@ -52,6 +58,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

@@ -60,7 +67,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
@@ -137,6 +145,18 @@ extern const char richace_group_who[];
extern const char richace_everyone_who[];

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;
+}
+
+static inline int
richace_is_owner(const struct richace *ace)
{
return (ace->e_flags & ACE4_SPECIAL_WHO) &&
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:22

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 02/23] vfs: Check for create permission during rename

If the new dentry is already present we were just checking
for the delete permission. We also need to check after
deletion whether we are allowed to create new name. This
is needed in case of a acl model that differentiate between
delete and create permission like NFSv4acl

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

diff --git a/fs/namei.c b/fs/namei.c
index 3e842ac..2a1a1d6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1380,12 +1380,11 @@ 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, int isdir)
+static inline int _do_may_create(struct inode *dir,
+ struct dentry *child, int isdir)
{
int error;

- if (child->d_inode)
- return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
if (dir->i_op->may_create) {
@@ -1403,6 +1402,13 @@ static inline int may_create(struct inode *dir, struct dentry *child, int isdir)
return error;
}

+static inline int may_create(struct inode *dir, struct dentry *child, int isdir)
+{
+ if (child->d_inode)
+ return -EEXIST;
+ return _do_may_create(dir, child, isdir);
+}
+
/*
* O_DIRECTORY translates into forcing a directory lookup.
*/
@@ -2673,8 +2679,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,

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

--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:50

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 08/23] vfs: Add a flag to denote posix mapped richacl

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/richacl_posix.c | 7 +++++++
include/linux/richacl.h | 10 ++++++----
2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
index 07db970..3cf2124 100644
--- a/fs/richacl_posix.c
+++ b/fs/richacl_posix.c
@@ -183,6 +183,13 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
acl->a_other_mask = richacl_mode_to_mask(mode);

+ /*
+ * Mark that the acl as mapped from posix
+ * This gives user space the chance to verify
+ * whether the mapping was correct
+ */
+ acl->a_flags |= ACL4_POSIX_MAPPED;
+
return;
}

diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index b08fdf1..41d93d8 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -36,12 +36,14 @@ struct richacl {
#define ACL4_AUTO_INHERIT 0x01
#define ACL4_PROTECTED 0x02
#define ACL4_DEFAULTED 0x04
+#define ACL4_POSIX_MAPPED 0x10
#define ACL4_WRITE_THROUGH 0x40

-#define ACL4_VALID_FLAGS ( \
- ACL4_AUTO_INHERIT | \
- ACL4_PROTECTED | \
- ACL4_DEFAULTED | \
+#define ACL4_VALID_FLAGS ( \
+ ACL4_AUTO_INHERIT | \
+ ACL4_PROTECTED | \
+ ACL4_DEFAULTED | \
+ ACL4_POSIX_MAPPED | \
ACL4_WRITE_THROUGH)

/* e_type values */
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:02

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 20/23] nfsd: Apply NFSv4acl to posix acl mapping only if MS_POSIXACL is set

We should be doing NFSv4 acl to POSIX acl mapping only if
MS_POSIXACL flag is set in the superblock.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/nfsd/vfs.c | 87 ++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 58 insertions(+), 29 deletions(-)

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index c194793..3046ecd 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -476,31 +476,18 @@ out:
return error;
}

-__be32
-nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
- struct nfs4_acl *acl)
+static __be32
+nfsd4_set_posix_acl(struct dentry *dentry,
+ struct nfs4_acl *acl, unsigned int flags)
{
- __be32 error;
int host_error;
- struct dentry *dentry;
- struct inode *inode;
+ struct inode *inode = dentry->d_inode;
struct posix_acl *pacl = NULL, *dpacl = NULL;
- unsigned int flags = 0;
-
- /* Get inode */
- error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
- if (error)
- return error;
-
- dentry = fhp->fh_dentry;
- inode = dentry->d_inode;
- if (S_ISDIR(inode->i_mode))
- flags = NFS4_ACL_DIR;

host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
- if (host_error == -EINVAL) {
+ if (host_error == -EINVAL)
return nfserr_attrnotsupp;
- } else if (host_error < 0)
+ else if (host_error < 0)
goto out_nfserr;

host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
@@ -520,6 +507,33 @@ out_nfserr:
return nfserrno(host_error);
}

+__be32
+nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct nfs4_acl *acl)
+{
+ __be32 error;
+ struct dentry *dentry;
+ struct inode *inode;
+ unsigned int flags = 0;
+
+ /* Get inode */
+ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
+ if (error)
+ return error;
+
+ dentry = fhp->fh_dentry;
+ inode = dentry->d_inode;
+ if (S_ISDIR(inode->i_mode))
+ flags = NFS4_ACL_DIR;
+
+ if (IS_POSIXACL(inode))
+ error = nfsd4_set_posix_acl(dentry, acl, flags);
+ else
+ error = nfserr_attrnotsupp;
+
+ return error;
+}
+
static struct posix_acl *
_get_posix_acl(struct dentry *dentry, char *key)
{
@@ -538,19 +552,19 @@ _get_posix_acl(struct dentry *dentry, char *key)
return pacl;
}

-int
-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
+static struct nfs4_acl *
+nfsd4_get_posix_acl(struct dentry *dentry)
{
+ struct nfs4_acl *acl;
+ unsigned int flags = 0;
struct inode *inode = dentry->d_inode;
- int error = 0;
struct posix_acl *pacl = NULL, *dpacl = NULL;
- unsigned int flags = 0;

pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS);
if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
if (IS_ERR(pacl)) {
- error = PTR_ERR(pacl);
+ acl = ERR_PTR(PTR_ERR(pacl));
pacl = NULL;
goto out;
}
@@ -560,21 +574,36 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac
if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
dpacl = NULL;
else if (IS_ERR(dpacl)) {
- error = PTR_ERR(dpacl);
+ acl = ERR_PTR(PTR_ERR(dpacl));
dpacl = NULL;
goto out;
}
flags = NFS4_ACL_DIR;
}

- *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
+ acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
+ out:
+ posix_acl_release(pacl);
+ posix_acl_release(dpacl);
+ return acl;
+}
+
+int
+nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
+{
+ int error = 0;
+ struct inode *inode = dentry->d_inode;
+
+ if (IS_POSIXACL(inode))
+ *acl = nfsd4_get_posix_acl(dentry);
+ else {
+ *acl = NULL;
+ error = -EOPNOTSUPP;
+ }
if (IS_ERR(*acl)) {
error = PTR_ERR(*acl);
*acl = NULL;
}
- out:
- posix_acl_release(pacl);
- posix_acl_release(dpacl);
return error;
}

--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:05

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 23/23] nfsd: Add support for saving richacl

With this patch nfsd will check whether the exported filesystem
use richacl format. If yes nfsd will map NFSv4 acl to richacl
and save the acl in richacl format ondisk. NFSv4 acl can be
better mapped to richacl format without loosing information.

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

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ce727a5..a1e5895 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -508,6 +508,50 @@ out_nfserr:
return nfserrno(host_error);
}

+static int
+__set_richacl(struct dentry *dentry, struct richacl *racl)
+{
+ size_t buflen;
+ char *buf = NULL;
+ int error = 0;
+
+ buflen = richacl_xattr_size(racl);
+ buf = kmalloc(buflen, GFP_KERNEL);
+ error = -ENOMEM;
+ if (buf == NULL)
+ goto out;
+
+ richacl_to_xattr(racl, buf);
+ error = vfs_setxattr(dentry, RICHACL_XATTR, buf, buflen, 0);
+out:
+ kfree(buf);
+ return error;
+}
+
+static __be32
+nfsd4_set_richacl(struct dentry *dentry, struct nfs4_acl *acl)
+{
+ int host_error;
+ struct richacl *racl;
+
+ host_error = nfs4_acl_nfsv4_to_richacl(acl, &racl);
+ if (host_error == -EINVAL)
+ return nfserr_attrnotsupp;
+ else if (host_error < 0)
+ goto out_nfserr;
+
+ host_error = __set_richacl(dentry, racl);
+ if (host_error < 0)
+ goto out_release;
+out_release:
+ richacl_put(racl);
+out_nfserr:
+ if (host_error == -EOPNOTSUPP)
+ return nfserr_attrnotsupp;
+ else
+ return nfserrno(host_error);
+}
+
__be32
nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfs4_acl *acl)
@@ -529,6 +573,8 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,

if (IS_POSIXACL(inode))
error = nfsd4_set_posix_acl(dentry, acl, flags);
+ else if (IS_RICHACL(inode))
+ error = nfsd4_set_richacl(dentry, acl);
else
error = nfserr_attrnotsupp;

--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:24

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 19/23] fs: Use the correct MS_*ACL flags in file system code

We should use MS_ACL flag when we want to check whether
we want to enforce some generic acl rules like not
using umask values.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/super.c | 26 ++++++++++++++++++++------
fs/nfs/nfs4proc.c | 2 +-
fs/nfsd/nfs4proc.c | 2 +-
3 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index fb051a1..bd75b27 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2500,9 +2500,16 @@ 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) |
- ((sbi->s_mount_opt & EXT4_MOUNT_ACL) ? MS_POSIXACL : 0);
-
+ if (test_opt(sb, ACL)) {
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_RICHACL)) {
+ sb->s_flags &= ~MS_POSIXACL;
+ sb->s_flags |= MS_RICHACL;
+ } else {
+ sb->s_flags &= ~MS_RICHACL;
+ sb->s_flags |= MS_POSIXACL;
+ }
+ }
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) ||
@@ -3537,9 +3544,16 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
ext4_abort(sb, __func__, "Abort forced by user");

- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- ((sbi->s_mount_opt & EXT4_MOUNT_ACL) ? MS_POSIXACL : 0);
-
+ if (test_opt(sb, ACL)) {
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_RICHACL)) {
+ sb->s_flags &= ~MS_POSIXACL;
+ sb->s_flags |= MS_RICHACL;
+ } else {
+ sb->s_flags &= ~MS_RICHACL;
+ sb->s_flags |= MS_POSIXACL;
+ }
+ }
es = sbi->s_es;

if (sbi->s_journal) {
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 198d51d..fa957f1 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2005,7 +2005,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
attr.ia_valid = ATTR_MODE;
- if (!IS_POSIXACL(dir))
+ if (!IS_ACL(dir))
attr.ia_mode &= ~current_umask();
} else {
attr.ia_valid = 0;
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 37514c4..9ac23d6 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -72,7 +72,7 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* in current environment or not.
*/
if (bmval[0] & FATTR4_WORD0_ACL) {
- if (!IS_POSIXACL(dentry->d_inode))
+ if (!IS_ACL(dentry->d_inode))
return nfserr_attrnotsupp;
}

--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:55

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 13/23] richacl: Follow nfs4 acl delete definition

We SHOULD allow unlink if either ACE4_DELETE is permitted on the target,
or ACE4_DELETE_CHILD is permitted on the parent. (Note that this is true
even if the parent or target explicitly denies one of these permissions.)

If the ACLs in question neither explicitly ALLOW nor DENY either of the above,
and if MODE4_SVTX is not set on the parent, then the we SHOULD allow the
removal if and only if ACE4_ADD_FILE is permitted. In the case where MODE4_SVTX
is set, the we may also require the remover to own either the parent or the
target, or may require the target to be writable.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/richacl.c | 41 ++++++++++++++++++++++++++++++++++++-
fs/richacl_base.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 2 +
3 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
index a8702c7..73c14dd 100644
--- a/fs/ext4/richacl.c
+++ b/fs/ext4/richacl.c
@@ -190,6 +190,28 @@ ext4_richacl_permission(struct inode *inode, unsigned int mask)
return retval;
}

+int ext4_richacl_mask_present(struct inode *inode, unsigned int mask)
+{
+ struct richacl *acl;
+ int retval;
+
+ if (!richacl_enabled(inode->i_sb))
+ BUG();
+
+ acl = ext4_get_richacl(inode);
+ if (!acl)
+ return 0;
+ else if (IS_ERR(acl))
+ return 0;
+ else {
+ retval = richacl_mask_present(inode, acl, mask);
+ richacl_put(acl);
+ }
+
+ return retval;
+
+
+}
int ext4_may_create(struct inode *dir, int isdir)
{
int error;
@@ -223,10 +245,25 @@ int ext4_may_delete(struct inode *dir, struct inode *inode)
if (richacl_enabled(inode->i_sb)) {
error = ext4_richacl_permission(dir,
ACE4_DELETE_CHILD|ACE4_EXECUTE);
- if (!error && check_sticky(dir, inode))
- error = -EPERM;
if (error && !ext4_richacl_permission(inode, ACE4_DELETE))
error = 0;
+
+ if (!error)
+ return error;
+
+ /*
+ * if we neither explicity allow nor deny both the above
+ * then are depend on stick and add file flag
+ */
+ if (!ext4_richacl_mask_present(dir, ACE4_DELETE_CHILD) &&
+ !ext4_richacl_mask_present(inode, ACE4_DELETE)) {
+
+ error = ext4_richacl_permission(dir,
+ ACE4_ADD_FILE | ACE4_EXECUTE);
+ if (!error && check_sticky(dir, inode))
+ error = -EPERM;
+ }
+
} else {
error = ext4_permission(dir, MAY_WRITE | MAY_EXEC);
if (!error && check_sticky(dir, inode))
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index b5c28cf..4a340d7 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -350,6 +350,56 @@ is_everyone:
EXPORT_SYMBOL_GPL(richacl_permission);

/**
+ * richacl_mask_present - check whether the specified masks are present in the acl
+ * @inode: inode to check
+ * @acl: rich acl of the inode
+ * @mask: requested access (ACE4_* bitmask)
+ *
+ * Check wether the specified mask are explicity specified in the allow or
+ * deny aces. If not return 0. If yes return 1;
+ */
+int richacl_mask_present(struct inode *inode, const struct richacl *acl,
+ unsigned int mask)
+{
+ const struct richace *ace;
+
+ 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;
+ } else if (richace_is_group(ace)) {
+ if (!in_group_p(inode->i_gid))
+ 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;
+ }
+ }
+ if (mask & ace_mask)
+ /* ace contain some of the mask */
+ mask &= ~ace_mask;
+
+ if (!mask)
+ break;
+ }
+
+ if (mask)
+ /* some of the mask specified are not present */
+ return 0;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(richacl_mask_present);
+
+/**
* richacl_generic_permission - permission check algorithm without explicit acl
* @inode: inode to check permissions for
* @mask: requested access (ACE4_* bitmask)
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index de71ca5..705e061 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -225,6 +225,8 @@ extern struct richacl *richacl_clone(const struct richacl *acl);
extern unsigned int richacl_want_to_mask(int want, int is_dir);
extern int richacl_permission(struct inode *,
const struct richacl *, unsigned int);
+extern int richacl_mask_present(struct inode *,
+ const struct richacl *, unsigned int);
extern int richacl_generic_permission(struct inode *, unsigned int);
extern int richace_is_same_who(const struct richace *, const struct richace *);
extern int richace_set_who(struct richace *ace, const char *who);
--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:46

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 04/23] richacl: Add write retention and retention hold access mask

These new flags are added in nfsv4.1. They don't have any
meaning in posix. So we don't need to do access checks with
these mask. But nfsd should be able to save these masks

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
include/linux/richacl.h | 47 +++++++++++++++++++++++++++--------------------
1 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index a2b4bd0..c56f152 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -75,32 +75,39 @@ struct richacl {
#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

-#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_DELETE | \
- ACE4_READ_ACL | \
- ACE4_WRITE_ACL | \
- ACE4_WRITE_OWNER | \
- ACE4_SYNCHRONIZE)
-
-#define ACE4_POSIX_ALWAYS_ALLOWED ( \
- ACE4_SYNCHRONIZE | \
- ACE4_READ_ATTRIBUTES | \
- ACE4_READ_ACL)
+#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)
+
+#define ACE4_POSIX_ALWAYS_ALLOWED ( \
+ ACE4_SYNCHRONIZE | \
+ ACE4_READ_ATTRIBUTES | \
+ ACE4_READ_ACL)
/*
* Duplicate an RICHACL handle.
*/
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:24

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 17/23] vfs: Add new MS_ACL and MS_RICHACL flag

ADD MS_ACL and MS_RICHACL flag which can be used to
indicate whether richacl is enabled or not. This will
help nfsd to use the right acl mapping when storing
NFSv4 ACL to disk

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

diff --git a/fs/namei.c b/fs/namei.c
index 2a1a1d6..ecc7476 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -471,6 +471,9 @@ static int exec_permission(struct inode *inode)
goto ok;
return ret;
}
+ /*
+ * Do the basic POSIX ACL permission checks.
+ */
ret = acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl);
if (!ret)
goto ok;
@@ -1573,7 +1576,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)
@@ -2065,7 +2068,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)
@@ -2143,7 +2146,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 2191464..2ff6114 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -197,7 +197,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_ACL (1<<16) /* VFS does not apply the umask */
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
#define MS_PRIVATE (1<<18) /* change to private */
#define MS_SLAVE (1<<19) /* change to slave */
@@ -206,6 +206,8 @@ 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_POSIXACL (MS_ACL | 1<<25)/* Default ACL is POSIX ACL */
+#define MS_RICHACL (MS_ACL | 1<<26)/* VFS uses richacl to check access */
#define MS_ACTIVE (1<<30)
#define MS_NOUSER (1<<31)

@@ -247,6 +249,7 @@ struct inodes_stat_t {
* flags, so these have to be checked separately. -- [email protected]
*/
#define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
+#define __IS_EXFLG(inode,flg) (((inode)->i_sb->s_flags & (flg)) == (flg))

#define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
#define IS_SYNC(inode) (__IS_FLG(inode, MS_SYNCHRONOUS) || \
@@ -260,7 +263,9 @@ struct inodes_stat_t {
#define IS_NOQUOTA(inode) ((inode)->i_flags & S_NOQUOTA)
#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_ACL(inode) __IS_FLG(inode, MS_ACL)
+#define IS_POSIXACL(inode) __IS_EXFLG(inode, MS_POSIXACL)
+#define IS_RICHACL(inode) __IS_EXFLG(inode, MS_RICHACL)

#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:23

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 09/23] ext4: Add posix acl to rich acl mapping

If we have richacl format enabled on ext4 we don't return the posix acl
value stored in the inode. This ensures that we only follow one acl model
when enabled. In our case if we have richacl format enabled we always
enforce richacl mode. But we also need to obey the posix acl restrictions
placed on the inode. For this we map the posix acls to richacl format
and use richacl to validate access permissions. We can also use this
to migrate posix acl to rich acl by doing a --get followed by a --set
using richacl tools. We have ACL4_POSIX_MAPPED flag set to indicate that
the richacl values returned is derived out of posix acl. This gives the
user a chance to validate the mapping before migrating to richacl format.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/acl.c | 14 ++++++---
fs/ext4/acl.h | 1 +
fs/ext4/richacl.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++------
3 files changed, 75 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 5f09df4..e17e1a9 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -152,8 +152,10 @@ fail:
* Inode operation get_posix_acl().
*
* inode->i_mutex: don't care
+ * We don't check whether posix acl is enabled or not.
+ * Caller should make sure of that.
*/
-static struct posix_acl *
+struct posix_acl *
ext4_get_acl(struct inode *inode, int type)
{
int name_index;
@@ -161,9 +163,6 @@ ext4_get_acl(struct inode *inode, int type)
struct posix_acl *acl;
int retval;

- if (!posix_acl_enabled(inode->i_sb))
- return NULL;
-
acl = get_cached_acl(inode, type);
if (acl != ACL_NOT_CACHED)
return acl;
@@ -261,7 +260,12 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
int
ext4_check_acl(struct inode *inode, int mask)
{
- struct posix_acl *acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
+ struct posix_acl *acl;
+
+ if (!posix_acl_enabled(inode->i_sb))
+ return -EAGAIN;
+
+ acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);

if (IS_ERR(acl))
return PTR_ERR(acl);
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index 9d843d5..3e47cf3 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -57,6 +57,7 @@ static inline int ext4_acl_count(size_t size)
extern int ext4_check_acl(struct inode *, int);
extern int ext4_acl_chmod(struct inode *);
extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
+extern struct posix_acl *ext4_get_acl(struct inode *inode, int type);

#else /* CONFIG_EXT4_FS_POSIX_ACL */
#include <linux/sched.h>
diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
index 1c78086..a8702c7 100644
--- a/fs/ext4/richacl.c
+++ b/fs/ext4/richacl.c
@@ -20,6 +20,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "richacl.h"
+#include "acl.h"

static inline struct richacl *
ext4_iget_richacl(struct inode *inode)
@@ -47,13 +48,60 @@ ext4_iset_richacl(struct inode *inode, struct richacl *acl)
spin_unlock(&inode->i_lock);
}

+static int ext4_map_pacl_to_richacl(struct inode *inode,
+ struct richacl **richacl)
+{
+ int retval = 0;
+ struct posix_acl *pacl = NULL, *dpacl = NULL;
+
+ *richacl = NULL;
+ pacl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
+ if (IS_ERR(pacl))
+ return PTR_ERR(pacl);
+
+
+ if (S_ISDIR(inode->i_mode)) {
+ dpacl = ext4_get_acl(inode, ACL_TYPE_DEFAULT);
+ if (IS_ERR(dpacl)) {
+ /* we need to fail for all errors
+ * we will continue only with NULL dpacl
+ * which is ENODATA on dpacl
+ */
+ posix_acl_release(pacl);
+ return PTR_ERR(dpacl);
+ }
+ }
+
+ if (pacl == NULL && dpacl != NULL) {
+ /*
+ * We have a default acl list. So derive the access acl
+ * list from the mode so that we get a richacl that
+ * include mode bits
+ */
+ pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+ }
+
+ if (pacl == NULL && dpacl == NULL)
+ return -ENODATA;
+
+ *richacl = map_posix_to_richacl(inode, pacl, dpacl);
+
+ if (IS_ERR(*richacl)) {
+ retval = PTR_ERR(*richacl);
+ *richacl = NULL;
+ }
+ posix_acl_release(pacl);
+ posix_acl_release(dpacl);
+ return retval;
+}
+
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;
+ int retval = 0;

if (!richacl_enabled(inode->i_sb))
return NULL;
@@ -61,22 +109,31 @@ ext4_get_richacl(struct inode *inode)
acl = ext4_iget_richacl(inode);
if (acl != EXT4_RICHACL_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);
+ }
+ kfree(value);
+ } else if (retval == -ENODATA) {
+ /*
+ * Check whether we have posix acl stored.
+ * If so convert them to richacl
+ */
+ retval = ext4_map_pacl_to_richacl(inode, &acl);
}
- if (retval > 0) {
- acl = richacl_from_xattr(value, retval);
- if (acl == ERR_PTR(-EINVAL))
- acl = ERR_PTR(-EIO);
- } else if (retval == -ENODATA || retval == -ENOSYS)
+
+ if (retval == -ENODATA || retval == -ENOSYS)
acl = NULL;
- else
+ else if (retval < 0)
acl = ERR_PTR(retval);
- kfree(value);

if (!IS_ERR(acl))
ext4_iset_richacl(inode, acl);
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:04

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 22/23] nfsd: Add support for reading rich acl from file system

With this patch nfsd will check whether exported file system
use richacl format. If yes it will read richacl format and
map it to NFSv4acl. Richacl can be better mapped to NFSv4
acl format.

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

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 3046ecd..ce727a5 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -23,6 +23,7 @@
#include <linux/quotaops.h>
#include <linux/fsnotify.h>
#include <linux/posix_acl_xattr.h>
+#include <linux/richacl_xattr.h>
#include <linux/xattr.h>
#include <linux/jhash.h>
#include <linux/ima.h>
@@ -588,6 +589,40 @@ nfsd4_get_posix_acl(struct dentry *dentry)
return acl;
}

+static struct richacl *
+__get_richacl(struct dentry *dentry)
+{
+ int buflen;
+ void *buf = NULL;
+ struct richacl *racl;
+
+ buflen = nfsd_getxattr(dentry, RICHACL_XATTR, &buf);
+ if (!buflen)
+ buflen = -ENODATA;
+ if (buflen <= 0)
+ return ERR_PTR(buflen);
+
+ racl = richacl_from_xattr(buf, buflen);
+ kfree(buf);
+ return racl;
+}
+
+static struct nfs4_acl *
+nfsd4_get_richacl(struct dentry *dentry)
+{
+ struct nfs4_acl *acl;
+ struct richacl *racl;
+ racl = __get_richacl(dentry);
+ if (IS_ERR(racl) && PTR_ERR(racl) == -ENODATA)
+ racl = richacl_from_mode(dentry->d_inode->i_mode);
+ if (IS_ERR(racl))
+ return ERR_PTR(PTR_ERR(racl));
+ acl = nfs4_acl_richacl_to_nfsv4(racl);
+
+ richacl_put(racl);
+ return acl;
+}
+
int
nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
{
@@ -596,6 +631,8 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac

if (IS_POSIXACL(inode))
*acl = nfsd4_get_posix_acl(dentry);
+ else if (IS_RICHACL(inode))
+ *acl = nfsd4_get_richacl(dentry);
else {
*acl = NULL;
error = -EOPNOTSUPP;
--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:47

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 05/23] ext4: Implement rich acl for ext4

This need to be enabled by tune2fs or during mkfs.ext4

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/Kconfig | 7 +
fs/ext4/Makefile | 1 +
fs/ext4/acl.c | 40 +++++--
fs/ext4/ext4.h | 13 ++-
fs/ext4/file.c | 5 +-
fs/ext4/ialloc.c | 7 +-
fs/ext4/inode.c | 77 ++++++++++++-
fs/ext4/namei.c | 21 +++-
fs/ext4/richacl.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/richacl.h | 47 ++++++++
fs/ext4/super.c | 58 +++++++---
fs/ext4/xattr.c | 6 +
fs/ext4/xattr.h | 2 +
13 files changed, 563 insertions(+), 38 deletions(-)
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..39fcb29 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -83,3 +83,10 @@ 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 native rich Access Control List (EXPERIMENTAL)"
+ depends on EXT4_FS_XATTR && EXPERIMENTAL
+ select FS_RICHACL
+ help
+ Allow to use rich acl model
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 8867b2a..4acbffe 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -11,3 +11,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.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_SECURITY) += xattr_security.o
+ext4-$(CONFIG_EXT4_FS_RICHACL) += richacl.o
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 8a2a29d..5f09df4 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -14,6 +14,28 @@
#include "xattr.h"
#include "acl.h"

+
+/*
+ * Check wether posix acl need to be enabled for the
+ * file system
+ */
+static inline int posix_acl_enabled(struct super_block *sb)
+{
+
+ if (!test_opt(sb, ACL))
+ return 0;
+ /*
+ * we disable posix acl if the fil system have
+ * richacl feature enabled. Having richacl feature
+ * enabled indicate that the user prever richacl model
+ */
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_RICHACL))
+ return 0;
+ else
+ return 1;
+}
+
/*
* Convert from filesystem to in-memory representation.
*/
@@ -139,7 +161,7 @@ ext4_get_acl(struct inode *inode, int type)
struct posix_acl *acl;
int retval;

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

acl = get_cached_acl(inode, type);
@@ -265,7 +287,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 (test_opt(dir->i_sb, ACL)) {
acl = ext4_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
@@ -273,7 +295,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 (test_opt(inode->i_sb, ACL) && acl) {
struct posix_acl *clone;
mode_t mode;

@@ -325,10 +347,10 @@ ext4_acl_chmod(struct inode *inode)
struct posix_acl *acl, *clone;
int error;

+ if (!posix_acl_enabled(inode->i_sb))
+ return 0;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
- if (!test_opt(inode->i_sb, POSIX_ACL))
- return 0;
acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl)
return PTR_ERR(acl);
@@ -369,7 +391,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 (!posix_acl_enabled(dentry->d_sb))
return 0;
if (list && size <= list_len)
memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
@@ -382,7 +404,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 (!posix_acl_enabled(dentry->d_sb))
return 0;
if (list && size <= list_len)
memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
@@ -398,7 +420,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 (!posix_acl_enabled(dentry->d_sb))
return -EOPNOTSUPP;

acl = ext4_get_acl(dentry->d_inode, type);
@@ -423,7 +445,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 (!posix_acl_enabled(dentry->d_sb))
return -EOPNOTSUPP;
if (!is_owner_or_cap(inode))
return -EPERM;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index af7b626..13df624 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -722,6 +722,10 @@ struct ext4_inode_info {
*/
tid_t i_sync_tid;
tid_t i_datasync_tid;
+#ifdef CONFIG_EXT4_FS_RICHACL
+ struct richacl *i_richacl;
+#endif
+
};

/*
@@ -756,7 +760,7 @@ 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_ACL 0x08000 /* Access Control Lists enabled*/
#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */
#define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */
#define EXT4_MOUNT_NOBH 0x40000 /* No bufferheads */
@@ -1129,14 +1133,16 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
+#define EXT4_FEATURE_INCOMPAT_RICHACL 0x0400

#define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
EXT4_FEATURE_INCOMPAT_RECOVER| \
EXT4_FEATURE_INCOMPAT_META_BG| \
EXT4_FEATURE_INCOMPAT_EXTENTS| \
- EXT4_FEATURE_INCOMPAT_64BIT| \
- EXT4_FEATURE_INCOMPAT_FLEX_BG)
+ EXT4_FEATURE_INCOMPAT_64BIT| \
+ EXT4_FEATURE_INCOMPAT_FLEX_BG| \
+ EXT4_FEATURE_INCOMPAT_RICHACL)
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
@@ -1717,6 +1723,7 @@ extern const struct file_operations ext4_file_operations;
extern const struct inode_operations ext4_dir_inode_operations;
extern const struct inode_operations ext4_special_inode_operations;
extern struct dentry *ext4_get_parent(struct dentry *child);
+extern int ext4_permission(struct inode *, int);

/* symlink.c */
extern const struct inode_operations ext4_symlink_inode_operations;
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 9630583..a300eb2 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -27,6 +27,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
@@ -158,8 +159,10 @@ const struct inode_operations ext4_file_inode_operations = {
.listxattr = ext4_listxattr,
.removexattr = generic_removexattr,
#endif
- .check_acl = ext4_check_acl,
.fallocate = ext4_fallocate,
.fiemap = ext4_fiemap,
+ .permission = ext4_permission,
+ .may_create = ext4_may_create,
+ .may_delete = ext4_may_delete,
};

diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index f3624ea..15e80b8 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,8 +1039,10 @@ got:
err = -EDQUOT;
goto fail_drop;
}
-
- err = ext4_init_acl(handle, inode, dir);
+ if (richacl_enabled(sb))
+ err = ext4_richacl_init(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 c818972..24506df 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -43,6 +43,7 @@
#include "xattr.h"
#include "acl.h"
#include "ext4_extents.h"
+#include "richacl.h"

#include <trace/events/ext4.h>

@@ -4792,6 +4793,9 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
}
inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);

+#ifdef CONFIG_EXT4_FS_RICHACL
+ ei->i_richacl = EXT4_RICHACL_NOT_CACHED;
+#endif
ei->i_state = 0;
ei->i_dir_start_lookup = 0;
ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
@@ -5193,6 +5197,68 @@ int ext4_write_inode(struct inode *inode, int wait)
return err;
}

+#ifdef CONFIG_EXT4_FS_RICHACL
+static int ext4_inode_change_ok(struct inode *inode, struct iattr *attr)
+{
+ unsigned int ia_valid = attr->ia_valid;
+
+ if (!richacl_enabled(inode->i_sb))
+ return inode_change_ok(inode, attr);
+
+ /* If force is set do it anyway. */
+ if (ia_valid & ATTR_FORCE)
+ return 0;
+
+ /* Make sure a caller can chown. */
+ if ((ia_valid & ATTR_UID) &&
+ (current_fsuid() != inode->i_uid ||
+ attr->ia_uid != inode->i_uid) &&
+ (current_fsuid() != attr->ia_uid ||
+ ext4_richacl_permission(inode, ACE4_WRITE_OWNER)) &&
+ !capable(CAP_CHOWN))
+ goto error;
+
+ /* Make sure caller can chgrp. */
+ if ((ia_valid & ATTR_GID)) {
+ int in_group = in_group_p(attr->ia_gid);
+ if ((current_fsuid() != inode->i_uid ||
+ (!in_group && attr->ia_gid != inode->i_gid)) &&
+ (!in_group ||
+ ext4_richacl_permission(inode, ACE4_WRITE_OWNER)) &&
+ !capable(CAP_CHOWN))
+ goto error;
+ }
+
+ /* Make sure a caller can chmod. */
+ if (ia_valid & ATTR_MODE) {
+ if (current_fsuid() != inode->i_uid &&
+ ext4_richacl_permission(inode, ACE4_WRITE_ACL) &&
+ !capable(CAP_FOWNER))
+ goto error;
+ /* Also check the setgid bit! */
+ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
+ inode->i_gid) && !capable(CAP_FSETID))
+ attr->ia_mode &= ~S_ISGID;
+ }
+
+ /* Check for setting the inode time. */
+ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {
+ if (current_fsuid() != inode->i_uid &&
+ ext4_richacl_permission(inode, ACE4_WRITE_ATTRIBUTES) &&
+ !capable(CAP_FOWNER))
+ goto error;
+ }
+ return 0;
+error:
+ return -EPERM;
+}
+#else
+static inline int ext4_inode_change_ok(struct inode *inode, struct iattr *attr)
+{
+ return inode_change_ok(inode, attr);
+}
+#endif
+
/*
* ext4_setattr()
*
@@ -5223,7 +5289,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
int error, rc = 0;
const unsigned int ia_valid = attr->ia_valid;

- error = inode_change_ok(inode, attr);
+ error = ext4_inode_change_ok(inode, attr);
if (error)
return error;

@@ -5307,9 +5373,12 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
if (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 (richacl_enabled(inode->i_sb))
+ 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/namei.c b/fs/ext4/namei.c
index 17a17e1..48ee6a3 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -39,6 +39,7 @@

#include "xattr.h"
#include "acl.h"
+#include "richacl.h"

/*
* define how far ahead to read directories while searching them.
@@ -2511,6 +2512,18 @@ end_rename:
return retval;
}

+int ext4_permission(struct inode *inode, int mask)
+{
+
+#ifdef CONFIG_EXT4_FS_RICHACL
+ if (richacl_enabled(inode->i_sb))
+ return ext4_richacl_permission(inode,
+ richacl_want_to_mask(mask));
+ else
+#endif
+ return generic_permission(inode, mask, ext4_check_acl);
+}
+
/*
* directories can handle most operations...
*/
@@ -2531,8 +2544,10 @@ const struct inode_operations ext4_dir_inode_operations = {
.listxattr = ext4_listxattr,
.removexattr = generic_removexattr,
#endif
- .check_acl = ext4_check_acl,
.fiemap = ext4_fiemap,
+ .permission = ext4_permission,
+ .may_create = ext4_may_create,
+ .may_delete = ext4_may_delete,
};

const struct inode_operations ext4_special_inode_operations = {
@@ -2543,5 +2558,7 @@ const struct inode_operations ext4_special_inode_operations = {
.listxattr = ext4_listxattr,
.removexattr = generic_removexattr,
#endif
- .check_acl = ext4_check_acl,
+ .permission = ext4_permission,
+ .may_create = ext4_may_create,
+ .may_delete = ext4_may_delete,
};
diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
new file mode 100644
index 0000000..1c78086
--- /dev/null
+++ b/fs/ext4/richacl.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2009 Aneesh Kumar K.V <[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/richacl_xattr.h>
+
+#include "ext4.h"
+#include "ext4_jbd2.h"
+#include "xattr.h"
+#include "richacl.h"
+
+static inline struct richacl *
+ext4_iget_richacl(struct inode *inode)
+{
+ struct richacl *acl = EXT4_RICHACL_NOT_CACHED;
+ struct ext4_inode_info *ei = EXT4_I(inode);
+
+ spin_lock(&inode->i_lock);
+ if (ei->i_richacl != EXT4_RICHACL_NOT_CACHED)
+ acl = richacl_get(ei->i_richacl);
+ spin_unlock(&inode->i_lock);
+
+ return acl;
+}
+
+static inline void
+ext4_iset_richacl(struct inode *inode, struct richacl *acl)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+
+ spin_lock(&inode->i_lock);
+ if (ei->i_richacl != EXT4_RICHACL_NOT_CACHED)
+ richacl_put(ei->i_richacl);
+ ei->i_richacl = richacl_get(acl);
+ spin_unlock(&inode->i_lock);
+}
+
+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 (!richacl_enabled(inode->i_sb))
+ return NULL;
+
+ acl = ext4_iget_richacl(inode);
+ if (acl != EXT4_RICHACL_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(acl))
+ ext4_iset_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) {
+ 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)
+ ext4_iset_richacl(inode, acl);
+
+ return retval;
+}
+
+int
+ext4_richacl_permission(struct inode *inode, unsigned int mask)
+{
+ struct richacl *acl;
+ int retval;
+
+ if (!richacl_enabled(inode->i_sb))
+ BUG();
+
+ acl = ext4_get_richacl(inode);
+ if (!acl)
+ retval = richacl_generic_permission(inode, mask);
+ else if (IS_ERR(acl))
+ retval = PTR_ERR(acl);
+ else {
+ retval = richacl_permission(inode, acl, mask);
+ richacl_put(acl);
+ }
+
+ return retval;
+}
+
+int ext4_may_create(struct inode *dir, int isdir)
+{
+ int error;
+
+ if (richacl_enabled(dir->i_sb)) {
+ unsigned int mask = (isdir ? ACE4_ADD_SUBDIRECTORY :
+ ACE4_ADD_FILE) | ACE4_EXECUTE;
+
+ error = ext4_richacl_permission(dir, mask);
+ } else
+ error = ext4_permission(dir, MAY_WRITE | MAY_EXEC);
+
+ return error;
+}
+
+static int check_sticky(struct inode *dir, struct inode *inode)
+{
+ if (!(dir->i_mode & S_ISVTX))
+ return 0;
+ if (inode->i_uid == current_fsuid())
+ return 0;
+ if (dir->i_uid == current_fsuid())
+ return 0;
+ return !capable(CAP_FOWNER);
+}
+
+int ext4_may_delete(struct inode *dir, struct inode *inode)
+{
+ int error;
+
+ if (richacl_enabled(inode->i_sb)) {
+ error = ext4_richacl_permission(dir,
+ ACE4_DELETE_CHILD|ACE4_EXECUTE);
+ if (!error && check_sticky(dir, inode))
+ error = -EPERM;
+ if (error && !ext4_richacl_permission(inode, ACE4_DELETE))
+ error = 0;
+ } else {
+ error = ext4_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (!error && check_sticky(dir, inode))
+ error = -EPERM;
+ }
+
+ return error;
+}
+
+int
+ext4_richacl_init(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ struct richacl *dir_acl = NULL, *acl;
+ int retval;
+
+ if (!test_opt(dir->i_sb, ACL))
+ return 0;
+ if (!S_ISLNK(inode->i_mode))
+ dir_acl = ext4_get_richacl(dir);
+ if (!dir_acl || IS_ERR(dir_acl)) {
+ inode->i_mode &= ~current_umask();
+ return PTR_ERR(dir_acl);
+ }
+ acl = richacl_inherit(dir_acl, inode->i_mode);
+ richacl_put(dir_acl);
+
+ retval = PTR_ERR(acl);
+ if (acl && !IS_ERR(acl)) {
+ retval = ext4_set_richacl(handle, inode, acl);
+ inode->i_mode = (inode->i_mode & ~S_IRWXUGO) |
+ richacl_masks_to_mode(acl);
+ richacl_put(acl);
+ }
+ return retval;
+}
+
+int
+ext4_richacl_chmod(struct inode *inode)
+{
+ struct richacl *acl;
+ int retval;
+
+ if (!richacl_enabled(inode->i_sb))
+ return 0;
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ acl = ext4_get_richacl(inode);
+ if (!acl || IS_ERR(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 (!richacl_enabled(dentry->d_sb))
+ 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 (!richacl_enabled(dentry->d_sb))
+ return -EOPNOTSUPP;
+ 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 (!richacl_enabled(dentry->d_sb))
+ return -EOPNOTSUPP;
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ if (current_fsuid() != inode->i_uid &&
+ ext4_richacl_permission(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;
+}
+
+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..3dc162c
--- /dev/null
+++ b/fs/ext4/richacl.h
@@ -0,0 +1,47 @@
+#ifndef __FS_EXT4_RICHACL_H
+#define __FS_EXT4_RICHACL_H
+
+#ifdef CONFIG_EXT4_FS_RICHACL
+
+#include <linux/richacl.h>
+
+/* Value for i_richacl if RICHACL has not been cached */
+#define EXT4_RICHACL_NOT_CACHED ((void *)-1)
+
+extern int ext4_richacl_permission(struct inode *, unsigned int);
+extern int ext4_may_create(struct inode *, int);
+extern int ext4_may_delete(struct inode *, struct inode *);
+extern int ext4_richacl_init(handle_t *, struct inode *, struct inode *);
+extern int ext4_richacl_chmod(struct inode *);
+
+#else /* CONFIG_FS_EXT4_RICHACL */
+
+#define ext4_may_create NULL
+#define ext4_may_delete NULL
+
+static inline int
+ext4_richacl_init(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 */
+
+static inline int richacl_enabled(struct super_block *sb)
+{
+ if (!test_opt(sb, ACL))
+ return 0;
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_RICHACL))
+ return 1;
+ else
+ return 0;
+}
+
+#endif /* __FS_EXT4_RICHACL_H */
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 735c20d..fb051a1 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -46,6 +46,7 @@
#include "xattr.h"
#include "acl.h"
#include "mballoc.h"
+#include "richacl.h"

#define CREATE_TRACE_POINTS
#include <trace/events/ext4.h>
@@ -687,7 +688,9 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
ei = kmem_cache_alloc(ext4_inode_cachep, GFP_NOFS);
if (!ei)
return NULL;
-
+#ifdef CONFIG_EXT4_FS_RICHACL
+ ei->i_richacl = EXT4_RICHACL_NOT_CACHED;
+#endif
ei->vfs_inode.i_version = 1;
ei->vfs_inode.i_data.writeback_index = 0;
memset(&ei->i_cached_extent, 0, sizeof(struct ext4_ext_cache));
@@ -761,6 +764,14 @@ static void destroy_inodecache(void)

static void ext4_clear_inode(struct inode *inode)
{
+
+#ifdef CONFIG_EXT4_FS_RICHACL
+ if (EXT4_I(inode)->i_richacl &&
+ EXT4_I(inode)->i_richacl != EXT4_RICHACL_NOT_CACHED) {
+ richacl_put(EXT4_I(inode)->i_richacl);
+ EXT4_I(inode)->i_richacl = EXT4_RICHACL_NOT_CACHED;
+ }
+#endif
ext4_discard_preallocations(inode);
if (EXT4_JOURNAL(inode))
jbd2_journal_release_jbd_inode(EXT4_SB(inode->i_sb)->s_journal,
@@ -861,10 +872,10 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
seq_puts(seq, ",nouser_xattr");
}
#endif
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
- if (test_opt(sb, POSIX_ACL) && !(def_mount_opts & EXT4_DEFM_ACL))
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+ if (test_opt(sb, ACL) && !(def_mount_opts & EXT4_DEFM_ACL))
seq_puts(seq, ",acl");
- if (!test_opt(sb, POSIX_ACL) && (def_mount_opts & EXT4_DEFM_ACL))
+ if (!test_opt(sb, ACL) && (def_mount_opts & EXT4_DEFM_ACL))
seq_puts(seq, ",noacl");
#endif
if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {
@@ -1204,6 +1215,26 @@ static ext4_fsblk_t get_sb_block(void **data)
return sb_block;
}

+static void enable_acl(struct ext4_sb_info *sbi)
+{
+#if !defined(CONFIG_EXT4_FS_POSIX_ACL) && !defined(CONFIG_EXT4_FS_RICHACL)
+ ext4_msg(sb, KERN_ERR, "acl options not supported");
+ return;
+#endif
+ set_opt(sbi->s_mount_opt, ACL);
+ return;
+}
+
+static void disable_acl(struct ext4_sb_info *sbi)
+{
+#if !defined(CONFIG_EXT4_FS_POSIX_ACL) && !defined(CONFIG_EXT4_FS_RICHACL)
+ ext4_msg(sb, KERN_ERR, "noacl options not supported");
+ return;
+#endif
+ clear_opt(sbi->s_mount_opt, ACL);
+ return;
+}
+
#define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))

static int parse_options(char *options, struct super_block *sb,
@@ -1297,19 +1328,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:
- set_opt(sbi->s_mount_opt, POSIX_ACL);
+ enable_acl(sbi);
break;
case Opt_noacl:
- clear_opt(sbi->s_mount_opt, POSIX_ACL);
+ disable_acl(sbi);
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
@@ -2440,9 +2464,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (def_mount_opts & EXT4_DEFM_XATTR_USER)
set_opt(sbi->s_mount_opt, XATTR_USER);
#endif
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
if (def_mount_opts & EXT4_DEFM_ACL)
- set_opt(sbi->s_mount_opt, POSIX_ACL);
+ set_opt(sbi->s_mount_opt, ACL);
#endif
if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)
sbi->s_mount_opt |= EXT4_MOUNT_JOURNAL_DATA;
@@ -2477,7 +2501,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
goto failed_mount;

sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- ((sbi->s_mount_opt & EXT4_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+ ((sbi->s_mount_opt & EXT4_MOUNT_ACL) ? MS_POSIXACL : 0);

if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&
(EXT4_HAS_COMPAT_FEATURE(sb, ~0U) ||
@@ -3514,7 +3538,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
ext4_abort(sb, __func__, "Abort forced by user");

sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- ((sbi->s_mount_opt & EXT4_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+ ((sbi->s_mount_opt & EXT4_MOUNT_ACL) ? MS_POSIXACL : 0);

es = sbi->s_es;

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index f3a2f7e..649aeea 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -107,6 +107,9 @@ static 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
};

struct xattr_handler *ext4_xattr_handlers[] = {
@@ -119,6 +122,9 @@ 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 8ede88b..daef032 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 struct xattr_handler ext4_xattr_trusted_handler;
extern struct xattr_handler ext4_xattr_acl_access_handler;
extern struct xattr_handler ext4_xattr_acl_default_handler;
extern struct xattr_handler ext4_xattr_security_handler;
+extern struct xattr_handler ext4_richacl_xattr_handler;

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

--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:24

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 18/23] richacl: Add helper function for creating richacl from mode values.

When we don't have richacl stored on disk for permission check
we need to generate richacl from mode values. This patch adds
a helper function for doing that.

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

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 4a340d7..5d61b30 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -640,3 +640,32 @@ richacl_inherit(const struct richacl *dir_acl, mode_t mode)
return acl;
}
EXPORT_SYMBOL_GPL(richacl_inherit);
+
+struct richacl *
+richacl_from_mode(mode_t mode)
+{
+ struct richacl *acl;
+ struct richace *ace;
+ int is_dir = S_ISDIR(mode);
+
+ acl = richacl_alloc(1);
+ if (!acl)
+ return NULL;
+ ace = acl->a_entries;
+
+ acl->a_owner_mask = richacl_mode_to_mask(mode >> 6, is_dir);
+ acl->a_group_mask = richacl_mode_to_mask(mode >> 3, is_dir);
+ acl->a_other_mask = richacl_mode_to_mask(mode, is_dir);
+
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = ACE4_VALID_MASK;
+ ace->u.e_who = richace_everyone_who;
+ if (richacl_apply_masks(&acl)) {
+ richacl_put(acl);
+ acl = NULL;
+ }
+ return acl;
+
+}
+EXPORT_SYMBOL_GPL(richacl_from_mode);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 705e061..ecb76bc 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -238,5 +238,6 @@ extern int richacl_apply_masks(struct richacl **acl);
extern int richacl_write_through(struct richacl **acl);
extern struct richacl *map_posix_to_richacl(struct inode *, struct posix_acl *,
struct posix_acl *);
+extern struct richacl *richacl_from_mode(mode_t);

#endif /* __RICHACL_H */
--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:45

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 03/23] vfs: rich ACL in-memory representation and manipulation

From: Andreas Gruenbacher <[email protected]>

* In-memory representation (struct richacl).
* Functionality a filesystem needs such as permission checking,
apply mode to acl, compute mode from acl, inheritance upon file
create.
* Compute a mask-less acl from struct richacl that grants the same
permissions. Protocols which don't understand the masks need
this.
* Convert to/from xattrs.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/Kconfig | 4 +
fs/Makefile | 4 +
fs/richacl_base.c | 568 +++++++++++++++++++++++++++++++
fs/richacl_compat.c | 757 +++++++++++++++++++++++++++++++++++++++++
fs/richacl_xattr.c | 146 ++++++++
include/linux/richacl.h | 208 +++++++++++
include/linux/richacl_xattr.h | 32 ++
7 files changed, 1719 insertions(+), 0 deletions(-)
create mode 100644 fs/richacl_base.c
create mode 100644 fs/richacl_compat.c
create mode 100644 fs/richacl_xattr.c
create mode 100644 include/linux/richacl.h
create mode 100644 include/linux/richacl_xattr.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 64d44ef..adbda7c 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -39,6 +39,10 @@ config FS_POSIX_ACL
bool
default n

+config FS_RICHACL
+ bool
+ default n
+
source "fs/xfs/Kconfig"
source "fs/gfs2/Kconfig"
source "fs/ocfs2/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index af6d047..0a046a1 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -51,6 +51,10 @@ 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 richacl_xattr.o \
+ richacl_compat.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..de99340
--- /dev/null
+++ b/fs/richacl_base.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2006 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");
+
+/*
+ * ACL entries that have ACE4_SPECIAL_WHO set in ace->e_flags use the
+ * pointer values of these constants in ace->u.e_who to avoid massive
+ * amounts of string comparisons.
+ */
+
+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 an acl
+ * @count: number of entries
+ */
+struct richacl *
+richacl_alloc(int count)
+{
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ struct richacl *acl = kmalloc(size, GFP_KERNEL);
+
+ if (acl) {
+ memset(acl, 0, size);
+ atomic_set(&acl->a_refcount, 1);
+ acl->a_count = count;
+ }
+ return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_alloc);
+
+/**
+ * richacl_clone - create a copy of an acl
+ */
+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;
+}
+
+/*
+ * The POSIX permissions are supersets of the below mask flags.
+ *
+ * The ACE4_READ_ATTRIBUTES and ACE4_READ_ACL flags are always granted
+ * in POSIX. The ACE4_SYNCHRONIZE flag has no meaning under POSIX. We
+ * make sure that we do not mask them if they are set, so that users who
+ * rely on these flags won't get confused.
+ */
+#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)
+
+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 file mode permission bits from file masks
+ *
+ * Compute the file mode permission bits from the file masks in the acl.
+ */
+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);
+
+static 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_chmod - update the file masks to reflect the new mode
+ * @mode: file mode permission bits to apply to the @acl
+ *
+ * Converts the mask flags corresponding to the owner, group, and other file
+ * permissions and computes the file masks. Returns @acl if it already has the
+ * appropriate file masks, or updates the flags in a copy of @acl. Takes over
+ * @acl.
+ */
+struct richacl *
+richacl_chmod(struct richacl *acl, mode_t mode)
+{
+ unsigned int owner_mask, group_mask, other_mask;
+ struct richacl *clone;
+
+ owner_mask = richacl_mode_to_mask(mode >> 6);
+ group_mask = richacl_mode_to_mask(mode >> 3);
+ other_mask = richacl_mode_to_mask(mode);
+
+ if (acl->a_owner_mask == owner_mask &&
+ acl->a_group_mask == group_mask &&
+ acl->a_other_mask == other_mask)
+ return acl;
+
+ clone = richacl_clone(acl);
+ richacl_put(acl);
+ if (!clone)
+ return ERR_PTR(-ENOMEM);
+
+ clone->a_owner_mask = owner_mask;
+ clone->a_group_mask = group_mask;
+ clone->a_other_mask = other_mask;
+
+ if (richacl_write_through(&clone)) {
+ richacl_put(clone);
+ clone = ERR_PTR(-ENOMEM);
+ }
+ return clone;
+}
+EXPORT_SYMBOL_GPL(richacl_chmod);
+
+/**
+ * richacl_want_to_mask - convert permission want argument to a mask
+ * @want: @want argument of the permission inode operation
+ *
+ * When checking for append, @want is (MAY_WRITE | MAY_APPEND).
+ */
+unsigned int
+richacl_want_to_mask(int want)
+{
+ unsigned int mask = 0;
+
+ if (want & MAY_READ)
+ mask |= ACE4_READ_DATA;
+ if (want & MAY_APPEND)
+ mask |= ACE4_APPEND_DATA;
+ 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);
+
+/**
+ * richacl_capability_check -check for capabilities overriding read/write access
+ * @inode: inode to check
+ * @mask: requested access (ACE4_* bitmask)
+ *
+ * Capabilities other than CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH
+ * must be checked separately.
+ */
+static inline int richacl_capability_check(struct inode *inode,
+ unsigned int mask)
+{
+ /*
+ * Read/write DACs are always overridable.
+ * Executable DACs are overridable if at least one exec bit is set.
+ */
+ if (!(mask & (ACE4_WRITE_ACL | ACE4_WRITE_OWNER)) &&
+ (!(mask & ACE4_EXECUTE) ||
+ (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)))
+ if (capable(CAP_DAC_OVERRIDE))
+ return 0;
+
+ /*
+ * Searching includes executable on directories, else just read.
+ */
+ if (!(mask & ~(ACE4_READ_DATA | ACE4_EXECUTE)) &&
+ (S_ISDIR(inode->i_mode) || !(mask & ACE4_EXECUTE)))
+ if (capable(CAP_DAC_READ_SEARCH))
+ return 0;
+
+ return -EACCES;
+}
+
+/**
+ * richacl_permission - permission check algorithm with masking
+ * @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. With
+ * write-through, the OWNER@ is always granted the owner file mask, the
+ * GROUP@ is always granted the group file mask, and EVERYONE@ is always
+ * granted the other file mask. Otherwise, processes are only granted
+ * @mask flags which they are granted in the @acl as well as in their
+ * file mask.
+ */
+int richacl_permission(struct inode *inode, const struct richacl *acl,
+ unsigned int mask)
+{
+ const struct richace *ace;
+ unsigned int file_mask, requested = mask, denied = 0;
+ int in_owning_group = in_group_p(inode->i_gid);
+ int owner_or_group_class = in_owning_group;
+
+ /*
+ * 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.
+ */
+
+ 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;
+
+ /*
+ * Apply the group file mask to entries other than OWNER@ and
+ * EVERYONE@. This is not required for correct access checking
+ * but ensures that we grant the same permissions as the acl
+ * computed by richacl_apply_masks().
+ *
+ * For example, without this restriction, 'group@:rw::allow'
+ * with mode 0600 would grant rw access to owner processes
+ * which are also in the owning group. This cannot be expressed
+ * in an acl.
+ */
+ if (richace_is_allow(ace))
+ ace_mask &= acl->a_group_mask;
+
+is_owner:
+ /* The process is in the owner or group file class. */
+ 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 && owner_or_group_class)
+ break;
+ }
+ denied |= mask;
+
+ /*
+ * Figure out which file mask applies.
+ * Clear write-through if the process is in the file group class but
+ * not in the owning group, and so the denied permissions apply.
+ */
+ if (current_fsuid() == inode->i_uid)
+ file_mask = acl->a_owner_mask;
+ else if (in_owning_group || owner_or_group_class)
+ file_mask = acl->a_group_mask;
+ else
+ file_mask = acl->a_other_mask;
+
+ denied |= requested & ~file_mask;
+ if (!denied)
+ return 0;
+ return richacl_capability_check(inode, requested);
+}
+EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * richacl_generic_permission - permission check algorithm without explicit acl
+ * @inode: inode to check permissions for
+ * @mask: requested access (ACE4_* bitmask)
+ *
+ * The file mode of a file without ACL corresponds to an ACL with a single
+ * "EVERYONE:~0::ALLOW" entry, with file masks that correspond to the file mode
+ * permissions. Instead of constructing a temporary ACL and applying
+ * richacl_permission() to it, compute the identical result directly from
+ * the file mode.
+ */
+int richacl_generic_permission(struct inode *inode, unsigned int mask)
+{
+ int mode = inode->i_mode;
+
+ if (current_fsuid() == inode->i_uid)
+ mode >>= 6;
+ else if (in_group_p(inode->i_gid))
+ mode >>= 3;
+ if (!(mask & ~richacl_mode_to_mask(mode)))
+ return 0;
+ return richacl_capability_check(inode, mask);
+}
+EXPORT_SYMBOL_GPL(richacl_generic_permission);
+
+/*
+ * richace_is_same_who - do both acl entries refer to the same identifier?
+ */
+int
+richace_is_same_who(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);
+
+/**
+ * richacl_allowed_to_who - mask flags allowed to a specific who value
+ *
+ * Computes the mask values allowed to a specific who value, taking
+ * EVERYONE@ entries into account.
+ */
+static unsigned int
+richacl_allowed_to_who(struct richacl *acl, struct richace *who)
+{
+ struct richace *ace;
+ unsigned int allowed = 0;
+
+ richacl_for_each_entry_reverse(ace, acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_same_who(ace, who) ||
+ richace_is_everyone(ace)) {
+ if (richace_is_allow(ace))
+ allowed |= ace->e_mask;
+ else if (richace_is_deny(ace))
+ allowed &= ~ace->e_mask;
+ }
+ }
+ return allowed;
+}
+
+/**
+ * richacl_compute_max_masks - compute upper bound masks
+ *
+ * Computes upper bound owner, group, and other masks so that none of
+ * the mask flags allowed by the acl are disabled (for any choice of the
+ * file owner or group membership).
+ */
+static void
+richacl_compute_max_masks(struct richacl *acl)
+{
+ struct richace *ace;
+
+ acl->a_owner_mask = 0;
+ acl->a_group_mask = 0;
+ acl->a_other_mask = 0;
+
+ richacl_for_each_entry_reverse(ace, acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+
+ if (richace_is_owner(ace)) {
+ if (richace_is_allow(ace))
+ acl->a_owner_mask |= ace->e_mask;
+ else if (richace_is_deny(ace))
+ acl->a_owner_mask &= ~ace->e_mask;
+ } else if (richace_is_everyone(ace)) {
+ if (richace_is_allow(ace)) {
+ struct richace who = {
+ .e_flags = ACE4_SPECIAL_WHO,
+ .u.e_who = richace_group_who,
+ };
+
+ acl->a_other_mask |= ace->e_mask;
+ acl->a_group_mask |=
+ richacl_allowed_to_who(acl, &who);
+ acl->a_owner_mask |= ace->e_mask;
+ } else if (richace_is_deny(ace)) {
+ acl->a_other_mask &= ~ace->e_mask;
+ acl->a_group_mask &= ~ace->e_mask;
+ acl->a_owner_mask &= ~ace->e_mask;
+ }
+ } else {
+ if (richace_is_allow(ace)) {
+ unsigned int mask =
+ richacl_allowed_to_who(acl, ace);
+
+ acl->a_group_mask |= mask;
+ acl->a_owner_mask |= mask;
+ }
+ }
+ }
+}
+
+/**
+ * richacl_inherit - compute the acl a new file will inherit
+ * @dir_acl: acl of the containing direcory
+ * @mode: file type and create mode of the new file
+ *
+ * Given the containing directory's acl, this function will compute the
+ * acl that new files in that directory will inherit, or %NULL if
+ * @dir_acl does not contain acl entries inheritable by this file.
+ *
+ * Without write-through, the file masks in the returned acl are set to
+ * the intersection of the create mode and the maximum permissions
+ * allowed to each file class. With write-through, the file masks are
+ * set to the create mode.
+ */
+struct richacl *
+richacl_inherit(const struct richacl *dir_acl, mode_t mode)
+{
+ const struct richace *dir_ace;
+ struct richacl *acl;
+ struct richace *ace;
+ int count = 0;
+
+ if (S_ISDIR(mode)) {
+ 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);
+ ace++;
+ }
+ }
+
+ /* The maximum max flags that the owner, group, and other classes
+ are allowed. */
+ if (dir_acl->a_flags & ACL4_WRITE_THROUGH) {
+ acl->a_owner_mask = ACE4_VALID_MASK;
+ acl->a_group_mask = ACE4_VALID_MASK;
+ acl->a_other_mask = ACE4_VALID_MASK;
+
+ mode &= ~current_umask();
+ } else
+ richacl_compute_max_masks(acl);
+
+ /* Apply the create mode. */
+ acl->a_owner_mask &= richacl_mode_to_mask(mode >> 6);
+ acl->a_group_mask &= richacl_mode_to_mask(mode >> 3);
+ acl->a_other_mask &= richacl_mode_to_mask(mode);
+
+ if (richacl_write_through(&acl)) {
+ richacl_put(acl);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH);
+
+ return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_inherit);
diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
new file mode 100644
index 0000000..a985bbf
--- /dev/null
+++ b/fs/richacl_compat.c
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2006 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/module.h>
+#include <linux/fs.h>
+#include <linux/richacl.h>
+
+/**
+ * struct richacl_alloc - remember how many entries are actually allocated
+ * @acl: acl with a_count <= @count
+ * @count: the actual number of entries allocated in @acl
+ *
+ * We pass around this structure while modifying an acl, so that we do
+ * not have to reallocate when we remove existing entries followed by
+ * adding new entries.
+ */
+struct richacl_alloc {
+ struct richacl *acl;
+ unsigned int count;
+};
+
+/**
+ * richacl_delete_entry - delete an entry in an acl
+ * @x: acl and number of allocated entries
+ * @ace: an entry in @x->acl
+ *
+ * Updates @ace so that it points to the entry before the deleted entry
+ * on return. (When deleting the first entry, @ace will point to the
+ * (non-existant) entry before the first entry). This behavior is the
+ * expected behavior when deleting entries while forward iterating over
+ * an acl.
+ */
+static void
+richacl_delete_entry(struct richacl_alloc *x, struct richace **ace)
+{
+ void *end = x->acl->a_entries + x->acl->a_count;
+
+ memmove(*ace, *ace + 1, end - (void *)(*ace + 1));
+ (*ace)--;
+ x->acl->a_count--;
+}
+
+/**
+ * richacl_insert_entry - insert an entry in an acl
+ * @x: acl and number of allocated entries
+ * @ace: entry before which the new entry shall be inserted
+ *
+ * Insert a new entry in @x->acl at position @ace, and zero-initialize
+ * it. This may require reallocating @x->acl.
+ */
+static int
+richacl_insert_entry(struct richacl_alloc *x, struct richace **ace)
+{
+ if (x->count == x->acl->a_count) {
+ int n = *ace - x->acl->a_entries;
+ struct richacl *acl2;
+
+ acl2 = richacl_alloc(x->acl->a_count + 1);
+ if (!acl2)
+ return -1;
+ acl2->a_flags = x->acl->a_flags;
+ acl2->a_owner_mask = x->acl->a_owner_mask;
+ acl2->a_group_mask = x->acl->a_group_mask;
+ acl2->a_other_mask = x->acl->a_other_mask;
+ memcpy(acl2->a_entries, x->acl->a_entries,
+ n * sizeof(struct richace));
+ memcpy(acl2->a_entries + n + 1, *ace,
+ (x->acl->a_count - n) * sizeof(struct richace));
+ kfree(x->acl);
+ x->acl = acl2;
+ x->count = acl2->a_count;
+ *ace = acl2->a_entries + n;
+ } else {
+ void *end = x->acl->a_entries + x->acl->a_count;
+
+ memmove(*ace + 1, *ace, end - (void *)*ace);
+ x->acl->a_count++;
+ }
+ memset(*ace, 0, sizeof(struct richace));
+ return 0;
+}
+
+/**
+ * richace_change_mask - change the mask in @ace to @mask
+ * @x: acl and number of allocated entries
+ * @ace: entry to modify
+ * @mask: new mask for @ace
+ *
+ * Set the effective mask of @ace to @mask. This will require splitting
+ * off a separate acl entry if @ace is inheritable. In that case, the
+ * effective- only acl entry is inserted after the inheritable acl
+ * entry, end the inheritable acl entry is set to inheritable-only. If
+ * @mode is 0, either set the original acl entry to inheritable-only if
+ * it was inheritable, or remove it otherwise. The returned @ace points
+ * to the modified or inserted effective-only acl entry if that entry
+ * exists, to the entry that has become inheritable-only, or else to the
+ * previous entry in the acl. This is the expected behavior when
+ * modifying masks while forward iterating over an acl.
+ */
+static int
+richace_change_mask(struct richacl_alloc *x, struct richace **ace,
+ unsigned int mask)
+{
+ if (mask && (*ace)->e_mask == mask)
+ return 0;
+ if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) {
+ if (richace_is_inheritable(*ace)) {
+ if (richacl_insert_entry(x, ace))
+ return -1;
+ memcpy(*ace, *ace + 1, sizeof(struct richace));
+ (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
+ (*ace)++;
+ richace_clear_inheritance_flags(*ace);
+ }
+ (*ace)->e_mask = mask;
+ } else {
+ if (richace_is_inheritable(*ace))
+ (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
+ else
+ richacl_delete_entry(x, ace);
+ }
+ return 0;
+}
+
+/**
+ * richacl_move_everyone_aces_down - move everyone@ acl entries to the end
+ * @x: acl and number of allocated entries
+ *
+ * Move all everyone acl entries to the bottom of the acl so that only a
+ * single everyone@ allow acl entry remains at the end, and update the
+ * mask fields of all acl entries on the way. If everyone@ is not
+ * granted any permissions, no empty everyone@ acl entry is inserted.
+ *
+ * This transformation does not modify the permissions that the acl
+ * grants, but we need it to simplify successive transformations.
+ */
+static int
+richacl_move_everyone_aces_down(struct richacl_alloc *x)
+{
+ struct richace *ace;
+ unsigned int allowed = 0, denied = 0;
+
+ richacl_for_each_entry(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_everyone(ace)) {
+ if (richace_is_allow(ace))
+ allowed |= (ace->e_mask & ~denied);
+ else if (richace_is_deny(ace))
+ denied |= (ace->e_mask & ~allowed);
+ else
+ continue;
+ if (richace_change_mask(x, &ace, 0))
+ return -1;
+ } else {
+ if (richace_is_allow(ace)) {
+ if (richace_change_mask(x, &ace, allowed |
+ (ace->e_mask & ~denied)))
+ return -1;
+ } else if (richace_is_deny(ace)) {
+ if (richace_change_mask(x, &ace, denied |
+ (ace->e_mask & ~allowed)))
+ return -1;
+ }
+ }
+ }
+ if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
+ struct richace *last_ace = ace - 1;
+
+ if (richace_is_everyone(last_ace) &&
+ richace_is_allow(last_ace) &&
+ richace_is_inherit_only(last_ace) &&
+ last_ace->e_mask == allowed)
+ last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE;
+ else {
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = allowed;
+ ace->u.e_who = richace_everyone_who;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __richacl_propagate_everyone - propagate everyone@ mask flags up for @who
+ * @x: acl and number of allocated entries
+ * @who: identifier to propagate mask flags for
+ * @allow: mask flags to propagate up
+ *
+ * Propagate mask flags from the trailing everyone@ allow acl entry up
+ * for the specified @who.
+ *
+ * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an
+ * additional @who ALLOW entry, but with the following optimizations:
+ * (1) we don't bother setting any flags in the new @who ALLOW entry
+ * that has already been allowed or denied by a previous @who entry, (2)
+ * we merge the new @who entry with a previous @who entry if there is
+ * such a previous @who entry and there are no intervening DENY entries
+ * with mask flags that overlap the flags we care about.
+ */
+static int
+__richacl_propagate_everyone(struct richacl_alloc *x, struct richace *who,
+ unsigned int allow)
+{
+ struct richace *allow_last = NULL, *ace;
+
+ /* Remove the mask flags from allow that are already determined for
+ this who value, and figure out if there is an ALLOW entry for
+ this who value that is "reachable" from the trailing EVERYONE@
+ ALLOW ACE. */
+ richacl_for_each_entry(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_allow(ace)) {
+ if (richace_is_same_who(ace, who)) {
+ allow &= ~ace->e_mask;
+ allow_last = ace;
+ }
+ } else if (richace_is_deny(ace)) {
+ if (richace_is_same_who(ace, who))
+ allow &= ~ace->e_mask;
+ if (allow & ace->e_mask)
+ allow_last = NULL;
+ }
+ }
+
+ if (allow) {
+ if (allow_last)
+ return richace_change_mask(x, &allow_last,
+ allow_last->e_mask | allow);
+ else {
+ struct richace who_copy;
+
+ ace = x->acl->a_entries + x->acl->a_count - 1;
+ memcpy(&who_copy, who, sizeof(struct richace));
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ memcpy(ace, &who_copy, sizeof(struct richace));
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ richace_clear_inheritance_flags(ace);
+ ace->e_mask = allow;
+ }
+ }
+ return 0;
+}
+
+/**
+ * richacl_propagate_everyone - propagate everyone@ mask flags up the acl
+ * @x: acl and number of allocated entries
+ *
+ * Make sure for owner@, group@, and all other users, groups, and
+ * special identifiers that they are allowed or denied all permissions
+ * that are granted be the trailing everyone@ acl entry. If they are
+ * not, try to add the missing permissions to existing allow acl entries
+ * for those users, or introduce additional acl entries if that is not
+ * possible.
+ *
+ * We do this so that no mask flags will get lost when finally applying
+ * the file masks to the acl entries: otherwise, with an other file mask
+ * that is more restrictive than the owner and/or group file mask, mask
+ * flags that were allowed to processes in the owner and group classes
+ * and that the other mask denies would be lost. For example, the
+ * following two acls show the problem when mode 0664 is applied to
+ * them:
+ *
+ * masking without propagation (wrong)
+ * ===========================================================
+ * joe:r::allow => joe:r::allow
+ * everyone@:rwx::allow => everyone@:r::allow
+ * -----------------------------------------------------------
+ * joe:w::deny => joe:w::deny
+ * everyone@:rwx::allow everyone@:r::allow
+ *
+ * Note that the permissions of joe end up being more restrictive than
+ * what the acl would allow when first computing the allowed flags and
+ * then applying the respective mask. With propagation of permissions,
+ * we get:
+ *
+ * masking after propagation (correct)
+ * ===========================================================
+ * joe:r::allow => joe:rw::allow
+ * owner@:rw::allow
+ * group@:rw::allow
+ * everyone@:rwx::allow everyone@:r::allow
+ * -----------------------------------------------------------
+ * joe:w::deny => owner@:x::deny
+ * joe:w::deny
+ * owner@:rw::allow
+ * owner@:rw::allow
+ * joe:r::allow
+ * everyone@:rwx::allow everyone@:r::allow
+ *
+ * The examples show the acls that would result from propagation with no
+ * masking performed. In fact, we do apply the respective mask to the
+ * acl entries before computing the propagation because this will save
+ * us from adding acl entries that would end up with empty mask fields
+ * after applying the masks.
+ *
+ * It is ensured that no more than one entry will be inserted for each
+ * who value, no matter how many entries each who value has already.
+ */
+static int
+richacl_propagate_everyone(struct richacl_alloc *x)
+{
+ int write_through = (x->acl->a_flags & ACL4_WRITE_THROUGH);
+ struct richace who = { .e_flags = ACE4_SPECIAL_WHO };
+ struct richace *ace;
+ unsigned int owner_allow, group_allow;
+ int retval;
+
+ if (!((x->acl->a_owner_mask | x->acl->a_group_mask) &
+ ~x->acl->a_other_mask))
+ return 0;
+ if (!x->acl->a_count)
+ return 0;
+ ace = x->acl->a_entries + x->acl->a_count - 1;
+ if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
+ return 0;
+ if (!(ace->e_mask & ~x->acl->a_other_mask)) {
+ /* None of the allowed permissions will get masked. */
+ return 0;
+ }
+ owner_allow = ace->e_mask & x->acl->a_owner_mask;
+ group_allow = ace->e_mask & x->acl->a_group_mask;
+
+ /* Propagate everyone@ permissions through to owner@. */
+ if (owner_allow && !write_through &&
+ (x->acl->a_owner_mask & ~x->acl->a_other_mask)) {
+ who.u.e_who = richace_owner_who;
+ retval = __richacl_propagate_everyone(x, &who, owner_allow);
+ if (retval)
+ return -1;
+ }
+
+ if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) {
+ int n;
+
+ if (!write_through) {
+ /* Propagate everyone@ permissions through to group@. */
+ who.u.e_who = richace_group_who;
+ retval = __richacl_propagate_everyone(x, &who,
+ group_allow);
+ if (retval)
+ return -1;
+ }
+
+ /* Start from the entry before the trailing EVERYONE@ ALLOW
+ entry. We will not hit EVERYONE@ entries in the loop. */
+ for (n = x->acl->a_count - 2; n != -1; n--) {
+ ace = x->acl->a_entries + n;
+
+ if (richace_is_inherit_only(ace) ||
+ richace_is_owner(ace) ||
+ richace_is_group(ace))
+ continue;
+ if (richace_is_allow(ace) || richace_is_deny(ace)) {
+ /* Any inserted entry will end up below the
+ current entry. */
+ retval = __richacl_propagate_everyone(x, ace,
+ group_allow);
+ if (retval)
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * __richacl_apply_masks - apply the masks to the acl entries
+ * @x: acl and number of allocated entries
+ *
+ * Apply the owner file mask to owner@ entries, the intersection of the
+ * group and other file masks to everyone@ entries, and the group file
+ * mask to all other entries.
+ */
+static int
+__richacl_apply_masks(struct richacl_alloc *x)
+{
+ struct richace *ace;
+
+ richacl_for_each_entry(ace, x->acl) {
+ unsigned int mask;
+
+ if (richace_is_inherit_only(ace) || !richace_is_allow(ace))
+ continue;
+ if (richace_is_owner(ace))
+ mask = x->acl->a_owner_mask;
+ else if (richace_is_everyone(ace))
+ mask = x->acl->a_other_mask;
+ else
+ mask = x->acl->a_group_mask;
+ if (richace_change_mask(x, &ace, ace->e_mask & mask))
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * richacl_max_allowed - maximum mask flags that anybody is allowed
+ */
+static unsigned int
+richacl_max_allowed(struct richacl *acl)
+{
+ struct richace *ace;
+ unsigned int allowed = 0;
+
+ richacl_for_each_entry_reverse(ace, acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_allow(ace))
+ allowed |= ace->e_mask;
+ else if (richace_is_deny(ace)) {
+ if (richace_is_everyone(ace))
+ allowed &= ~ace->e_mask;
+ }
+ }
+ return allowed;
+}
+
+/**
+ * richacl_isolate_owner_class - limit the owner class to the owner file mask
+ * @x: acl and number of allocated entries
+ *
+ * Make sure the owner class (owner@) is granted no more than the owner
+ * mask by first checking which permissions anyone is granted, and then
+ * denying owner@ all permissions beyond that.
+ */
+static int
+richacl_isolate_owner_class(struct richacl_alloc *x)
+{
+ struct richace *ace;
+ unsigned int allowed = 0;
+
+ allowed = richacl_max_allowed(x->acl);
+ if (allowed & ~x->acl->a_owner_mask) {
+ /* Figure out if we can update an existig OWNER@ DENY entry. */
+ richacl_for_each_entry(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_deny(ace)) {
+ if (richace_is_owner(ace))
+ break;
+ } else if (richace_is_allow(ace)) {
+ ace = x->acl->a_entries + x->acl->a_count;
+ break;
+ }
+ }
+ if (ace != x->acl->a_entries + x->acl->a_count) {
+ if (richace_change_mask(x, &ace, ace->e_mask |
+ (allowed & ~x->acl->a_owner_mask)))
+ return -1;
+ } else {
+ /* Insert an owner@ deny entry at the front. */
+ ace = x->acl->a_entries;
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = allowed & ~x->acl->a_owner_mask;
+ ace->u.e_who = richace_owner_who;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __richacl_isolate_who - isolate entry from EVERYONE@ ALLOW entry
+ * @x: acl and number of allocated entries
+ * @who: identifier to isolate
+ * @deny: mask flags this identifier should not be allowed
+ *
+ * Make sure that @who is not allowed any mask flags in @deny by checking
+ * which mask flags this identifier is allowed, and adding excess allowed
+ * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW
+ * entry, or inserting such an entry.
+ */
+static int
+__richacl_isolate_who(struct richacl_alloc *x, struct richace *who,
+ unsigned int deny)
+{
+ struct richace *ace;
+ unsigned int allowed = 0, n;
+
+ /* Compute the mask flags granted to this who value. */
+ richacl_for_each_entry_reverse(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_same_who(ace, who)) {
+ if (richace_is_allow(ace))
+ allowed |= ace->e_mask;
+ else if (richace_is_deny(ace))
+ allowed &= ~ace->e_mask;
+ deny &= ~ace->e_mask;
+ }
+ }
+ if (!deny)
+ return 0;
+
+ /* Figure out if we can update an existig DENY entry. Start
+ from the entry before the trailing EVERYONE@ ALLOW entry. We
+ will not hit EVERYONE@ entries in the loop. */
+ for (n = x->acl->a_count - 2; n != -1; n--) {
+ ace = x->acl->a_entries + n;
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_deny(ace)) {
+ if (richace_is_same_who(ace, who))
+ break;
+ } else if (richace_is_allow(ace) &&
+ (ace->e_mask & deny)) {
+ n = -1;
+ break;
+ }
+ }
+ if (n != -1) {
+ if (richace_change_mask(x, &ace, ace->e_mask | deny))
+ return -1;
+ } else {
+ /* Insert a eny entry before the trailing EVERYONE@ DENY
+ entry. */
+ struct richace who_copy;
+
+ ace = x->acl->a_entries + x->acl->a_count - 1;
+ memcpy(&who_copy, who, sizeof(struct richace));
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ memcpy(ace, &who_copy, sizeof(struct richace));
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ richace_clear_inheritance_flags(ace);
+ ace->e_mask = deny;
+ }
+ return 0;
+}
+
+/**
+ * richacl_isolate_group_class - limit the group class to the group file mask
+ * @x: acl and number of allocated entries
+ *
+ * Make sure the group class (all entries except owner@ and everyone@) is
+ * granted no more than the group mask by inserting DENY entries for group
+ * class entries where necessary.
+ */
+static int
+richacl_isolate_group_class(struct richacl_alloc *x)
+{
+ struct richace who = {
+ .e_flags = ACE4_SPECIAL_WHO,
+ .u.e_who = richace_group_who,
+ };
+ struct richace *ace;
+ unsigned int deny;
+
+ if (!x->acl->a_count)
+ return 0;
+ ace = x->acl->a_entries + x->acl->a_count - 1;
+ if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
+ return 0;
+ deny = ace->e_mask & ~x->acl->a_group_mask;
+
+ if (deny) {
+ unsigned int n;
+
+ if (__richacl_isolate_who(x, &who, deny))
+ return -1;
+
+ /* Start from the entry before the trailing EVERYONE@ ALLOW
+ entry. We will not hit EVERYONE@ entries in the loop. */
+ for (n = x->acl->a_count - 2; n != -1; n--) {
+ ace = x->acl->a_entries + n;
+
+ if (richace_is_inherit_only(ace) ||
+ richace_is_owner(ace) ||
+ richace_is_group(ace))
+ continue;
+ if (__richacl_isolate_who(x, ace, deny))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __richacl_write_through - grant the full masks to owner@, group@, everyone@
+ *
+ * Make sure that owner, group@, and everyone@ are allowed the full mask
+ * permissions, and not only the permissions granted both by the acl and
+ * the masks.
+ */
+static int
+__richacl_write_through(struct richacl_alloc *x)
+{
+ struct richace *ace;
+ unsigned int allowed;
+
+ /* Remove all owner@ and group@ ACEs: we re-insert them at the
+ top. */
+ richacl_for_each_entry(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if ((richace_is_owner(ace) || richace_is_group(ace)) &&
+ richace_change_mask(x, &ace, 0))
+ return -1;
+ }
+
+ /* Insert the everyone@ allow entry at the end, or update the
+ existing entry. */
+ allowed = x->acl->a_other_mask;
+ if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
+ ace = x->acl->a_entries + x->acl->a_count - 1;
+ if (x->acl->a_count && richace_is_everyone(ace) &&
+ !richace_is_inherit_only(ace)) {
+ if (richace_change_mask(x, &ace, allowed))
+ return -1;
+ } else {
+ ace = x->acl->a_entries + x->acl->a_count;
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = allowed;
+ ace->u.e_who = richace_everyone_who;
+ }
+ }
+
+ /* Compute the permissions that owner@ and group@ are already granted
+ though the everyone@ allow entry at the end. Note that the acl
+ contains no owner@ or group@ entries at this point. */
+ allowed = 0;
+ richacl_for_each_entry_reverse(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_allow(ace)) {
+ if (richace_is_everyone(ace))
+ allowed |= ace->e_mask;
+ } else if (richace_is_deny(ace))
+ allowed &= ~ace->e_mask;
+ }
+
+ /* Insert the appropriate group@ allow entry at the front. */
+ if (x->acl->a_group_mask & ~allowed) {
+ ace = x->acl->a_entries;
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = x->acl->a_group_mask /*& ~allowed*/;
+ ace->u.e_who = richace_group_who;
+ }
+
+ /* Insert the appropriate owner@ allow entry at the front. */
+ if (x->acl->a_owner_mask & ~allowed) {
+ ace = x->acl->a_entries;
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = x->acl->a_owner_mask /*& ~allowed*/;
+ ace->u.e_who = richace_owner_who;
+ }
+
+ /* Insert the appropriate owner@ deny entry at the front. */
+ allowed = richacl_max_allowed(x->acl);
+ if (allowed & ~x->acl->a_owner_mask) {
+ richacl_for_each_entry(ace, x->acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_allow(ace)) {
+ ace = x->acl->a_entries + x->acl->a_count;
+ break;
+ }
+ if (richace_is_deny(ace) && richace_is_owner(ace))
+ break;
+ }
+ if (ace != x->acl->a_entries + x->acl->a_count) {
+ if (richace_change_mask(x, &ace, ace->e_mask |
+ (allowed & ~x->acl->a_owner_mask)))
+ return -1;
+ } else {
+ ace = x->acl->a_entries;
+ if (richacl_insert_entry(x, &ace))
+ return -1;
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = ACE4_SPECIAL_WHO;
+ ace->e_mask = allowed & ~x->acl->a_owner_mask;
+ ace->u.e_who = richace_owner_who;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * richacl_apply_masks - apply the masks to the acl
+ *
+ * Apply the masks so that the acl allows no more flags than the
+ * intersection between the flags that the original acl allows and the
+ * mask matching the process.
+ *
+ * Note: this algorithm may push the number of entries in the acl above
+ * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail.
+ */
+int
+richacl_apply_masks(struct richacl **acl)
+{
+ struct richacl_alloc x = {
+ .acl = *acl,
+ .count = (*acl)->a_count,
+ };
+ int retval = 0;
+
+ if (richacl_move_everyone_aces_down(&x) ||
+ richacl_propagate_everyone(&x) ||
+ __richacl_apply_masks(&x) ||
+ richacl_isolate_owner_class(&x) ||
+ richacl_isolate_group_class(&x))
+ retval = -ENOMEM;
+
+ *acl = x.acl;
+ return retval;
+}
+EXPORT_SYMBOL_GPL(richacl_apply_masks);
+
+int richacl_write_through(struct richacl **acl)
+{
+ struct richacl_alloc x = {
+ .acl = *acl,
+ .count = (*acl)->a_count,
+ };
+ int retval = 0;
+
+ if (!((*acl)->a_flags & ACL4_WRITE_THROUGH))
+ goto out;
+
+ if (richacl_move_everyone_aces_down(&x) ||
+ richacl_propagate_everyone(&x) ||
+ __richacl_write_through(&x))
+ retval = -ENOMEM;
+
+ *acl = x.acl;
+out:
+ return retval;
+}
diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
new file mode 100644
index 0000000..4c04417
--- /dev/null
+++ b/fs/richacl_xattr.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2006 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");
+
+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 = be16_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 = be32_to_cpu(xattr_acl->a_owner_mask);
+ if (acl->a_owner_mask & ~ACE4_VALID_MASK)
+ goto fail_einval;
+ acl->a_group_mask = be32_to_cpu(xattr_acl->a_group_mask);
+ if (acl->a_group_mask & ~ACE4_VALID_MASK)
+ goto fail_einval;
+ acl->a_other_mask = be32_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 = be16_to_cpu(xattr_ace->e_type);
+ ace->e_flags = be16_to_cpu(xattr_ace->e_flags);
+ ace->e_mask = be32_to_cpu(xattr_ace->e_mask);
+ ace->u.e_id = be32_to_cpu(xattr_ace->e_id);
+
+ if (ace->e_flags & ~ACE4_VALID_FLAGS) {
+ memset(ace, 0, sizeof(struct richace));
+ 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);
+
+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);
+
+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_be16(acl->a_count);
+
+ xattr_acl->a_owner_mask = cpu_to_be32(acl->a_owner_mask);
+ xattr_acl->a_group_mask = cpu_to_be32(acl->a_group_mask);
+ xattr_acl->a_other_mask = cpu_to_be32(acl->a_other_mask);
+
+ xattr_ace = (void *)(xattr_acl + 1);
+ richacl_for_each_entry(ace, acl) {
+ xattr_ace->e_type = cpu_to_be16(ace->e_type);
+ xattr_ace->e_flags = cpu_to_be16(ace->e_flags &
+ ACE4_VALID_FLAGS);
+ xattr_ace->e_mask = cpu_to_be32(ace->e_mask);
+ if (richace_is_unix_id(ace)) {
+ xattr_ace->e_id = cpu_to_be32(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_be32(-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.h b/include/linux/richacl.h
new file mode 100644
index 0000000..a2b4bd0
--- /dev/null
+++ b/include/linux/richacl.h
@@ -0,0 +1,208 @@
+#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--)
+
+/* a_flags values */
+#define ACL4_WRITE_THROUGH 0x40
+
+#define ACL4_VALID_FLAGS \
+ ACL4_WRITE_THROUGH
+
+/* 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_DELETE 0x00010000
+#define ACE4_READ_ACL 0x00020000
+#define ACE4_WRITE_ACL 0x00040000
+#define ACE4_WRITE_OWNER 0x00080000
+#define ACE4_SYNCHRONIZE 0x00100000
+
+#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_DELETE | \
+ ACE4_READ_ACL | \
+ ACE4_WRITE_ACL | \
+ ACE4_WRITE_OWNER | \
+ ACE4_SYNCHRONIZE)
+
+#define ACE4_POSIX_ALWAYS_ALLOWED ( \
+ ACE4_SYNCHRONIZE | \
+ ACE4_READ_ATTRIBUTES | \
+ ACE4_READ_ACL)
+/*
+ * Duplicate an RICHACL handle.
+ */
+static inline struct richacl *
+richacl_get(struct richacl *acl)
+{
+ if (acl)
+ atomic_inc(&acl->a_refcount);
+ return acl;
+}
+
+/*
+ * Free an 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 strcmp for efficiency. */
+
+extern const char richace_owner_who[];
+extern const char richace_group_who[];
+extern const char richace_everyone_who[];
+
+static inline int
+richace_is_owner(const struct richace *ace)
+{
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
+ ace->u.e_who == richace_owner_who;
+}
+
+static inline int
+richace_is_group(const struct richace *ace)
+{
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
+ ace->u.e_who == richace_group_who;
+}
+
+static inline int
+richace_is_everyone(const struct richace *ace)
+{
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
+ ace->u.e_who == richace_everyone_who;
+}
+
+static inline int
+richace_is_unix_id(const struct richace *ace)
+{
+ return !(ace->e_flags & ACE4_SPECIAL_WHO);
+}
+
+static inline int
+richace_is_inherit_only(const struct richace *ace)
+{
+ return ace->e_flags & ACE4_INHERIT_ONLY_ACE;
+}
+
+static inline int
+richace_is_inheritable(const struct richace *ace)
+{
+ return ace->e_flags & (ACE4_FILE_INHERIT_ACE |
+ ACE4_DIRECTORY_INHERIT_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);
+}
+
+static inline int
+richace_is_allow(const struct richace *ace)
+{
+ return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE;
+}
+
+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 count);
+extern struct richacl *richacl_clone(const struct richacl *acl);
+
+extern unsigned int richacl_want_to_mask(int want);
+extern int richacl_permission(struct inode *,
+ const struct richacl *, unsigned int);
+extern int richacl_generic_permission(struct inode *, unsigned int);
+extern int richace_is_same_who(const struct richace *, const struct richace *);
+extern int richace_set_who(struct richace *ace, const char *who);
+extern struct richacl *richacl_inherit(const struct richacl *, mode_t);
+extern int richacl_masks_to_mode(const struct richacl *);
+extern struct richacl *richacl_chmod(struct richacl *, mode_t);
+extern int richacl_apply_masks(struct richacl **acl);
+extern int richacl_write_through(struct richacl **acl);
+
+#endif /* __RICHACL_H */
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
new file mode 100644
index 0000000..5a75284
--- /dev/null
+++ b/include/linux/richacl_xattr.h
@@ -0,0 +1,32 @@
+#ifndef __RICHACL_XATTR_H
+#define __RICHACL_XATTR_H
+
+#include <linux/richacl.h>
+
+#define RICHACL_XATTR "system.richacl"
+
+struct richace_xattr {
+ __be16 e_type;
+ __be16 e_flags;
+ __be32 e_mask;
+ __be32 e_id;
+ char e_who[0];
+};
+
+struct richacl_xattr {
+ unsigned char a_version;
+ unsigned char a_flags;
+ __be16 a_count;
+ __be32 a_owner_mask;
+ __be32 a_group_mask;
+ __be32 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.0.rc0.48.gdace5


2010-02-01 05:35:25

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 11/23] richacl: Move the xattr representation to little-endian format

Posix acl already use little-endian representation on disk. Move
richacl also to little-endian representation

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

diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
index 4c04417..46e966a 100644
--- a/fs/richacl_xattr.c
+++ b/fs/richacl_xattr.c
@@ -34,7 +34,7 @@ richacl_from_xattr(const void *value, size_t size)
(xattr_acl->a_flags & ~ACL4_VALID_FLAGS))
return ERR_PTR(-EINVAL);

- count = be16_to_cpu(xattr_acl->a_count);
+ count = le16_to_cpu(xattr_acl->a_count);
if (count > ACL4_XATTR_MAX_COUNT)
return ERR_PTR(-EINVAL);

@@ -43,13 +43,13 @@ richacl_from_xattr(const void *value, size_t size)
return ERR_PTR(-ENOMEM);

acl->a_flags = xattr_acl->a_flags;
- acl->a_owner_mask = be32_to_cpu(xattr_acl->a_owner_mask);
+ 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 = be32_to_cpu(xattr_acl->a_group_mask);
+ 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 = be32_to_cpu(xattr_acl->a_other_mask);
+ acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
if (acl->a_other_mask & ~ACE4_VALID_MASK)
goto fail_einval;

@@ -63,10 +63,10 @@ richacl_from_xattr(const void *value, size_t size)
if (!end)
goto fail_einval;

- ace->e_type = be16_to_cpu(xattr_ace->e_type);
- ace->e_flags = be16_to_cpu(xattr_ace->e_flags);
- ace->e_mask = be32_to_cpu(xattr_ace->e_mask);
- ace->u.e_id = be32_to_cpu(xattr_ace->e_id);
+ 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) {
memset(ace, 0, sizeof(struct richace));
@@ -117,26 +117,26 @@ richacl_to_xattr(const struct richacl *acl, void *buffer)

xattr_acl->a_version = ACL4_XATTR_VERSION;
xattr_acl->a_flags = acl->a_flags;
- xattr_acl->a_count = cpu_to_be16(acl->a_count);
+ xattr_acl->a_count = cpu_to_le16(acl->a_count);

- xattr_acl->a_owner_mask = cpu_to_be32(acl->a_owner_mask);
- xattr_acl->a_group_mask = cpu_to_be32(acl->a_group_mask);
- xattr_acl->a_other_mask = cpu_to_be32(acl->a_other_mask);
+ 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_be16(ace->e_type);
- xattr_ace->e_flags = cpu_to_be16(ace->e_flags &
+ 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_be32(ace->e_mask);
+ xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
if (richace_is_unix_id(ace)) {
- xattr_ace->e_id = cpu_to_be32(ace->u.e_id);
+ 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_be32(-1);
+ 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;
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
index 5a75284..de7acf7 100644
--- a/include/linux/richacl_xattr.h
+++ b/include/linux/richacl_xattr.h
@@ -6,20 +6,20 @@
#define RICHACL_XATTR "system.richacl"

struct richace_xattr {
- __be16 e_type;
- __be16 e_flags;
- __be32 e_mask;
- __be32 e_id;
+ __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;
- __be16 a_count;
- __be32 a_owner_mask;
- __be32 a_group_mask;
- __be32 a_other_mask;
+ __le16 a_count;
+ __le32 a_owner_mask;
+ __le32 a_group_mask;
+ __le32 a_other_mask;
};

#define ACL4_XATTR_VERSION 0
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:23

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 15/23] richacl: Delete posix acl if present on richacl set

When trying to set richacl on a file system object if we
have ACL4_POSIX_MAPPED flag set on the acl delete posix
acl stored with the object. This helps us to migrate from
posix acl to richacl. If we have posix acl stored with
the inode, a getxattr on the inode would return a mapped
richacl with ACL4_POSIX_MAPPED set. Now when we set the
acl back it will result in posix acl being deleted and
richacl being stored.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/acl.c | 2 +-
fs/ext4/acl.h | 2 ++
fs/ext4/richacl.c | 26 ++++++++++++++++++++++++--
3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index e17e1a9..3dd3058 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -203,7 +203,7 @@ ext4_get_acl(struct inode *inode, int type)
*
* inode->i_mutex: down unless called from ext4_new_inode
*/
-static int
+int
ext4_set_acl(handle_t *handle, struct inode *inode, int type,
struct posix_acl *acl)
{
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index 3e47cf3..adcb6e7 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -58,6 +58,8 @@ extern int ext4_check_acl(struct inode *, int);
extern int ext4_acl_chmod(struct inode *);
extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
extern struct posix_acl *ext4_get_acl(struct inode *inode, int type);
+extern int ext4_set_acl(handle_t *handle, struct inode *inode, int type,
+ struct posix_acl *acl);

#else /* CONFIG_EXT4_FS_POSIX_ACL */
#include <linux/sched.h>
diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
index 73c14dd..ca8f28e 100644
--- a/fs/ext4/richacl.c
+++ b/fs/ext4/richacl.c
@@ -368,6 +368,7 @@ ext4_xattr_set_richacl(struct dentry *dentry, const char *name,
{
handle_t *handle;
struct richacl *acl = NULL;
+ struct posix_acl *pacl = NULL, *pdacl = NULL;
int retval, retries = 0;
struct inode *inode = dentry->d_inode;

@@ -388,18 +389,39 @@ ext4_xattr_set_richacl(struct dentry *dentry, const char *name,

inode->i_mode &= ~S_IRWXUGO;
inode->i_mode |= richacl_masks_to_mode(acl);
- }
+ /*
+ * check whether we have posix acl. If so delete them
+ *
+ */

+ if (acl->a_flags & ACL4_POSIX_MAPPED) {
+ pacl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
+ pdacl = ext4_get_acl(inode, ACL_TYPE_DEFAULT);
+ acl->a_flags &= ~ACL4_POSIX_MAPPED;
+ }
+ }
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);
+ if (pacl)
+ ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, NULL);
+ if (pdacl)
+ ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT, NULL);
+
retval = ext4_set_richacl(handle, inode, acl);
ext4_journal_stop(handle);
- if (retval == ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+ if (retval == ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) {
+ posix_acl_release(pacl);
+ posix_acl_release(pdacl);
+ pacl = pdacl = NULL;
goto retry;
+ }
richacl_put(acl);
+ posix_acl_release(pacl);
+ posix_acl_release(pdacl);
+ pacl = pdacl = NULL;
return retval;
}

--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:23

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 14/23] richacl: Disable automatic inheritance with posix mapped acls

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

diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
index 437d3ac..ae49106 100644
--- a/fs/richacl_posix.c
+++ b/fs/richacl_posix.c
@@ -191,6 +191,12 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
*/
acl->a_flags |= ACL4_POSIX_MAPPED;

+ /*
+ * Mark the acl as ACL4_PROTECTED so that we don't
+ * do automatic inheritance with posix mapped acls
+ */
+ acl->a_flags |= ACL4_PROTECTED;
+
return;
}

--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:43

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 01/23] vfs: VFS hooks for per-filesystem permission models

From: Andreas Gruenbacher <[email protected]>

Add may_create and may_delete inode operations that filesystems can
implement in order to override the vfs provided default behavior.
This is required for implementing permission models which go beyond
the traditional UNIX semantics.

If a filesystem does not implement these hooks, the behavior is
unchanged.

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

diff --git a/fs/namei.c b/fs/namei.c
index b55440b..3e842ac 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1337,14 +1337,26 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)

BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(victim->d_name.name, victim, dir);
-
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (dir->i_op->may_delete) {
+ if (IS_RDONLY(dir))
+ return -EROFS;
+ if (IS_IMMUTABLE(dir))
+ return -EACCES;
+ error = dir->i_op->may_delete(dir, victim->d_inode);
+ if (!error)
+ error = security_inode_permission(dir,
+ MAY_WRITE | MAY_EXEC);
+ } else {
+ error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (!error && check_sticky(dir, victim->d_inode))
+ 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(victim->d_inode) || IS_IMMUTABLE(victim->d_inode) ||
+ IS_SWAPFILE(victim->d_inode))
return -EPERM;
if (isdir) {
if (!S_ISDIR(victim->d_inode->i_mode))
@@ -1368,13 +1380,27 @@ 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 error;
+
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (dir->i_op->may_create) {
+ if (IS_RDONLY(dir))
+ return -EROFS;
+ if (IS_IMMUTABLE(dir))
+ return -EACCES;
+ error = dir->i_op->may_create(dir, isdir);
+ if (!error)
+ error = security_inode_permission(dir,
+ MAY_WRITE | MAY_EXEC);
+ } else
+ error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+
+ return error;
}

/*
@@ -1438,7 +1464,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;
@@ -1970,7 +1996,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;
@@ -2075,7 +2101,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;
@@ -2360,7 +2386,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;
@@ -2434,7 +2460,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode)
return -ENOENT;

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

@@ -2646,7 +2672,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 9147ca8..2191464 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1534,6 +1534,10 @@ struct inode_operations {
loff_t len);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
+ int (*may_create) (struct inode *, int);
+ int (*may_delete) (struct inode *, struct inode *);
+
+
};

struct seq_file;
--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:52

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 10/23] richacl: Add separate file and dir acl masks

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/richacl_base.c | 54 +++++++++++++++++++++++++++--------------------
fs/richacl_posix.c | 27 ++++++++++++-----------
include/linux/richacl.h | 2 +-
3 files changed, 46 insertions(+), 37 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index a176399..0d8953c 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -76,23 +76,23 @@ richacl_clone(const struct richacl *acl)
* make sure that we do not mask them if they are set, so that users who
* rely on these flags won't get confused.
*/
-#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 | \
+#define ACE4_POSIX_MODE_FILE_READ ACE4_READ_DATA
+#define ACE4_POSIX_MODE_FILE_WRITE ( \
+ ACE4_WRITE_DATA | ACE4_APPEND_DATA)
+#define ACE4_POSIX_MODE_DIR_READ ACE4_LIST_DIRECTORY
+#define ACE4_POSIX_MODE_DIR_WRITE ( \
+ ACE4_ADD_FILE | ACE4_ADD_SUBDIRECTORY | \
ACE4_DELETE_CHILD)
-#define ACE4_POSIX_MODE_EXEC ( \
- ACE4_EXECUTE)
+#define ACE4_POSIX_MODE_EXEC ACE4_EXECUTE

static int
richacl_mask_to_mode(unsigned int mask)
{
int mode = 0;

- if (mask & ACE4_POSIX_MODE_READ)
+ if (mask & (ACE4_POSIX_MODE_FILE_READ | ACE4_POSIX_MODE_DIR_READ))
mode |= MAY_READ;
- if (mask & ACE4_POSIX_MODE_WRITE)
+ if (mask & (ACE4_POSIX_MODE_FILE_WRITE | ACE4_POSIX_MODE_DIR_WRITE))
mode |= MAY_WRITE;
if (mask & ACE4_POSIX_MODE_EXEC)
mode |= MAY_EXEC;
@@ -115,14 +115,21 @@ richacl_masks_to_mode(const struct richacl *acl)
EXPORT_SYMBOL_GPL(richacl_masks_to_mode);

unsigned int
-richacl_mode_to_mask(mode_t mode)
+richacl_mode_to_mask(mode_t mode, int is_dir)
{
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 (is_dir) {
+ if (mode & MAY_READ)
+ mask |= ACE4_POSIX_MODE_DIR_READ;
+ if (mode & MAY_WRITE)
+ mask |= ACE4_POSIX_MODE_DIR_WRITE;
+ } else {
+ if (mode & MAY_READ)
+ mask |= ACE4_POSIX_MODE_FILE_READ;
+ if (mode & MAY_WRITE)
+ mask |= ACE4_POSIX_MODE_FILE_WRITE;
+ }
if (mode & MAY_EXEC)
mask |= ACE4_POSIX_MODE_EXEC;

@@ -141,12 +148,13 @@ richacl_mode_to_mask(mode_t mode)
struct richacl *
richacl_chmod(struct richacl *acl, mode_t mode)
{
+ int is_dir = S_ISDIR(mode);
unsigned int owner_mask, group_mask, other_mask;
struct richacl *clone;

- owner_mask = richacl_mode_to_mask(mode >> 6);
- group_mask = richacl_mode_to_mask(mode >> 3);
- other_mask = richacl_mode_to_mask(mode);
+ owner_mask = richacl_mode_to_mask(mode >> 6, is_dir);
+ group_mask = richacl_mode_to_mask(mode >> 3, is_dir);
+ other_mask = richacl_mode_to_mask(mode, is_dir);

if (acl->a_owner_mask == owner_mask &&
acl->a_group_mask == group_mask &&
@@ -352,7 +360,7 @@ int richacl_generic_permission(struct inode *inode, unsigned int mask)
mode >>= 6;
else if (in_group_p(inode->i_gid))
mode >>= 3;
- if (!(mask & ~richacl_mode_to_mask(mode)))
+ if (!(mask & ~richacl_mode_to_mask(mode, S_ISDIR(inode->i_mode))))
return 0;
return richacl_capability_check(inode, mask);
}
@@ -497,9 +505,9 @@ richacl_inherit(const struct richacl *dir_acl, mode_t mode)
const struct richace *dir_ace;
struct richacl *acl;
struct richace *ace;
- int count = 0;
+ int count = 0, is_dir = S_ISDIR(mode);

- if (S_ISDIR(mode)) {
+ if (is_dir) {
richacl_for_each_entry(dir_ace, dir_acl) {
if (!richace_is_inheritable(dir_ace))
continue;
@@ -555,9 +563,9 @@ richacl_inherit(const struct richacl *dir_acl, mode_t mode)
richacl_compute_max_masks(acl);

/* Apply the create mode. */
- acl->a_owner_mask &= richacl_mode_to_mask(mode >> 6);
- acl->a_group_mask &= richacl_mode_to_mask(mode >> 3);
- acl->a_other_mask &= richacl_mode_to_mask(mode);
+ acl->a_owner_mask &= richacl_mode_to_mask(mode >> 6, is_dir);
+ acl->a_group_mask &= richacl_mode_to_mask(mode >> 3, is_dir);
+ acl->a_other_mask &= richacl_mode_to_mask(mode, is_dir);

if (richacl_write_through(&acl)) {
richacl_put(acl);
diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
index 3cf2124..437d3ac 100644
--- a/fs/richacl_posix.c
+++ b/fs/richacl_posix.c
@@ -20,11 +20,12 @@
static void posix_to_richacl(struct posix_acl *pacl, int type,
mode_t mode, struct richacl *acl)
{
- int eflags;
+ int eflags, is_dir;
struct richace *ace;
unsigned short deny;
struct posix_acl_entry *pa, *pe, *acl_other = NULL;

+ is_dir = S_ISDIR(mode);
if (type == ACL_TYPE_DEFAULT)
eflags = ACE4_FILE_INHERIT_ACE |
ACE4_DIRECTORY_INHERIT_ACE | ACE4_INHERIT_ONLY_ACE;
@@ -43,7 +44,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
if (deny & 0x7) {
ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(deny);
+ ace->e_mask = richacl_mode_to_mask(deny, is_dir);
richace_set_who(ace, richace_owner_who);
acl->a_count++;
ace++;
@@ -51,7 +52,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
/* Add allow entry */
ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm, is_dir);
ace->e_mask |= ACE4_WRITE_ATTRIBUTES | ACE4_WRITE_ACL;
richace_set_who(ace, richace_owner_who);
acl->a_count++;
@@ -65,7 +66,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
if (deny & 0x7) {
ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(deny);
+ ace->e_mask = richacl_mode_to_mask(deny, is_dir);
ace->u.e_id = pa->e_id;
acl->a_count++;
ace++;
@@ -73,7 +74,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
/* Add allow entry */
ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm, is_dir);
ace->u.e_id = pa->e_id;
acl->a_count++;
ace++;
@@ -88,7 +89,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
*/
ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm, is_dir);
richace_set_who(ace, richace_group_who);
acl->a_count++;
ace++;
@@ -100,7 +101,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
/* Add allow entries only */
ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = eflags | ACE4_IDENTIFIER_GROUP;
- ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm, is_dir);
ace->u.e_id = pa->e_id;
acl->a_count++;
ace++;
@@ -145,7 +146,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
if (deny & 0x7) {
ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(deny);
+ ace->e_mask = richacl_mode_to_mask(deny, is_dir);
richace_set_who(ace, richace_group_who);
acl->a_count++;
ace++;
@@ -159,7 +160,7 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
if (deny & 0x7) {
ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = eflags | ACE4_IDENTIFIER_GROUP;
- ace->e_mask = richacl_mode_to_mask(deny);
+ ace->e_mask = richacl_mode_to_mask(deny, is_dir);
ace->u.e_id = pa->e_id;
acl->a_count++;
ace++;
@@ -172,16 +173,16 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
if (acl_other) {
ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = eflags;
- ace->e_mask = richacl_mode_to_mask(acl_other->e_perm);
+ ace->e_mask = richacl_mode_to_mask(acl_other->e_perm, is_dir);
richace_set_who(ace, richace_everyone_who);
acl->a_count++;
ace++;
}

/* set acl mask values */
- acl->a_owner_mask = richacl_mode_to_mask(mode >> 6);
- acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
- acl->a_other_mask = richacl_mode_to_mask(mode);
+ acl->a_owner_mask = richacl_mode_to_mask(mode >> 6, is_dir);
+ acl->a_group_mask = richacl_mode_to_mask(mode >> 3, is_dir);
+ acl->a_other_mask = richacl_mode_to_mask(mode, is_dir);

/*
* Mark that the acl as mapped from posix
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 41d93d8..b0df740 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -230,7 +230,7 @@ extern int richace_is_same_who(const struct richace *, const struct richace *);
extern int richace_set_who(struct richace *ace, const char *who);
extern struct richacl *richacl_inherit(const struct richacl *, mode_t);
extern int richacl_masks_to_mode(const struct richacl *);
-extern unsigned int richacl_mode_to_mask(mode_t mode);
+extern unsigned int richacl_mode_to_mask(mode_t, int);
extern struct richacl *richacl_chmod(struct richacl *, mode_t);
extern int richacl_apply_masks(struct richacl **acl);
extern int richacl_write_through(struct richacl **acl);
--
1.7.0.rc0.48.gdace5


2010-02-01 05:34:58

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 16/23] ext4: Update richacl incompat flag value

Update the incompat flag value so that we don't
conflict with ext4 features which are not yet
upstream but are in use.


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

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 13df624..bcbff59 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1133,7 +1133,9 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
-#define EXT4_FEATURE_INCOMPAT_RICHACL 0x0400
+#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400
+#define EXT4_FEATURE_INCOMPAT_RICHACL 0x0800
+#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000

#define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:23

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 21/23] richacl: Add helpers for NFSv4 acl to richacl conversion

Add helper function for mapping NFSv4acl to richacl and vice versa
Using richacl as the ondisk format ensures that we can map
NFSv4acl better to richacl.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/nfsd/nfs4acl.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/nfs4_acl.h | 4 ++
2 files changed, 76 insertions(+), 0 deletions(-)

diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 8815068..6724235 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -835,3 +835,75 @@ nfs4_acl_write_who(int who, char *p)
EXPORT_SYMBOL(nfs4_acl_new);
EXPORT_SYMBOL(nfs4_acl_get_whotype);
EXPORT_SYMBOL(nfs4_acl_write_who);
+
+struct nfs4_acl *
+nfs4_acl_richacl_to_nfsv4(struct richacl *racl)
+{
+ int error;
+ struct nfs4_acl *acl;
+ struct richace *race;
+ struct nfs4_ace *ace;
+
+ error = richacl_apply_masks(&racl);
+ if (error)
+ ERR_PTR(error);
+
+ acl = nfs4_acl_new(racl->a_count);
+ if (acl == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ace = acl->aces;
+ richacl_for_each_entry(race, racl) {
+ ace->type = race->e_type;
+ ace->access_mask = race->e_mask;
+ ace->flag = race->e_flags & ~ACE4_SPECIAL_WHO;
+ if (richace_is_owner(race))
+ ace->whotype = NFS4_ACL_WHO_OWNER;
+ else if (richace_is_group(race))
+ ace->whotype = NFS4_ACL_WHO_GROUP;
+ else if (richace_is_everyone(race))
+ ace->whotype = NFS4_ACL_WHO_EVERYONE;
+ else {
+ ace->whotype = NFS4_ACL_WHO_NAMED;
+ ace->who = race->u.e_id;
+ }
+ ace++;
+ acl->naces++;
+ }
+ return acl;
+}
+
+int nfs4_acl_nfsv4_to_richacl(struct nfs4_acl *acl, struct richacl **racl)
+{
+ struct richace *race;
+ struct nfs4_ace *ace;
+
+ *racl = richacl_alloc(acl->naces);
+ if (*racl == NULL)
+ return -ENOMEM;
+ race = (*racl)->a_entries;
+ for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
+ race->e_type = ace->type;
+ race->e_flags = ace->flag;
+ race->e_mask = ace->access_mask;
+ switch (ace->whotype) {
+ case NFS4_ACL_WHO_OWNER:
+ richace_set_who(race, richace_owner_who);
+ break;
+ case NFS4_ACL_WHO_GROUP:
+ richace_set_who(race, richace_group_who);
+ break;
+ case NFS4_ACL_WHO_EVERYONE:
+ richace_set_who(race, richace_everyone_who);
+ break;
+ case NFS4_ACL_WHO_NAMED:
+ race->u.e_id = ace->who;
+ break;
+ default:
+ richacl_put(*racl);
+ return -EINVAL;
+ }
+ race++;
+ }
+ return 0;
+}
diff --git a/include/linux/nfs4_acl.h b/include/linux/nfs4_acl.h
index c9c05a7..e14b03f 100644
--- a/include/linux/nfs4_acl.h
+++ b/include/linux/nfs4_acl.h
@@ -38,6 +38,7 @@
#define LINUX_NFS4_ACL_H

#include <linux/posix_acl.h>
+#include <linux/richacl.h>

/* Maximum ACL we'll accept from client; chosen (somewhat arbitrarily) to
* fit in a page: */
@@ -58,4 +59,7 @@ struct nfs4_acl *nfs4_acl_posix_to_nfsv4(struct posix_acl *,
int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *, struct posix_acl **,
struct posix_acl **, unsigned int flags);

+struct nfs4_acl *nfs4_acl_richacl_to_nfsv4(struct richacl *racl);
+int nfs4_acl_nfsv4_to_richacl(struct nfs4_acl *acl, struct richacl **racl);
+
#endif /* LINUX_NFS4_ACL_H */
--
1.7.0.rc0.48.gdace5


2010-02-01 05:35:23

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [PATCH 07/23] vfs: Add Posix acl to rich acl mapping helpers

This patch add helpers that can be used by the file system to map
posix acls to rich acl format. This enables the file system to
return rich acl mapping the posix acls stored on disk when the
file system is enabled with rich acl format.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/Makefile | 2 +-
fs/richacl_base.c | 2 +-
fs/richacl_posix.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 3 +
4 files changed, 228 insertions(+), 2 deletions(-)
create mode 100644 fs/richacl_posix.c

diff --git a/fs/Makefile b/fs/Makefile
index 0a046a1..a5f3ee3 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -53,7 +53,7 @@ obj-$(CONFIG_GENERIC_ACL) += generic_acl.o

obj-$(CONFIG_FS_RICHACL) += richacl.o
richacl-y := richacl_base.o richacl_xattr.o \
- richacl_compat.o
+ richacl_compat.o richacl_posix.o

obj-y += quota/

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index e75f713..a176399 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -114,7 +114,7 @@ richacl_masks_to_mode(const struct richacl *acl)
}
EXPORT_SYMBOL_GPL(richacl_masks_to_mode);

-static unsigned int
+unsigned int
richacl_mode_to_mask(mode_t mode)
{
unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED;
diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
new file mode 100644
index 0000000..07db970
--- /dev/null
+++ b/fs/richacl_posix.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright IBM Corporation, 2009
+ * 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/fs.h>
+#include <linux/richacl.h>
+#include <linux/posix_acl.h>
+
+static void posix_to_richacl(struct posix_acl *pacl, int type,
+ mode_t mode, struct richacl *acl)
+{
+ int eflags;
+ struct richace *ace;
+ unsigned short deny;
+ struct posix_acl_entry *pa, *pe, *acl_other = NULL;
+
+ if (type == ACL_TYPE_DEFAULT)
+ eflags = ACE4_FILE_INHERIT_ACE |
+ ACE4_DIRECTORY_INHERIT_ACE | ACE4_INHERIT_ONLY_ACE;
+ else
+ eflags = 0;
+
+ BUG_ON(pacl->a_count < 3);
+ /* Go to the last saved entry */
+ ace = acl->a_entries + acl->a_count;
+ FOREACH_ACL_ENTRY(pa, pacl, pe) {
+ switch (pa->e_tag) {
+ case ACL_USER_OBJ:
+ {
+ /* Everything that is not granted we deny */
+ deny = ~pa->e_perm ;
+ if (deny & 0x7) {
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(deny);
+ richace_set_who(ace, richace_owner_who);
+ acl->a_count++;
+ ace++;
+ }
+ /* Add allow entry */
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->e_mask |= ACE4_WRITE_ATTRIBUTES | ACE4_WRITE_ACL;
+ richace_set_who(ace, richace_owner_who);
+ acl->a_count++;
+ ace++;
+ break;
+ }
+ case ACL_USER:
+ {
+ /* Everything that is not granted we deny */
+ deny = ~pa->e_perm ;
+ if (deny & 0x7) {
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(deny);
+ ace->u.e_id = pa->e_id;
+ acl->a_count++;
+ ace++;
+ }
+ /* Add allow entry */
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->u.e_id = pa->e_id;
+ acl->a_count++;
+ ace++;
+ break;
+
+ }
+ case ACL_GROUP_OBJ:
+ {
+ /*
+ * In the case of group we apply allow first
+ * since a person can be in different group
+ */
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ richace_set_who(ace, richace_group_who);
+ acl->a_count++;
+ ace++;
+ break;
+
+ }
+ case ACL_GROUP:
+ {
+ /* Add allow entries only */
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = eflags | ACE4_IDENTIFIER_GROUP;
+ ace->e_mask = richacl_mode_to_mask(pa->e_perm);
+ ace->u.e_id = pa->e_id;
+ acl->a_count++;
+ ace++;
+ break;
+
+ }
+ case ACL_MASK:
+ {
+ /*
+ * We can ignore ACL_MASK values. We derive the
+ * respective values from the inode mode values
+ */
+ break;
+ }
+ case ACL_OTHER:
+ {
+ /*
+ * We should precess ACL_OTHER only after getting all
+ * user and group pa and then adding there respective
+ * deny entries
+ */
+ acl_other = pa;
+ break;
+
+ }
+ }
+ }
+ /*
+ * Now add the deny entries for ACL_GROUP and ACL_GROUP_OBJ entries
+ */
+ FOREACH_ACL_ENTRY(pa, pacl, pe) {
+ switch (pa->e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_USER:
+ case ACL_MASK:
+ case ACL_OTHER:
+ break;
+ case ACL_GROUP_OBJ:
+ {
+ /* Everything that is not granted we deny */
+ deny = ~pa->e_perm ;
+ if (deny & 0x7) {
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(deny);
+ richace_set_who(ace, richace_group_who);
+ acl->a_count++;
+ ace++;
+ }
+ break;
+ }
+ case ACL_GROUP:
+ {
+ /* Everything that is not granted we deny */
+ deny = ~pa->e_perm ;
+ if (deny & 0x7) {
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = eflags | ACE4_IDENTIFIER_GROUP;
+ ace->e_mask = richacl_mode_to_mask(deny);
+ ace->u.e_id = pa->e_id;
+ acl->a_count++;
+ ace++;
+ }
+ break;
+ }
+ }
+ }
+ /* Now handle ACL_OTHER entry */
+ if (acl_other) {
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = eflags;
+ ace->e_mask = richacl_mode_to_mask(acl_other->e_perm);
+ richace_set_who(ace, richace_everyone_who);
+ acl->a_count++;
+ ace++;
+ }
+
+ /* set acl mask values */
+ acl->a_owner_mask = richacl_mode_to_mask(mode >> 6);
+ acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
+ acl->a_other_mask = richacl_mode_to_mask(mode);
+
+ return;
+}
+
+struct richacl *map_posix_to_richacl(struct inode *inode,
+ struct posix_acl *pacl,
+ struct posix_acl *dpacl)
+{
+
+ struct richacl *acl;
+ int size = 0;
+
+ if (pacl) {
+ if (posix_acl_valid(pacl) < 0)
+ return ERR_PTR(-EINVAL);
+ size += 2*pacl->a_count;
+ }
+
+ if (dpacl) {
+ if (posix_acl_valid(dpacl) < 0)
+ return ERR_PTR(-EINVAL);
+ size += 2*dpacl->a_count;
+ }
+
+ /* Allocate the worst case one deny, allow pair each */
+ acl = richacl_alloc(size);
+ if (acl == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ /* We will count actual number of entries when we map */
+ acl->a_count = 0;
+ if (pacl)
+ posix_to_richacl(pacl, ACL_TYPE_ACCESS , inode->i_mode, acl);
+
+ if (dpacl)
+ posix_to_richacl(dpacl, ACL_TYPE_DEFAULT, inode->i_mode, acl);
+
+ return acl;
+}
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index f9089dc..b08fdf1 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -228,8 +228,11 @@ extern int richace_is_same_who(const struct richace *, const struct richace *);
extern int richace_set_who(struct richace *ace, const char *who);
extern struct richacl *richacl_inherit(const struct richacl *, mode_t);
extern int richacl_masks_to_mode(const struct richacl *);
+extern unsigned int richacl_mode_to_mask(mode_t mode);
extern struct richacl *richacl_chmod(struct richacl *, mode_t);
extern int richacl_apply_masks(struct richacl **acl);
extern int richacl_write_through(struct richacl **acl);
+extern struct richacl *map_posix_to_richacl(struct inode *, struct posix_acl *,
+ struct posix_acl *);

#endif /* __RICHACL_H */
--
1.7.0.rc0.48.gdace5


2010-02-01 08:05:22

by Brad Boyer

[permalink] [raw]
Subject: Re: [PATCH 03/23] vfs: rich ACL in-memory representation and manipulation


I have one suggestion about this part of the code.

On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:

<snip>

> +/*
> + * ACL entries that have ACE4_SPECIAL_WHO set in ace->e_flags use the
> + * pointer values of these constants in ace->u.e_who to avoid massive
> + * amounts of string comparisons.
> + */
> +
> +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);

<snip>

> +/*
> + * richace_is_same_who - do both acl entries refer to the same identifier?
> + */
> +int
> +richace_is_same_who(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);
> +
> +/**
> + * richacl_allowed_to_who - mask flags allowed to a specific who value
> + *
> + * Computes the mask values allowed to a specific who value, taking
> + * EVERYONE@ entries into account.
> + */
> +static unsigned int
> +richacl_allowed_to_who(struct richacl *acl, struct richace *who)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_same_who(ace, who) ||
> + richace_is_everyone(ace)) {
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_is_deny(ace))
> + allowed &= ~ace->e_mask;
> + }
> + }
> + return allowed;
> +}

<snip>

> +struct richace {
> + unsigned short e_type;
> + unsigned short e_flags;
> + unsigned int e_mask;
> + union {
> + unsigned int e_id;
> + const char *e_who;
> + } u;
> +};

<snip>

> +/* Special e_who identifiers: we use these pointer values in comparisons
> + instead of strcmp for efficiency. */
> +
> +extern const char richace_owner_who[];
> +extern const char richace_group_who[];
> +extern const char richace_everyone_who[];
> +
> +static inline int
> +richace_is_owner(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_owner_who;
> +}
> +
> +static inline int
> +richace_is_group(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_group_who;
> +}
> +
> +static inline int
> +richace_is_everyone(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_everyone_who;
> +}
> +
> +static inline int
> +richace_is_unix_id(const struct richace *ace)
> +{
> + return !(ace->e_flags & ACE4_SPECIAL_WHO);
> +}

Wouldn't it make more sense to just store some small numeric value
in ace->u.e_who rather than a pointer? It seems to me that the
savings of storing and comparing an integer type would more than
make up for the slight overhead of needing to lookup a pointer
in the places where the code must handle an external representation.
I know there is really only a difference on 64-bit systems, but my
impression has been that most people are going that way. In particular,
the struct richace would go to 12 bytes with 4-byte alignment from
16 bytes with 8-byte alignment on an architecture with 8-byte pointers.
Plus doing an integer instead of a pointer should eliminate the need
to export the constant pointer values. Even in 32-bit, there are a
few odd architectures (like m68k) that have separate address and
data registers, so using an integer may have some benefits there
due to the fact that the instruction set treats them differently.

I know you mentioned that you didn't originally write this code, but
it seems a logical change to me.

Brad Boyer
[email protected]


2010-02-01 18:02:59

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH 03/23] vfs: rich ACL in-memory representation and manipulation

On Sun, 31 Jan 2010 23:28:52 -0800, Brad Boyer <[email protected]> wrote:
>
> I have one suggestion about this part of the code.
>
> On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:
>
> <snip>
>
> > +/*
> > + * ACL entries that have ACE4_SPECIAL_WHO set in ace->e_flags use the
> > + * pointer values of these constants in ace->u.e_who to avoid massive
> > + * amounts of string comparisons.
> > + */
> > +
> > +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);
>
> <snip>
>
> > +/*
> > + * richace_is_same_who - do both acl entries refer to the same identifier?
> > + */
> > +int
> > +richace_is_same_who(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);
> > +
> > +/**
> > + * richacl_allowed_to_who - mask flags allowed to a specific who value
> > + *
> > + * Computes the mask values allowed to a specific who value, taking
> > + * EVERYONE@ entries into account.
> > + */
> > +static unsigned int
> > +richacl_allowed_to_who(struct richacl *acl, struct richace *who)
> > +{
> > + struct richace *ace;
> > + unsigned int allowed = 0;
> > +
> > + richacl_for_each_entry_reverse(ace, acl) {
> > + if (richace_is_inherit_only(ace))
> > + continue;
> > + if (richace_is_same_who(ace, who) ||
> > + richace_is_everyone(ace)) {
> > + if (richace_is_allow(ace))
> > + allowed |= ace->e_mask;
> > + else if (richace_is_deny(ace))
> > + allowed &= ~ace->e_mask;
> > + }
> > + }
> > + return allowed;
> > +}
>
> <snip>
>
> > +struct richace {
> > + unsigned short e_type;
> > + unsigned short e_flags;
> > + unsigned int e_mask;
> > + union {
> > + unsigned int e_id;
> > + const char *e_who;
> > + } u;
> > +};
>
> <snip>
>
> > +/* Special e_who identifiers: we use these pointer values in comparisons
> > + instead of strcmp for efficiency. */
> > +
> > +extern const char richace_owner_who[];
> > +extern const char richace_group_who[];
> > +extern const char richace_everyone_who[];
> > +
> > +static inline int
> > +richace_is_owner(const struct richace *ace)
> > +{
> > + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> > + ace->u.e_who == richace_owner_who;
> > +}
> > +
> > +static inline int
> > +richace_is_group(const struct richace *ace)
> > +{
> > + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> > + ace->u.e_who == richace_group_who;
> > +}
> > +
> > +static inline int
> > +richace_is_everyone(const struct richace *ace)
> > +{
> > + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> > + ace->u.e_who == richace_everyone_who;
> > +}
> > +
> > +static inline int
> > +richace_is_unix_id(const struct richace *ace)
> > +{
> > + return !(ace->e_flags & ACE4_SPECIAL_WHO);
> > +}
>
> Wouldn't it make more sense to just store some small numeric value
> in ace->u.e_who rather than a pointer? It seems to me that the
> savings of storing and comparing an integer type would more than
> make up for the slight overhead of needing to lookup a pointer
> in the places where the code must handle an external representation.
> I know there is really only a difference on 64-bit systems, but my
> impression has been that most people are going that way. In particular,
> the struct richace would go to 12 bytes with 4-byte alignment from
> 16 bytes with 8-byte alignment on an architecture with 8-byte pointers.
> Plus doing an integer instead of a pointer should eliminate the need
> to export the constant pointer values. Even in 32-bit, there are a
> few odd architectures (like m68k) that have separate address and
> data registers, so using an integer may have some benefits there
> due to the fact that the instruction set treats them differently.
>
> I know you mentioned that you didn't originally write this code, but
> it seems a logical change to me.

I guess id mapping needs more work in the patch. I would really like
to hear from both NFS and Samba people in how they would like the
id details to be stored. If we can't map an incoming user@domain
request on nfs, I guess we definitely don't want to store the acl with
'nobody' id

-aneesh

2010-02-01 23:06:45

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 03/23] vfs: rich ACL in-memory representation and manipulation

On Mon, Feb 01, 2010 at 11:32:59PM +0530, Aneesh Kumar K. V wrote:
> I guess id mapping needs more work in the patch. I would really like
> to hear from both NFS and Samba people in how they would like the
> id details to be stored. If we can't map an incoming user@domain
> request on nfs, I guess we definitely don't want to store the acl with
> 'nobody' id

I don't see the point in allowing the acl's to refer to arbitrary
user@domain strings unless we're also going to allow those strings as
file owners, allow processes to run *as* one of those strings, etc.

If we're really going to try to teach the core kernel to handle foreign
NFS or Samba identities, that's a separate project.

As long as the kernel's working with ordinary uid's and gid's, the acl's
should do the same, and NFS and Samba can take care of the conversion as
needed.

So I agree that we should be able to use a more compact representation
here.

--b.

2010-02-01 23:18:16

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 07/23] vfs: Add Posix acl to rich acl mapping helpers

On Mon, Feb 01, 2010 at 11:04:49AM +0530, Aneesh Kumar K.V wrote:
> This patch add helpers that can be used by the file system to map
> posix acls to rich acl format. This enables the file system to
> return rich acl mapping the posix acls stored on disk when the

You mean, to return a rich acl which is a mapped version of the posix
acl stored on disk?

> file system is enabled with rich acl format.

Then I assume if you modified the acl, the filesystem would replace
the existing posix acl by a "rich acl"?

The idea being to allow you to convert an existing posix-acl-using
filesystem to rich acl's? (But not the reverse.)

--b.

>
> Signed-off-by: Aneesh Kumar K.V <[email protected]>
> ---
> fs/Makefile | 2 +-
> fs/richacl_base.c | 2 +-
> fs/richacl_posix.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/richacl.h | 3 +
> 4 files changed, 228 insertions(+), 2 deletions(-)
> create mode 100644 fs/richacl_posix.c
>
> diff --git a/fs/Makefile b/fs/Makefile
> index 0a046a1..a5f3ee3 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -53,7 +53,7 @@ obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
>
> obj-$(CONFIG_FS_RICHACL) += richacl.o
> richacl-y := richacl_base.o richacl_xattr.o \
> - richacl_compat.o
> + richacl_compat.o richacl_posix.o
>
> obj-y += quota/
>
> diff --git a/fs/richacl_base.c b/fs/richacl_base.c
> index e75f713..a176399 100644
> --- a/fs/richacl_base.c
> +++ b/fs/richacl_base.c
> @@ -114,7 +114,7 @@ richacl_masks_to_mode(const struct richacl *acl)
> }
> EXPORT_SYMBOL_GPL(richacl_masks_to_mode);
>
> -static unsigned int
> +unsigned int
> richacl_mode_to_mask(mode_t mode)
> {
> unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED;
> diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
> new file mode 100644
> index 0000000..07db970
> --- /dev/null
> +++ b/fs/richacl_posix.c
> @@ -0,0 +1,223 @@
> +/*
> + * Copyright IBM Corporation, 2009
> + * 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/fs.h>
> +#include <linux/richacl.h>
> +#include <linux/posix_acl.h>
> +
> +static void posix_to_richacl(struct posix_acl *pacl, int type,
> + mode_t mode, struct richacl *acl)
> +{
> + int eflags;
> + struct richace *ace;
> + unsigned short deny;
> + struct posix_acl_entry *pa, *pe, *acl_other = NULL;
> +
> + if (type == ACL_TYPE_DEFAULT)
> + eflags = ACE4_FILE_INHERIT_ACE |
> + ACE4_DIRECTORY_INHERIT_ACE | ACE4_INHERIT_ONLY_ACE;
> + else
> + eflags = 0;
> +
> + BUG_ON(pacl->a_count < 3);
> + /* Go to the last saved entry */
> + ace = acl->a_entries + acl->a_count;
> + FOREACH_ACL_ENTRY(pa, pacl, pe) {
> + switch (pa->e_tag) {
> + case ACL_USER_OBJ:
> + {
> + /* Everything that is not granted we deny */
> + deny = ~pa->e_perm ;
> + if (deny & 0x7) {
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(deny);
> + richace_set_who(ace, richace_owner_who);
> + acl->a_count++;
> + ace++;
> + }
> + /* Add allow entry */
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(pa->e_perm);
> + ace->e_mask |= ACE4_WRITE_ATTRIBUTES | ACE4_WRITE_ACL;
> + richace_set_who(ace, richace_owner_who);
> + acl->a_count++;
> + ace++;
> + break;
> + }
> + case ACL_USER:
> + {
> + /* Everything that is not granted we deny */
> + deny = ~pa->e_perm ;
> + if (deny & 0x7) {
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(deny);
> + ace->u.e_id = pa->e_id;
> + acl->a_count++;
> + ace++;
> + }
> + /* Add allow entry */
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(pa->e_perm);
> + ace->u.e_id = pa->e_id;
> + acl->a_count++;
> + ace++;
> + break;
> +
> + }
> + case ACL_GROUP_OBJ:
> + {
> + /*
> + * In the case of group we apply allow first
> + * since a person can be in different group
> + */
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(pa->e_perm);
> + richace_set_who(ace, richace_group_who);
> + acl->a_count++;
> + ace++;
> + break;
> +
> + }
> + case ACL_GROUP:
> + {
> + /* Add allow entries only */
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = eflags | ACE4_IDENTIFIER_GROUP;
> + ace->e_mask = richacl_mode_to_mask(pa->e_perm);
> + ace->u.e_id = pa->e_id;
> + acl->a_count++;
> + ace++;
> + break;
> +
> + }
> + case ACL_MASK:
> + {
> + /*
> + * We can ignore ACL_MASK values. We derive the
> + * respective values from the inode mode values
> + */
> + break;
> + }
> + case ACL_OTHER:
> + {
> + /*
> + * We should precess ACL_OTHER only after getting all
> + * user and group pa and then adding there respective
> + * deny entries
> + */
> + acl_other = pa;
> + break;
> +
> + }
> + }
> + }
> + /*
> + * Now add the deny entries for ACL_GROUP and ACL_GROUP_OBJ entries
> + */
> + FOREACH_ACL_ENTRY(pa, pacl, pe) {
> + switch (pa->e_tag) {
> + case ACL_USER_OBJ:
> + case ACL_USER:
> + case ACL_MASK:
> + case ACL_OTHER:
> + break;
> + case ACL_GROUP_OBJ:
> + {
> + /* Everything that is not granted we deny */
> + deny = ~pa->e_perm ;
> + if (deny & 0x7) {
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(deny);
> + richace_set_who(ace, richace_group_who);
> + acl->a_count++;
> + ace++;
> + }
> + break;
> + }
> + case ACL_GROUP:
> + {
> + /* Everything that is not granted we deny */
> + deny = ~pa->e_perm ;
> + if (deny & 0x7) {
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = eflags | ACE4_IDENTIFIER_GROUP;
> + ace->e_mask = richacl_mode_to_mask(deny);
> + ace->u.e_id = pa->e_id;
> + acl->a_count++;
> + ace++;
> + }
> + break;
> + }
> + }
> + }
> + /* Now handle ACL_OTHER entry */
> + if (acl_other) {
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = eflags;
> + ace->e_mask = richacl_mode_to_mask(acl_other->e_perm);
> + richace_set_who(ace, richace_everyone_who);
> + acl->a_count++;
> + ace++;
> + }
> +
> + /* set acl mask values */
> + acl->a_owner_mask = richacl_mode_to_mask(mode >> 6);
> + acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
> + acl->a_other_mask = richacl_mode_to_mask(mode);
> +
> + return;
> +}
> +
> +struct richacl *map_posix_to_richacl(struct inode *inode,
> + struct posix_acl *pacl,
> + struct posix_acl *dpacl)
> +{
> +
> + struct richacl *acl;
> + int size = 0;
> +
> + if (pacl) {
> + if (posix_acl_valid(pacl) < 0)
> + return ERR_PTR(-EINVAL);
> + size += 2*pacl->a_count;
> + }
> +
> + if (dpacl) {
> + if (posix_acl_valid(dpacl) < 0)
> + return ERR_PTR(-EINVAL);
> + size += 2*dpacl->a_count;
> + }
> +
> + /* Allocate the worst case one deny, allow pair each */
> + acl = richacl_alloc(size);
> + if (acl == NULL)
> + return ERR_PTR(-ENOMEM);
> +
> + /* We will count actual number of entries when we map */
> + acl->a_count = 0;
> + if (pacl)
> + posix_to_richacl(pacl, ACL_TYPE_ACCESS , inode->i_mode, acl);
> +
> + if (dpacl)
> + posix_to_richacl(dpacl, ACL_TYPE_DEFAULT, inode->i_mode, acl);
> +
> + return acl;
> +}
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index f9089dc..b08fdf1 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -228,8 +228,11 @@ extern int richace_is_same_who(const struct richace *, const struct richace *);
> extern int richace_set_who(struct richace *ace, const char *who);
> extern struct richacl *richacl_inherit(const struct richacl *, mode_t);
> extern int richacl_masks_to_mode(const struct richacl *);
> +extern unsigned int richacl_mode_to_mask(mode_t mode);
> extern struct richacl *richacl_chmod(struct richacl *, mode_t);
> extern int richacl_apply_masks(struct richacl **acl);
> extern int richacl_write_through(struct richacl **acl);
> +extern struct richacl *map_posix_to_richacl(struct inode *, struct posix_acl *,
> + struct posix_acl *);
>
> #endif /* __RICHACL_H */
> --
> 1.7.0.rc0.48.gdace5
>

2010-02-01 23:18:58

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 08/23] vfs: Add a flag to denote posix mapped richacl

On Mon, Feb 01, 2010 at 11:04:50AM +0530, Aneesh Kumar K.V wrote:
> Signed-off-by: Aneesh Kumar K.V <[email protected]>
> ---
> fs/richacl_posix.c | 7 +++++++
> include/linux/richacl.h | 10 ++++++----
> 2 files changed, 13 insertions(+), 4 deletions(-)
>
> diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
> index 07db970..3cf2124 100644
> --- a/fs/richacl_posix.c
> +++ b/fs/richacl_posix.c
> @@ -183,6 +183,13 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
> acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
> acl->a_other_mask = richacl_mode_to_mask(mode);
>
> + /*
> + * Mark that the acl as mapped from posix
> + * This gives user space the chance to verify
> + * whether the mapping was correct
> + */

How would it use this information? (And how could it be incorrect?)

--b.

> + acl->a_flags |= ACL4_POSIX_MAPPED;
> +
> return;
> }
>
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index b08fdf1..41d93d8 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -36,12 +36,14 @@ struct richacl {
> #define ACL4_AUTO_INHERIT 0x01
> #define ACL4_PROTECTED 0x02
> #define ACL4_DEFAULTED 0x04
> +#define ACL4_POSIX_MAPPED 0x10
> #define ACL4_WRITE_THROUGH 0x40
>
> -#define ACL4_VALID_FLAGS ( \
> - ACL4_AUTO_INHERIT | \
> - ACL4_PROTECTED | \
> - ACL4_DEFAULTED | \
> +#define ACL4_VALID_FLAGS ( \
> + ACL4_AUTO_INHERIT | \
> + ACL4_PROTECTED | \
> + ACL4_DEFAULTED | \
> + ACL4_POSIX_MAPPED | \
> ACL4_WRITE_THROUGH)
>
> /* e_type values */
> --
> 1.7.0.rc0.48.gdace5
>

2010-02-01 23:21:41

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 03/23] vfs: rich ACL in-memory representation and manipulation

On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:
> From: Andreas Gruenbacher <[email protected]>
>
> * In-memory representation (struct richacl).
> * Functionality a filesystem needs such as permission checking,
> apply mode to acl, compute mode from acl, inheritance upon file
> create.
> * Compute a mask-less acl from struct richacl that grants the same
> permissions. Protocols which don't understand the masks need
> this.

The mask calculations are a little complicated. I'd like to review
them, but I think I'd need to sit down and think hard about it for a day
or so, and I'm not sure when.

--b.

> * Convert to/from xattrs.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> Signed-off-by: Aneesh Kumar K.V <[email protected]>
> ---
> fs/Kconfig | 4 +
> fs/Makefile | 4 +
> fs/richacl_base.c | 568 +++++++++++++++++++++++++++++++
> fs/richacl_compat.c | 757 +++++++++++++++++++++++++++++++++++++++++
> fs/richacl_xattr.c | 146 ++++++++
> include/linux/richacl.h | 208 +++++++++++
> include/linux/richacl_xattr.h | 32 ++
> 7 files changed, 1719 insertions(+), 0 deletions(-)
> create mode 100644 fs/richacl_base.c
> create mode 100644 fs/richacl_compat.c
> create mode 100644 fs/richacl_xattr.c
> create mode 100644 include/linux/richacl.h
> create mode 100644 include/linux/richacl_xattr.h
>
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 64d44ef..adbda7c 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -39,6 +39,10 @@ config FS_POSIX_ACL
> bool
> default n
>
> +config FS_RICHACL
> + bool
> + default n
> +
> source "fs/xfs/Kconfig"
> source "fs/gfs2/Kconfig"
> source "fs/ocfs2/Kconfig"
> diff --git a/fs/Makefile b/fs/Makefile
> index af6d047..0a046a1 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -51,6 +51,10 @@ 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 richacl_xattr.o \
> + richacl_compat.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..de99340
> --- /dev/null
> +++ b/fs/richacl_base.c
> @@ -0,0 +1,568 @@
> +/*
> + * Copyright (C) 2006 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");
> +
> +/*
> + * ACL entries that have ACE4_SPECIAL_WHO set in ace->e_flags use the
> + * pointer values of these constants in ace->u.e_who to avoid massive
> + * amounts of string comparisons.
> + */
> +
> +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 an acl
> + * @count: number of entries
> + */
> +struct richacl *
> +richacl_alloc(int count)
> +{
> + size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
> + struct richacl *acl = kmalloc(size, GFP_KERNEL);
> +
> + if (acl) {
> + memset(acl, 0, size);
> + atomic_set(&acl->a_refcount, 1);
> + acl->a_count = count;
> + }
> + return acl;
> +}
> +EXPORT_SYMBOL_GPL(richacl_alloc);
> +
> +/**
> + * richacl_clone - create a copy of an acl
> + */
> +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;
> +}
> +
> +/*
> + * The POSIX permissions are supersets of the below mask flags.
> + *
> + * The ACE4_READ_ATTRIBUTES and ACE4_READ_ACL flags are always granted
> + * in POSIX. The ACE4_SYNCHRONIZE flag has no meaning under POSIX. We
> + * make sure that we do not mask them if they are set, so that users who
> + * rely on these flags won't get confused.
> + */
> +#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)
> +
> +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 file mode permission bits from file masks
> + *
> + * Compute the file mode permission bits from the file masks in the acl.
> + */
> +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);
> +
> +static 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_chmod - update the file masks to reflect the new mode
> + * @mode: file mode permission bits to apply to the @acl
> + *
> + * Converts the mask flags corresponding to the owner, group, and other file
> + * permissions and computes the file masks. Returns @acl if it already has the
> + * appropriate file masks, or updates the flags in a copy of @acl. Takes over
> + * @acl.
> + */
> +struct richacl *
> +richacl_chmod(struct richacl *acl, mode_t mode)
> +{
> + unsigned int owner_mask, group_mask, other_mask;
> + struct richacl *clone;
> +
> + owner_mask = richacl_mode_to_mask(mode >> 6);
> + group_mask = richacl_mode_to_mask(mode >> 3);
> + other_mask = richacl_mode_to_mask(mode);
> +
> + if (acl->a_owner_mask == owner_mask &&
> + acl->a_group_mask == group_mask &&
> + acl->a_other_mask == other_mask)
> + return acl;
> +
> + clone = richacl_clone(acl);
> + richacl_put(acl);
> + if (!clone)
> + return ERR_PTR(-ENOMEM);
> +
> + clone->a_owner_mask = owner_mask;
> + clone->a_group_mask = group_mask;
> + clone->a_other_mask = other_mask;
> +
> + if (richacl_write_through(&clone)) {
> + richacl_put(clone);
> + clone = ERR_PTR(-ENOMEM);
> + }
> + return clone;
> +}
> +EXPORT_SYMBOL_GPL(richacl_chmod);
> +
> +/**
> + * richacl_want_to_mask - convert permission want argument to a mask
> + * @want: @want argument of the permission inode operation
> + *
> + * When checking for append, @want is (MAY_WRITE | MAY_APPEND).
> + */
> +unsigned int
> +richacl_want_to_mask(int want)
> +{
> + unsigned int mask = 0;
> +
> + if (want & MAY_READ)
> + mask |= ACE4_READ_DATA;
> + if (want & MAY_APPEND)
> + mask |= ACE4_APPEND_DATA;
> + 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);
> +
> +/**
> + * richacl_capability_check -check for capabilities overriding read/write access
> + * @inode: inode to check
> + * @mask: requested access (ACE4_* bitmask)
> + *
> + * Capabilities other than CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH
> + * must be checked separately.
> + */
> +static inline int richacl_capability_check(struct inode *inode,
> + unsigned int mask)
> +{
> + /*
> + * Read/write DACs are always overridable.
> + * Executable DACs are overridable if at least one exec bit is set.
> + */
> + if (!(mask & (ACE4_WRITE_ACL | ACE4_WRITE_OWNER)) &&
> + (!(mask & ACE4_EXECUTE) ||
> + (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)))
> + if (capable(CAP_DAC_OVERRIDE))
> + return 0;
> +
> + /*
> + * Searching includes executable on directories, else just read.
> + */
> + if (!(mask & ~(ACE4_READ_DATA | ACE4_EXECUTE)) &&
> + (S_ISDIR(inode->i_mode) || !(mask & ACE4_EXECUTE)))
> + if (capable(CAP_DAC_READ_SEARCH))
> + return 0;
> +
> + return -EACCES;
> +}
> +
> +/**
> + * richacl_permission - permission check algorithm with masking
> + * @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. With
> + * write-through, the OWNER@ is always granted the owner file mask, the
> + * GROUP@ is always granted the group file mask, and EVERYONE@ is always
> + * granted the other file mask. Otherwise, processes are only granted
> + * @mask flags which they are granted in the @acl as well as in their
> + * file mask.
> + */
> +int richacl_permission(struct inode *inode, const struct richacl *acl,
> + unsigned int mask)
> +{
> + const struct richace *ace;
> + unsigned int file_mask, requested = mask, denied = 0;
> + int in_owning_group = in_group_p(inode->i_gid);
> + int owner_or_group_class = in_owning_group;
> +
> + /*
> + * 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.
> + */
> +
> + 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;
> +
> + /*
> + * Apply the group file mask to entries other than OWNER@ and
> + * EVERYONE@. This is not required for correct access checking
> + * but ensures that we grant the same permissions as the acl
> + * computed by richacl_apply_masks().
> + *
> + * For example, without this restriction, 'group@:rw::allow'
> + * with mode 0600 would grant rw access to owner processes
> + * which are also in the owning group. This cannot be expressed
> + * in an acl.
> + */
> + if (richace_is_allow(ace))
> + ace_mask &= acl->a_group_mask;
> +
> +is_owner:
> + /* The process is in the owner or group file class. */
> + 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 && owner_or_group_class)
> + break;
> + }
> + denied |= mask;
> +
> + /*
> + * Figure out which file mask applies.
> + * Clear write-through if the process is in the file group class but
> + * not in the owning group, and so the denied permissions apply.
> + */
> + if (current_fsuid() == inode->i_uid)
> + file_mask = acl->a_owner_mask;
> + else if (in_owning_group || owner_or_group_class)
> + file_mask = acl->a_group_mask;
> + else
> + file_mask = acl->a_other_mask;
> +
> + denied |= requested & ~file_mask;
> + if (!denied)
> + return 0;
> + return richacl_capability_check(inode, requested);
> +}
> +EXPORT_SYMBOL_GPL(richacl_permission);
> +
> +/**
> + * richacl_generic_permission - permission check algorithm without explicit acl
> + * @inode: inode to check permissions for
> + * @mask: requested access (ACE4_* bitmask)
> + *
> + * The file mode of a file without ACL corresponds to an ACL with a single
> + * "EVERYONE:~0::ALLOW" entry, with file masks that correspond to the file mode
> + * permissions. Instead of constructing a temporary ACL and applying
> + * richacl_permission() to it, compute the identical result directly from
> + * the file mode.
> + */
> +int richacl_generic_permission(struct inode *inode, unsigned int mask)
> +{
> + int mode = inode->i_mode;
> +
> + if (current_fsuid() == inode->i_uid)
> + mode >>= 6;
> + else if (in_group_p(inode->i_gid))
> + mode >>= 3;
> + if (!(mask & ~richacl_mode_to_mask(mode)))
> + return 0;
> + return richacl_capability_check(inode, mask);
> +}
> +EXPORT_SYMBOL_GPL(richacl_generic_permission);
> +
> +/*
> + * richace_is_same_who - do both acl entries refer to the same identifier?
> + */
> +int
> +richace_is_same_who(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);
> +
> +/**
> + * richacl_allowed_to_who - mask flags allowed to a specific who value
> + *
> + * Computes the mask values allowed to a specific who value, taking
> + * EVERYONE@ entries into account.
> + */
> +static unsigned int
> +richacl_allowed_to_who(struct richacl *acl, struct richace *who)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_same_who(ace, who) ||
> + richace_is_everyone(ace)) {
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_is_deny(ace))
> + allowed &= ~ace->e_mask;
> + }
> + }
> + return allowed;
> +}
> +
> +/**
> + * richacl_compute_max_masks - compute upper bound masks
> + *
> + * Computes upper bound owner, group, and other masks so that none of
> + * the mask flags allowed by the acl are disabled (for any choice of the
> + * file owner or group membership).
> + */
> +static void
> +richacl_compute_max_masks(struct richacl *acl)
> +{
> + struct richace *ace;
> +
> + acl->a_owner_mask = 0;
> + acl->a_group_mask = 0;
> + acl->a_other_mask = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> +
> + if (richace_is_owner(ace)) {
> + if (richace_is_allow(ace))
> + acl->a_owner_mask |= ace->e_mask;
> + else if (richace_is_deny(ace))
> + acl->a_owner_mask &= ~ace->e_mask;
> + } else if (richace_is_everyone(ace)) {
> + if (richace_is_allow(ace)) {
> + struct richace who = {
> + .e_flags = ACE4_SPECIAL_WHO,
> + .u.e_who = richace_group_who,
> + };
> +
> + acl->a_other_mask |= ace->e_mask;
> + acl->a_group_mask |=
> + richacl_allowed_to_who(acl, &who);
> + acl->a_owner_mask |= ace->e_mask;
> + } else if (richace_is_deny(ace)) {
> + acl->a_other_mask &= ~ace->e_mask;
> + acl->a_group_mask &= ~ace->e_mask;
> + acl->a_owner_mask &= ~ace->e_mask;
> + }
> + } else {
> + if (richace_is_allow(ace)) {
> + unsigned int mask =
> + richacl_allowed_to_who(acl, ace);
> +
> + acl->a_group_mask |= mask;
> + acl->a_owner_mask |= mask;
> + }
> + }
> + }
> +}
> +
> +/**
> + * richacl_inherit - compute the acl a new file will inherit
> + * @dir_acl: acl of the containing direcory
> + * @mode: file type and create mode of the new file
> + *
> + * Given the containing directory's acl, this function will compute the
> + * acl that new files in that directory will inherit, or %NULL if
> + * @dir_acl does not contain acl entries inheritable by this file.
> + *
> + * Without write-through, the file masks in the returned acl are set to
> + * the intersection of the create mode and the maximum permissions
> + * allowed to each file class. With write-through, the file masks are
> + * set to the create mode.
> + */
> +struct richacl *
> +richacl_inherit(const struct richacl *dir_acl, mode_t mode)
> +{
> + const struct richace *dir_ace;
> + struct richacl *acl;
> + struct richace *ace;
> + int count = 0;
> +
> + if (S_ISDIR(mode)) {
> + 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);
> + ace++;
> + }
> + }
> +
> + /* The maximum max flags that the owner, group, and other classes
> + are allowed. */
> + if (dir_acl->a_flags & ACL4_WRITE_THROUGH) {
> + acl->a_owner_mask = ACE4_VALID_MASK;
> + acl->a_group_mask = ACE4_VALID_MASK;
> + acl->a_other_mask = ACE4_VALID_MASK;
> +
> + mode &= ~current_umask();
> + } else
> + richacl_compute_max_masks(acl);
> +
> + /* Apply the create mode. */
> + acl->a_owner_mask &= richacl_mode_to_mask(mode >> 6);
> + acl->a_group_mask &= richacl_mode_to_mask(mode >> 3);
> + acl->a_other_mask &= richacl_mode_to_mask(mode);
> +
> + if (richacl_write_through(&acl)) {
> + richacl_put(acl);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH);
> +
> + return acl;
> +}
> +EXPORT_SYMBOL_GPL(richacl_inherit);
> diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
> new file mode 100644
> index 0000000..a985bbf
> --- /dev/null
> +++ b/fs/richacl_compat.c
> @@ -0,0 +1,757 @@
> +/*
> + * Copyright (C) 2006 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/module.h>
> +#include <linux/fs.h>
> +#include <linux/richacl.h>
> +
> +/**
> + * struct richacl_alloc - remember how many entries are actually allocated
> + * @acl: acl with a_count <= @count
> + * @count: the actual number of entries allocated in @acl
> + *
> + * We pass around this structure while modifying an acl, so that we do
> + * not have to reallocate when we remove existing entries followed by
> + * adding new entries.
> + */
> +struct richacl_alloc {
> + struct richacl *acl;
> + unsigned int count;
> +};
> +
> +/**
> + * richacl_delete_entry - delete an entry in an acl
> + * @x: acl and number of allocated entries
> + * @ace: an entry in @x->acl
> + *
> + * Updates @ace so that it points to the entry before the deleted entry
> + * on return. (When deleting the first entry, @ace will point to the
> + * (non-existant) entry before the first entry). This behavior is the
> + * expected behavior when deleting entries while forward iterating over
> + * an acl.
> + */
> +static void
> +richacl_delete_entry(struct richacl_alloc *x, struct richace **ace)
> +{
> + void *end = x->acl->a_entries + x->acl->a_count;
> +
> + memmove(*ace, *ace + 1, end - (void *)(*ace + 1));
> + (*ace)--;
> + x->acl->a_count--;
> +}
> +
> +/**
> + * richacl_insert_entry - insert an entry in an acl
> + * @x: acl and number of allocated entries
> + * @ace: entry before which the new entry shall be inserted
> + *
> + * Insert a new entry in @x->acl at position @ace, and zero-initialize
> + * it. This may require reallocating @x->acl.
> + */
> +static int
> +richacl_insert_entry(struct richacl_alloc *x, struct richace **ace)
> +{
> + if (x->count == x->acl->a_count) {
> + int n = *ace - x->acl->a_entries;
> + struct richacl *acl2;
> +
> + acl2 = richacl_alloc(x->acl->a_count + 1);
> + if (!acl2)
> + return -1;
> + acl2->a_flags = x->acl->a_flags;
> + acl2->a_owner_mask = x->acl->a_owner_mask;
> + acl2->a_group_mask = x->acl->a_group_mask;
> + acl2->a_other_mask = x->acl->a_other_mask;
> + memcpy(acl2->a_entries, x->acl->a_entries,
> + n * sizeof(struct richace));
> + memcpy(acl2->a_entries + n + 1, *ace,
> + (x->acl->a_count - n) * sizeof(struct richace));
> + kfree(x->acl);
> + x->acl = acl2;
> + x->count = acl2->a_count;
> + *ace = acl2->a_entries + n;
> + } else {
> + void *end = x->acl->a_entries + x->acl->a_count;
> +
> + memmove(*ace + 1, *ace, end - (void *)*ace);
> + x->acl->a_count++;
> + }
> + memset(*ace, 0, sizeof(struct richace));
> + return 0;
> +}
> +
> +/**
> + * richace_change_mask - change the mask in @ace to @mask
> + * @x: acl and number of allocated entries
> + * @ace: entry to modify
> + * @mask: new mask for @ace
> + *
> + * Set the effective mask of @ace to @mask. This will require splitting
> + * off a separate acl entry if @ace is inheritable. In that case, the
> + * effective- only acl entry is inserted after the inheritable acl
> + * entry, end the inheritable acl entry is set to inheritable-only. If
> + * @mode is 0, either set the original acl entry to inheritable-only if
> + * it was inheritable, or remove it otherwise. The returned @ace points
> + * to the modified or inserted effective-only acl entry if that entry
> + * exists, to the entry that has become inheritable-only, or else to the
> + * previous entry in the acl. This is the expected behavior when
> + * modifying masks while forward iterating over an acl.
> + */
> +static int
> +richace_change_mask(struct richacl_alloc *x, struct richace **ace,
> + unsigned int mask)
> +{
> + if (mask && (*ace)->e_mask == mask)
> + return 0;
> + if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) {
> + if (richace_is_inheritable(*ace)) {
> + if (richacl_insert_entry(x, ace))
> + return -1;
> + memcpy(*ace, *ace + 1, sizeof(struct richace));
> + (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
> + (*ace)++;
> + richace_clear_inheritance_flags(*ace);
> + }
> + (*ace)->e_mask = mask;
> + } else {
> + if (richace_is_inheritable(*ace))
> + (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
> + else
> + richacl_delete_entry(x, ace);
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_move_everyone_aces_down - move everyone@ acl entries to the end
> + * @x: acl and number of allocated entries
> + *
> + * Move all everyone acl entries to the bottom of the acl so that only a
> + * single everyone@ allow acl entry remains at the end, and update the
> + * mask fields of all acl entries on the way. If everyone@ is not
> + * granted any permissions, no empty everyone@ acl entry is inserted.
> + *
> + * This transformation does not modify the permissions that the acl
> + * grants, but we need it to simplify successive transformations.
> + */
> +static int
> +richacl_move_everyone_aces_down(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0, denied = 0;
> +
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_everyone(ace)) {
> + if (richace_is_allow(ace))
> + allowed |= (ace->e_mask & ~denied);
> + else if (richace_is_deny(ace))
> + denied |= (ace->e_mask & ~allowed);
> + else
> + continue;
> + if (richace_change_mask(x, &ace, 0))
> + return -1;
> + } else {
> + if (richace_is_allow(ace)) {
> + if (richace_change_mask(x, &ace, allowed |
> + (ace->e_mask & ~denied)))
> + return -1;
> + } else if (richace_is_deny(ace)) {
> + if (richace_change_mask(x, &ace, denied |
> + (ace->e_mask & ~allowed)))
> + return -1;
> + }
> + }
> + }
> + if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
> + struct richace *last_ace = ace - 1;
> +
> + if (richace_is_everyone(last_ace) &&
> + richace_is_allow(last_ace) &&
> + richace_is_inherit_only(last_ace) &&
> + last_ace->e_mask == allowed)
> + last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE;
> + else {
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = ACE4_SPECIAL_WHO;
> + ace->e_mask = allowed;
> + ace->u.e_who = richace_everyone_who;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_propagate_everyone - propagate everyone@ mask flags up for @who
> + * @x: acl and number of allocated entries
> + * @who: identifier to propagate mask flags for
> + * @allow: mask flags to propagate up
> + *
> + * Propagate mask flags from the trailing everyone@ allow acl entry up
> + * for the specified @who.
> + *
> + * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an
> + * additional @who ALLOW entry, but with the following optimizations:
> + * (1) we don't bother setting any flags in the new @who ALLOW entry
> + * that has already been allowed or denied by a previous @who entry, (2)
> + * we merge the new @who entry with a previous @who entry if there is
> + * such a previous @who entry and there are no intervening DENY entries
> + * with mask flags that overlap the flags we care about.
> + */
> +static int
> +__richacl_propagate_everyone(struct richacl_alloc *x, struct richace *who,
> + unsigned int allow)
> +{
> + struct richace *allow_last = NULL, *ace;
> +
> + /* Remove the mask flags from allow that are already determined for
> + this who value, and figure out if there is an ALLOW entry for
> + this who value that is "reachable" from the trailing EVERYONE@
> + ALLOW ACE. */
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace)) {
> + if (richace_is_same_who(ace, who)) {
> + allow &= ~ace->e_mask;
> + allow_last = ace;
> + }
> + } else if (richace_is_deny(ace)) {
> + if (richace_is_same_who(ace, who))
> + allow &= ~ace->e_mask;
> + if (allow & ace->e_mask)
> + allow_last = NULL;
> + }
> + }
> +
> + if (allow) {
> + if (allow_last)
> + return richace_change_mask(x, &allow_last,
> + allow_last->e_mask | allow);
> + else {
> + struct richace who_copy;
> +
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + memcpy(&who_copy, who, sizeof(struct richace));
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + memcpy(ace, &who_copy, sizeof(struct richace));
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + richace_clear_inheritance_flags(ace);
> + ace->e_mask = allow;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_propagate_everyone - propagate everyone@ mask flags up the acl
> + * @x: acl and number of allocated entries
> + *
> + * Make sure for owner@, group@, and all other users, groups, and
> + * special identifiers that they are allowed or denied all permissions
> + * that are granted be the trailing everyone@ acl entry. If they are
> + * not, try to add the missing permissions to existing allow acl entries
> + * for those users, or introduce additional acl entries if that is not
> + * possible.
> + *
> + * We do this so that no mask flags will get lost when finally applying
> + * the file masks to the acl entries: otherwise, with an other file mask
> + * that is more restrictive than the owner and/or group file mask, mask
> + * flags that were allowed to processes in the owner and group classes
> + * and that the other mask denies would be lost. For example, the
> + * following two acls show the problem when mode 0664 is applied to
> + * them:
> + *
> + * masking without propagation (wrong)
> + * ===========================================================
> + * joe:r::allow => joe:r::allow
> + * everyone@:rwx::allow => everyone@:r::allow
> + * -----------------------------------------------------------
> + * joe:w::deny => joe:w::deny
> + * everyone@:rwx::allow everyone@:r::allow
> + *
> + * Note that the permissions of joe end up being more restrictive than
> + * what the acl would allow when first computing the allowed flags and
> + * then applying the respective mask. With propagation of permissions,
> + * we get:
> + *
> + * masking after propagation (correct)
> + * ===========================================================
> + * joe:r::allow => joe:rw::allow
> + * owner@:rw::allow
> + * group@:rw::allow
> + * everyone@:rwx::allow everyone@:r::allow
> + * -----------------------------------------------------------
> + * joe:w::deny => owner@:x::deny
> + * joe:w::deny
> + * owner@:rw::allow
> + * owner@:rw::allow
> + * joe:r::allow
> + * everyone@:rwx::allow everyone@:r::allow
> + *
> + * The examples show the acls that would result from propagation with no
> + * masking performed. In fact, we do apply the respective mask to the
> + * acl entries before computing the propagation because this will save
> + * us from adding acl entries that would end up with empty mask fields
> + * after applying the masks.
> + *
> + * It is ensured that no more than one entry will be inserted for each
> + * who value, no matter how many entries each who value has already.
> + */
> +static int
> +richacl_propagate_everyone(struct richacl_alloc *x)
> +{
> + int write_through = (x->acl->a_flags & ACL4_WRITE_THROUGH);
> + struct richace who = { .e_flags = ACE4_SPECIAL_WHO };
> + struct richace *ace;
> + unsigned int owner_allow, group_allow;
> + int retval;
> +
> + if (!((x->acl->a_owner_mask | x->acl->a_group_mask) &
> + ~x->acl->a_other_mask))
> + return 0;
> + if (!x->acl->a_count)
> + return 0;
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
> + return 0;
> + if (!(ace->e_mask & ~x->acl->a_other_mask)) {
> + /* None of the allowed permissions will get masked. */
> + return 0;
> + }
> + owner_allow = ace->e_mask & x->acl->a_owner_mask;
> + group_allow = ace->e_mask & x->acl->a_group_mask;
> +
> + /* Propagate everyone@ permissions through to owner@. */
> + if (owner_allow && !write_through &&
> + (x->acl->a_owner_mask & ~x->acl->a_other_mask)) {
> + who.u.e_who = richace_owner_who;
> + retval = __richacl_propagate_everyone(x, &who, owner_allow);
> + if (retval)
> + return -1;
> + }
> +
> + if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) {
> + int n;
> +
> + if (!write_through) {
> + /* Propagate everyone@ permissions through to group@. */
> + who.u.e_who = richace_group_who;
> + retval = __richacl_propagate_everyone(x, &who,
> + group_allow);
> + if (retval)
> + return -1;
> + }
> +
> + /* Start from the entry before the trailing EVERYONE@ ALLOW
> + entry. We will not hit EVERYONE@ entries in the loop. */
> + for (n = x->acl->a_count - 2; n != -1; n--) {
> + ace = x->acl->a_entries + n;
> +
> + if (richace_is_inherit_only(ace) ||
> + richace_is_owner(ace) ||
> + richace_is_group(ace))
> + continue;
> + if (richace_is_allow(ace) || richace_is_deny(ace)) {
> + /* Any inserted entry will end up below the
> + current entry. */
> + retval = __richacl_propagate_everyone(x, ace,
> + group_allow);
> + if (retval)
> + return -1;
> + }
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_apply_masks - apply the masks to the acl entries
> + * @x: acl and number of allocated entries
> + *
> + * Apply the owner file mask to owner@ entries, the intersection of the
> + * group and other file masks to everyone@ entries, and the group file
> + * mask to all other entries.
> + */
> +static int
> +__richacl_apply_masks(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> +
> + richacl_for_each_entry(ace, x->acl) {
> + unsigned int mask;
> +
> + if (richace_is_inherit_only(ace) || !richace_is_allow(ace))
> + continue;
> + if (richace_is_owner(ace))
> + mask = x->acl->a_owner_mask;
> + else if (richace_is_everyone(ace))
> + mask = x->acl->a_other_mask;
> + else
> + mask = x->acl->a_group_mask;
> + if (richace_change_mask(x, &ace, ace->e_mask & mask))
> + return -1;
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_max_allowed - maximum mask flags that anybody is allowed
> + */
> +static unsigned int
> +richacl_max_allowed(struct richacl *acl)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_is_deny(ace)) {
> + if (richace_is_everyone(ace))
> + allowed &= ~ace->e_mask;
> + }
> + }
> + return allowed;
> +}
> +
> +/**
> + * richacl_isolate_owner_class - limit the owner class to the owner file mask
> + * @x: acl and number of allocated entries
> + *
> + * Make sure the owner class (owner@) is granted no more than the owner
> + * mask by first checking which permissions anyone is granted, and then
> + * denying owner@ all permissions beyond that.
> + */
> +static int
> +richacl_isolate_owner_class(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + allowed = richacl_max_allowed(x->acl);
> + if (allowed & ~x->acl->a_owner_mask) {
> + /* Figure out if we can update an existig OWNER@ DENY entry. */
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_deny(ace)) {
> + if (richace_is_owner(ace))
> + break;
> + } else if (richace_is_allow(ace)) {
> + ace = x->acl->a_entries + x->acl->a_count;
> + break;
> + }
> + }
> + if (ace != x->acl->a_entries + x->acl->a_count) {
> + if (richace_change_mask(x, &ace, ace->e_mask |
> + (allowed & ~x->acl->a_owner_mask)))
> + return -1;
> + } else {
> + /* Insert an owner@ deny entry at the front. */
> + ace = x->acl->a_entries;
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = ACE4_SPECIAL_WHO;
> + ace->e_mask = allowed & ~x->acl->a_owner_mask;
> + ace->u.e_who = richace_owner_who;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_isolate_who - isolate entry from EVERYONE@ ALLOW entry
> + * @x: acl and number of allocated entries
> + * @who: identifier to isolate
> + * @deny: mask flags this identifier should not be allowed
> + *
> + * Make sure that @who is not allowed any mask flags in @deny by checking
> + * which mask flags this identifier is allowed, and adding excess allowed
> + * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW
> + * entry, or inserting such an entry.
> + */
> +static int
> +__richacl_isolate_who(struct richacl_alloc *x, struct richace *who,
> + unsigned int deny)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0, n;
> +
> + /* Compute the mask flags granted to this who value. */
> + richacl_for_each_entry_reverse(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_same_who(ace, who)) {
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_is_deny(ace))
> + allowed &= ~ace->e_mask;
> + deny &= ~ace->e_mask;
> + }
> + }
> + if (!deny)
> + return 0;
> +
> + /* Figure out if we can update an existig DENY entry. Start
> + from the entry before the trailing EVERYONE@ ALLOW entry. We
> + will not hit EVERYONE@ entries in the loop. */
> + for (n = x->acl->a_count - 2; n != -1; n--) {
> + ace = x->acl->a_entries + n;
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_deny(ace)) {
> + if (richace_is_same_who(ace, who))
> + break;
> + } else if (richace_is_allow(ace) &&
> + (ace->e_mask & deny)) {
> + n = -1;
> + break;
> + }
> + }
> + if (n != -1) {
> + if (richace_change_mask(x, &ace, ace->e_mask | deny))
> + return -1;
> + } else {
> + /* Insert a eny entry before the trailing EVERYONE@ DENY
> + entry. */
> + struct richace who_copy;
> +
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + memcpy(&who_copy, who, sizeof(struct richace));
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + memcpy(ace, &who_copy, sizeof(struct richace));
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + richace_clear_inheritance_flags(ace);
> + ace->e_mask = deny;
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_isolate_group_class - limit the group class to the group file mask
> + * @x: acl and number of allocated entries
> + *
> + * Make sure the group class (all entries except owner@ and everyone@) is
> + * granted no more than the group mask by inserting DENY entries for group
> + * class entries where necessary.
> + */
> +static int
> +richacl_isolate_group_class(struct richacl_alloc *x)
> +{
> + struct richace who = {
> + .e_flags = ACE4_SPECIAL_WHO,
> + .u.e_who = richace_group_who,
> + };
> + struct richace *ace;
> + unsigned int deny;
> +
> + if (!x->acl->a_count)
> + return 0;
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
> + return 0;
> + deny = ace->e_mask & ~x->acl->a_group_mask;
> +
> + if (deny) {
> + unsigned int n;
> +
> + if (__richacl_isolate_who(x, &who, deny))
> + return -1;
> +
> + /* Start from the entry before the trailing EVERYONE@ ALLOW
> + entry. We will not hit EVERYONE@ entries in the loop. */
> + for (n = x->acl->a_count - 2; n != -1; n--) {
> + ace = x->acl->a_entries + n;
> +
> + if (richace_is_inherit_only(ace) ||
> + richace_is_owner(ace) ||
> + richace_is_group(ace))
> + continue;
> + if (__richacl_isolate_who(x, ace, deny))
> + return -1;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_write_through - grant the full masks to owner@, group@, everyone@
> + *
> + * Make sure that owner, group@, and everyone@ are allowed the full mask
> + * permissions, and not only the permissions granted both by the acl and
> + * the masks.
> + */
> +static int
> +__richacl_write_through(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> + unsigned int allowed;
> +
> + /* Remove all owner@ and group@ ACEs: we re-insert them at the
> + top. */
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if ((richace_is_owner(ace) || richace_is_group(ace)) &&
> + richace_change_mask(x, &ace, 0))
> + return -1;
> + }
> +
> + /* Insert the everyone@ allow entry at the end, or update the
> + existing entry. */
> + allowed = x->acl->a_other_mask;
> + if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + if (x->acl->a_count && richace_is_everyone(ace) &&
> + !richace_is_inherit_only(ace)) {
> + if (richace_change_mask(x, &ace, allowed))
> + return -1;
> + } else {
> + ace = x->acl->a_entries + x->acl->a_count;
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = ACE4_SPECIAL_WHO;
> + ace->e_mask = allowed;
> + ace->u.e_who = richace_everyone_who;
> + }
> + }
> +
> + /* Compute the permissions that owner@ and group@ are already granted
> + though the everyone@ allow entry at the end. Note that the acl
> + contains no owner@ or group@ entries at this point. */
> + allowed = 0;
> + richacl_for_each_entry_reverse(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace)) {
> + if (richace_is_everyone(ace))
> + allowed |= ace->e_mask;
> + } else if (richace_is_deny(ace))
> + allowed &= ~ace->e_mask;
> + }
> +
> + /* Insert the appropriate group@ allow entry at the front. */
> + if (x->acl->a_group_mask & ~allowed) {
> + ace = x->acl->a_entries;
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = ACE4_SPECIAL_WHO;
> + ace->e_mask = x->acl->a_group_mask /*& ~allowed*/;
> + ace->u.e_who = richace_group_who;
> + }
> +
> + /* Insert the appropriate owner@ allow entry at the front. */
> + if (x->acl->a_owner_mask & ~allowed) {
> + ace = x->acl->a_entries;
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = ACE4_SPECIAL_WHO;
> + ace->e_mask = x->acl->a_owner_mask /*& ~allowed*/;
> + ace->u.e_who = richace_owner_who;
> + }
> +
> + /* Insert the appropriate owner@ deny entry at the front. */
> + allowed = richacl_max_allowed(x->acl);
> + if (allowed & ~x->acl->a_owner_mask) {
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace)) {
> + ace = x->acl->a_entries + x->acl->a_count;
> + break;
> + }
> + if (richace_is_deny(ace) && richace_is_owner(ace))
> + break;
> + }
> + if (ace != x->acl->a_entries + x->acl->a_count) {
> + if (richace_change_mask(x, &ace, ace->e_mask |
> + (allowed & ~x->acl->a_owner_mask)))
> + return -1;
> + } else {
> + ace = x->acl->a_entries;
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = ACE4_SPECIAL_WHO;
> + ace->e_mask = allowed & ~x->acl->a_owner_mask;
> + ace->u.e_who = richace_owner_who;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * richacl_apply_masks - apply the masks to the acl
> + *
> + * Apply the masks so that the acl allows no more flags than the
> + * intersection between the flags that the original acl allows and the
> + * mask matching the process.
> + *
> + * Note: this algorithm may push the number of entries in the acl above
> + * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail.
> + */
> +int
> +richacl_apply_masks(struct richacl **acl)
> +{
> + struct richacl_alloc x = {
> + .acl = *acl,
> + .count = (*acl)->a_count,
> + };
> + int retval = 0;
> +
> + if (richacl_move_everyone_aces_down(&x) ||
> + richacl_propagate_everyone(&x) ||
> + __richacl_apply_masks(&x) ||
> + richacl_isolate_owner_class(&x) ||
> + richacl_isolate_group_class(&x))
> + retval = -ENOMEM;
> +
> + *acl = x.acl;
> + return retval;
> +}
> +EXPORT_SYMBOL_GPL(richacl_apply_masks);
> +
> +int richacl_write_through(struct richacl **acl)
> +{
> + struct richacl_alloc x = {
> + .acl = *acl,
> + .count = (*acl)->a_count,
> + };
> + int retval = 0;
> +
> + if (!((*acl)->a_flags & ACL4_WRITE_THROUGH))
> + goto out;
> +
> + if (richacl_move_everyone_aces_down(&x) ||
> + richacl_propagate_everyone(&x) ||
> + __richacl_write_through(&x))
> + retval = -ENOMEM;
> +
> + *acl = x.acl;
> +out:
> + return retval;
> +}
> diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
> new file mode 100644
> index 0000000..4c04417
> --- /dev/null
> +++ b/fs/richacl_xattr.c
> @@ -0,0 +1,146 @@
> +/*
> + * Copyright (C) 2006 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");
> +
> +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 = be16_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 = be32_to_cpu(xattr_acl->a_owner_mask);
> + if (acl->a_owner_mask & ~ACE4_VALID_MASK)
> + goto fail_einval;
> + acl->a_group_mask = be32_to_cpu(xattr_acl->a_group_mask);
> + if (acl->a_group_mask & ~ACE4_VALID_MASK)
> + goto fail_einval;
> + acl->a_other_mask = be32_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 = be16_to_cpu(xattr_ace->e_type);
> + ace->e_flags = be16_to_cpu(xattr_ace->e_flags);
> + ace->e_mask = be32_to_cpu(xattr_ace->e_mask);
> + ace->u.e_id = be32_to_cpu(xattr_ace->e_id);
> +
> + if (ace->e_flags & ~ACE4_VALID_FLAGS) {
> + memset(ace, 0, sizeof(struct richace));
> + 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);
> +
> +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);
> +
> +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_be16(acl->a_count);
> +
> + xattr_acl->a_owner_mask = cpu_to_be32(acl->a_owner_mask);
> + xattr_acl->a_group_mask = cpu_to_be32(acl->a_group_mask);
> + xattr_acl->a_other_mask = cpu_to_be32(acl->a_other_mask);
> +
> + xattr_ace = (void *)(xattr_acl + 1);
> + richacl_for_each_entry(ace, acl) {
> + xattr_ace->e_type = cpu_to_be16(ace->e_type);
> + xattr_ace->e_flags = cpu_to_be16(ace->e_flags &
> + ACE4_VALID_FLAGS);
> + xattr_ace->e_mask = cpu_to_be32(ace->e_mask);
> + if (richace_is_unix_id(ace)) {
> + xattr_ace->e_id = cpu_to_be32(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_be32(-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.h b/include/linux/richacl.h
> new file mode 100644
> index 0000000..a2b4bd0
> --- /dev/null
> +++ b/include/linux/richacl.h
> @@ -0,0 +1,208 @@
> +#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--)
> +
> +/* a_flags values */
> +#define ACL4_WRITE_THROUGH 0x40
> +
> +#define ACL4_VALID_FLAGS \
> + ACL4_WRITE_THROUGH
> +
> +/* 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_DELETE 0x00010000
> +#define ACE4_READ_ACL 0x00020000
> +#define ACE4_WRITE_ACL 0x00040000
> +#define ACE4_WRITE_OWNER 0x00080000
> +#define ACE4_SYNCHRONIZE 0x00100000
> +
> +#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_DELETE | \
> + ACE4_READ_ACL | \
> + ACE4_WRITE_ACL | \
> + ACE4_WRITE_OWNER | \
> + ACE4_SYNCHRONIZE)
> +
> +#define ACE4_POSIX_ALWAYS_ALLOWED ( \
> + ACE4_SYNCHRONIZE | \
> + ACE4_READ_ATTRIBUTES | \
> + ACE4_READ_ACL)
> +/*
> + * Duplicate an RICHACL handle.
> + */
> +static inline struct richacl *
> +richacl_get(struct richacl *acl)
> +{
> + if (acl)
> + atomic_inc(&acl->a_refcount);
> + return acl;
> +}
> +
> +/*
> + * Free an 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 strcmp for efficiency. */
> +
> +extern const char richace_owner_who[];
> +extern const char richace_group_who[];
> +extern const char richace_everyone_who[];
> +
> +static inline int
> +richace_is_owner(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_owner_who;
> +}
> +
> +static inline int
> +richace_is_group(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_group_who;
> +}
> +
> +static inline int
> +richace_is_everyone(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_everyone_who;
> +}
> +
> +static inline int
> +richace_is_unix_id(const struct richace *ace)
> +{
> + return !(ace->e_flags & ACE4_SPECIAL_WHO);
> +}
> +
> +static inline int
> +richace_is_inherit_only(const struct richace *ace)
> +{
> + return ace->e_flags & ACE4_INHERIT_ONLY_ACE;
> +}
> +
> +static inline int
> +richace_is_inheritable(const struct richace *ace)
> +{
> + return ace->e_flags & (ACE4_FILE_INHERIT_ACE |
> + ACE4_DIRECTORY_INHERIT_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);
> +}
> +
> +static inline int
> +richace_is_allow(const struct richace *ace)
> +{
> + return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE;
> +}
> +
> +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 count);
> +extern struct richacl *richacl_clone(const struct richacl *acl);
> +
> +extern unsigned int richacl_want_to_mask(int want);
> +extern int richacl_permission(struct inode *,
> + const struct richacl *, unsigned int);
> +extern int richacl_generic_permission(struct inode *, unsigned int);
> +extern int richace_is_same_who(const struct richace *, const struct richace *);
> +extern int richace_set_who(struct richace *ace, const char *who);
> +extern struct richacl *richacl_inherit(const struct richacl *, mode_t);
> +extern int richacl_masks_to_mode(const struct richacl *);
> +extern struct richacl *richacl_chmod(struct richacl *, mode_t);
> +extern int richacl_apply_masks(struct richacl **acl);
> +extern int richacl_write_through(struct richacl **acl);
> +
> +#endif /* __RICHACL_H */
> diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
> new file mode 100644
> index 0000000..5a75284
> --- /dev/null
> +++ b/include/linux/richacl_xattr.h
> @@ -0,0 +1,32 @@
> +#ifndef __RICHACL_XATTR_H
> +#define __RICHACL_XATTR_H
> +
> +#include <linux/richacl.h>
> +
> +#define RICHACL_XATTR "system.richacl"
> +
> +struct richace_xattr {
> + __be16 e_type;
> + __be16 e_flags;
> + __be32 e_mask;
> + __be32 e_id;
> + char e_who[0];
> +};
> +
> +struct richacl_xattr {
> + unsigned char a_version;
> + unsigned char a_flags;
> + __be16 a_count;
> + __be32 a_owner_mask;
> + __be32 a_group_mask;
> + __be32 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.0.rc0.48.gdace5
>

2010-02-01 23:34:29

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 11/23] richacl: Move the xattr representation to little-endian format

On Mon, Feb 01, 2010 at 11:04:53AM +0530, Aneesh Kumar K.V wrote:
> Posix acl already use little-endian representation on disk. Move
> richacl also to little-endian representation

Merge this back into patch #3?

--b.

>
> Signed-off-by: Aneesh Kumar K.V <[email protected]>
> ---
> fs/richacl_xattr.c | 34 +++++++++++++++++-----------------
> include/linux/richacl_xattr.h | 16 ++++++++--------
> 2 files changed, 25 insertions(+), 25 deletions(-)
>
> diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
> index 4c04417..46e966a 100644
> --- a/fs/richacl_xattr.c
> +++ b/fs/richacl_xattr.c
> @@ -34,7 +34,7 @@ richacl_from_xattr(const void *value, size_t size)
> (xattr_acl->a_flags & ~ACL4_VALID_FLAGS))
> return ERR_PTR(-EINVAL);
>
> - count = be16_to_cpu(xattr_acl->a_count);
> + count = le16_to_cpu(xattr_acl->a_count);
> if (count > ACL4_XATTR_MAX_COUNT)
> return ERR_PTR(-EINVAL);
>
> @@ -43,13 +43,13 @@ richacl_from_xattr(const void *value, size_t size)
> return ERR_PTR(-ENOMEM);
>
> acl->a_flags = xattr_acl->a_flags;
> - acl->a_owner_mask = be32_to_cpu(xattr_acl->a_owner_mask);
> + 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 = be32_to_cpu(xattr_acl->a_group_mask);
> + 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 = be32_to_cpu(xattr_acl->a_other_mask);
> + acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
> if (acl->a_other_mask & ~ACE4_VALID_MASK)
> goto fail_einval;
>
> @@ -63,10 +63,10 @@ richacl_from_xattr(const void *value, size_t size)
> if (!end)
> goto fail_einval;
>
> - ace->e_type = be16_to_cpu(xattr_ace->e_type);
> - ace->e_flags = be16_to_cpu(xattr_ace->e_flags);
> - ace->e_mask = be32_to_cpu(xattr_ace->e_mask);
> - ace->u.e_id = be32_to_cpu(xattr_ace->e_id);
> + 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) {
> memset(ace, 0, sizeof(struct richace));
> @@ -117,26 +117,26 @@ richacl_to_xattr(const struct richacl *acl, void *buffer)
>
> xattr_acl->a_version = ACL4_XATTR_VERSION;
> xattr_acl->a_flags = acl->a_flags;
> - xattr_acl->a_count = cpu_to_be16(acl->a_count);
> + xattr_acl->a_count = cpu_to_le16(acl->a_count);
>
> - xattr_acl->a_owner_mask = cpu_to_be32(acl->a_owner_mask);
> - xattr_acl->a_group_mask = cpu_to_be32(acl->a_group_mask);
> - xattr_acl->a_other_mask = cpu_to_be32(acl->a_other_mask);
> + 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_be16(ace->e_type);
> - xattr_ace->e_flags = cpu_to_be16(ace->e_flags &
> + 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_be32(ace->e_mask);
> + xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
> if (richace_is_unix_id(ace)) {
> - xattr_ace->e_id = cpu_to_be32(ace->u.e_id);
> + 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_be32(-1);
> + 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;
> diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
> index 5a75284..de7acf7 100644
> --- a/include/linux/richacl_xattr.h
> +++ b/include/linux/richacl_xattr.h
> @@ -6,20 +6,20 @@
> #define RICHACL_XATTR "system.richacl"
>
> struct richace_xattr {
> - __be16 e_type;
> - __be16 e_flags;
> - __be32 e_mask;
> - __be32 e_id;
> + __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;
> - __be16 a_count;
> - __be32 a_owner_mask;
> - __be32 a_group_mask;
> - __be32 a_other_mask;
> + __le16 a_count;
> + __le32 a_owner_mask;
> + __le32 a_group_mask;
> + __le32 a_other_mask;
> };
>
> #define ACL4_XATTR_VERSION 0
> --
> 1.7.0.rc0.48.gdace5
>

2010-02-01 23:41:30

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 16/23] ext4: Update richacl incompat flag value

On Mon, Feb 01, 2010 at 11:04:58AM +0530, Aneesh Kumar K.V wrote:
> Update the incompat flag value so that we don't
> conflict with ext4 features which are not yet
> upstream but are in use.

In general, for any patches that just fix problems in preceding patches,
could you just fold those into the preceding patches?

--b.

>
>
> Signed-off-by: Aneesh Kumar K.V <[email protected]>
> ---
> fs/ext4/ext4.h | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 13df624..bcbff59 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1133,7 +1133,9 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
> #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
> #define EXT4_FEATURE_INCOMPAT_MMP 0x0100
> #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
> -#define EXT4_FEATURE_INCOMPAT_RICHACL 0x0400
> +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400
> +#define EXT4_FEATURE_INCOMPAT_RICHACL 0x0800
> +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000
>
> #define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
> #define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
> --
> 1.7.0.rc0.48.gdace5
>

2010-02-02 05:23:06

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH 07/23] vfs: Add Posix acl to rich acl mapping helpers

On Mon, 1 Feb 2010 18:18:16 -0500, "J. Bruce Fields" <[email protected]> wrote:
> On Mon, Feb 01, 2010 at 11:04:49AM +0530, Aneesh Kumar K.V wrote:
> > This patch add helpers that can be used by the file system to map
> > posix acls to rich acl format. This enables the file system to
> > return rich acl mapping the posix acls stored on disk when the
>
> You mean, to return a rich acl which is a mapped version of the posix
> acl stored on disk?

Yes


>
> > file system is enabled with rich acl format.
>
> Then I assume if you modified the acl, the filesystem would replace
> the existing posix acl by a "rich acl"?
>

Yes

> The idea being to allow you to convert an existing posix-acl-using
> filesystem to rich acl's? (But not the reverse.)
>

Exactly.

For ex:

/mnt# touch a
/mnt# getfacl a
# file: a
# owner: root
# group: root
user::rw-
group::r--
other::r--

/mnt# setfacl -m u:guest:rw a
/mnt# getfacl a
# file: a
# owner: root
# group: root
user::rw-
user:guest:rw-
group::r--
mask::rw-
other::r--

# umount /mnt/
# tune2fs -O richacl /dev/vdc
# mount /dev/vdc /mnt -o acl
# cd /mnt/
/mnt# getfacl a
# file: a
# acl format: richacl
# owner: root
# group: root
flags:pP <----- 'P' indicate Posix mapped
owner@:---------x-T--M--s::deny
owner@:-r-w-a-----T--M--s::allow
guest:---------x-T--M--s::deny
guest:-r-w-a-----T--M--s::allow
group@:-r---------T--M--s::allow
group@:---w-a---x-T--M--s::deny
everyone@:-r---------T--M--s::allow

/mnt# setrichacl --modify guest:r::allow a
/mnt# getfacl a
# file: a
# acl format: richacl
# owner: root
# group: root
flags:p <---- Posix mapped flag is dropped
owner@:---------x-T--M--s::deny
owner@:-r-w-a------------::allow
guest:---------x-T--M--s::deny
guest:-r----------------::allow
group@:-r---------T--M--s::allow
group@:---w-a---x-T--M--s::deny
everyone@:-r---------T--M--s::allow

2010-02-02 05:33:09

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH 08/23] vfs: Add a flag to denote posix mapped richacl

On Mon, 1 Feb 2010 18:18:58 -0500, "J. Bruce Fields" <[email protected]> wrote:
> On Mon, Feb 01, 2010 at 11:04:50AM +0530, Aneesh Kumar K.V wrote:
> > Signed-off-by: Aneesh Kumar K.V <[email protected]>
> > ---
> > fs/richacl_posix.c | 7 +++++++
> > include/linux/richacl.h | 10 ++++++----
> > 2 files changed, 13 insertions(+), 4 deletions(-)
> >
> > diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
> > index 07db970..3cf2124 100644
> > --- a/fs/richacl_posix.c
> > +++ b/fs/richacl_posix.c
> > @@ -183,6 +183,13 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
> > acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
> > acl->a_other_mask = richacl_mode_to_mask(mode);
> >
> > + /*
> > + * Mark that the acl as mapped from posix
> > + * This gives user space the chance to verify
> > + * whether the mapping was correct
> > + */
>
> How would it use this information? (And how could it be incorrect?)
>

Incorrect in the sense of what user expected the mapping should be.
This flag is later used by the userspace to indicate that the returned
richacl is a mapped richacl from Posix. The sysadmin should be able to
look at the flag and make sure the acl values are what he expected it
to be and the mapping code didn't map it wrongly.

NOTE: If the user belong to multiple groups, posix acl evaluation will
look at the group for which the requested access mask is allowed and
then apply the ACL_MASK values. That can be quiet confusing when we map to
richacl.

-aneesh

2010-02-02 05:35:51

by Aneesh Kumar K.V

[permalink] [raw]
Subject: Re: [PATCH 11/23] richacl: Move the xattr representation to little-endian format

On Mon, 1 Feb 2010 18:34:29 -0500, "J. Bruce Fields" <[email protected]> wrote:
> On Mon, Feb 01, 2010 at 11:04:53AM +0530, Aneesh Kumar K.V wrote:
> > Posix acl already use little-endian representation on disk. Move
> > richacl also to little-endian representation
>
> Merge this back into patch #3?
>
>

Yes. We should be merging some of these patches into earlier patches. I
want to keep the patches separate at this point to enable Andreas to
find out the changes done to original path so that he should be able to
review them easily. But yes before we can merge this to upstream some
of this patches to be folded back to earlier patches.

-aneesh

2010-02-02 15:18:24

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 08/23] vfs: Add a flag to denote posix mapped richacl

On Tue, Feb 02, 2010 at 11:03:09AM +0530, Aneesh Kumar K. V wrote:
> On Mon, 1 Feb 2010 18:18:58 -0500, "J. Bruce Fields" <[email protected]> wrote:
> > On Mon, Feb 01, 2010 at 11:04:50AM +0530, Aneesh Kumar K.V wrote:
> > > Signed-off-by: Aneesh Kumar K.V <[email protected]>
> > > ---
> > > fs/richacl_posix.c | 7 +++++++
> > > include/linux/richacl.h | 10 ++++++----
> > > 2 files changed, 13 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/fs/richacl_posix.c b/fs/richacl_posix.c
> > > index 07db970..3cf2124 100644
> > > --- a/fs/richacl_posix.c
> > > +++ b/fs/richacl_posix.c
> > > @@ -183,6 +183,13 @@ static void posix_to_richacl(struct posix_acl *pacl, int type,
> > > acl->a_group_mask = richacl_mode_to_mask(mode >> 3);
> > > acl->a_other_mask = richacl_mode_to_mask(mode);
> > >
> > > + /*
> > > + * Mark that the acl as mapped from posix
> > > + * This gives user space the chance to verify
> > > + * whether the mapping was correct
> > > + */
> >
> > How would it use this information? (And how could it be incorrect?)
> >
>
> Incorrect in the sense of what user expected the mapping should be.
> This flag is later used by the userspace to indicate that the returned
> richacl is a mapped richacl from Posix. The sysadmin should be able to
> look at the flag and make sure the acl values are what he expected it
> to be and the mapping code didn't map it wrongly.

It's not going to map wrongly--if it does, there's a bug, and we should
just fix the bug.

It's not reasonable to expect sysadmins to manually check mapped ACL's
to look for bugs in our mapping algorithm.

--b.

> NOTE: If the user belong to multiple groups, posix acl evaluation will
> look at the group for which the requested access mask is allowed and
> then apply the ACL_MASK values. That can be quiet confusing when we map to
> richacl.
>
> -aneesh
>