@@ -4,6 +4,7 @@ config XFS_FS
depends on (64BIT || LBDAF)
select EXPORTFS
select LIBCRC32C
+ select FS_RICHACL
help
XFS is a high performance journaling filesystem which originated
on the SGI IRIX platform. It is completely multi-threaded, can
@@ -84,6 +84,7 @@ xfs-y += xfs_aops.o \
xfs_message.o \
xfs_mount.o \
xfs_mru_cache.o \
+ xfs_richacl.o \
xfs_super.o \
xfs_symlink.o \
xfs_sysfs.o \
@@ -461,10 +461,13 @@ 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 */
+#define XFS_SB_FEAT_INCOMPAT_RICHACL (1 << 3) /* richacls */
+
#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 +533,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
*/
@@ -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 (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,11 @@ xfs_generic_create(
goto out_cleanup_inode;
}
#endif
+ if (richacl) {
+ error = xfs_set_richacl(inode, richacl);
+ if (error)
+ goto out_cleanup_inode;
+ }
if (tmpfile)
d_tmpfile(dentry, inode);
@@ -189,10 +203,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:
@@ -534,6 +547,13 @@ xfs_setattr_time(
}
}
+static inline int
+xfs_acl_chmod(struct inode *inode, umode_t mode)
+{
+ if (IS_RICHACL(inode))
+ return richacl_chmod(inode, inode->i_mode);
+ return posix_acl_chmod(inode, inode->i_mode);
+}
int
xfs_setattr_nonsize(
struct xfs_inode *ip,
@@ -722,7 +742,7 @@ 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);
+ error = xfs_acl_chmod(inode, inode->i_mode);
if (error)
return error;
}
@@ -1104,6 +1124,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 +1154,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 +1184,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,
new file mode 100644
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Author: Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "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 = NULL;
+ 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.
+ */
+ if (error != -ENOATTR)
+ acl = 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;
+}
+
+static int
+xfs_remove_richacl(struct inode *inode)
+{
+ struct xfs_inode *ip = XFS_I(inode);
+ int error;
+
+ error = xfs_attr_remove(ip, XATTR_NAME_RICHACL, ATTR_ROOT);
+ if (error == -ENOATTR)
+ error = 0;
+ if (!error)
+ set_cached_richacl(inode, NULL);
+ return error;
+}
+
+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, size;
+ void *value;
+
+ if (!acl)
+ return xfs_remove_richacl(inode);
+
+ if (richacl_equiv_mode(acl, &mode) == 0) {
+ xfs_set_mode(inode, mode);
+ return xfs_remove_richacl(inode);
+ }
+
+ 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)
+ return error;
+
+ mode &= ~S_IRWXUGO;
+ mode |= richacl_masks_to_mode(acl);
+ xfs_set_mode(inode, mode);
+ set_cached_richacl(inode, acl);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Author: Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __FS_XFS_RICHACL_H
+#define __FS_XFS_RICHACL_H
+
+struct richacl;
+
+extern struct richacl *xfs_get_richacl(struct inode *);
+extern int xfs_set_richacl(struct inode *, struct richacl *);
+
+#endif /* __FS_XFS_RICHACL_H */
@@ -1500,7 +1500,11 @@ 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))
+ set_richacl_flag(sb);
+ else
+ set_posix_acl_flag(sb);
/* version 5 superblocks support inode version counters. */
if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5)
@@ -36,6 +36,9 @@ extern void xfs_qm_exit(void);
# define set_posix_acl_flag(sb) do { } while (0)
#endif
+# define XFS_RICHACL_STRING "Richacls, "
+# define set_richacl_flag(sb) ((sb)->s_flags |= MS_RICHACL)
+
#define XFS_SECURITY_STRING "security attributes, "
#ifdef CONFIG_XFS_RT
@@ -52,6 +55,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 */
@@ -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,7 @@ const struct xattr_handler *xfs_xattr_handlers[] = {
&posix_acl_access_xattr_handler,
&posix_acl_default_xattr_handler,
#endif
+ &richacl_xattr_handler,
NULL
};
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. On the grounds that richacls add relatively little overhead compared to the size of xfs itself, to keep the testing matrix small, and because xfs users are highly likely to enable richacls anyway, richacl support cannot be compiled out in xfs. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> --- fs/xfs/Kconfig | 1 + fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_format.h | 11 ++++- fs/xfs/xfs_iops.c | 44 +++++++++++++++---- fs/xfs/xfs_richacl.c | 102 +++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_richacl.h | 23 ++++++++++ fs/xfs/xfs_super.c | 6 ++- fs/xfs/xfs_super.h | 4 ++ fs/xfs/xfs_xattr.c | 2 + 9 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 fs/xfs/xfs_richacl.c create mode 100644 fs/xfs/xfs_richacl.h