linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v19 00/23] Richacls (Core and Ext4)
@ 2016-03-14 14:54 Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 01/23] vfs: Add IS_ACL() and IS_RICHACL() tests Andreas Gruenbacher
                   ` (22 more replies)
  0 siblings, 23 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:54 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Al,

could you please merge the current richacl patch queue, up to and including
the ext4 support?


Changes since the last posting (https://lwn.net/Articles/677901/):

 * richacl_from_xattr now takes an invalid_error parameter specifying
   the error code to return when an xattr is invallid.  This allows to
   better use this function when decoding an xattr stored on a filesystem
   as well as when decoding a user-provided xattr.

 * ext4_acl_chmod and xfs_acl_chmod have lost their mode parameter;
   they have aready been using inode->i_mode instead of the parameter.

 * Use __u8 instead of unsigned char in uapi headers.  Drop left-over
   MODULE_LICENSE declarations.  Use IS_ERR_OR_NULL instead of IS_ERR in
   ext4_init_richacl.

 * Rebased on top of v4.5.


The complete patch queue is available here:

  git://git.kernel.org/pub/scm/linux/kernel/git/agruen/linux-richacl.git \
        richacl-2016-03-14


The richacl user-space utilitites, man pages, and test suite are available
here:

  https://github.com/andreas-gruenbacher/richacl


Changes to other user-space packages for richacl:

  https://github.com/andreas-gruenbacher/coreutils
  https://github.com/andreas-gruenbacher/e2fsprogs
  https://github.com/andreas-gruenbacher/samba
  https://github.com/andreas-gruenbacher/xfsprogs-dev
  https://github.com/andreas-gruenbacher/nfs-utils


Please see the richacl homepage for more information:

  http://www.bestbits.at/richacl/


Thanks,
Andreas

Andreas Gruenbacher (21):
  vfs: Add IS_ACL() and IS_RICHACL() tests
  vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags
  vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags
  vfs: Make the inode passed to inode_change_ok non-const
  vfs: Add permission flags for setting file attributes
  richacl: In-memory representation and helper functions
  richacl: Permission mapping functions
  richacl: Compute maximum file masks from an acl
  richacl: Permission check algorithm
  posix_acl: Unexport acl_by_type and make it static
  posix_acl: Improve xattr fixup code
  vfs: Cache base_acl objects in inodes
  vfs: Add get_richacl and set_richacl inode operations
  vfs: Cache richacl in struct inode
  richacl: Update the file masks in chmod()
  richacl: Check if an acl is equivalent to a file mode
  richacl: Create-time inheritance
  richacl: Automatic Inheritance
  richacl: xattr mapping functions
  richacl: Add richacl xattr handler
  vfs: Add richacl permission checking

Aneesh Kumar K.V (2):
  ext4: Add richacl support
  ext4: Add richacl feature flag

 drivers/staging/lustre/lustre/llite/llite_lib.c |   2 +-
 fs/Kconfig                                      |   3 +
 fs/Makefile                                     |   2 +
 fs/attr.c                                       |  81 +++-
 fs/ext4/Kconfig                                 |  11 +
 fs/ext4/Makefile                                |   1 +
 fs/ext4/ext4.h                                  |   6 +-
 fs/ext4/file.c                                  |   3 +
 fs/ext4/ialloc.c                                |  11 +-
 fs/ext4/inode.c                                 |  12 +-
 fs/ext4/namei.c                                 |   5 +
 fs/ext4/richacl.c                               | 137 ++++++
 fs/ext4/richacl.h                               |  40 ++
 fs/ext4/super.c                                 |  49 +-
 fs/ext4/xattr.c                                 |   7 +
 fs/f2fs/acl.c                                   |   4 +-
 fs/inode.c                                      |  15 +-
 fs/jffs2/acl.c                                  |  10 +-
 fs/namei.c                                      | 118 ++++-
 fs/posix_acl.c                                  |  50 +-
 fs/richacl_base.c                               | 578 ++++++++++++++++++++++++
 fs/richacl_inode.c                              | 333 ++++++++++++++
 fs/richacl_xattr.c                              | 235 ++++++++++
 fs/xattr.c                                      |  29 +-
 include/linux/fs.h                              |  60 ++-
 include/linux/posix_acl.h                       |  13 +-
 include/linux/richacl.h                         | 208 +++++++++
 include/linux/richacl_xattr.h                   |  31 ++
 include/uapi/linux/Kbuild                       |   2 +
 include/uapi/linux/fs.h                         |   3 +-
 include/uapi/linux/richacl.h                    | 152 +++++++
 include/uapi/linux/richacl_xattr.h              |  44 ++
 include/uapi/linux/xattr.h                      |   2 +
 33 files changed, 2150 insertions(+), 107 deletions(-)
 create mode 100644 fs/ext4/richacl.c
 create mode 100644 fs/ext4/richacl.h
 create mode 100644 fs/richacl_base.c
 create mode 100644 fs/richacl_inode.c
 create mode 100644 fs/richacl_xattr.c
 create mode 100644 include/linux/richacl.h
 create mode 100644 include/linux/richacl_xattr.h
 create mode 100644 include/uapi/linux/richacl.h
 create mode 100644 include/uapi/linux/richacl_xattr.h

-- 
2.5.0

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v19 01/23] vfs: Add IS_ACL() and IS_RICHACL() tests
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 02/23] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags Andreas Gruenbacher
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

The vfs does not apply the umask for file systems that support acls. The
test used for this used to be called IS_POSIXACL(). Switch to a new
IS_ACL() test to check for either posix acls or richacls instead. Add a new
MS_RICHACL flag and IS_RICHACL() test for richacls alone. The IS_POSIXACL()
test is still needed by nfsd.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 fs/Kconfig              |  3 +++
 fs/namei.c              |  8 ++++----
 include/linux/fs.h      | 12 ++++++++++++
 include/uapi/linux/fs.h |  3 ++-
 4 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 9adee0d..f7b26f3 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -63,6 +63,9 @@ endif # BLOCK
 config FS_POSIX_ACL
 	def_bool n
 
+config FS_RICHACL
+	def_bool n
+
 config EXPORTFS
 	tristate
 
diff --git a/fs/namei.c b/fs/namei.c
index 9c590e0..68e1056 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2861,7 +2861,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
 	}
 
 	mode = op->mode;
-	if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
+	if ((open_flag & O_CREAT) && !IS_ACL(dir))
 		mode &= ~current_umask();
 
 	excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
@@ -3045,7 +3045,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
 	/* Negative dentry, just create the file */
 	if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
 		umode_t mode = op->mode;
-		if (!IS_POSIXACL(dir->d_inode))
+		if (!IS_ACL(dir->d_inode))
 			mode &= ~current_umask();
 		/*
 		 * This write is needed to ensure that a
@@ -3620,7 +3620,7 @@ retry:
 	if (IS_ERR(dentry))
 		return PTR_ERR(dentry);
 
-	if (!IS_POSIXACL(path.dentry->d_inode))
+	if (!IS_ACL(path.dentry->d_inode))
 		mode &= ~current_umask();
 	error = security_path_mknod(&path, dentry, mode, dev);
 	if (error)
@@ -3689,7 +3689,7 @@ retry:
 	if (IS_ERR(dentry))
 		return PTR_ERR(dentry);
 
-	if (!IS_POSIXACL(path.dentry->d_inode))
+	if (!IS_ACL(path.dentry->d_inode))
 		mode &= ~current_umask();
 	error = security_path_mkdir(&path, dentry, mode);
 	if (!error)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index ae68100..f9d2b59 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1804,6 +1804,12 @@ struct super_operations {
 #define IS_IMMUTABLE(inode)	((inode)->i_flags & S_IMMUTABLE)
 #define IS_POSIXACL(inode)	__IS_FLG(inode, MS_POSIXACL)
 
+#ifdef CONFIG_FS_RICHACL
+#define IS_RICHACL(inode)	__IS_FLG(inode, MS_RICHACL)
+#else
+#define IS_RICHACL(inode)	0
+#endif
+
 #define IS_DEADDIR(inode)	((inode)->i_flags & S_DEAD)
 #define IS_NOCMTIME(inode)	((inode)->i_flags & S_NOCMTIME)
 #define IS_SWAPFILE(inode)	((inode)->i_flags & S_SWAPFILE)
@@ -1817,6 +1823,12 @@ struct super_operations {
 				 (inode)->i_rdev == WHITEOUT_DEV)
 
 /*
+ * IS_ACL() tells the VFS to not apply the umask
+ * and use check_acl for acl permission checks when defined.
+ */
+#define IS_ACL(inode)		__IS_FLG(inode, MS_POSIXACL | MS_RICHACL)
+
+/*
  * Inode state bits.  Protected by inode->i_lock
  *
  * Three bits determine the dirty state of the inode, I_DIRTY_SYNC,
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 149bec8..540dbef 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -120,7 +120,7 @@ struct inodes_stat_t {
 #define MS_VERBOSE	32768	/* War is peace. Verbosity is silence.
 				   MS_VERBOSE is deprecated. */
 #define MS_SILENT	32768
-#define MS_POSIXACL	(1<<16)	/* VFS does not apply the umask */
+#define MS_POSIXACL	(1<<16)	/* Supports POSIX ACLs */
 #define MS_UNBINDABLE	(1<<17)	/* change to unbindable */
 #define MS_PRIVATE	(1<<18)	/* change to private */
 #define MS_SLAVE	(1<<19)	/* change to slave */
@@ -130,6 +130,7 @@ struct inodes_stat_t {
 #define MS_I_VERSION	(1<<23) /* Update inode I_version field */
 #define MS_STRICTATIME	(1<<24) /* Always perform atime updates */
 #define MS_LAZYTIME	(1<<25) /* Update the on-disk [acm]times lazily */
+#define MS_RICHACL	(1<<26) /* Supports richacls */
 
 /* These sb flags are internal to the kernel */
 #define MS_NOSEC	(1<<28)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 02/23] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 01/23] vfs: Add IS_ACL() and IS_RICHACL() tests Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 03/23] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD " Andreas Gruenbacher
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Richacls distinguish between creating non-directories and directories. To
support that, add an isdir parameter to may_create(). When checking
inode_permission() for create permission, pass in an additional
MAY_CREATE_FILE or MAY_CREATE_DIR mask flag.

Add may_replace() to allow checking for delete and create access when
replacing an existing file in vfs_rename().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 fs/namei.c         | 49 +++++++++++++++++++++++++++++++++----------------
 include/linux/fs.h |  2 ++
 2 files changed, 35 insertions(+), 16 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 68e1056..2092742 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -453,7 +453,9 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
  * this, letting us set arbitrary permissions for filesystem access without
  * changing the "normal" UIDs which are used for other things.
  *
- * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
+ * MAY_WRITE must be set in @mask whenever MAY_APPEND, MAY_CREATE_FILE, or
+ * MAY_CREATE_DIR are set.  That way, file systems that don't support these
+ * permissions will check for MAY_WRITE instead.
  */
 int inode_permission(struct inode *inode, int mask)
 {
@@ -2616,7 +2618,8 @@ EXPORT_SYMBOL(__check_sticky);
  * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
  *     nfs_async_unlink().
  */
-static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+static int may_delete_or_replace(struct inode *dir, struct dentry *victim,
+				 bool isdir, int mask)
 {
 	struct inode *inode = d_backing_inode(victim);
 	int error;
@@ -2628,7 +2631,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
 	BUG_ON(victim->d_parent->d_inode != dir);
 	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
 
-	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+	error = inode_permission(dir, mask);
 	if (error)
 		return error;
 	if (IS_APPEND(dir))
@@ -2651,6 +2654,18 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
 	return 0;
 }
 
+static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+{
+	return may_delete_or_replace(dir, victim, isdir, MAY_WRITE | MAY_EXEC);
+}
+
+static int may_replace(struct inode *dir, struct dentry *victim, bool isdir)
+{
+	int mask = isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+
+	return may_delete_or_replace(dir, victim, isdir, mask | MAY_WRITE | MAY_EXEC);
+}
+
 /*	Check whether we can create an object with dentry child in directory
  *  dir.
  *  1. We can't do it if child already exists (open has special treatment for
@@ -2659,14 +2674,16 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool 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, bool isdir)
 {
+	int mask = isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+
 	audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
 	if (child->d_inode)
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
 		return -ENOENT;
-	return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+	return inode_permission(dir, MAY_WRITE | MAY_EXEC | mask);
 }
 
 /*
@@ -2716,7 +2733,7 @@ EXPORT_SYMBOL(unlock_rename);
 int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 		bool want_excl)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 	if (error)
 		return error;
 
@@ -3561,7 +3578,7 @@ EXPORT_SYMBOL(user_path_create);
 
 int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 
 	if (error)
 		return error;
@@ -3653,7 +3670,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
 
 int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, true);
 	unsigned max_links = dir->i_sb->s_max_links;
 
 	if (error)
@@ -3734,7 +3751,7 @@ EXPORT_SYMBOL(dentry_unhash);
 
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-	int error = may_delete(dir, dentry, 1);
+	int error = may_delete(dir, dentry, true);
 
 	if (error)
 		return error;
@@ -3856,7 +3873,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
 int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
 {
 	struct inode *target = dentry->d_inode;
-	int error = may_delete(dir, dentry, 0);
+	int error = may_delete(dir, dentry, false);
 
 	if (error)
 		return error;
@@ -3990,7 +4007,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, false);
 
 	if (error)
 		return error;
@@ -4073,7 +4090,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, false);
 	if (error)
 		return error;
 
@@ -4266,14 +4283,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		return error;
 
 	if (!target) {
-		error = may_create(new_dir, new_dentry);
+		error = may_create(new_dir, new_dentry, is_dir);
 	} else {
 		new_is_dir = d_is_dir(new_dentry);
 
 		if (!(flags & RENAME_EXCHANGE))
-			error = may_delete(new_dir, new_dentry, is_dir);
+			error = may_replace(new_dir, new_dentry, is_dir);
 		else
-			error = may_delete(new_dir, new_dentry, new_is_dir);
+			error = may_replace(new_dir, new_dentry, new_is_dir);
 	}
 	if (error)
 		return error;
@@ -4536,7 +4553,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
 
 int vfs_whiteout(struct inode *dir, struct dentry *dentry)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 	if (error)
 		return error;
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f9d2b59..02e99d7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -83,6 +83,8 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate);
 #define MAY_CHDIR		0x00000040
 /* called from RCU mode, don't block */
 #define MAY_NOT_BLOCK		0x00000080
+#define MAY_CREATE_FILE		0x00000100
+#define MAY_CREATE_DIR		0x00000200
 
 /*
  * flags in file.f_mode.  Note that FMODE_READ and FMODE_WRITE must correspond
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 03/23] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 01/23] vfs: Add IS_ACL() and IS_RICHACL() tests Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 02/23] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 04/23] vfs: Make the inode passed to inode_change_ok non-const Andreas Gruenbacher
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Normally, deleting a file requires MAY_WRITE access to the parent
directory.  With richacls, a file may be deleted with MAY_DELETE_CHILD access
to the parent directory or with MAY_DELETE_SELF access to the file.

To support that, pass the MAY_DELETE_CHILD mask flag to inode_permission()
when checking for delete access inside a directory, and MAY_DELETE_SELF
when checking for delete access to a file itelf.

The MAY_DELETE_SELF permission overrides the sticky directory check.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/namei.c         | 20 ++++++++++++--------
 include/linux/fs.h |  2 ++
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 2092742..28707ae 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -453,9 +453,9 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
  * this, letting us set arbitrary permissions for filesystem access without
  * changing the "normal" UIDs which are used for other things.
  *
- * MAY_WRITE must be set in @mask whenever MAY_APPEND, MAY_CREATE_FILE, or
- * MAY_CREATE_DIR are set.  That way, file systems that don't support these
- * permissions will check for MAY_WRITE instead.
+ * MAY_WRITE must be set in @mask whenever MAY_APPEND, MAY_CREATE_FILE,
+ * MAY_CREATE_DIR, or MAY_DELETE_CHILD are set.  That way, file systems that
+ * don't support these permissions will check for MAY_WRITE instead.
  */
 int inode_permission(struct inode *inode, int mask)
 {
@@ -2631,14 +2631,18 @@ static int may_delete_or_replace(struct inode *dir, struct dentry *victim,
 	BUG_ON(victim->d_parent->d_inode != dir);
 	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
 
-	error = inode_permission(dir, mask);
+	error = inode_permission(dir, mask | MAY_WRITE | MAY_DELETE_CHILD);
+	if (!error && check_sticky(dir, inode))
+		error = -EPERM;
+	if (error && IS_RICHACL(inode) &&
+	    inode_permission(inode, MAY_DELETE_SELF) == 0 &&
+	    inode_permission(dir, mask) == 0)
+		error = 0;
 	if (error)
 		return error;
 	if (IS_APPEND(dir))
 		return -EPERM;
-
-	if (check_sticky(dir, inode) || IS_APPEND(inode) ||
-	    IS_IMMUTABLE(inode) || IS_SWAPFILE(inode))
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode) || IS_SWAPFILE(inode))
 		return -EPERM;
 	if (isdir) {
 		if (!d_is_dir(victim))
@@ -2656,7 +2660,7 @@ static int may_delete_or_replace(struct inode *dir, struct dentry *victim,
 
 static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
 {
-	return may_delete_or_replace(dir, victim, isdir, MAY_WRITE | MAY_EXEC);
+	return may_delete_or_replace(dir, victim, isdir, MAY_EXEC);
 }
 
 static int may_replace(struct inode *dir, struct dentry *victim, bool isdir)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 02e99d7..97e34ba 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -85,6 +85,8 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate);
 #define MAY_NOT_BLOCK		0x00000080
 #define MAY_CREATE_FILE		0x00000100
 #define MAY_CREATE_DIR		0x00000200
+#define MAY_DELETE_CHILD	0x00000400
+#define MAY_DELETE_SELF		0x00000800
 
 /*
  * flags in file.f_mode.  Note that FMODE_READ and FMODE_WRITE must correspond
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 04/23] vfs: Make the inode passed to inode_change_ok non-const
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (2 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 03/23] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD " Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 05/23] vfs: Add permission flags for setting file attributes Andreas Gruenbacher
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

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

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 fs/attr.c          | 2 +-
 include/linux/fs.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 25b24d0..7ca7fa0 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -28,7 +28,7 @@
  * Should be called as the first thing in ->setattr implementations,
  * possibly after taking additional locks.
  */
-int inode_change_ok(const struct inode *inode, struct iattr *attr)
+int inode_change_ok(struct inode *inode, struct iattr *attr)
 {
 	unsigned int ia_valid = attr->ia_valid;
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 97e34ba..efff04f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2908,7 +2908,7 @@ extern int buffer_migrate_page(struct address_space *,
 #define buffer_migrate_page NULL
 #endif
 
-extern int inode_change_ok(const struct inode *, struct iattr *);
+extern int inode_change_ok(struct inode *, struct iattr *);
 extern int inode_newsize_ok(const struct inode *, loff_t offset);
 extern void setattr_copy(struct inode *inode, const struct iattr *attr);
 
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 05/23] vfs: Add permission flags for setting file attributes
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (3 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 04/23] vfs: Make the inode passed to inode_change_ok non-const Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 06/23] richacl: In-memory representation and helper functions Andreas Gruenbacher
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Richacls support permissions that allow to take ownership of a file,
change the file permissions, and set the file timestamps.  Support that
by introducing new permission mask flags and by checking for those mask
flags in inode_change_ok().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/attr.c          | 79 +++++++++++++++++++++++++++++++++++++++++++++---------
 include/linux/fs.h |  3 +++
 2 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 7ca7fa0..2a8c49c 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -17,6 +17,65 @@
 #include <linux/ima.h>
 
 /**
+ * inode_extended_permission  -  permissions beyond read/write/execute
+ *
+ * Check for permissions that only richacls can currently grant.
+ */
+static int inode_extended_permission(struct inode *inode, int mask)
+{
+	if (!IS_RICHACL(inode))
+		return -EPERM;
+	return inode_permission(inode, mask);
+}
+
+static bool inode_uid_change_ok(struct inode *inode, kuid_t ia_uid)
+{
+	if (uid_eq(current_fsuid(), inode->i_uid) &&
+	    uid_eq(ia_uid, inode->i_uid))
+		return true;
+	if (uid_eq(current_fsuid(), ia_uid) &&
+	    inode_extended_permission(inode, MAY_TAKE_OWNERSHIP) == 0)
+		return true;
+	if (capable_wrt_inode_uidgid(inode, CAP_CHOWN))
+		return true;
+	return false;
+}
+
+static bool inode_gid_change_ok(struct inode *inode, kgid_t ia_gid)
+{
+	int in_group = in_group_p(ia_gid);
+	if (uid_eq(current_fsuid(), inode->i_uid) &&
+	    (in_group || gid_eq(ia_gid, inode->i_gid)))
+		return true;
+	if (in_group && inode_extended_permission(inode, MAY_TAKE_OWNERSHIP) == 0)
+		return true;
+	if (capable_wrt_inode_uidgid(inode, CAP_CHOWN))
+		return true;
+	return false;
+}
+
+/**
+ * inode_owner_permitted_or_capable
+ *
+ * Check for permissions implicitly granted to the owner, like MAY_CHMOD or
+ * MAY_SET_TIMES.  Equivalent to inode_owner_or_capable for file systems
+ * without support for those permissions.
+ */
+static bool inode_owner_permitted_or_capable(struct inode *inode, int mask)
+{
+	struct user_namespace *ns;
+
+	if (uid_eq(current_fsuid(), inode->i_uid))
+		return true;
+	if (inode_extended_permission(inode, mask) == 0)
+		return true;
+	ns = current_user_ns();
+	if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid))
+		return true;
+	return false;
+}
+
+/**
  * inode_change_ok - check if attribute changes to an inode are allowed
  * @inode:	inode to check
  * @attr:	attributes to change
@@ -47,22 +106,18 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
 		return 0;
 
 	/* Make sure a caller can chown. */
-	if ((ia_valid & ATTR_UID) &&
-	    (!uid_eq(current_fsuid(), inode->i_uid) ||
-	     !uid_eq(attr->ia_uid, inode->i_uid)) &&
-	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))
-		return -EPERM;
+	if (ia_valid & ATTR_UID)
+		if (!inode_uid_change_ok(inode, attr->ia_uid))
+			return -EPERM;
 
 	/* Make sure caller can chgrp. */
-	if ((ia_valid & ATTR_GID) &&
-	    (!uid_eq(current_fsuid(), inode->i_uid) ||
-	    (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) &&
-	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))
-		return -EPERM;
+	if (ia_valid & ATTR_GID)
+		if (!inode_gid_change_ok(inode, attr->ia_gid))
+			return -EPERM;
 
 	/* Make sure a caller can chmod. */
 	if (ia_valid & ATTR_MODE) {
-		if (!inode_owner_or_capable(inode))
+		if (!inode_owner_permitted_or_capable(inode, MAY_CHMOD))
 			return -EPERM;
 		/* Also check the setgid bit! */
 		if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
@@ -73,7 +128,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
 
 	/* Check for setting the inode time. */
 	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
-		if (!inode_owner_or_capable(inode))
+		if (!inode_owner_permitted_or_capable(inode, MAY_SET_TIMES))
 			return -EPERM;
 	}
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index efff04f..0f560b7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -87,6 +87,9 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate);
 #define MAY_CREATE_DIR		0x00000200
 #define MAY_DELETE_CHILD	0x00000400
 #define MAY_DELETE_SELF		0x00000800
+#define MAY_TAKE_OWNERSHIP	0x00001000
+#define MAY_CHMOD		0x00002000
+#define MAY_SET_TIMES		0x00004000
 
 /*
  * flags in file.f_mode.  Note that FMODE_READ and FMODE_WRITE must correspond
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 06/23] richacl: In-memory representation and helper functions
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (4 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 05/23] vfs: Add permission flags for setting file attributes Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 07/23] richacl: Permission mapping functions Andreas Gruenbacher
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

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

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

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

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/Makefile                  |   2 +
 fs/richacl_base.c            |  65 ++++++++++++++++
 include/linux/richacl.h      | 179 +++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/Kbuild    |   1 +
 include/uapi/linux/richacl.h |  99 ++++++++++++++++++++++++
 5 files changed, 346 insertions(+)
 create mode 100644 fs/richacl_base.c
 create mode 100644 include/linux/richacl.h
 create mode 100644 include/uapi/linux/richacl.h

diff --git a/fs/Makefile b/fs/Makefile
index 79f5225..e5994c4 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -48,6 +48,8 @@ obj-$(CONFIG_COREDUMP)		+= coredump.o
 obj-$(CONFIG_SYSCTL)		+= drop_caches.o
 
 obj-$(CONFIG_FHANDLE)		+= fhandle.o
+obj-$(CONFIG_FS_RICHACL)	+= richacl.o
+richacl-y			:= richacl_base.o
 
 obj-y				+= quota/
 
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
new file mode 100644
index 0000000..bcc6591
--- /dev/null
+++ b/fs/richacl_base.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * 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/slab.h>
+#include <linux/richacl.h>
+
+/**
+ * richacl_alloc  -  allocate a richacl
+ * @count:	number of entries
+ */
+struct richacl *
+richacl_alloc(int count, gfp_t gfp)
+{
+	size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+	struct richacl *acl = kzalloc(size, gfp);
+
+	if (acl) {
+		atomic_set(&acl->a_refcount, 1);
+		acl->a_count = count;
+	}
+	return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_alloc);
+
+/**
+ * richacl_clone  -  create a copy of a richacl
+ */
+struct richacl *
+richacl_clone(const struct richacl *acl, gfp_t gfp)
+{
+	int count = acl->a_count;
+	size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+	struct richacl *dup = kmalloc(size, gfp);
+
+	if (dup) {
+		memcpy(dup, acl, size);
+		atomic_set(&dup->a_refcount, 1);
+	}
+	return dup;
+}
+
+/**
+ * richace_copy  -  copy an acl entry
+ */
+void
+richace_copy(struct richace *to, const struct richace *from)
+{
+	memcpy(to, from, sizeof(struct richace));
+}
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
new file mode 100644
index 0000000..edb8480
--- /dev/null
+++ b/include/linux/richacl.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __RICHACL_H
+#define __RICHACL_H
+
+#include <uapi/linux/richacl.h>
+
+struct richace {
+	unsigned short	e_type;
+	unsigned short	e_flags;
+	unsigned int	e_mask;
+	union {
+		kuid_t		uid;
+		kgid_t		gid;
+		unsigned int	special;
+	} e_id;
+};
+
+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--)
+
+/**
+ * richacl_get  -  grab another reference to a richacl handle
+ */
+static inline struct richacl *
+richacl_get(struct richacl *acl)
+{
+	if (acl)
+		atomic_inc(&acl->a_refcount);
+	return acl;
+}
+
+/**
+ * richacl_put  -  free a richacl handle
+ */
+static inline void
+richacl_put(struct richacl *acl)
+{
+	if (acl && atomic_dec_and_test(&acl->a_refcount))
+		kfree(acl);
+}
+
+/**
+ * richace_is_owner  -  check if @ace is an OWNER@ entry
+ */
+static inline bool
+richace_is_owner(const struct richace *ace)
+{
+	return (ace->e_flags & RICHACE_SPECIAL_WHO) &&
+	       ace->e_id.special == RICHACE_OWNER_SPECIAL_ID;
+}
+
+/**
+ * richace_is_group  -  check if @ace is a GROUP@ entry
+ */
+static inline bool
+richace_is_group(const struct richace *ace)
+{
+	return (ace->e_flags & RICHACE_SPECIAL_WHO) &&
+	       ace->e_id.special == RICHACE_GROUP_SPECIAL_ID;
+}
+
+/**
+ * richace_is_everyone  -  check if @ace is an EVERYONE@ entry
+ */
+static inline bool
+richace_is_everyone(const struct richace *ace)
+{
+	return (ace->e_flags & RICHACE_SPECIAL_WHO) &&
+	       ace->e_id.special == RICHACE_EVERYONE_SPECIAL_ID;
+}
+
+/**
+ * richace_is_unix_user  -  check if @ace applies to a specific user
+ */
+static inline bool
+richace_is_unix_user(const struct richace *ace)
+{
+	return !(ace->e_flags & RICHACE_SPECIAL_WHO) &&
+	       !(ace->e_flags & RICHACE_IDENTIFIER_GROUP);
+}
+
+/**
+ * richace_is_unix_group  -  check if @ace applies to a specific group
+ */
+static inline bool
+richace_is_unix_group(const struct richace *ace)
+{
+	return !(ace->e_flags & RICHACE_SPECIAL_WHO) &&
+	       (ace->e_flags & RICHACE_IDENTIFIER_GROUP);
+}
+
+/**
+ * richace_is_inherit_only  -  check if @ace is for inheritance only
+ *
+ * ACEs with the %RICHACE_INHERIT_ONLY_ACE flag set have no effect during
+ * permission checking.
+ */
+static inline bool
+richace_is_inherit_only(const struct richace *ace)
+{
+	return ace->e_flags & RICHACE_INHERIT_ONLY_ACE;
+}
+
+/**
+ * richace_is_inheritable  -  check if @ace is inheritable
+ */
+static inline bool
+richace_is_inheritable(const struct richace *ace)
+{
+	return ace->e_flags & (RICHACE_FILE_INHERIT_ACE |
+			       RICHACE_DIRECTORY_INHERIT_ACE);
+}
+
+/**
+ * richace_is_allow  -  check if @ace is an %ALLOW type entry
+ */
+static inline bool
+richace_is_allow(const struct richace *ace)
+{
+	return ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+}
+
+/**
+ * richace_is_deny  -  check if @ace is a %DENY type entry
+ */
+static inline bool
+richace_is_deny(const struct richace *ace)
+{
+	return ace->e_type == RICHACE_ACCESS_DENIED_ACE_TYPE;
+}
+
+/**
+ * richace_is_same_identifier  -  are both identifiers the same?
+ */
+static inline bool
+richace_is_same_identifier(const struct richace *a, const struct richace *b)
+{
+	return !((a->e_flags ^ b->e_flags) &
+		 (RICHACE_SPECIAL_WHO | RICHACE_IDENTIFIER_GROUP)) &&
+	       !memcmp(&a->e_id, &b->e_id, sizeof(a->e_id));
+}
+
+extern struct richacl *richacl_alloc(int, gfp_t);
+extern struct richacl *richacl_clone(const struct richacl *, gfp_t);
+extern void richace_copy(struct richace *, const struct richace *);
+
+#endif /* __RICHACL_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index ebd10e6..6e05dc8 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -351,6 +351,7 @@ header-y += reboot.h
 header-y += reiserfs_fs.h
 header-y += reiserfs_xattr.h
 header-y += resource.h
+header-y += richacl.h
 header-y += rfkill.h
 header-y += romfs_fs.h
 header-y += rose.h
diff --git a/include/uapi/linux/richacl.h b/include/uapi/linux/richacl.h
new file mode 100644
index 0000000..08856f8
--- /dev/null
+++ b/include/uapi/linux/richacl.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef __UAPI_RICHACL_H
+#define __UAPI_RICHACL_H
+
+/* a_flags values */
+#define RICHACL_WRITE_THROUGH			0x40
+#define RICHACL_MASKED				0x80
+
+/* e_type values */
+#define RICHACE_ACCESS_ALLOWED_ACE_TYPE		0x0000
+#define RICHACE_ACCESS_DENIED_ACE_TYPE		0x0001
+
+/* e_flags bitflags */
+#define RICHACE_FILE_INHERIT_ACE		0x0001
+#define RICHACE_DIRECTORY_INHERIT_ACE		0x0002
+#define RICHACE_NO_PROPAGATE_INHERIT_ACE	0x0004
+#define RICHACE_INHERIT_ONLY_ACE		0x0008
+#define RICHACE_IDENTIFIER_GROUP		0x0040
+#define RICHACE_SPECIAL_WHO			0x4000
+
+/* e_mask bitflags */
+#define RICHACE_READ_DATA			0x00000001
+#define RICHACE_LIST_DIRECTORY			0x00000001
+#define RICHACE_WRITE_DATA			0x00000002
+#define RICHACE_ADD_FILE			0x00000002
+#define RICHACE_APPEND_DATA			0x00000004
+#define RICHACE_ADD_SUBDIRECTORY		0x00000004
+#define RICHACE_READ_NAMED_ATTRS		0x00000008
+#define RICHACE_WRITE_NAMED_ATTRS		0x00000010
+#define RICHACE_EXECUTE				0x00000020
+#define RICHACE_DELETE_CHILD			0x00000040
+#define RICHACE_READ_ATTRIBUTES			0x00000080
+#define RICHACE_WRITE_ATTRIBUTES		0x00000100
+#define RICHACE_WRITE_RETENTION			0x00000200
+#define RICHACE_WRITE_RETENTION_HOLD		0x00000400
+#define RICHACE_DELETE				0x00010000
+#define RICHACE_READ_ACL			0x00020000
+#define RICHACE_WRITE_ACL			0x00040000
+#define RICHACE_WRITE_OWNER			0x00080000
+#define RICHACE_SYNCHRONIZE			0x00100000
+
+/* e_id values */
+#define RICHACE_OWNER_SPECIAL_ID		0
+#define RICHACE_GROUP_SPECIAL_ID		1
+#define RICHACE_EVERYONE_SPECIAL_ID		2
+
+#define RICHACL_VALID_FLAGS (					\
+	RICHACL_WRITE_THROUGH |					\
+	RICHACL_MASKED )
+
+#define RICHACE_VALID_FLAGS (					\
+	RICHACE_FILE_INHERIT_ACE |				\
+	RICHACE_DIRECTORY_INHERIT_ACE |				\
+	RICHACE_NO_PROPAGATE_INHERIT_ACE |			\
+	RICHACE_INHERIT_ONLY_ACE |				\
+	RICHACE_IDENTIFIER_GROUP |				\
+	RICHACE_SPECIAL_WHO )
+
+#define RICHACE_INHERITANCE_FLAGS (				\
+	RICHACE_FILE_INHERIT_ACE |				\
+	RICHACE_DIRECTORY_INHERIT_ACE |				\
+	RICHACE_NO_PROPAGATE_INHERIT_ACE |			\
+	RICHACE_INHERIT_ONLY_ACE )
+
+/* Valid RICHACE_* flags for directories and non-directories */
+#define RICHACE_VALID_MASK (					\
+	RICHACE_READ_DATA | RICHACE_LIST_DIRECTORY |		\
+	RICHACE_WRITE_DATA | RICHACE_ADD_FILE |			\
+	RICHACE_APPEND_DATA | RICHACE_ADD_SUBDIRECTORY |	\
+	RICHACE_READ_NAMED_ATTRS |				\
+	RICHACE_WRITE_NAMED_ATTRS |				\
+	RICHACE_EXECUTE |					\
+	RICHACE_DELETE_CHILD |					\
+	RICHACE_READ_ATTRIBUTES |				\
+	RICHACE_WRITE_ATTRIBUTES |				\
+	RICHACE_WRITE_RETENTION |				\
+	RICHACE_WRITE_RETENTION_HOLD |				\
+	RICHACE_DELETE |					\
+	RICHACE_READ_ACL |					\
+	RICHACE_WRITE_ACL |					\
+	RICHACE_WRITE_OWNER |					\
+	RICHACE_SYNCHRONIZE )
+
+#endif /* __UAPI_RICHACL_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 07/23] richacl: Permission mapping functions
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (5 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 06/23] richacl: In-memory representation and helper functions Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 08/23] richacl: Compute maximum file masks from an acl Andreas Gruenbacher
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

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

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/richacl_base.c            | 118 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h      |   3 ++
 include/uapi/linux/richacl.h |  44 ++++++++++++++++
 3 files changed, 165 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index bcc6591..d0a4135 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -63,3 +63,121 @@ richace_copy(struct richace *to, const struct richace *from)
 {
 	memcpy(to, from, sizeof(struct richace));
 }
+
+/*
+ * richacl_mask_to_mode  -  compute the file permission bits from mask
+ * @mask:	%RICHACE_* permission mask
+ *
+ * Compute the file permission bits corresponding to a particular set of
+ * richacl permissions.
+ *
+ * See richacl_masks_to_mode().
+ */
+static int
+richacl_mask_to_mode(unsigned int mask)
+{
+	int mode = 0;
+
+	if (mask & RICHACE_POSIX_MODE_READ)
+		mode |= S_IROTH;
+	if (mask & RICHACE_POSIX_MODE_WRITE)
+		mode |= S_IWOTH;
+	if (mask & RICHACE_POSIX_MODE_EXEC)
+		mode |= S_IXOTH;
+
+	return mode;
+}
+
+/**
+ * richacl_masks_to_mode  -  compute file permission bits from file masks
+ *
+ * When setting a richacl, we set the file permission bits to indicate maximum
+ * permissions: for example, we set the Write permission when a mask contains
+ * RICHACE_APPEND_DATA even if it does not also contain RICHACE_WRITE_DATA.
+ *
+ * Permissions which are not in RICHACE_POSIX_MODE_READ,
+ * RICHACE_POSIX_MODE_WRITE, or RICHACE_POSIX_MODE_EXEC cannot be represented
+ * in the file permission bits.  Such permissions can still be effective, but
+ * not for new files or after a chmod(); they must be explicitly enabled in the
+ * richacl.
+ */
+int
+richacl_masks_to_mode(const struct richacl *acl)
+{
+	return richacl_mask_to_mode(acl->a_owner_mask) << 6 |
+	       richacl_mask_to_mode(acl->a_group_mask) << 3 |
+	       richacl_mask_to_mode(acl->a_other_mask);
+}
+EXPORT_SYMBOL_GPL(richacl_masks_to_mode);
+
+/**
+ * richacl_mode_to_mask  - compute a file mask from the lowest three mode bits
+ * @mode:	mode to convert to richacl permissions
+ *
+ * When the file permission bits of a file are set with chmod(), this specifies
+ * the maximum permissions that processes will get.  All permissions beyond
+ * that will be removed from the file masks, and become ineffective.
+ */
+unsigned int
+richacl_mode_to_mask(umode_t mode)
+{
+	unsigned int mask = 0;
+
+	if (mode & S_IROTH)
+		mask |= RICHACE_POSIX_MODE_READ;
+	if (mode & S_IWOTH)
+		mask |= RICHACE_POSIX_MODE_WRITE;
+	if (mode & S_IXOTH)
+		mask |= RICHACE_POSIX_MODE_EXEC;
+
+	return mask;
+}
+
+/**
+ * richacl_want_to_mask  - convert the iop->permission want argument to a mask
+ * @want:	@want argument of the permission inode operation
+ *
+ * When checking for append, @want is (MAY_WRITE | MAY_APPEND).
+ *
+ * Richacls use the iop->may_create and iop->may_delete hooks which are used
+ * for checking if creating and deleting files is allowed.  These hooks do not
+ * use richacl_want_to_mask(), so we do not have to deal with mapping MAY_WRITE
+ * to RICHACE_ADD_FILE, RICHACE_ADD_SUBDIRECTORY, and RICHACE_DELETE_CHILD
+ * here.
+ */
+unsigned int
+richacl_want_to_mask(unsigned int want)
+{
+	unsigned int mask = 0;
+
+	if (want & MAY_READ)
+		mask |= RICHACE_READ_DATA;
+	if (want & MAY_DELETE_SELF)
+		mask |= RICHACE_DELETE;
+	if (want & MAY_TAKE_OWNERSHIP)
+		mask |= RICHACE_WRITE_OWNER;
+	if (want & MAY_CHMOD)
+		mask |= RICHACE_WRITE_ACL;
+	if (want & MAY_SET_TIMES)
+		mask |= RICHACE_WRITE_ATTRIBUTES;
+	if (want & MAY_EXEC)
+		mask |= RICHACE_EXECUTE;
+	/*
+	 * differentiate MAY_WRITE from these request
+	 */
+	if (want & (MAY_APPEND |
+		    MAY_CREATE_FILE | MAY_CREATE_DIR |
+		    MAY_DELETE_CHILD)) {
+		if (want & MAY_APPEND)
+			mask |= RICHACE_APPEND_DATA;
+		if (want & MAY_CREATE_FILE)
+			mask |= RICHACE_ADD_FILE;
+		if (want & MAY_CREATE_DIR)
+			mask |= RICHACE_ADD_SUBDIRECTORY;
+		if (want & MAY_DELETE_CHILD)
+			mask |= RICHACE_DELETE_CHILD;
+	} else if (want & MAY_WRITE)
+		mask |= RICHACE_WRITE_DATA;
+	return mask;
+}
+EXPORT_SYMBOL_GPL(richacl_want_to_mask);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index edb8480..9102ef0 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -175,5 +175,8 @@ richace_is_same_identifier(const struct richace *a, const struct richace *b)
 extern struct richacl *richacl_alloc(int, gfp_t);
 extern struct richacl *richacl_clone(const struct richacl *, gfp_t);
 extern void richace_copy(struct richace *, const struct richace *);
+extern int richacl_masks_to_mode(const struct richacl *);
+extern unsigned int richacl_mode_to_mask(umode_t);
+extern unsigned int richacl_want_to_mask(unsigned int);
 
 #endif /* __RICHACL_H */
diff --git a/include/uapi/linux/richacl.h b/include/uapi/linux/richacl.h
index 08856f8..1ed48ac 100644
--- a/include/uapi/linux/richacl.h
+++ b/include/uapi/linux/richacl.h
@@ -96,4 +96,48 @@
 	RICHACE_WRITE_OWNER |					\
 	RICHACE_SYNCHRONIZE )
 
+/*
+ * The POSIX permissions are supersets of the following richacl permissions:
+ *
+ *  - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type
+ *    of the file system object.
+ *
+ *  - MAY_WRITE maps to WRITE_DATA or RICHACE_APPEND_DATA for files, and to
+ *    ADD_FILE, RICHACE_ADD_SUBDIRECTORY, or RICHACE_DELETE_CHILD for directories.
+ *
+ *  - MAY_EXECUTE maps to RICHACE_EXECUTE.
+ *
+ *  (Some of these richacl permissions have the same bit values.)
+ */
+#define RICHACE_POSIX_MODE_READ (			\
+		RICHACE_READ_DATA |			\
+		RICHACE_LIST_DIRECTORY)
+#define RICHACE_POSIX_MODE_WRITE (			\
+		RICHACE_WRITE_DATA |			\
+		RICHACE_ADD_FILE |			\
+		RICHACE_APPEND_DATA |			\
+		RICHACE_ADD_SUBDIRECTORY |		\
+		RICHACE_DELETE_CHILD)
+#define RICHACE_POSIX_MODE_EXEC RICHACE_EXECUTE
+#define RICHACE_POSIX_MODE_ALL (			\
+		RICHACE_POSIX_MODE_READ |		\
+		RICHACE_POSIX_MODE_WRITE |		\
+		RICHACE_POSIX_MODE_EXEC)
+
+/*
+ * These permissions are always allowed no matter what the acl says.
+ */
+#define RICHACE_POSIX_ALWAYS_ALLOWED (			\
+		RICHACE_SYNCHRONIZE |			\
+		RICHACE_READ_ATTRIBUTES |		\
+		RICHACE_READ_ACL)
+
+/*
+ * The owner is implicitly granted these permissions under POSIX.
+ */
+#define RICHACE_POSIX_OWNER_ALLOWED (			\
+		RICHACE_WRITE_ATTRIBUTES |		\
+		RICHACE_WRITE_OWNER |			\
+		RICHACE_WRITE_ACL)
+
 #endif /* __UAPI_RICHACL_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 08/23] richacl: Compute maximum file masks from an acl
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (6 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 07/23] richacl: Permission mapping functions Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 09/23] richacl: Permission check algorithm Andreas Gruenbacher
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Compute upper bound owner, group, and other file masks with as few
permissions as possible without denying any permissions that the NFSv4
acl in a richacl grants.

This algorithm is used when a file inherits an acl at create time and
when an acl is set via a mechanism that does not provide file masks
(such as setting an acl via nfsd).  When user-space sets an acl via
setxattr, the extended attribute already includes the file masks.

Setting an acl also sets the file mode permission bits: they are
determined by the file masks; see richacl_masks_to_mode().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/richacl_base.c       | 157 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   1 +
 2 files changed, 158 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index d0a4135..056228f 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -181,3 +181,160 @@ richacl_want_to_mask(unsigned int want)
 	return mask;
 }
 EXPORT_SYMBOL_GPL(richacl_want_to_mask);
+
+/*
+ * Note: functions like richacl_allowed_to_who(), richacl_group_class_allowed(),
+ * and richacl_compute_max_masks() iterate through the entire acl in reverse
+ * order as an optimization.
+ *
+ * In the standard algorithm, aces are considered in forward order.  When a
+ * process matches an ace, the permissions in the ace are either allowed or
+ * denied depending on the ace type.  Once a permission has been allowed or
+ * denied, it is no longer considered in further aces.
+ *
+ * By iterating through the acl in reverse order, we can compute the same
+ * result without having to keep track of which permissions have been allowed
+ * and denied already.
+ */
+
+/**
+ * richacl_allowed_to_who  -  permissions allowed to a specific who value
+ *
+ * Compute the maximum mask values allowed to a specific who value, taking
+ * everyone@ aces 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_identifier(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_group_class_allowed  -  maximum permissions of the group class
+ *
+ * Compute the maximum mask values allowed to a process in the group class
+ * (i.e., a process which is not the owner but is in the owning group or
+ * matches a user or group acl entry).  This includes permissions granted or
+ * denied by everyone@ aces.
+ *
+ * See richacl_compute_max_masks().
+ */
+static unsigned int richacl_group_class_allowed(struct richacl *acl)
+{
+	struct richace *ace;
+	unsigned int everyone_allowed = 0, group_class_allowed = 0;
+	int had_group_ace = 0;
+
+	richacl_for_each_entry_reverse(ace, acl) {
+		if (richace_is_inherit_only(ace) ||
+		    richace_is_owner(ace))
+			continue;
+
+		if (richace_is_everyone(ace)) {
+			if (richace_is_allow(ace))
+				everyone_allowed |= ace->e_mask;
+			else if (richace_is_deny(ace))
+				everyone_allowed &= ~ace->e_mask;
+		} else {
+			group_class_allowed |=
+				richacl_allowed_to_who(acl, ace);
+
+			if (richace_is_group(ace))
+				had_group_ace = 1;
+		}
+	}
+	/*
+	 * If the acl doesn't contain any group@ aces, richacl_allowed_to_who()
+	 * wasn't called for the owning group.  We could make that call now, but
+	 * we already know the result (everyone_allowed).
+	 */
+	if (!had_group_ace)
+		group_class_allowed |= everyone_allowed;
+	return group_class_allowed;
+}
+
+/**
+ * richacl_compute_max_masks  -  compute upper bound masks
+ *
+ * Computes upper bound owner, group, and other masks so that none of the
+ * permissions allowed by the acl are disabled.
+ *
+ * We don't make assumptions about who the owner is so that the owner can
+ * change with no effect on the file masks or file mode permission bits; this
+ * means that we must assume that all entries can match the owner.
+ */
+void richacl_compute_max_masks(struct richacl *acl)
+{
+	unsigned int gmask = ~0;
+	struct richace *ace;
+
+	/*
+	 * @gmask contains all permissions which the group class is ever
+	 * allowed.  We use it to avoid adding permissions to the group mask
+	 * from everyone@ allow aces which the group class is always denied
+	 * through other aces.  For example, the following acl would otherwise
+	 * result in a group mask of rw:
+	 *
+	 *	group@:w::deny
+	 *	everyone@:rw::allow
+	 *
+	 * Avoid computing @gmask for acls which do not include any group class
+	 * deny aces: in such acls, the group class is never denied any
+	 * permissions from everyone@ allow aces, and the group class cannot
+	 * have fewer permissions than the other class.
+	 */
+
+restart:
+	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)) {
+				acl->a_owner_mask |= ace->e_mask;
+				acl->a_group_mask |= ace->e_mask & gmask;
+				acl->a_other_mask |= ace->e_mask;
+			} else if (richace_is_deny(ace)) {
+				acl->a_owner_mask &= ~ace->e_mask;
+				acl->a_group_mask &= ~ace->e_mask;
+				acl->a_other_mask &= ~ace->e_mask;
+			}
+		} else {
+			if (richace_is_allow(ace)) {
+				acl->a_owner_mask |= ace->e_mask & gmask;
+				acl->a_group_mask |= ace->e_mask & gmask;
+			} else if (richace_is_deny(ace) && gmask == ~0) {
+				gmask = richacl_group_class_allowed(acl);
+				if (likely(gmask != ~0))
+					/* should always be true */
+					goto restart;
+			}
+		}
+	}
+
+	acl->a_flags &= ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED);
+}
+EXPORT_SYMBOL_GPL(richacl_compute_max_masks);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 9102ef0..3559b2c 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -178,5 +178,6 @@ extern void richace_copy(struct richace *, const struct richace *);
 extern int richacl_masks_to_mode(const struct richacl *);
 extern unsigned int richacl_mode_to_mask(umode_t);
 extern unsigned int richacl_want_to_mask(unsigned int);
+extern void richacl_compute_max_masks(struct richacl *);
 
 #endif /* __RICHACL_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 09/23] richacl: Permission check algorithm
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (7 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 08/23] richacl: Compute maximum file masks from an acl Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 10/23] posix_acl: Unexport acl_by_type and make it static Andreas Gruenbacher
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

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

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: "J. Bruce Fields" <bfields@fieldses.org>
---
 fs/Makefile             |   2 +-
 fs/richacl_inode.c      | 149 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   3 +
 3 files changed, 153 insertions(+), 1 deletion(-)
 create mode 100644 fs/richacl_inode.c

diff --git a/fs/Makefile b/fs/Makefile
index e5994c4..d5b45ca 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_SYSCTL)		+= drop_caches.o
 
 obj-$(CONFIG_FHANDLE)		+= fhandle.o
 obj-$(CONFIG_FS_RICHACL)	+= richacl.o
-richacl-y			:= richacl_base.o
+richacl-y			:= richacl_base.o richacl_inode.o
 
 obj-y				+= quota/
 
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
new file mode 100644
index 0000000..99b3c93
--- /dev/null
+++ b/fs/richacl_inode.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * 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/slab.h>
+#include <linux/richacl.h>
+
+/**
+ * richacl_permission  -  richacl permission check algorithm
+ * @inode:	inode to check
+ * @acl:	rich acl of the inode
+ * @want:	requested access (MAY_* flags)
+ *
+ * Checks if the current process is granted @mask flags in @acl.
+ */
+int
+richacl_permission(struct inode *inode, const struct richacl *acl,
+		   int want)
+{
+	const struct richace *ace;
+	unsigned int mask = richacl_want_to_mask(want);
+	unsigned int requested = mask, denied = 0;
+	int in_owning_group = in_group_p(inode->i_gid);
+	int in_owner_or_group_class = in_owning_group;
+
+	/*
+	 * 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.
+	 * The file class is only relevant for determining which file mask to
+	 * apply, which only happens for masked acls.
+	 */
+	if (acl->a_flags & RICHACL_MASKED) {
+		if ((acl->a_flags & RICHACL_WRITE_THROUGH) &&
+		    uid_eq(current_fsuid(), inode->i_uid)) {
+			denied = requested & ~acl->a_owner_mask;
+			goto out;
+		}
+	} else {
+		/*
+		 * When the acl is not masked, there is no need to determine if
+		 * the process is in the group class and we can break out
+		 * earlier of the loop below.
+		 */
+		in_owner_or_group_class = 1;
+	}
+
+	/*
+	 * Check if the acl grants the requested access and determine which
+	 * file class the process is in.
+	 */
+	richacl_for_each_entry(ace, acl) {
+		unsigned int ace_mask = ace->e_mask;
+
+		if (richace_is_inherit_only(ace))
+			continue;
+		if (richace_is_owner(ace)) {
+			if (!uid_eq(current_fsuid(), inode->i_uid))
+				continue;
+			goto entry_matches_owner;
+		} else if (richace_is_group(ace)) {
+			if (!in_owning_group)
+				continue;
+		} else if (richace_is_unix_user(ace)) {
+			if (!uid_eq(current_fsuid(), ace->e_id.uid))
+				continue;
+			if (uid_eq(current_fsuid(), inode->i_uid))
+				goto entry_matches_owner;
+		} else if (richace_is_unix_group(ace)) {
+			if (!in_group_p(ace->e_id.gid))
+				continue;
+		} else
+			goto entry_matches_everyone;
+
+		/*
+		 * Apply the group file mask to entries other than owner@ and
+		 * everyone@ or user entries matching the owner.  This ensures
+		 * that we grant the same permissions as the acl computed by
+		 * richacl_apply_masks().
+		 *
+		 * Without this restriction, the following richacl would grant
+		 * rw access to processes which are both the owner and in the
+		 * owning group, but not to other users in the owning group,
+		 * which could not be represented without masks:
+		 *
+		 *  owner:rw::mask
+		 *  group@:rw::allow
+		 */
+		if ((acl->a_flags & RICHACL_MASKED) && richace_is_allow(ace))
+			ace_mask &= acl->a_group_mask;
+
+entry_matches_owner:
+		/* The process is in the owner or group file class. */
+		in_owner_or_group_class = 1;
+
+entry_matches_everyone:
+		/* Check which mask flags the ACE allows or denies. */
+		if (richace_is_deny(ace))
+			denied |= ace_mask & mask;
+		mask &= ~ace_mask;
+
+		/*
+		 * Keep going until we know which file class
+		 * the process is in.
+		 */
+		if (!mask && in_owner_or_group_class)
+			break;
+	}
+	denied |= mask;
+
+	if (acl->a_flags & RICHACL_MASKED) {
+		/*
+		 * The file class a process is in determines which file mask
+		 * applies.  Check if that file mask also grants the requested
+		 * access.
+		 */
+		if (uid_eq(current_fsuid(), inode->i_uid))
+			denied |= requested & ~acl->a_owner_mask;
+		else if (in_owner_or_group_class)
+			denied |= requested & ~acl->a_group_mask;
+		else {
+			if (acl->a_flags & RICHACL_WRITE_THROUGH)
+				denied = requested & ~acl->a_other_mask;
+			else
+				denied |= requested & ~acl->a_other_mask;
+		}
+	}
+
+out:
+	return denied ? -EACCES : 0;
+}
+EXPORT_SYMBOL_GPL(richacl_permission);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 3559b2c..1d9f5f7 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -180,4 +180,7 @@ extern unsigned int richacl_mode_to_mask(umode_t);
 extern unsigned int richacl_want_to_mask(unsigned int);
 extern void richacl_compute_max_masks(struct richacl *);
 
+/* richacl_inode.c */
+extern int richacl_permission(struct inode *, const struct richacl *, int);
+
 #endif /* __RICHACL_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 10/23] posix_acl: Unexport acl_by_type and make it static
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (8 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 09/23] richacl: Permission check algorithm Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 11/23] posix_acl: Improve xattr fixup code Andreas Gruenbacher
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

acl_by_type(inode, type) returns a pointer to either inode->i_acl or
inode->i_default_acl depending on type.  This is useful in
fs/posix_acl.c, but should never have been visible outside that file.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 fs/posix_acl.c            | 3 +--
 include/linux/posix_acl.h | 1 -
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 711dd51..55f2445 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -21,7 +21,7 @@
 #include <linux/export.h>
 #include <linux/user_namespace.h>
 
-struct posix_acl **acl_by_type(struct inode *inode, int type)
+static struct posix_acl **acl_by_type(struct inode *inode, int type)
 {
 	switch (type) {
 	case ACL_TYPE_ACCESS:
@@ -32,7 +32,6 @@ struct posix_acl **acl_by_type(struct inode *inode, int type)
 		BUG();
 	}
 }
-EXPORT_SYMBOL(acl_by_type);
 
 struct posix_acl *get_cached_acl(struct inode *inode, int type)
 {
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index 3e96a6a..5b5a80c 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -99,7 +99,6 @@ extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
 extern int simple_set_acl(struct inode *, struct posix_acl *, int);
 extern int simple_acl_create(struct inode *, struct inode *);
 
-struct posix_acl **acl_by_type(struct inode *inode, int type);
 struct posix_acl *get_cached_acl(struct inode *inode, int type);
 struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
 void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 11/23] posix_acl: Improve xattr fixup code
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (9 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 10/23] posix_acl: Unexport acl_by_type and make it static Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 12/23] vfs: Cache base_acl objects in inodes Andreas Gruenbacher
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Both XATTR_NAME_POSIX_ACL_ACCESS and XATTR_NAME_POSIX_ACL_DEFAULT have
the same XATTR_SYSTEM_PREFIX prefix; don't check for the same prefix
repeatedly.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/xattr.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 4861322..c364696 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -295,6 +295,16 @@ out:
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
 
+static void
+fix_xattr_from_user(const char *kname, void *kvalue, size_t size)
+{
+	if (strncmp(kname, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
+		return;
+	kname += XATTR_SYSTEM_PREFIX_LEN;
+	if (!strcmp(kname, XATTR_POSIX_ACL_ACCESS) ||
+	    !strcmp(kname, XATTR_POSIX_ACL_DEFAULT))
+		posix_acl_fix_xattr_from_user(kvalue, size);
+}
 
 /*
  * Extended attribute SET operations
@@ -329,9 +339,7 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
 			error = -EFAULT;
 			goto out;
 		}
-		if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-		    (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
-			posix_acl_fix_xattr_from_user(kvalue, size);
+		fix_xattr_from_user(kname, kvalue, size);
 	}
 
 	error = vfs_setxattr(d, kname, kvalue, size, flags);
@@ -396,6 +404,17 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 	return error;
 }
 
+static void
+fix_xattr_to_user(const char *kname, void *kvalue, size_t size)
+{
+	if (strncmp(kname, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
+		return;
+	kname += XATTR_SYSTEM_PREFIX_LEN;
+	if (!strcmp(kname, XATTR_POSIX_ACL_ACCESS) ||
+	    !strcmp(kname, XATTR_POSIX_ACL_DEFAULT))
+		posix_acl_fix_xattr_to_user(kvalue, size);
+}
+
 /*
  * Extended attribute GET operations
  */
@@ -426,9 +445,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
 
 	error = vfs_getxattr(d, kname, kvalue, size);
 	if (error > 0) {
-		if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-		    (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
-			posix_acl_fix_xattr_to_user(kvalue, size);
+		fix_xattr_to_user(kname, kvalue, size);
 		if (size && copy_to_user(value, kvalue, error))
 			error = -EFAULT;
 	} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 12/23] vfs: Cache base_acl objects in inodes
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (10 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 11/23] posix_acl: Improve xattr fixup code Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 13/23] vfs: Add get_richacl and set_richacl inode operations Andreas Gruenbacher
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

POSIX ACLs and richacls are both objects allocated by kmalloc() with a
reference count which are freed by kfree_rcu().  An inode can either
cache an access and a default POSIX ACL, or a richacl (richacls do not
have default acls).  To allow an inode to cache either of the two kinds
of acls, introduce a new base_acl type and convert i_acl and
i_default_acl to that type. In most cases, the vfs then doesn't care which
kind of acl an inode caches (if any).

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 drivers/staging/lustre/lustre/llite/llite_lib.c |  2 +-
 fs/f2fs/acl.c                                   |  4 +--
 fs/inode.c                                      |  4 +--
 fs/jffs2/acl.c                                  | 10 ++++--
 fs/posix_acl.c                                  | 41 +++++++++++++------------
 fs/richacl_base.c                               |  4 +--
 include/linux/fs.h                              | 34 ++++++++++++++++++--
 include/linux/posix_acl.h                       | 12 +++-----
 include/linux/richacl.h                         |  9 +++---
 9 files changed, 75 insertions(+), 45 deletions(-)

diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
index b2fc5b3..b587944 100644
--- a/drivers/staging/lustre/lustre/llite/llite_lib.c
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -1073,7 +1073,7 @@ void ll_clear_inode(struct inode *inode)
 	}
 #ifdef CONFIG_FS_POSIX_ACL
 	else if (lli->lli_posix_acl) {
-		LASSERT(atomic_read(&lli->lli_posix_acl->a_refcount) == 1);
+		LASSERT(base_acl_refcount(&lli->lli_posix_acl->a_base) == 1);
 		LASSERT(lli->lli_remote_perms == NULL);
 		posix_acl_release(lli->lli_posix_acl);
 		lli->lli_posix_acl = NULL;
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index c8f25f7..9646197 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -270,7 +270,7 @@ static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
 				sizeof(struct posix_acl_entry);
 		clone = kmemdup(acl, size, flags);
 		if (clone)
-			atomic_set(&clone->a_refcount, 1);
+			base_acl_init(&clone->a_base);
 	}
 	return clone;
 }
@@ -282,7 +282,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 	umode_t mode = *mode_p;
 	int not_equiv = 0;
 
-	/* assert(atomic_read(acl->a_refcount) == 1); */
+	/* assert(base_acl_refcount(&acl->a_base) == 1); */
 
 	FOREACH_ACL_ENTRY(pa, acl, pe) {
 		switch(pa->e_tag) {
diff --git a/fs/inode.c b/fs/inode.c
index 69b8b52..101d806 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -239,9 +239,9 @@ void __destroy_inode(struct inode *inode)
 
 #ifdef CONFIG_FS_POSIX_ACL
 	if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
-		posix_acl_release(inode->i_acl);
+		base_acl_put(inode->i_acl);
 	if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
-		posix_acl_release(inode->i_default_acl);
+		base_acl_put(inode->i_default_acl);
 #endif
 	this_cpu_dec(nr_inodes);
 }
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 2f7a3c0..569cb1b 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -294,13 +294,19 @@ int jffs2_init_acl_post(struct inode *inode)
 	int rc;
 
 	if (inode->i_default_acl) {
-		rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, inode->i_default_acl);
+		struct posix_acl *default_acl = container_of(
+			inode->i_default_acl, struct posix_acl, a_base);
+
+		rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, default_acl);
 		if (rc)
 			return rc;
 	}
 
 	if (inode->i_acl) {
-		rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, inode->i_acl);
+		struct posix_acl *acl = container_of(
+			inode->i_acl, struct posix_acl, a_base);
+
+		rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, acl);
 		if (rc)
 			return rc;
 	}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 55f2445..743b6dc 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -21,7 +21,7 @@
 #include <linux/export.h>
 #include <linux/user_namespace.h>
 
-static struct posix_acl **acl_by_type(struct inode *inode, int type)
+static struct base_acl **acl_by_type(struct inode *inode, int type)
 {
 	switch (type) {
 	case ACL_TYPE_ACCESS:
@@ -35,63 +35,64 @@ static struct posix_acl **acl_by_type(struct inode *inode, int type)
 
 struct posix_acl *get_cached_acl(struct inode *inode, int type)
 {
-	struct posix_acl **p = acl_by_type(inode, type);
-	struct posix_acl *acl = ACCESS_ONCE(*p);
+	struct base_acl **p = acl_by_type(inode, type);
+	struct base_acl *acl = ACCESS_ONCE(*p);
 	if (acl) {
 		spin_lock(&inode->i_lock);
 		acl = *p;
 		if (acl != ACL_NOT_CACHED)
-			acl = posix_acl_dup(acl);
+			base_acl_get(acl);
 		spin_unlock(&inode->i_lock);
 	}
-	return acl;
+	return container_of(acl, struct posix_acl, a_base);
 }
 EXPORT_SYMBOL(get_cached_acl);
 
 struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
 {
-	return rcu_dereference(*acl_by_type(inode, type));
+	struct base_acl *acl = rcu_dereference(*acl_by_type(inode, type));
+	return container_of(acl, struct posix_acl, a_base);
 }
 EXPORT_SYMBOL(get_cached_acl_rcu);
 
 void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl)
 {
-	struct posix_acl **p = acl_by_type(inode, type);
-	struct posix_acl *old;
+	struct base_acl **p = acl_by_type(inode, type);
+	struct base_acl *old;
 	spin_lock(&inode->i_lock);
 	old = *p;
-	rcu_assign_pointer(*p, posix_acl_dup(acl));
+	rcu_assign_pointer(*p, &posix_acl_dup(acl)->a_base);
 	spin_unlock(&inode->i_lock);
 	if (old != ACL_NOT_CACHED)
-		posix_acl_release(old);
+		base_acl_put(old);
 }
 EXPORT_SYMBOL(set_cached_acl);
 
 void forget_cached_acl(struct inode *inode, int type)
 {
-	struct posix_acl **p = acl_by_type(inode, type);
-	struct posix_acl *old;
+	struct base_acl **p = acl_by_type(inode, type);
+	struct base_acl *old;
 	spin_lock(&inode->i_lock);
 	old = *p;
 	*p = ACL_NOT_CACHED;
 	spin_unlock(&inode->i_lock);
 	if (old != ACL_NOT_CACHED)
-		posix_acl_release(old);
+		base_acl_put(old);
 }
 EXPORT_SYMBOL(forget_cached_acl);
 
 void forget_all_cached_acls(struct inode *inode)
 {
-	struct posix_acl *old_access, *old_default;
+	struct base_acl *old_access, *old_default;
 	spin_lock(&inode->i_lock);
 	old_access = inode->i_acl;
 	old_default = inode->i_default_acl;
 	inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
 	spin_unlock(&inode->i_lock);
 	if (old_access != ACL_NOT_CACHED)
-		posix_acl_release(old_access);
+		base_acl_put(old_access);
 	if (old_default != ACL_NOT_CACHED)
-		posix_acl_release(old_default);
+		base_acl_put(old_default);
 }
 EXPORT_SYMBOL(forget_all_cached_acls);
 
@@ -128,7 +129,7 @@ EXPORT_SYMBOL(get_acl);
 void
 posix_acl_init(struct posix_acl *acl, int count)
 {
-	atomic_set(&acl->a_refcount, 1);
+	base_acl_init(&acl->a_base);
 	acl->a_count = count;
 }
 EXPORT_SYMBOL(posix_acl_init);
@@ -161,7 +162,7 @@ posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
 		           sizeof(struct posix_acl_entry);
 		clone = kmemdup(acl, size, flags);
 		if (clone)
-			atomic_set(&clone->a_refcount, 1);
+			base_acl_init(&clone->a_base);
 	}
 	return clone;
 }
@@ -383,7 +384,7 @@ static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 	umode_t mode = *mode_p;
 	int not_equiv = 0;
 
-	/* assert(atomic_read(acl->a_refcount) == 1); */
+	/* assert(base_acl_refcount(&acl->a_base) == 1); */
 
 	FOREACH_ACL_ENTRY(pa, acl, pe) {
                 switch(pa->e_tag) {
@@ -438,7 +439,7 @@ static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
 	struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 	struct posix_acl_entry *pa, *pe;
 
-	/* assert(atomic_read(acl->a_refcount) == 1); */
+	/* assert(base_acl_refcount(&acl->a_base) == 1); */
 
 	FOREACH_ACL_ENTRY(pa, acl, pe) {
 		switch(pa->e_tag) {
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 056228f..ce611d0 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -31,7 +31,7 @@ richacl_alloc(int count, gfp_t gfp)
 	struct richacl *acl = kzalloc(size, gfp);
 
 	if (acl) {
-		atomic_set(&acl->a_refcount, 1);
+		base_acl_init(&acl->a_base);
 		acl->a_count = count;
 	}
 	return acl;
@@ -50,7 +50,7 @@ richacl_clone(const struct richacl *acl, gfp_t gfp)
 
 	if (dup) {
 		memcpy(dup, acl, size);
-		atomic_set(&dup->a_refcount, 1);
+		base_acl_init(&dup->a_base);
 	}
 	return dup;
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0f560b7..3db7729 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -578,6 +578,12 @@ static inline void mapping_allow_writable(struct address_space *mapping)
 #define i_size_ordered_init(inode) do { } while (0)
 #endif
 
+struct base_acl {
+	union {
+		atomic_t ba_refcount;
+		struct rcu_head ba_rcu;
+	};
+};
 struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
 
@@ -597,9 +603,9 @@ struct inode {
 	kgid_t			i_gid;
 	unsigned int		i_flags;
 
-#ifdef CONFIG_FS_POSIX_ACL
-	struct posix_acl	*i_acl;
-	struct posix_acl	*i_default_acl;
+#if defined(CONFIG_FS_POSIX_ACL)
+	struct base_acl		*i_acl;
+	struct base_acl		*i_default_acl;
 #endif
 
 	const struct inode_operations	*i_op;
@@ -3097,4 +3103,26 @@ static inline bool dir_relax(struct inode *inode)
 extern bool path_noexec(const struct path *path);
 extern void inode_nohighmem(struct inode *inode);
 
+static inline void base_acl_get(struct base_acl *acl)
+{
+	if (acl)
+		atomic_inc(&acl->ba_refcount);
+}
+
+static inline void base_acl_put(struct base_acl *acl)
+{
+	if (acl && atomic_dec_and_test(&acl->ba_refcount))
+		kfree_rcu(acl, ba_rcu);
+}
+
+static inline void base_acl_init(struct base_acl *acl)
+{
+	atomic_set(&acl->ba_refcount, 1);
+}
+
+static inline int base_acl_refcount(struct base_acl *acl)
+{
+	return atomic_read(&acl->ba_refcount);
+}
+
 #endif /* _LINUX_FS_H */
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index 5b5a80c..cef5428 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -43,10 +43,7 @@ struct posix_acl_entry {
 };
 
 struct posix_acl {
-	union {
-		atomic_t		a_refcount;
-		struct rcu_head		a_rcu;
-	};
+	struct base_acl		a_base;  /* must be first, see posix_acl_release() */
 	unsigned int		a_count;
 	struct posix_acl_entry	a_entries[0];
 };
@@ -61,8 +58,7 @@ struct posix_acl {
 static inline struct posix_acl *
 posix_acl_dup(struct posix_acl *acl)
 {
-	if (acl)
-		atomic_inc(&acl->a_refcount);
+	base_acl_get(&acl->a_base);
 	return acl;
 }
 
@@ -72,8 +68,8 @@ posix_acl_dup(struct posix_acl *acl)
 static inline void
 posix_acl_release(struct posix_acl *acl)
 {
-	if (acl && atomic_dec_and_test(&acl->a_refcount))
-		kfree_rcu(acl, a_rcu);
+	BUILD_BUG_ON(offsetof(struct posix_acl, a_base) != 0);
+	base_acl_put(&acl->a_base);
 }
 
 
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 1d9f5f7..7628fad 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -31,7 +31,7 @@ struct richace {
 };
 
 struct richacl {
-	atomic_t	a_refcount;
+	struct base_acl	a_base;  /* must be first, see richacl_put() */
 	unsigned int	a_owner_mask;
 	unsigned int	a_group_mask;
 	unsigned int	a_other_mask;
@@ -56,8 +56,7 @@ struct richacl {
 static inline struct richacl *
 richacl_get(struct richacl *acl)
 {
-	if (acl)
-		atomic_inc(&acl->a_refcount);
+	base_acl_get(&acl->a_base);
 	return acl;
 }
 
@@ -67,8 +66,8 @@ richacl_get(struct richacl *acl)
 static inline void
 richacl_put(struct richacl *acl)
 {
-	if (acl && atomic_dec_and_test(&acl->a_refcount))
-		kfree(acl);
+	BUILD_BUG_ON(offsetof(struct richacl, a_base) != 0);
+	base_acl_put(&acl->a_base);
 }
 
 /**
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 13/23] vfs: Add get_richacl and set_richacl inode operations
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (11 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 12/23] vfs: Cache base_acl objects in inodes Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 14/23] vfs: Cache richacl in struct inode Andreas Gruenbacher
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

These operations are similar to the get_acl and set_acl operations for
POSIX ACLs.  The distinction between access and default ACLs doesn't exist
for richacls.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 include/linux/fs.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3db7729..e04dbb4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1682,6 +1682,7 @@ struct inode_operations {
 	const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
 	int (*permission) (struct inode *, int);
 	struct posix_acl * (*get_acl)(struct inode *, int);
+	struct richacl * (*get_richacl)(struct inode *);
 
 	int (*readlink) (struct dentry *, char __user *,int);
 
@@ -1710,6 +1711,7 @@ struct inode_operations {
 			   umode_t create_mode, int *opened);
 	int (*tmpfile) (struct inode *, struct dentry *, umode_t);
 	int (*set_acl)(struct inode *, struct posix_acl *, int);
+	int (*set_richacl)(struct inode *, struct richacl *);
 } ____cacheline_aligned;
 
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 14/23] vfs: Cache richacl in struct inode
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (12 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 13/23] vfs: Add get_richacl and set_richacl inode operations Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 15/23] richacl: Update the file masks in chmod() Andreas Gruenbacher
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Cache richacls in struct inode so that this doesn't have to be done
individually in each filesystem.  This is similar to POSIX ACLs.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/inode.c              | 11 +++++--
 fs/posix_acl.c          |  2 +-
 fs/richacl_inode.c      | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h      |  5 +++-
 include/linux/richacl.h |  6 ++++
 5 files changed, 96 insertions(+), 5 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index 101d806..de80116 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -180,8 +180,11 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
 	inode->i_private = NULL;
 	inode->i_mapping = mapping;
 	INIT_HLIST_HEAD(&inode->i_dentry);	/* buggered by rcu freeing */
-#ifdef CONFIG_FS_POSIX_ACL
-	inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
+#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL)
+	inode->i_acl = ACL_NOT_CACHED;
+# if defined(CONFIG_FS_POSIX_ACL)
+	inode->i_default_acl = ACL_NOT_CACHED;
+# endif
 #endif
 
 #ifdef CONFIG_FSNOTIFY
@@ -237,11 +240,13 @@ void __destroy_inode(struct inode *inode)
 		atomic_long_dec(&inode->i_sb->s_remove_count);
 	}
 
-#ifdef CONFIG_FS_POSIX_ACL
+#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL)
 	if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
 		base_acl_put(inode->i_acl);
+# if defined(CONFIG_FS_POSIX_ACL)
 	if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
 		base_acl_put(inode->i_default_acl);
+# endif
 #endif
 	this_cpu_dec(nr_inodes);
 }
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 743b6dc..f24646e 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -37,7 +37,7 @@ struct posix_acl *get_cached_acl(struct inode *inode, int type)
 {
 	struct base_acl **p = acl_by_type(inode, type);
 	struct base_acl *acl = ACCESS_ONCE(*p);
-	if (acl) {
+	if (acl && IS_POSIXACL(inode)) {
 		spin_lock(&inode->i_lock);
 		acl = *p;
 		if (acl != ACL_NOT_CACHED)
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index 99b3c93..52c1595 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -20,6 +20,83 @@
 #include <linux/slab.h>
 #include <linux/richacl.h>
 
+struct richacl *get_cached_richacl(struct inode *inode)
+{
+	struct base_acl *acl;
+
+	acl = ACCESS_ONCE(inode->i_acl);
+	if (acl && IS_RICHACL(inode)) {
+		spin_lock(&inode->i_lock);
+		acl = inode->i_acl;
+		if (acl != ACL_NOT_CACHED)
+			base_acl_get(acl);
+		spin_unlock(&inode->i_lock);
+	}
+	return container_of(acl, struct richacl, a_base);
+}
+EXPORT_SYMBOL_GPL(get_cached_richacl);
+
+struct richacl *get_cached_richacl_rcu(struct inode *inode)
+{
+	struct base_acl *acl = rcu_dereference(inode->i_acl);
+
+	return container_of(acl, struct richacl, a_base);
+}
+EXPORT_SYMBOL_GPL(get_cached_richacl_rcu);
+
+void set_cached_richacl(struct inode *inode, struct richacl *acl)
+{
+	struct base_acl *old = NULL;
+
+	spin_lock(&inode->i_lock);
+	old = inode->i_acl;
+	rcu_assign_pointer(inode->i_acl, &richacl_get(acl)->a_base);
+	spin_unlock(&inode->i_lock);
+	if (old != ACL_NOT_CACHED)
+		base_acl_put(old);
+}
+EXPORT_SYMBOL_GPL(set_cached_richacl);
+
+void forget_cached_richacl(struct inode *inode)
+{
+	struct base_acl *old = NULL;
+
+	spin_lock(&inode->i_lock);
+	old = inode->i_acl;
+	inode->i_acl = ACL_NOT_CACHED;
+	spin_unlock(&inode->i_lock);
+	if (old != ACL_NOT_CACHED)
+		base_acl_put(old);
+}
+EXPORT_SYMBOL_GPL(forget_cached_richacl);
+
+struct richacl *get_richacl(struct inode *inode)
+{
+	struct richacl *acl;
+
+	acl = get_cached_richacl(inode);
+	if (acl != ACL_NOT_CACHED)
+		return acl;
+
+	if (!IS_RICHACL(inode))
+		return NULL;
+
+	/*
+	 * A filesystem can force a ACL callback by just never filling the
+	 * ACL cache. But normally you'd fill the cache either at inode
+	 * instantiation time, or on the first ->get_richacl call.
+	 *
+	 * If the filesystem doesn't have a get_richacl() function at all,
+	 * we'll just create the negative cache entry.
+	 */
+	if (!inode->i_op->get_richacl) {
+		set_cached_richacl(inode, NULL);
+		return NULL;
+	}
+	return inode->i_op->get_richacl(inode);
+}
+EXPORT_SYMBOL_GPL(get_richacl);
+
 /**
  * richacl_permission  -  richacl permission check algorithm
  * @inode:	inode to check
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e04dbb4..560f4e0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -585,6 +585,7 @@ struct base_acl {
 	};
 };
 struct posix_acl;
+struct richacl;
 #define ACL_NOT_CACHED ((void *)(-1))
 
 #define IOP_FASTPERM	0x0001
@@ -603,9 +604,11 @@ struct inode {
 	kgid_t			i_gid;
 	unsigned int		i_flags;
 
-#if defined(CONFIG_FS_POSIX_ACL)
+#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL)
 	struct base_acl		*i_acl;
+# if defined(CONFIG_FS_POSIX_ACL)
 	struct base_acl		*i_default_acl;
+# endif
 #endif
 
 	const struct inode_operations	*i_op;
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 7628fad..7bf912b6 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -70,6 +70,12 @@ richacl_put(struct richacl *acl)
 	base_acl_put(&acl->a_base);
 }
 
+extern struct richacl *get_cached_richacl(struct inode *);
+extern struct richacl *get_cached_richacl_rcu(struct inode *);
+extern void set_cached_richacl(struct inode *, struct richacl *);
+extern void forget_cached_richacl(struct inode *);
+extern struct richacl *get_richacl(struct inode *);
+
 /**
  * richace_is_owner  -  check if @ace is an OWNER@ entry
  */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 15/23] richacl: Update the file masks in chmod()
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (13 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 14/23] vfs: Cache richacl in struct inode Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 16/23] richacl: Check if an acl is equivalent to a file mode Andreas Gruenbacher
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Doing a chmod() sets the file mode, which includes the file permission
bits.  When a file has a richacl, the permissions that the richacl
grants need to be limited to what the new file permission bits allow.

This is done by setting the file masks in the richacl to what the file
permission bits map to.  The richacl access check algorithm takes the
file masks into account, which ensures that the richacl cannot grant too
many permissions.

It is possible to explicitly add permissions to the file masks which go
beyond what the file permission bits can grant (like the
RICHACE_WRITE_ACL permission).  The POSIX.1 standard calls this an
alternate file access control mechanism.  A subsequent chmod() would
ensure that those permissions are disabled again.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/richacl_base.c       | 42 ++++++++++++++++++++++++++++++++++++++++++
 fs/richacl_inode.c      | 30 ++++++++++++++++++++++++++++++
 include/linux/richacl.h |  2 ++
 3 files changed, 74 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index ce611d0..9976bf1 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -338,3 +338,45 @@ restart:
 	acl->a_flags &= ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED);
 }
 EXPORT_SYMBOL_GPL(richacl_compute_max_masks);
+
+/**
+ * __richacl_chmod  -  update the file masks to reflect the new mode
+ * @acl:	access control list
+ * @mode:	new file permission bits including the file type
+ *
+ * Return a copy of @acl where the file masks have been replaced by the file
+ * masks corresponding to the file permission bits in @mode, or returns @acl
+ * itself if the file masks are already up to date.  Takes over a reference
+ * to @acl.
+ */
+struct richacl *
+__richacl_chmod(struct richacl *acl, umode_t mode)
+{
+	unsigned int x = S_ISDIR(mode) ? 0 : RICHACE_DELETE_CHILD;
+	unsigned int owner_mask, group_mask, other_mask;
+	struct richacl *clone;
+
+	owner_mask = richacl_mode_to_mask(mode >> 6) & ~x;
+	group_mask = richacl_mode_to_mask(mode >> 3) & ~x;
+	other_mask = richacl_mode_to_mask(mode)      & ~x;
+
+	if (acl->a_owner_mask == owner_mask &&
+	    acl->a_group_mask == group_mask &&
+	    acl->a_other_mask == other_mask &&
+	    (acl->a_flags & RICHACL_MASKED) &&
+	    (acl->a_flags & RICHACL_WRITE_THROUGH))
+		return acl;
+
+	clone = richacl_clone(acl, GFP_KERNEL);
+	richacl_put(acl);
+	if (!clone)
+		return ERR_PTR(-ENOMEM);
+
+	clone->a_flags |= (RICHACL_WRITE_THROUGH | RICHACL_MASKED);
+	clone->a_owner_mask = owner_mask;
+	clone->a_group_mask = group_mask;
+	clone->a_other_mask = other_mask;
+
+	return clone;
+}
+EXPORT_SYMBOL_GPL(__richacl_chmod);
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index 52c1595..e329826 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -224,3 +224,33 @@ out:
 	return denied ? -EACCES : 0;
 }
 EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * richacl_chmod  -  filesystem chmod helper
+ * @inode:	inode whose file permission bits to change
+ * @mode:	new file permission bits including the file type
+ *
+ * Helper for filesystems to use to perform a chmod on the richacl of an inode.
+ */
+int
+richacl_chmod(struct inode *inode, umode_t mode)
+{
+	struct richacl *acl;
+	int retval;
+
+	if (S_ISLNK(mode))
+		return -EOPNOTSUPP;
+	if (!inode->i_op->set_richacl)
+		return -EOPNOTSUPP;
+	acl = get_richacl(inode);
+	if (IS_ERR_OR_NULL(acl))
+		return PTR_ERR(acl);
+	acl = __richacl_chmod(acl, mode);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	retval = inode->i_op->set_richacl(inode, acl);
+	richacl_put(acl);
+
+	return retval;
+}
+EXPORT_SYMBOL(richacl_chmod);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 7bf912b6..a2d5600 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -184,8 +184,10 @@ extern int richacl_masks_to_mode(const struct richacl *);
 extern unsigned int richacl_mode_to_mask(umode_t);
 extern unsigned int richacl_want_to_mask(unsigned int);
 extern void richacl_compute_max_masks(struct richacl *);
+extern struct richacl *__richacl_chmod(struct richacl *, umode_t);
 
 /* richacl_inode.c */
 extern int richacl_permission(struct inode *, const struct richacl *, int);
+extern int richacl_chmod(struct inode *, umode_t);
 
 #endif /* __RICHACL_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 16/23] richacl: Check if an acl is equivalent to a file mode
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (14 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 15/23] richacl: Update the file masks in chmod() Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 17/23] richacl: Create-time inheritance Andreas Gruenbacher
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

ACLs are considered equivalent to file modes if they only consist of
owner@, group@, and everyone@ entries, the owner@ permissions do not
depend on whether the owner is a member in the owning group, and no
inheritance flags are set.  This test is used to avoid storing richacls
if the acl can be computed from the file permission bits.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
 fs/richacl_base.c       | 104 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   1 +
 2 files changed, 105 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 9976bf1..5fc1cfc 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -380,3 +380,107 @@ __richacl_chmod(struct richacl *acl, umode_t mode)
 	return clone;
 }
 EXPORT_SYMBOL_GPL(__richacl_chmod);
+
+/**
+ * richacl_equiv_mode  -  compute the mode equivalent of @acl
+ *
+ * An acl is considered equivalent to a file mode if it only consists of
+ * owner@, group@, and everyone@ entries and the owner@ permissions do not
+ * depend on whether the owner is a member in the owning group.
+ */
+int
+richacl_equiv_mode(const struct richacl *acl, umode_t *mode_p)
+{
+	umode_t mode = *mode_p;
+
+	/*
+	 * The RICHACE_DELETE_CHILD flag is meaningless for non-directories, so
+	 * we ignore it.
+	 */
+	unsigned int x = S_ISDIR(mode) ? 0 : RICHACE_DELETE_CHILD;
+	struct {
+		unsigned int allowed;
+		unsigned int defined;  /* allowed or denied */
+	} owner = {
+		.defined = RICHACE_POSIX_ALWAYS_ALLOWED |
+			   RICHACE_POSIX_OWNER_ALLOWED  | x,
+	}, group = {
+		.defined = RICHACE_POSIX_ALWAYS_ALLOWED | x,
+	}, everyone = {
+		.defined = RICHACE_POSIX_ALWAYS_ALLOWED | x,
+	};
+	const struct richace *ace;
+
+	if (acl->a_flags & ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED))
+		return -1;
+
+	richacl_for_each_entry(ace, acl) {
+		if (ace->e_flags & ~RICHACE_SPECIAL_WHO)
+			return -1;
+
+		if (richace_is_owner(ace) || richace_is_everyone(ace)) {
+			x = ace->e_mask & ~owner.defined;
+			if (richace_is_allow(ace)) {
+				unsigned int group_denied =
+					group.defined & ~group.allowed;
+
+				if (x & group_denied)
+					return -1;
+				owner.allowed |= x;
+			} else /* if (richace_is_deny(ace)) */ {
+				if (x & group.allowed)
+					return -1;
+			}
+			owner.defined |= x;
+
+			if (richace_is_everyone(ace)) {
+				x = ace->e_mask;
+				if (richace_is_allow(ace)) {
+					group.allowed |=
+						x & ~group.defined;
+					everyone.allowed |=
+						x & ~everyone.defined;
+				}
+				group.defined |= x;
+				everyone.defined |= x;
+			}
+		} else if (richace_is_group(ace)) {
+			x = ace->e_mask & ~group.defined;
+			if (richace_is_allow(ace))
+				group.allowed |= x;
+			group.defined |= x;
+		} else
+			return -1;
+	}
+
+	if (group.allowed & ~owner.defined)
+		return -1;
+
+	if (acl->a_flags & RICHACL_MASKED) {
+		if (acl->a_flags & RICHACL_WRITE_THROUGH) {
+			owner.allowed = acl->a_owner_mask;
+			everyone.allowed = acl->a_other_mask;
+		} else {
+			owner.allowed &= acl->a_owner_mask;
+			everyone.allowed &= acl->a_other_mask;
+		}
+		group.allowed &= acl->a_group_mask;
+	}
+
+	mode = (mode & ~S_IRWXUGO) |
+	       (richacl_mask_to_mode(owner.allowed) << 6) |
+	       (richacl_mask_to_mode(group.allowed) << 3) |
+		richacl_mask_to_mode(everyone.allowed);
+
+	/* Mask flags we can ignore */
+	x = S_ISDIR(mode) ? 0 : RICHACE_DELETE_CHILD;
+
+	if (((richacl_mode_to_mask(mode >> 6) ^ owner.allowed)    & ~x) ||
+	    ((richacl_mode_to_mask(mode >> 3) ^ group.allowed)    & ~x) ||
+	    ((richacl_mode_to_mask(mode)      ^ everyone.allowed) & ~x))
+		return -1;
+
+	*mode_p = mode;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(richacl_equiv_mode);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index a2d5600..b7128bf 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -185,6 +185,7 @@ extern unsigned int richacl_mode_to_mask(umode_t);
 extern unsigned int richacl_want_to_mask(unsigned int);
 extern void richacl_compute_max_masks(struct richacl *);
 extern struct richacl *__richacl_chmod(struct richacl *, umode_t);
+extern int richacl_equiv_mode(const struct richacl *, umode_t *);
 
 /* richacl_inode.c */
 extern int richacl_permission(struct inode *, const struct richacl *, int);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 17/23] richacl: Create-time inheritance
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (15 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 16/23] richacl: Check if an acl is equivalent to a file mode Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 18/23] richacl: Automatic Inheritance Andreas Gruenbacher
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

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

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

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/richacl_base.c       | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/richacl_inode.c      | 70 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  2 ++
 3 files changed, 153 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 5fc1cfc..b66d940 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -484,3 +484,84 @@ richacl_equiv_mode(const struct richacl *acl, umode_t *mode_p)
 	return 0;
 }
 EXPORT_SYMBOL_GPL(richacl_equiv_mode);
+
+static inline bool
+ace_inherits_to_directory(const struct richace *ace)
+{
+	if (ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE)
+		return true;
+	if ((ace->e_flags & RICHACE_FILE_INHERIT_ACE) &&
+	    !(ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE))
+		return true;
+	return false;
+}
+
+/**
+ * richacl_inherit  -  compute the inherited acl of a new file
+ * @dir_acl:	acl of the containing directory
+ * @isdir:	inherit by a directory or non-directory?
+ *
+ * A directory can have acl entries which files and/or directories created
+ * inside the directory will inherit.  This function computes the acl for such
+ * a new file.  If there is no inheritable acl, it will return %NULL.
+ */
+struct richacl *
+richacl_inherit(const struct richacl *dir_acl, int isdir)
+{
+	const struct richace *dir_ace;
+	struct richacl *acl = NULL;
+	struct richace *ace;
+	int count = 0;
+
+	if (isdir) {
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!ace_inherits_to_directory(dir_ace))
+				continue;
+			count++;
+		}
+		if (!count)
+			return NULL;
+		acl = richacl_alloc(count, GFP_KERNEL);
+		if (!acl)
+			return ERR_PTR(-ENOMEM);
+		ace = acl->a_entries;
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!ace_inherits_to_directory(dir_ace))
+				continue;
+			richace_copy(ace, dir_ace);
+			if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE)
+				ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
+			else if (dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE)
+				ace->e_flags &= ~RICHACE_INHERIT_ONLY_ACE;
+			else
+				ace->e_flags |= RICHACE_INHERIT_ONLY_ACE;
+			ace++;
+		}
+	} else {
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
+				continue;
+			count++;
+		}
+		if (!count)
+			return NULL;
+		acl = richacl_alloc(count, GFP_KERNEL);
+		if (!acl)
+			return ERR_PTR(-ENOMEM);
+		ace = acl->a_entries;
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
+				continue;
+			richace_copy(ace, dir_ace);
+			ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
+			/*
+			 * RICHACE_DELETE_CHILD is meaningless for
+			 * non-directories, so clear it.
+			 */
+			ace->e_mask &= ~RICHACE_DELETE_CHILD;
+			ace++;
+		}
+	}
+
+	return acl;
+}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index e329826..ec3d2c8 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -254,3 +254,73 @@ richacl_chmod(struct inode *inode, umode_t mode)
 	return retval;
 }
 EXPORT_SYMBOL(richacl_chmod);
+
+/*
+ * richacl_inherit_inode  -  compute inherited acl and file mode
+ * @dir_acl:	acl of the containing directory
+ * @mode_p:	mode of the new inode
+ *
+ * The file permission bits in @mode_p must be set to the create mode by the
+ * caller.
+ *
+ * If there is an inheritable acl, the maximum permissions that the acl grants
+ * are computed and the file masks of the new acl are set accordingly.
+ */
+static struct richacl *
+richacl_inherit_inode(const struct richacl *dir_acl, umode_t *mode_p)
+{
+	struct richacl *acl;
+	umode_t mode = *mode_p;
+
+	acl = richacl_inherit(dir_acl, S_ISDIR(mode));
+	if (acl) {
+		if (richacl_equiv_mode(acl, &mode) == 0) {
+			*mode_p &= mode;
+			richacl_put(acl);
+			acl = NULL;
+		} else {
+			richacl_compute_max_masks(acl);
+			/*
+			 * Ensure that the acl will not grant any permissions
+			 * beyond the create mode.
+			 */
+			acl->a_flags |= RICHACL_MASKED;
+			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);
+		}
+	} else
+		*mode_p &= ~current_umask();
+
+	return acl;
+}
+
+/**
+ * richacl_create  -  filesystem create helper
+ * @mode_p:	mode of the new inode
+ * @dir:	containing directory
+ *
+ * Compute the inherited acl for a new inode.  If there is no acl to inherit,
+ * apply the umask.  Use when creating a new inode on a richacl enabled file
+ * system.
+ */
+struct richacl *richacl_create(umode_t *mode_p, struct inode *dir)
+{
+	struct richacl *dir_acl, *acl = NULL;
+
+	if (S_ISLNK(*mode_p))
+		return NULL;
+	dir_acl = get_richacl(dir);
+	if (dir_acl) {
+		if (IS_ERR(dir_acl))
+			return dir_acl;
+		acl = richacl_inherit_inode(dir_acl, mode_p);
+		richacl_put(dir_acl);
+	} else
+		*mode_p &= ~current_umask();
+	return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_create);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index b7128bf..c8fae91a 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -186,9 +186,11 @@ extern unsigned int richacl_want_to_mask(unsigned int);
 extern void richacl_compute_max_masks(struct richacl *);
 extern struct richacl *__richacl_chmod(struct richacl *, umode_t);
 extern int richacl_equiv_mode(const struct richacl *, umode_t *);
+extern struct richacl *richacl_inherit(const struct richacl *, int);
 
 /* richacl_inode.c */
 extern int richacl_permission(struct inode *, const struct richacl *, int);
 extern int richacl_chmod(struct inode *, umode_t);
+extern struct richacl *richacl_create(umode_t *, struct inode *);
 
 #endif /* __RICHACL_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 18/23] richacl: Automatic Inheritance
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (16 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 17/23] richacl: Create-time inheritance Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 19/23] richacl: xattr mapping functions Andreas Gruenbacher
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Automatic Inheritance (AI) allows changes to the acl of a directory to
propagate down to children.

This is mostly implemented in user space: when a process changes the
permissions of a directory and Automatic Inheritance is enabled for that
directory, the process must propagate those changes to all children,
recursively.

The kernel enables this by keeping track of which permissions have been
inherited at create time.  In addition, it makes sure that permission
propagation is turned off when the permissions are set explicitly (for
example, upon create or chmod).

Automatic Inheritance works as follows:

 - When the RICHACL_AUTO_INHERIT flag in the acl of a file or directory
   is not set, the file or directory is not affected by AI.

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

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

Linux does not have a way of creating files or directories without setting the
file permission bits, so all files created inside a directory with
RICHACL_AUTO_INHERIT set will have the RICHACL_PROTECTED flag set.  This
effectively disables Automatic Inheritance.

Protocols which support creating files without specifying permissions can
explicitly clear the RICHACL_PROTECTED flag after creating a file and reset the
file masks to "undo" applying the create mode; see richacl_compute_max_masks().
They should set the RICHACL_DEFAULTED flag.  (A mechanism that would allow to
indicate to the kernel to ignore the create mode in the first place when there
are inherited permissions would be nice to have.)

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/richacl_base.c            | 13 ++++++++++++-
 fs/richacl_inode.c           |  7 +++++++
 include/linux/richacl.h      | 12 ++++++++++++
 include/uapi/linux/richacl.h | 11 ++++++++++-
 4 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index b66d940..724b5e0 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -364,7 +364,8 @@ __richacl_chmod(struct richacl *acl, umode_t mode)
 	    acl->a_group_mask == group_mask &&
 	    acl->a_other_mask == other_mask &&
 	    (acl->a_flags & RICHACL_MASKED) &&
-	    (acl->a_flags & RICHACL_WRITE_THROUGH))
+	    (acl->a_flags & RICHACL_WRITE_THROUGH) &&
+	    (!richacl_is_auto_inherit(acl) || richacl_is_protected(acl)))
 		return acl;
 
 	clone = richacl_clone(acl, GFP_KERNEL);
@@ -376,6 +377,8 @@ __richacl_chmod(struct richacl *acl, umode_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 |= RICHACL_PROTECTED;
 
 	return clone;
 }
@@ -562,6 +565,14 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
 			ace++;
 		}
 	}
+	if (richacl_is_auto_inherit(dir_acl)) {
+		acl->a_flags = RICHACL_AUTO_INHERIT;
+		richacl_for_each_entry(ace, acl)
+			ace->e_flags |= RICHACE_INHERITED_ACE;
+	} else {
+		richacl_for_each_entry(ace, acl)
+			ace->e_flags &= ~RICHACE_INHERITED_ACE;
+	}
 
 	return acl;
 }
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index ec3d2c8..99a1ab6 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -279,6 +279,13 @@ richacl_inherit_inode(const struct richacl *dir_acl, umode_t *mode_p)
 			richacl_put(acl);
 			acl = NULL;
 		} else {
+			/*
+			 * We need to set RICHACL_PROTECTED because we are
+			 * doing an implicit chmod
+			 */
+			if (richacl_is_auto_inherit(acl))
+				acl->a_flags |= RICHACL_PROTECTED;
+
 			richacl_compute_max_masks(acl);
 			/*
 			 * Ensure that the acl will not grant any permissions
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index c8fae91a..c524a4b 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -76,6 +76,18 @@ extern void set_cached_richacl(struct inode *, struct richacl *);
 extern void forget_cached_richacl(struct inode *);
 extern struct richacl *get_richacl(struct inode *);
 
+static inline int
+richacl_is_auto_inherit(const struct richacl *acl)
+{
+	return acl->a_flags & RICHACL_AUTO_INHERIT;
+}
+
+static inline int
+richacl_is_protected(const struct richacl *acl)
+{
+	return acl->a_flags & RICHACL_PROTECTED;
+}
+
 /**
  * richace_is_owner  -  check if @ace is an OWNER@ entry
  */
diff --git a/include/uapi/linux/richacl.h b/include/uapi/linux/richacl.h
index 1ed48ac..8849a53 100644
--- a/include/uapi/linux/richacl.h
+++ b/include/uapi/linux/richacl.h
@@ -18,6 +18,9 @@
 #define __UAPI_RICHACL_H
 
 /* a_flags values */
+#define RICHACL_AUTO_INHERIT			0x01
+#define RICHACL_PROTECTED			0x02
+#define RICHACL_DEFAULTED			0x04
 #define RICHACL_WRITE_THROUGH			0x40
 #define RICHACL_MASKED				0x80
 
@@ -31,6 +34,7 @@
 #define RICHACE_NO_PROPAGATE_INHERIT_ACE	0x0004
 #define RICHACE_INHERIT_ONLY_ACE		0x0008
 #define RICHACE_IDENTIFIER_GROUP		0x0040
+#define RICHACE_INHERITED_ACE			0x0080
 #define RICHACE_SPECIAL_WHO			0x4000
 
 /* e_mask bitflags */
@@ -60,6 +64,9 @@
 #define RICHACE_EVERYONE_SPECIAL_ID		2
 
 #define RICHACL_VALID_FLAGS (					\
+	RICHACL_AUTO_INHERIT |					\
+	RICHACL_PROTECTED |					\
+	RICHACL_DEFAULTED |					\
 	RICHACL_WRITE_THROUGH |					\
 	RICHACL_MASKED )
 
@@ -69,13 +76,15 @@
 	RICHACE_NO_PROPAGATE_INHERIT_ACE |			\
 	RICHACE_INHERIT_ONLY_ACE |				\
 	RICHACE_IDENTIFIER_GROUP |				\
+	RICHACE_INHERITED_ACE |					\
 	RICHACE_SPECIAL_WHO )
 
 #define RICHACE_INHERITANCE_FLAGS (				\
 	RICHACE_FILE_INHERIT_ACE |				\
 	RICHACE_DIRECTORY_INHERIT_ACE |				\
 	RICHACE_NO_PROPAGATE_INHERIT_ACE |			\
-	RICHACE_INHERIT_ONLY_ACE )
+	RICHACE_INHERIT_ONLY_ACE |				\
+	RICHACE_INHERITED_ACE )
 
 /* Valid RICHACE_* flags for directories and non-directories */
 #define RICHACE_VALID_MASK (					\
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 19/23] richacl: xattr mapping functions
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (17 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 18/23] richacl: Automatic Inheritance Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 20/23] richacl: Add richacl xattr handler Andreas Gruenbacher
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

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

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/Makefile                        |   2 +-
 fs/richacl_xattr.c                 | 161 +++++++++++++++++++++++++++++++++++++
 include/linux/richacl_xattr.h      |  29 +++++++
 include/uapi/linux/Kbuild          |   1 +
 include/uapi/linux/richacl_xattr.h |  44 ++++++++++
 include/uapi/linux/xattr.h         |   2 +
 6 files changed, 238 insertions(+), 1 deletion(-)
 create mode 100644 fs/richacl_xattr.c
 create mode 100644 include/linux/richacl_xattr.h
 create mode 100644 include/uapi/linux/richacl_xattr.h

diff --git a/fs/Makefile b/fs/Makefile
index d5b45ca..2d6d224 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_SYSCTL)		+= drop_caches.o
 
 obj-$(CONFIG_FHANDLE)		+= fhandle.o
 obj-$(CONFIG_FS_RICHACL)	+= richacl.o
-richacl-y			:= richacl_base.o richacl_inode.o
+richacl-y			:= richacl_base.o richacl_inode.o richacl_xattr.o
 
 obj-y				+= quota/
 
diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
new file mode 100644
index 0000000..dc1ad36
--- /dev/null
+++ b/fs/richacl_xattr.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * 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/fs.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/richacl_xattr.h>
+
+/**
+ * richacl_from_xattr  -  convert a richacl xattr into the in-memory representation
+ */
+struct richacl *
+richacl_from_xattr(struct user_namespace *user_ns,
+		   const void *value, size_t size, int invalid_error)
+{
+	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(*xattr_acl) ||
+	    xattr_acl->a_version != RICHACL_XATTR_VERSION ||
+	    (xattr_acl->a_flags & ~RICHACL_VALID_FLAGS))
+		goto invalid;
+	size -= sizeof(*xattr_acl);
+	count = le16_to_cpu(xattr_acl->a_count);
+	if (count > RICHACL_XATTR_MAX_COUNT)
+		goto invalid;
+	if (size != count * sizeof(*xattr_ace))
+		goto invalid;
+
+	acl = richacl_alloc(count, GFP_NOFS);
+	if (!acl)
+		return ERR_PTR(-ENOMEM);
+
+	acl->a_flags = xattr_acl->a_flags;
+	acl->a_owner_mask = le32_to_cpu(xattr_acl->a_owner_mask);
+	if (acl->a_owner_mask & ~RICHACE_VALID_MASK)
+		goto put_invalid;
+	acl->a_group_mask = le32_to_cpu(xattr_acl->a_group_mask);
+	if (acl->a_group_mask & ~RICHACE_VALID_MASK)
+		goto put_invalid;
+	acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
+	if (acl->a_other_mask & ~RICHACE_VALID_MASK)
+		goto put_invalid;
+
+	richacl_for_each_entry(ace, acl) {
+		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);
+
+		if (ace->e_flags & ~RICHACE_VALID_FLAGS)
+			goto put_invalid;
+		if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+			ace->e_id.special = le32_to_cpu(xattr_ace->e_id);
+			if (ace->e_id.special > RICHACE_EVERYONE_SPECIAL_ID)
+				goto put_invalid;
+		} else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+			u32 id = le32_to_cpu(xattr_ace->e_id);
+
+			ace->e_id.gid = make_kgid(user_ns, id);
+			if (!gid_valid(ace->e_id.gid))
+				goto put_invalid;
+		} else {
+			u32 id = le32_to_cpu(xattr_ace->e_id);
+
+			ace->e_id.uid = make_kuid(user_ns, id);
+			if (!uid_valid(ace->e_id.uid))
+				goto put_invalid;
+		}
+		if (ace->e_type > RICHACE_ACCESS_DENIED_ACE_TYPE ||
+		    (ace->e_mask & ~RICHACE_VALID_MASK))
+			goto put_invalid;
+
+		xattr_ace++;
+	}
+
+	return acl;
+
+put_invalid:
+	richacl_put(acl);
+invalid:
+	return ERR_PTR(invalid_error);
+}
+EXPORT_SYMBOL_GPL(richacl_from_xattr);
+
+/**
+ * richacl_xattr_size  -  compute the size of the xattr representation of @acl
+ */
+size_t
+richacl_xattr_size(const struct richacl *acl)
+{
+	size_t size = sizeof(struct richacl_xattr);
+
+	size += sizeof(struct richace_xattr) * acl->a_count;
+	return size;
+}
+EXPORT_SYMBOL_GPL(richacl_xattr_size);
+
+/**
+ * richacl_to_xattr  -  convert @acl into its xattr representation
+ * @acl:	the richacl to convert
+ * @buffer:	buffer for the result
+ * @size:	size of @buffer
+ */
+int
+richacl_to_xattr(struct user_namespace *user_ns,
+		 const struct richacl *acl, void *buffer, size_t size)
+{
+	struct richacl_xattr *xattr_acl = buffer;
+	struct richace_xattr *xattr_ace;
+	const struct richace *ace;
+	size_t real_size;
+
+	real_size = richacl_xattr_size(acl);
+	if (!buffer)
+		return real_size;
+	if (real_size > size)
+		return -ERANGE;
+
+	xattr_acl->a_version = RICHACL_XATTR_VERSION;
+	xattr_acl->a_flags = acl->a_flags;
+	xattr_acl->a_count = cpu_to_le16(acl->a_count);
+
+	xattr_acl->a_owner_mask = cpu_to_le32(acl->a_owner_mask);
+	xattr_acl->a_group_mask = cpu_to_le32(acl->a_group_mask);
+	xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
+
+	xattr_ace = (void *)(xattr_acl + 1);
+	richacl_for_each_entry(ace, acl) {
+		xattr_ace->e_type = cpu_to_le16(ace->e_type);
+		xattr_ace->e_flags = cpu_to_le16(ace->e_flags);
+		xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
+		if (ace->e_flags & RICHACE_SPECIAL_WHO)
+			xattr_ace->e_id = cpu_to_le32(ace->e_id.special);
+		else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+			xattr_ace->e_id =
+				cpu_to_le32(from_kgid(user_ns, ace->e_id.gid));
+		else
+			xattr_ace->e_id =
+				cpu_to_le32(from_kuid(user_ns, ace->e_id.uid));
+		xattr_ace++;
+	}
+	return real_size;
+}
+EXPORT_SYMBOL_GPL(richacl_to_xattr);
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
new file mode 100644
index 0000000..0efa14b
--- /dev/null
+++ b/include/linux/richacl_xattr.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __RICHACL_XATTR_H
+#define __RICHACL_XATTR_H
+
+#include <uapi/linux/richacl_xattr.h>
+#include <linux/richacl.h>
+
+extern struct richacl *richacl_from_xattr(struct user_namespace *, const void *,
+					  size_t, int);
+extern size_t richacl_xattr_size(const struct richacl *);
+extern int richacl_to_xattr(struct user_namespace *, const struct richacl *,
+			    void *, size_t);
+
+#endif /* __RICHACL_XATTR_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 6e05dc8..14b8a06 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -352,6 +352,7 @@ header-y += reiserfs_fs.h
 header-y += reiserfs_xattr.h
 header-y += resource.h
 header-y += richacl.h
+header-y += richacl_xattr.h
 header-y += rfkill.h
 header-y += romfs_fs.h
 header-y += rose.h
diff --git a/include/uapi/linux/richacl_xattr.h b/include/uapi/linux/richacl_xattr.h
new file mode 100644
index 0000000..20da204
--- /dev/null
+++ b/include/uapi/linux/richacl_xattr.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef __UAPI_RICHACL_XATTR_H
+#define __UAPI_RICHACL_XATTR_H
+
+#include <linux/types.h>
+#include <linux/limits.h>
+
+struct richace_xattr {
+	__le16	e_type;
+	__le16	e_flags;
+	__le32	e_mask;
+	__le32	e_id;
+};
+
+struct richacl_xattr {
+	__u8	a_version;
+	__u8	a_flags;
+	__le16	a_count;
+	__le32	a_owner_mask;
+	__le32	a_group_mask;
+	__le32	a_other_mask;
+};
+
+#define RICHACL_XATTR_VERSION 0
+#define RICHACL_XATTR_MAX_COUNT \
+	((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
+	 sizeof(struct richace_xattr))
+
+#endif  /* __UAPI_RICHACL_XATTR_H */
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 1590c49..1996903 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -73,5 +73,7 @@
 #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
 #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
 
+#define XATTR_RICHACL "richacl"
+#define XATTR_NAME_RICHACL XATTR_SYSTEM_PREFIX XATTR_RICHACL
 
 #endif /* _UAPI_LINUX_XATTR_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 20/23] richacl: Add richacl xattr handler
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (18 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 19/23] richacl: xattr mapping functions Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 21/23] vfs: Add richacl permission checking Andreas Gruenbacher
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Add richacl xattr handler implementing the xattr operations based on the
get_richacl and set_richacl inode operations.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/richacl_xattr.c            | 74 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl_xattr.h |  2 ++
 2 files changed, 76 insertions(+)

diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
index dc1ad36..5cb5e11 100644
--- a/fs/richacl_xattr.c
+++ b/fs/richacl_xattr.c
@@ -18,7 +18,9 @@
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/xattr.h>
 #include <linux/richacl_xattr.h>
+#include <uapi/linux/xattr.h>
 
 /**
  * richacl_from_xattr  -  convert a richacl xattr into the in-memory representation
@@ -159,3 +161,75 @@ richacl_to_xattr(struct user_namespace *user_ns,
 	return real_size;
 }
 EXPORT_SYMBOL_GPL(richacl_to_xattr);
+
+static bool
+richacl_xattr_list(struct dentry *dentry)
+{
+	return IS_RICHACL(d_backing_inode(dentry));
+}
+
+static int
+richacl_xattr_get(const struct xattr_handler *handler,
+		  struct dentry *dentry, const char *name, void *buffer,
+		  size_t buffer_size)
+{
+	struct inode *inode = d_backing_inode(dentry);
+	struct richacl *acl;
+	int error;
+
+	if (*name)
+		return -EINVAL;
+	if (!IS_RICHACL(inode))
+		return -EOPNOTSUPP;
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+	acl = get_richacl(inode);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl == NULL)
+		return -ENODATA;
+	error = richacl_to_xattr(current_user_ns(), acl, buffer, buffer_size);
+	richacl_put(acl);
+	return error;
+}
+
+static int
+richacl_xattr_set(const struct xattr_handler *handler,
+		  struct dentry *dentry, const char *name,
+		  const void *value, size_t size, int flags)
+{
+	struct inode *inode = d_backing_inode(dentry);
+	struct richacl *acl = NULL;
+	int ret;
+
+	if (*name)
+		return -EINVAL;
+	if (!IS_RICHACL(inode))
+		return -EOPNOTSUPP;
+	if (!inode->i_op->set_richacl)
+		return -EOPNOTSUPP;
+
+	if (!uid_eq(current_fsuid(), inode->i_uid) &&
+	    inode_permission(inode, MAY_CHMOD) &&
+	    !capable(CAP_FOWNER))
+		return -EPERM;
+
+	if (value) {
+		acl = richacl_from_xattr(current_user_ns(), value, size,
+					 -EINVAL);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+	}
+
+	ret = inode->i_op->set_richacl(inode, acl);
+	richacl_put(acl);
+	return ret;
+}
+
+struct xattr_handler richacl_xattr_handler = {
+	.name = XATTR_NAME_RICHACL,
+	.list = richacl_xattr_list,
+	.get = richacl_xattr_get,
+	.set = richacl_xattr_set,
+};
+EXPORT_SYMBOL(richacl_xattr_handler);
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
index 0efa14b..6c6adb1 100644
--- a/include/linux/richacl_xattr.h
+++ b/include/linux/richacl_xattr.h
@@ -26,4 +26,6 @@ extern size_t richacl_xattr_size(const struct richacl *);
 extern int richacl_to_xattr(struct user_namespace *, const struct richacl *,
 			    void *, size_t);
 
+extern struct xattr_handler richacl_xattr_handler;
+
 #endif /* __RICHACL_XATTR_H */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 21/23] vfs: Add richacl permission checking
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (19 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 20/23] richacl: Add richacl xattr handler Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 22/23] ext4: Add richacl support Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 23/23] ext4: Add richacl feature flag Andreas Gruenbacher
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Andreas Gruenbacher, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api

Hook the richacl permission checking function into the vfs.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/namei.c     | 51 +++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/posix_acl.c |  6 +++---
 2 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 28707ae..b55cee6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -35,6 +35,7 @@
 #include <linux/fs_struct.h>
 #include <linux/posix_acl.h>
 #include <linux/hash.h>
+#include <linux/richacl.h>
 #include <asm/uaccess.h>
 
 #include "internal.h"
@@ -255,7 +256,40 @@ void putname(struct filename *name)
 		__putname(name);
 }
 
-static int check_acl(struct inode *inode, int mask)
+static int check_richacl(struct inode *inode, int mask)
+{
+#ifdef CONFIG_FS_RICHACL
+	struct richacl *acl;
+
+	if (mask & MAY_NOT_BLOCK) {
+		acl = get_cached_richacl_rcu(inode);
+		if (!acl)
+			goto no_acl;
+		/* no ->get_richacl() calls in RCU mode... */
+		if (acl == ACL_NOT_CACHED)
+			return -ECHILD;
+		return richacl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
+	}
+
+	acl = get_richacl(inode);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl) {
+		int error = richacl_permission(inode, acl, mask);
+		richacl_put(acl);
+		return error;
+	}
+no_acl:
+#endif
+	if (mask & (MAY_DELETE_SELF | MAY_TAKE_OWNERSHIP |
+		    MAY_CHMOD | MAY_SET_TIMES)) {
+		/* File permission bits cannot grant this. */
+		return -EACCES;
+	}
+	return -EAGAIN;
+}
+
+static int check_posix_acl(struct inode *inode, int mask)
 {
 #ifdef CONFIG_FS_POSIX_ACL
 	struct posix_acl *acl;
@@ -290,11 +324,24 @@ static int acl_permission_check(struct inode *inode, int mask)
 {
 	unsigned int mode = inode->i_mode;
 
+	/*
+	 * With POSIX ACLs, the (mode & S_IRWXU) bits exactly match the owner
+	 * permissions, and we can skip checking posix acls for the owner.
+	 * With richacls, the owner may be granted fewer permissions than the
+	 * mode bits seem to suggest (for example, append but not write), and
+	 * we always need to check the richacl.
+	 */
+
+	if (IS_RICHACL(inode)) {
+		int error = check_richacl(inode, mask);
+		if (error != -EAGAIN)
+			return error;
+	}
 	if (likely(uid_eq(current_fsuid(), inode->i_uid)))
 		mode >>= 6;
 	else {
 		if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
-			int error = check_acl(inode, mask);
+			int error = check_posix_acl(inode, mask);
 			if (error != -EAGAIN)
 				return error;
 		}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index f24646e..7810c6f 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -100,13 +100,13 @@ struct posix_acl *get_acl(struct inode *inode, int type)
 {
 	struct posix_acl *acl;
 
+	if (!IS_POSIXACL(inode))
+		return NULL;
+
 	acl = get_cached_acl(inode, type);
 	if (acl != ACL_NOT_CACHED)
 		return acl;
 
-	if (!IS_POSIXACL(inode))
-		return NULL;
-
 	/*
 	 * A filesystem can force a ACL callback by just never filling the
 	 * ACL cache. But normally you'd fill the cache either at inode
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 22/23] ext4: Add richacl support
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (20 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 21/23] vfs: Add richacl permission checking Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  2016-03-14 14:55 ` [PATCH v19 23/23] ext4: Add richacl feature flag Andreas Gruenbacher
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Aneesh Kumar K.V, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api,
	Andreas Gruenbacher

