[PATCH v20 00/23] Richacls (Core and Ext4)

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[PATCH v20 00/23] Richacls (Core and Ext4)

Andreas Gruenbacher-6
Al,

here is a minor update to the richacl patches with some feedback from Christoph
incorporated.

Changes since the last posting (https://lkml.org/lkml/2016/3/14/503):

 * Merge richacl_base.c and richacl_inode.c into richacl.c.

 * Add linux/acl.h header including function acl_chmod.
   Use it in richacl enabled filesystems.


The complete patch queue is available here:

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


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                                     |   1 +
 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                                 |   5 +-
 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.c                                    | 889 ++++++++++++++++++++++++
 fs/richacl_xattr.c                              | 235 +++++++
 fs/xattr.c                                      |  29 +-
 include/linux/acl.h                             |  15 +
 include/linux/fs.h                              |  60 +-
 include/linux/posix_acl.h                       |  13 +-
 include/linux/richacl.h                         | 205 ++++++
 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, 2131 insertions(+), 108 deletions(-)
 create mode 100644 fs/ext4/richacl.c
 create mode 100644 fs/ext4/richacl.h
 create mode 100644 fs/richacl.c
 create mode 100644 fs/richacl_xattr.c
 create mode 100644 include/linux/acl.h
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 01/23] vfs: Add IS_ACL() and IS_RICHACL() tests

Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
Reviewed-by: Andreas Dilger <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 03/23] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 08/23] richacl: Compute maximum file masks from an acl

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl.c            | 157 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   1 +
 2 files changed, 158 insertions(+)

diff --git a/fs/richacl.c b/fs/richacl.c
index d0a4135..056228f 100644
--- a/fs/richacl.c
+++ b/fs/richacl.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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 11/23] posix_acl: Improve xattr fixup code

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 13/23] vfs: Add get_richacl and set_richacl inode operations

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 19/23] richacl: xattr mapping functions

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Map between "system.richacl" xattrs and the in-kernel representation.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 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 884d7a4..a6f15f1 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -48,7 +48,7 @@ obj-$(CONFIG_COREDUMP) += coredump.o
 obj-$(CONFIG_SYSCTL) += drop_caches.o
 
 obj-$(CONFIG_FHANDLE) += fhandle.o
-obj-$(CONFIG_FS_RICHACL) += richacl.o
+obj-$(CONFIG_FS_RICHACL) += richacl.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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 22/23] ext4: Add richacl support

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
From: "Aneesh Kumar K.V" <[hidden email]>

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 <[hidden email]>
Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: Andreas Dilger <[hidden email]>
---
 fs/ext4/Kconfig     |  11 +++++
 fs/ext4/Makefile    |   1 +
 fs/ext4/file.c      |   3 ++
 fs/ext4/ialloc.c    |  11 ++++-
 fs/ext4/inode.c     |   5 +-
 fs/ext4/namei.c     |   5 ++
 fs/ext4/richacl.c   | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/richacl.h   |  40 +++++++++++++++
 fs/ext4/xattr.c     |   7 +++
 include/linux/acl.h |  15 ++++++
 10 files changed, 231 insertions(+), 4 deletions(-)
 create mode 100644 fs/ext4/richacl.c
 create mode 100644 fs/ext4/richacl.h
 create mode 100644 include/linux/acl.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..1dbd4c2 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -37,10 +37,10 @@
 #include <linux/printk.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
+#include <linux/acl.h>
 
 #include "ext4_jbd2.h"
 #include "xattr.h"
-#include "acl.h"
 #include "truncate.h"
 
 #include <trace/events/ext4.h>
@@ -5025,8 +5025,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 = 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 <[hidden email]>,
+ *   Andreas Gruenbacher <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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
 };
 
diff --git a/include/linux/acl.h b/include/linux/acl.h
new file mode 100644
index 0000000..3c3b5ce
--- /dev/null
+++ b/include/linux/acl.h
@@ -0,0 +1,15 @@
+#ifndef __LINUX_ACL_H
+#define __LINUX_ACL_H
+
+#include <linux/posix_acl.h>
+#include <linux/richacl.h>
+
+static inline int
+acl_chmod(struct inode *inode)
+{
+ if (IS_RICHACL(inode))
+ return richacl_chmod(inode, inode->i_mode);
+ return posix_acl_chmod(inode, inode->i_mode);
+}
+
+#endif
--
2.5.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 23/23] ext4: Add richacl feature flag

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
From: "Aneesh Kumar K.V" <[hidden email]>

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 <[hidden email]>
Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: Andreas Dilger <[hidden email]>
---
 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 [hidden email] 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 21/23] vfs: Add richacl permission checking

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Hook the richacl permission checking function into the vfs.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 20/23] richacl: Add richacl xattr handler

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Add richacl xattr handler implementing the xattr operations based on the
get_richacl and set_richacl inode operations.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 05/23] vfs: Add permission flags for setting file attributes

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 06/23] richacl: In-memory representation and helper functions

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/Makefile                  |   1 +
 fs/richacl.c                 |  65 ++++++++++++++++
 include/linux/richacl.h      | 179 +++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/Kbuild    |   1 +
 include/uapi/linux/richacl.h |  99 ++++++++++++++++++++++++
 5 files changed, 345 insertions(+)
 create mode 100644 fs/richacl.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..884d7a4 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_COREDUMP) += coredump.o
 obj-$(CONFIG_SYSCTL) += drop_caches.o
 
 obj-$(CONFIG_FHANDLE) += fhandle.o
