From: Andreas Gruenbacher <agrue...@redhat.com>

The richacl feature flag (mkfs.xfs -m richacl=1) determines whether an xfs
filesystem supports posix acls or richacls.  Richacls are stored in
"system.richacl" xattrs.

If richacls are not compiled into the kernel, mounting richacl filesystems
will fail.

Signed-off-by: Andreas Gruenbacher <agrue...@redhat.com>
---
 fs/ext4/richacl.h          |  1 -
 fs/xfs/Kconfig             | 11 ++++++
 fs/xfs/Makefile            |  1 +
 fs/xfs/libxfs/xfs_format.h | 16 +++++++-
 fs/xfs/xfs_iops.c          | 42 +++++++++++++++-----
 fs/xfs/xfs_richacl.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_richacl.h       | 34 ++++++++++++++++
 fs/xfs/xfs_super.c         | 14 ++++++-
 fs/xfs/xfs_super.h         |  9 ++++-
 fs/xfs/xfs_xattr.c         |  4 ++
 10 files changed, 215 insertions(+), 14 deletions(-)
 create mode 100644 fs/xfs/xfs_richacl.c
 create mode 100644 fs/xfs/xfs_richacl.h

diff --git a/fs/ext4/richacl.h b/fs/ext4/richacl.h
index fc7826f..f26fbdd 100644
--- a/fs/ext4/richacl.h
+++ b/fs/ext4/richacl.h
@@ -24,7 +24,6 @@
 
 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 */
diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig
index 5d47b4d..05dd312 100644
--- a/fs/xfs/Kconfig
+++ b/fs/xfs/Kconfig
@@ -52,6 +52,17 @@ config XFS_POSIX_ACL
 
          If you don't know what Access Control Lists are, say N.
 
+config XFS_RICHACL
+        bool "XFS Rich Access Control Lists (EXPERIMENTAL)"
+        depends on XFS_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 XFS_RT
        bool "XFS Realtime subvolume support"
        depends on XFS_FS
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index a096841..7df9ae3 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -118,6 +118,7 @@ xfs-$(CONFIG_XFS_QUOTA)             += xfs_dquot.o \
 xfs-$(CONFIG_XFS_RT)           += xfs_rtalloc.o
 
 xfs-$(CONFIG_XFS_POSIX_ACL)    += xfs_acl.o
+xfs-$(CONFIG_XFS_RICHACL)      += xfs_richacl.o
 xfs-$(CONFIG_PROC_FS)          += xfs_stats.o
 xfs-$(CONFIG_SYSCTL)           += xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)           += xfs_ioctl32.o
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 9590a06..8c6da45 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -461,10 +461,18 @@ xfs_sb_has_ro_compat_feature(
 #define XFS_SB_FEAT_INCOMPAT_FTYPE     (1 << 0)        /* filetype in dirent */
 #define XFS_SB_FEAT_INCOMPAT_SPINODES  (1 << 1)        /* sparse inode chunks 
*/
 #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2)        /* metadata UUID */
+
+#ifdef CONFIG_XFS_RICHACL
+#define XFS_SB_FEAT_INCOMPAT_RICHACL   (1 << 3)        /* richacls */
+#else
+#define XFS_SB_FEAT_INCOMPAT_RICHACL   0
+#endif
+
 #define XFS_SB_FEAT_INCOMPAT_ALL \
                (XFS_SB_FEAT_INCOMPAT_FTYPE|    \
                 XFS_SB_FEAT_INCOMPAT_SPINODES| \
-                XFS_SB_FEAT_INCOMPAT_META_UUID)
+                XFS_SB_FEAT_INCOMPAT_META_UUID| \
+                XFS_SB_FEAT_INCOMPAT_RICHACL)
 
 #define XFS_SB_FEAT_INCOMPAT_UNKNOWN   ~XFS_SB_FEAT_INCOMPAT_ALL
 static inline bool
@@ -530,6 +538,12 @@ static inline bool xfs_sb_version_hasmetauuid(struct 
xfs_sb *sbp)
                (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_META_UUID);
 }
 
+static inline bool xfs_sb_version_hasrichacl(struct xfs_sb *sbp)
+{
+       return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) &&
+               (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RICHACL);
+}
+
 /*
  * end of superblock version macros
  */
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 8294132..e37bdf87 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -27,6 +27,7 @@
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_acl.h"
+#include "xfs_richacl.h"
 #include "xfs_quota.h"
 #include "xfs_error.h"
 #include "xfs_attr.h"
@@ -42,6 +43,7 @@
 #include <linux/capability.h>
 #include <linux/xattr.h>
 #include <linux/posix_acl.h>
+#include <linux/richacl.h>
 #include <linux/security.h>
 #include <linux/fiemap.h>
 #include <linux/slab.h>