From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>

Support the richacl permission model in ext4.  The richacls are stored
in "system.richacl" xattrs.  Richacls need to be enabled by tune2fs or
at file system create time.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 fs/ext4/Kconfig   |  11 +++++
 fs/ext4/Makefile  |   1 +
 fs/ext4/file.c    |   3 ++
 fs/ext4/ialloc.c  |  11 ++++-
 fs/ext4/inode.c   |  12 ++++-
 fs/ext4/namei.c   |   5 ++
 fs/ext4/richacl.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/richacl.h |  40 ++++++++++++++++
 fs/ext4/xattr.c   |   7 +++
 9 files changed, 224 insertions(+), 3 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 b46e9fc..4e21c18 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -22,6 +22,17 @@ config EXT3_FS_POSIX_ACL
 	  This config option is here only for backward compatibility. ext3
 	  filesystem is now handled by the ext4 driver.
 
+config EXT4_FS_RICHACL
+	bool "Ext4 Rich Access Control Lists"
+	depends on EXT4_FS
+	select FS_RICHACL
+	help
+	  Richacls are an implementation of NFSv4 ACLs, extended by file masks
+	  to cleanly integrate into the POSIX file permission model.  To learn
+	  more about them, see http://www.bestbits.at/richacl/.
+
+	  If you don't know what Richacls are, say N.
+
 config EXT3_FS_SECURITY
 	bool "Ext3 Security Labels"
 	depends on EXT3_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index f52cf54..1fb7f11 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -14,3 +14,4 @@ ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
 ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o \
 		crypto_key.o crypto_fname.o