+obj-$(CONFIG_FS_RICHACL) += richacl.o
 
 obj-y += quota/
 
diff --git a/fs/richacl.c b/fs/richacl.c
new file mode 100644
index 0000000..bcc6591
--- /dev/null
+++ b/fs/richacl.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 10/23] posix_acl: Unexport acl_by_type and make it static

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: Christoph Hellwig <[hidden email]>
---
 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 14/23] vfs: Cache richacl in struct inode

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
---
 fs/inode.c              | 11 +++++--
 fs/posix_acl.c          |  2 +-
 fs/richacl.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.c b/fs/richacl.c
index 8971ead..59ef600 100644
--- a/fs/richacl.c
+++ b/fs/richacl.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_alloc  -  allocate a richacl
  * @count: number of entries
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 35a5bcb..d19b61b 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 17/23] richacl: Create-time inheritance

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
---
 fs/richacl.c            | 151 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   2 +
 2 files changed, 153 insertions(+)

diff --git a/fs/richacl.c b/fs/richacl.c
index 0df359c..7c82bc2 100644
--- a/fs/richacl.c
+++ b/fs/richacl.c
@@ -718,3 +718,154 @@ 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;
+}
+
+/*
+ * 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 f61475b..199800a 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -187,5 +187,7 @@ extern void richacl_compute_max_masks(struct richacl *);
 extern int richacl_permission(struct inode *, const struct richacl *, int);
 extern int richacl_chmod(struct inode *, umode_t);
 extern int richacl_equiv_mode(const struct richacl *, umode_t *);
+extern struct richacl *richacl_inherit(const struct richacl *, int);
+extern struct richacl *richacl_create(umode_t *, struct inode *);
 
 #endif /* __RICHACL_H */
--
2.5.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 16/23] richacl: Check if an acl is equivalent to a file mode

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl.c            | 104 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   1 +
 2 files changed, 105 insertions(+)

diff --git a/fs/richacl.c b/fs/richacl.c
index 23c1aa9..0df359c 100644
--- a/fs/richacl.c
+++ b/fs/richacl.c
@@ -614,3 +614,107 @@ richacl_chmod(struct inode *inode, umode_t mode)
  return retval;
 }
 EXPORT_SYMBOL(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 554f050..f61475b 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -186,5 +186,6 @@ extern unsigned int richacl_want_to_mask(unsigned int);
 extern void richacl_compute_max_masks(struct richacl *);
 extern int richacl_permission(struct inode *, const struct richacl *, int);
 extern int richacl_chmod(struct inode *, umode_t);
+extern int richacl_equiv_mode(const struct richacl *, umode_t *);
 
 #endif /* __RICHACL_H */
--
2.5.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 18/23] richacl: Automatic Inheritance

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
---
 fs/richacl.c                 | 20 +++++++++++++++++++-
 include/linux/richacl.h      | 12 ++++++++++++
 include/uapi/linux/richacl.h | 11 ++++++++++-
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/fs/richacl.c b/fs/richacl.c
index 7c82bc2..1bada18 100644
--- a/fs/richacl.c
+++ b/fs/richacl.c
@@ -569,7 +569,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);
@@ -581,6 +582,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;
 }
@@ -796,6 +799,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;
 }
@@ -824,6 +835,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 199800a..e1c6c79 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

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 15/23] richacl: Update the file masks in chmod()

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl.c            | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  1 +
 2 files changed, 72 insertions(+)

diff --git a/fs/richacl.c b/fs/richacl.c
index 59ef600..23c1aa9 100644
--- a/fs/richacl.c
+++ b/fs/richacl.c
@@ -543,3 +543,74 @@ out:
  return denied ? -EACCES : 0;
 }
 EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * __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.
+ */
+static 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;
+}
+
+/**
+ * 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 d19b61b..554f050 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -185,5 +185,6 @@ 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 int richacl_permission(struct inode *, const struct richacl *, int);
+extern int richacl_chmod(struct inode *, umode_t);
 
 #endif /* __RICHACL_H */
--
2.5.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v20 12/23] vfs: Cache base_acl objects in inodes

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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 <[hidden email]>
Reviewed-by: Andreas Dilger <[hidden email]>
---
 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.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.c b/fs/richacl.c
index cb0ef3f..8971ead 100644
--- a/fs/richacl.c
+++ b/fs/richacl.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 be9fb65..35a5bcb 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

12