@@ -133,7 +135,8 @@ xfs_generic_create(
 {
        struct inode    *inode;
        struct xfs_inode *ip = NULL;
-       struct posix_acl *default_acl, *acl;
+       struct posix_acl *default_acl = NULL, *acl = NULL;
+       struct richacl *richacl = NULL;
        struct xfs_name name;
        int             error;
 
@@ -149,9 +152,15 @@ xfs_generic_create(
                rdev = 0;
        }
 
-       error = posix_acl_create(dir, &mode, &default_acl, &acl);
-       if (error)
-               return error;
+       if (XFS_IS_RICHACL(dir)) {
+               richacl = richacl_create(&mode, dir);
+               if (IS_ERR(richacl))
+                       return PTR_ERR(richacl);
+       } else {
+               error = posix_acl_create(dir, &mode, &default_acl, &acl);
+               if (error)
+                       return error;
+       }
 
        if (!tmpfile) {
                xfs_dentry_to_name(&name, dentry, mode);
@@ -180,6 +189,13 @@ xfs_generic_create(
                        goto out_cleanup_inode;
        }
 #endif
+#ifdef CONFIG_XFS_RICHACL
+       if (richacl) {
+               error = xfs_set_richacl(inode, richacl);
+               if (error)
+                       goto out_cleanup_inode;
+       }
+#endif
 
        if (tmpfile)
                d_tmpfile(dentry, inode);
@@ -189,10 +205,9 @@ xfs_generic_create(
        xfs_finish_inode_setup(ip);
 
  out_free_acl:
-       if (default_acl)
-               posix_acl_release(default_acl);
-       if (acl)
-               posix_acl_release(acl);
+       posix_acl_release(default_acl);
+       posix_acl_release(acl);
+       richacl_put(richacl);
        return error;
 
  out_cleanup_inode:
@@ -722,7 +737,10 @@ xfs_setattr_nonsize(
         *           Posix ACL code seems to care about this issue either.
         */
        if ((mask & ATTR_MODE) && !(flags & XFS_ATTR_NOACL)) {
-               error = posix_acl_chmod(inode, inode->i_mode);
+               if (XFS_IS_RICHACL(inode))
+                       error = richacl_chmod(inode, inode->i_mode);
+               else
+                       error = posix_acl_chmod(inode, inode->i_mode);
                if (error)
                        return error;
        }
@@ -1104,6 +1122,8 @@ xfs_vn_tmpfile(
 static const struct inode_operations xfs_inode_operations = {
        .get_acl                = xfs_get_acl,
        .set_acl                = xfs_set_acl,
+       .get_richacl            = xfs_get_richacl,
+       .set_richacl            = xfs_set_richacl,
        .getattr                = xfs_vn_getattr,
        .setattr                = xfs_vn_setattr,
        .setxattr               = generic_setxattr,
@@ -1132,6 +1152,8 @@ static const struct inode_operations 
xfs_dir_inode_operations = {
        .rename2                = xfs_vn_rename,
        .get_acl                = xfs_get_acl,
        .set_acl                = xfs_set_acl,
+       .get_richacl            = xfs_get_richacl,
+       .set_richacl            = xfs_set_richacl,
        .getattr                = xfs_vn_getattr,
        .setattr                = xfs_vn_setattr,
        .setxattr               = generic_setxattr,
@@ -1160,6 +1182,8 @@ static const struct inode_operations 
xfs_dir_ci_inode_operations = {
        .rename2                = xfs_vn_rename,
        .get_acl                = xfs_get_acl,
        .set_acl                = xfs_set_acl,
+       .get_richacl            = xfs_get_richacl,
+       .set_richacl            = xfs_set_richacl,
        .getattr                = xfs_vn_getattr,
        .setattr                = xfs_vn_setattr,
        .setxattr               = generic_setxattr,
diff --git a/fs/xfs/xfs_richacl.c b/fs/xfs/xfs_richacl.c
new file mode 100644
index 0000000..af74e92
--- /dev/null
+++ b/fs/xfs/xfs_richacl.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Author: Andreas Gruenbacher <agrue...@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "xfs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_inode.h"
+#include "xfs_attr.h"
+
+#include <linux/richacl_xattr.h>
+
+struct richacl *
+xfs_get_richacl(struct inode *inode)
+{
+       struct xfs_inode *ip = XFS_I(inode);
+       struct richacl *acl;
+       int size = XATTR_SIZE_MAX;
+       void *value;
+       int error;
+
+       value = kmem_zalloc_large(size, KM_SLEEP);
+       if (!value)
+               return ERR_PTR(-ENOMEM);
+
+       error = xfs_attr_get(ip, XATTR_NAME_RICHACL, value, &size, ATTR_ROOT);
+       if (error) {
+               /*
+                * If the attribute doesn't exist make sure we have a negative
+                * cache entry, for any other error assume it is transient and
+                * leave the cache entry as ACL_NOT_CACHED.
+                */
+               acl = (error == -ENOATTR) ? NULL : ERR_PTR(error);
+       } else
+               acl = richacl_from_xattr(&init_user_ns, value, size);
+
+       if (!IS_ERR(acl))
+               set_cached_richacl(inode, acl);
+       kfree(value);
+
+       return acl;
+}
+
+int
+xfs_set_richacl(struct inode *inode, struct richacl *acl)
+{
+       struct xfs_inode *ip = XFS_I(inode);
+       umode_t mode = inode->i_mode;
+       int error;
+
+       if (acl) {
+               /* Don't allow acls with unmapped identifiers. */
+               if (richacl_has_unmapped_identifiers(acl))
+                       return -EINVAL;
+
+               if (richacl_equiv_mode(acl, &mode) == 0) {
+                       xfs_set_mode(inode, mode);
+                       acl = NULL;
+               }
+       }
+       if (acl) {
+               void *value;
+               int size = richacl_xattr_size(acl);
+
+               value = kmem_zalloc_large(size, KM_SLEEP);
+               if (!value)
+                       return -ENOMEM;
+               richacl_to_xattr(&init_user_ns, acl, value, size);
+               error = xfs_attr_set(ip, XATTR_NAME_RICHACL, value, size,
+                                    ATTR_ROOT);
+               kfree(value);
+
+               if (!error) {
+                       mode &= ~S_IRWXUGO;
+                       mode |= richacl_masks_to_mode(acl);
+                       xfs_set_mode(inode, mode);
+               }
+       } else {
+               error = xfs_attr_remove(ip, XATTR_NAME_RICHACL, ATTR_ROOT);
+               if (error == -ENOATTR)
+                       error = 0;
+       }
+       if (!error)
+               set_cached_richacl(inode, acl);
+
+       return 0;
+}
diff --git a/fs/xfs/xfs_richacl.h b/fs/xfs/xfs_richacl.h
new file mode 100644
index 0000000..78ad314
--- /dev/null
+++ b/fs/xfs/xfs_richacl.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C)  2015 Red Hat, Inc.
+ * Author: Andreas Gruenbacher <agrue...@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __FS_XFS_RICHACL_H
+#define __FS_XFS_RICHACL_H
+
+#include <linux/richacl.h>
+
+#ifdef CONFIG_XFS_RICHACL
+
+#define XFS_IS_RICHACL(inode) IS_RICHACL(inode)
+
+extern struct richacl *xfs_get_richacl(struct inode *);
+extern int xfs_set_richacl(struct inode *, struct richacl *);
+
+#else  /* CONFIG_XFS_RICHACL */
+
+#define XFS_IS_RICHACL(inode) (0)
+#define xfs_get_richacl NULL
+#define xfs_set_richacl NULL
+
+#endif  /* CONFIG_XFS_RICHACL */
+#endif  /* __FS_XFS_RICHACL_H */
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 904f637..74eeb0e 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1500,7 +1500,19 @@ xfs_fs_fill_super(
        sb->s_maxbytes = xfs_max_file_offset(sb->s_blocksize_bits);
        sb->s_max_links = XFS_MAXLINK;
        sb->s_time_gran = 1;
-       set_posix_acl_flag(sb);
+
+       if (xfs_sb_version_hasrichacl(&mp->m_sb)) {
+#ifdef CONFIG_XFS_RICHACL
+               sb->s_flags |= MS_RICHACL;
+#else
+               error = -EOPNOTSUPP;
+               goto out_free_sb;
+#endif
+       } else {
+#ifdef CONFIG_XFS_POSIX_ACL
+               sb->s_flags |= MS_POSIXACL;
+#endif
+       }
 
        /* version 5 superblocks support inode version counters. */
        if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5)
diff --git a/fs/xfs/xfs_super.h b/fs/xfs/xfs_super.h
index 499058f..072fd57 100644
--- a/fs/xfs/xfs_super.h
+++ b/fs/xfs/xfs_super.h
@@ -30,10 +30,14 @@ extern void xfs_qm_exit(void);
 
 #ifdef CONFIG_XFS_POSIX_ACL
 # define XFS_ACL_STRING                "ACLs, "
-# define set_posix_acl_flag(sb)        ((sb)->s_flags |= MS_POSIXACL)
 #else
 # define XFS_ACL_STRING
-# define set_posix_acl_flag(sb)        do { } while (0)
+#endif
+
+#ifdef CONFIG_XFS_RICHACL
+# define XFS_RICHACL_STRING    "Richacls, "
+#else
+# define XFS_RICHACL_STRING
 #endif
 
 #define XFS_SECURITY_STRING    "security attributes, "
@@ -52,6 +56,7 @@ extern void xfs_qm_exit(void);
 
 #define XFS_VERSION_STRING     "SGI XFS"
 #define XFS_BUILD_OPTIONS      XFS_ACL_STRING \
+                               XFS_RICHACL_STRING \
                                XFS_SECURITY_STRING \
                                XFS_REALTIME_STRING \
                                XFS_DBG_STRING /* DBG must be last */
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index c0368151..238b39f 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -28,6 +28,7 @@
 #include "xfs_acl.h"
 
 #include <linux/posix_acl_xattr.h>
+#include <linux/richacl_xattr.h>
 #include <linux/xattr.h>
 
 
@@ -103,6 +104,9 @@ const struct xattr_handler *xfs_xattr_handlers[] = {
        &posix_acl_access_xattr_handler,
        &posix_acl_default_xattr_handler,
 #endif
+#ifdef CONFIG_XFS_RICHACL
+       &richacl_xattr_handler,
+#endif
        NULL
 };
 
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to