+ext4-$(CONFIG_EXT4_FS_RICHACL) 		+= richacl.o
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 4cd318f..92e35bc 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -30,6 +30,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
@@ -749,6 +750,8 @@ const struct inode_operations ext4_file_inode_operations = {
 	.removexattr	= generic_removexattr,
 	.get_acl	= ext4_get_acl,
 	.set_acl	= ext4_set_acl,
+	.get_richacl	= ext4_get_richacl,
+	.set_richacl	= ext4_set_richacl,
 	.fiemap		= ext4_fiemap,
 };
 
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index acc0ad5..f2d31c2 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -27,6 +27,7 @@
 #include "ext4_jbd2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "richacl.h"
 
 #include <trace/events/ext4.h>
 
@@ -729,6 +730,14 @@ out:
 	return ret;
 }
 
+static inline int
+ext4_new_acl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+	if (IS_RICHACL(dir))
+		return ext4_init_richacl(handle, inode, dir);
+	return ext4_init_acl(handle, inode, dir);
+}
+
 /*
  * There are two policies for allocating an inode.  If the new inode is
  * a directory, then a forward search is made for a block group with both
@@ -1093,7 +1102,7 @@ got:
 	if (err)
 		goto fail_drop;
 
-	err = ext4_init_acl(handle, inode, dir);
+	err = ext4_new_acl(handle, inode, dir);
 	if (err)
 		goto fail_free_drop;
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index aee960b..72c046d 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@
 #include "xattr.h"
 #include "acl.h"
 #include "truncate.h"
+#include "richacl.h"
 
 #include <trace/events/ext4.h>
 
@@ -4855,6 +4856,14 @@ static void ext4_wait_for_tail_page_commit(struct inode *inode)
 	}
 }
 
+static inline int
+ext4_acl_chmod(struct inode *inode)
+{
+	if (IS_RICHACL(inode))
+		return richacl_chmod(inode, inode->i_mode);
+	return posix_acl_chmod(inode, inode->i_mode);
+}
+
 /*
  * ext4_setattr()
  *
@@ -5025,8 +5034,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 		ext4_orphan_del(NULL, inode);
 
 	if (!rc && (ia_valid & ATTR_MODE))
-		rc = posix_acl_chmod(inode, inode->i_mode);
-
+		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 48e4b89..d86c5f2 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -38,6 +38,7 @@
 
 #include "xattr.h"
 #include "acl.h"
+#include "richacl.h"
 
 #include <trace/events/ext4.h>
 /*
@@ -3888,6 +3889,8 @@ const struct inode_operations ext4_dir_inode_operations = {
 	.removexattr	= generic_removexattr,
 	.get_acl	= ext4_get_acl,
 	.set_acl	= ext4_set_acl,
+	.get_richacl	= ext4_get_richacl,
+	.set_richacl	= ext4_set_richacl,
 	.fiemap         = ext4_fiemap,
 };
 
@@ -3899,4 +3902,6 @@ const struct inode_operations ext4_special_inode_operations = {
 	.removexattr	= generic_removexattr,
 	.get_acl	= ext4_get_acl,
 	.set_acl	= ext4_set_acl,
+	.get_richacl	= ext4_get_richacl,
+	.set_richacl	= ext4_set_richacl,
 };
diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
new file mode 100644
index 0000000..13a88e2
--- /dev/null
+++ b/fs/ext4/richacl.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Author: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>,
+ * 	   Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/richacl_xattr.h>
+
+#include "ext4.h"
+#include "ext4_jbd2.h"
+#include "xattr.h"
+#include "acl.h"
+#include "richacl.h"
+
+struct richacl *
+ext4_get_richacl(struct inode *inode)
+{
+	const int name_index = EXT4_XATTR_INDEX_RICHACL;
+	void *value = NULL;
+	struct richacl *acl = NULL;
+	int retval;
+
+	retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
+	if (retval > 0) {
+		value = kmalloc(retval, GFP_NOFS);
+		if (!value)
+			return ERR_PTR(-ENOMEM);
+		retval = ext4_xattr_get(inode, name_index, "", value, retval);
+	}
+	if (retval > 0)
+		acl = richacl_from_xattr(&init_user_ns, value, retval, -EIO);
+	else if (retval != -ENODATA && retval != -ENOSYS)
+		acl = ERR_PTR(retval);
+	kfree(value);
+
+	if (!IS_ERR(acl))
+		set_cached_richacl(inode, acl);
+
+	return acl;
+}
+
+static int
+__ext4_remove_richacl(handle_t *handle, struct inode *inode)
+{
+	const int name_index = EXT4_XATTR_INDEX_RICHACL;
+	int retval;
+
+	retval = ext4_xattr_set_handle(handle, inode, name_index, "",
+				       NULL, 0, 0);
+	if (!retval)
+		set_cached_richacl(inode, NULL);
+	return retval;
+}
+
+static int
+__ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl)
+{
+	const int name_index = EXT4_XATTR_INDEX_RICHACL;
+	umode_t mode = inode->i_mode;
+	int retval, size;
+	void *value;
+
+	if (richacl_equiv_mode(acl, &mode) == 0) {
+		inode->i_ctime = ext4_current_time(inode);
+		inode->i_mode = mode;
+		ext4_mark_inode_dirty(handle, inode);
+		return __ext4_remove_richacl(handle, inode);
+	}
+
+	mode &= ~S_IRWXUGO;
+	mode |= richacl_masks_to_mode(acl);
+
+	size = richacl_xattr_size(acl);
+	value = kmalloc(size, GFP_NOFS);
+	if (!value)
+		return -ENOMEM;
+	richacl_to_xattr(&init_user_ns, acl, value, size);
+	inode->i_mode = mode;
+	retval = ext4_xattr_set_handle(handle, inode, name_index, "",
+				       value, size, 0);
+	kfree(value);
+	if (retval)
+		return retval;
+
+	set_cached_richacl(inode, acl);
+
+	return 0;
+}
+
+int
+ext4_set_richacl(struct inode *inode, struct richacl *acl)
+{
+	handle_t *handle;
+	int retval, retries = 0;
+
+retry:
+	handle = ext4_journal_start(inode, EXT4_HT_XATTR,
+				    ext4_jbd2_credits_xattr(inode));
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	if (acl)
+		retval = __ext4_set_richacl(handle, inode, acl);
+	else
+		retval = __ext4_remove_richacl(handle, inode);
+
+	ext4_journal_stop(handle);
+	if (retval == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+		goto retry;
+	return retval;
+}
+
+int
+ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+	struct richacl *acl = richacl_create(&inode->i_mode, dir);
+	int error;
+
+	error = PTR_ERR(acl);
+	if (!IS_ERR_OR_NULL(acl)) {
+		error = __ext4_set_richacl(handle, inode, acl);
+		richacl_put(acl);
+	}
+	return error;
+}
diff --git a/fs/ext4/richacl.h b/fs/ext4/richacl.h
new file mode 100644
index 0000000..6fe9a92
--- /dev/null
+++ b/fs/ext4/richacl.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Copyright (C)  2015 Red Hat, Inc.
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __FS_EXT4_RICHACL_H
+#define __FS_EXT4_RICHACL_H
+
+#include <linux/richacl.h>
+
+#ifdef CONFIG_EXT4_FS_RICHACL
+
+extern struct richacl *ext4_get_richacl(struct inode *);
+extern int ext4_set_richacl(struct inode *, struct richacl *);
+
+extern int ext4_init_richacl(handle_t *, struct inode *, struct inode *);
+
+#else  /* CONFIG_EXT4_FS_RICHACL */
+
+#define ext4_get_richacl NULL
+#define ext4_set_richacl NULL
+
+static inline int
+ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+	return 0;
+}
+
+#endif  /* CONFIG_EXT4_FS_RICHACL */
+#endif  /* __FS_EXT4_RICHACL_H */
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index a95151e..ef76630 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -55,6 +55,7 @@
 #include <linux/slab.h>
 #include <linux/mbcache.h>
 #include <linux/quotaops.h>
+#include <linux/richacl_xattr.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
 #include "xattr.h"
@@ -97,6 +98,9 @@ static const struct xattr_handler *ext4_xattr_handler_map[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	[EXT4_XATTR_INDEX_SECURITY]	     = &ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+	[EXT4_XATTR_INDEX_RICHACL]           = &richacl_xattr_handler,
+#endif
 };
 
 const struct xattr_handler *ext4_xattr_handlers[] = {
@@ -109,6 +113,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	&ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+	&richacl_xattr_handler,
+#endif
 	NULL
 };
 
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v19 23/23] ext4: Add richacl feature flag
  2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
                   ` (21 preceding siblings ...)
  2016-03-14 14:55 ` [PATCH v19 22/23] ext4: Add richacl support Andreas Gruenbacher
@ 2016-03-14 14:55 ` Andreas Gruenbacher
  22 siblings, 0 replies; 24+ messages in thread
From: Andreas Gruenbacher @ 2016-03-14 14:55 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Aneesh Kumar K.V, Christoph Hellwig, Theodore Ts'o,
	Andreas Dilger, J. Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, Dave Chinner, linux-ext4, xfs, linux-kernel,
	linux-fsdevel, linux-nfs, linux-cifs, linux-api,
	Andreas Gruenbacher

From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>

This feature flag selects richacl instead of POSIX ACL support on the
filesystem.  When this feature is off, the "acl" and "noacl" mount options
control whether POSIX ACLs are enabled.  When it is on, richacls are
automatically enabled and using the "noacl" mount option leads to an error.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 fs/ext4/ext4.h  |  6 ++++--
 fs/ext4/super.c | 49 ++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 157b458..44277f9 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1092,7 +1092,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 */
 #define EXT4_MOUNT_NO_AUTO_DA_ALLOC	0x10000	/* No auto delalloc mapping */
 #define EXT4_MOUNT_BARRIER		0x20000 /* Use block barriers */
 #define EXT4_MOUNT_QUOTA		0x80000 /* Some quota option set */
@@ -1690,6 +1690,7 @@ static inline int ext4_encrypted_inode(struct inode *inode)
 #define EXT4_FEATURE_INCOMPAT_LARGEDIR		0x4000 /* >2GB or 3-lvl htree */
 #define EXT4_FEATURE_INCOMPAT_INLINE_DATA	0x8000 /* data in inode */
 #define EXT4_FEATURE_INCOMPAT_ENCRYPT		0x10000
+#define EXT4_FEATURE_INCOMPAT_RICHACL		0x20000
 
 #define EXT4_FEATURE_COMPAT_FUNCS(name, flagname) \
 static inline bool ext4_has_feature_##name(struct super_block *sb) \
@@ -1804,7 +1805,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt,		ENCRYPT)
 					 EXT4_FEATURE_INCOMPAT_MMP | \
 					 EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
 					 EXT4_FEATURE_INCOMPAT_ENCRYPT | \
-					 EXT4_FEATURE_INCOMPAT_CSUM_SEED)
+					 EXT4_FEATURE_INCOMPAT_CSUM_SEED | \
+					 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| \
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 3ed01ec..2ed8485 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1305,6 +1305,28 @@ static ext4_fsblk_t get_sb_block(void **data)
 	return sb_block;
 }
 
+static int enable_acl(struct super_block *sb)
+{
+	sb->s_flags &= ~(MS_POSIXACL | MS_RICHACL);
+	if (test_opt(sb, ACL)) {
+		if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+					      EXT4_FEATURE_INCOMPAT_RICHACL)) {
+#ifdef CONFIG_EXT4_FS_RICHACL
+			sb->s_flags |= MS_RICHACL;
+#else
+			return -EOPNOTSUPP;
+#endif
+		} else {
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
+			sb->s_flags |= MS_POSIXACL;
+#else
+			return -EOPNOTSUPP;
+#endif
+		}
+	}
+	return 0;
+}
+
 #define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
 static char deprecated_msg[] = "Mount option \"%s\" will be removed by %s\n"
 	"Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
@@ -1451,9 +1473,9 @@ static const struct mount_opts {
 	 MOPT_NO_EXT2 | MOPT_DATAJ},
 	{Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET},
 	{Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR},
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
-	{Opt_acl, EXT4_MOUNT_POSIX_ACL, MOPT_SET},
-	{Opt_noacl, EXT4_MOUNT_POSIX_ACL, MOPT_CLEAR},
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+	{Opt_acl, EXT4_MOUNT_ACL, MOPT_SET},
+	{Opt_noacl, EXT4_MOUNT_ACL, MOPT_CLEAR},
 #else
 	{Opt_acl, 0, MOPT_NOSUPPORT},
 	{Opt_noacl, 0, MOPT_NOSUPPORT},
@@ -1501,6 +1523,13 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 #endif
 	switch (token) {
 	case Opt_noacl:
+#ifdef CONFIG_EXT4_FS_RICHACL
+	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RICHACL)) {
+		ext4_msg(sb, KERN_ERR, "Mount option \"%s\" incompatible "
+			 "with richacl feature", opt);
+		return -1;
+	}
+#endif
 	case Opt_nouser_xattr:
 		ext4_msg(sb, KERN_WARNING, deprecated_msg, opt, "3.5");
 		break;
@@ -3267,8 +3296,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		set_opt(sb, NO_UID32);
 	/* xattr user namespace & acls are now defaulted on */
 	set_opt(sb, XATTR_USER);
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
-	set_opt(sb, POSIX_ACL);
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+	set_opt(sb, ACL);
 #endif
 	/* don't forget to enable journal_csum when metadata_csum is enabled. */
 	if (ext4_has_metadata_csum(sb))
@@ -3351,8 +3380,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		sb->s_iflags |= SB_I_CGROUPWB;
 	}
 
-	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
-		(test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
+	err = enable_acl(sb);
+	if (err)
+		goto failed_mount;
 
 	if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&
 	    (ext4_has_compat_features(sb) ||
@@ -4668,8 +4698,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 	if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
 		ext4_abort(sb, "Abort forced by user");
 
-	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
-		(test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
+	err = enable_acl(sb);
+	if (err)
+		goto restore_opts;
 
 	es = sbi->s_es;
 
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2016-03-14 15:05 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-14 14:54 [PATCH v19 00/23] Richacls (Core and Ext4) Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 01/23] vfs: Add IS_ACL() and IS_RICHACL() tests Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 02/23] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 03/23] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD " Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 04/23] vfs: Make the inode passed to inode_change_ok non-const Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 05/23] vfs: Add permission flags for setting file attributes Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 06/23] richacl: In-memory representation and helper functions Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 07/23] richacl: Permission mapping functions Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 08/23] richacl: Compute maximum file masks from an acl Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 09/23] richacl: Permission check algorithm Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 10/23] posix_acl: Unexport acl_by_type and make it static Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 11/23] posix_acl: Improve xattr fixup code Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 12/23] vfs: Cache base_acl objects in inodes Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 13/23] vfs: Add get_richacl and set_richacl inode operations Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 14/23] vfs: Cache richacl in struct inode Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 15/23] richacl: Update the file masks in chmod() Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 16/23] richacl: Check if an acl is equivalent to a file mode Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 17/23] richacl: Create-time inheritance Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 18/23] richacl: Automatic Inheritance Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 19/23] richacl: xattr mapping functions Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 20/23] richacl: Add richacl xattr handler Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 21/23] vfs: Add richacl permission checking Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 22/23] ext4: Add richacl support Andreas Gruenbacher
2016-03-14 14:55 ` [PATCH v19 23/23] ext4: Add richacl feature flag Andreas Gruenbacher

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).