@@ -17,7 +17,7 @@ zuf-y += md.o t1.o t2.o
zuf-y += zuf-core.o zuf-root.o
# Main FS
-zuf-y += xattr.o
+zuf-y += acl.o xattr.o
zuf-y += rw.o mmap.o ioctl.o
zuf-y += super.o inode.o directory.o namei.o file.o symlink.o
zuf-y += module.o
@@ -83,6 +83,15 @@ int __zuf_setxattr(struct inode *inode, int type, const char *name,
ssize_t zuf_listxattr(struct dentry *dentry, char *buffer, size_t size);
extern const struct xattr_handler *zuf_xattr_handlers[];
+/* acl.c */
+int zuf_set_acl(struct inode *inode, struct posix_acl *acl, int type);
+struct posix_acl *zuf_get_acl(struct inode *inode, int type);
+int zuf_acls_create_pre(struct inode *dir, struct inode *inode,
+ struct posix_acl **user_acl);
+int zuf_acls_create_post(struct inode *dir, struct inode *inode,
+ struct posix_acl *acl);
+extern const struct xattr_handler zuf_acl_access_xattr_handler;
+extern const struct xattr_handler zuf_acl_default_xattr_handler;
/* super.c */
int zuf_init_inodecache(void);
@@ -43,6 +43,7 @@
#define zuf_dbg_rw(s, args ...) zuf_chan_debug("rw ", s, ##args)
#define zuf_dbg_t1(s, args ...) zuf_chan_debug("t1 ", s, ##args)
#define zuf_dbg_xattr(s, args ...) zuf_chan_debug("xattr", s, ##args)
+#define zuf_dbg_acl(s, args ...) zuf_chan_debug("acl ", s, ##args)
#define zuf_dbg_t2(s, args ...) zuf_chan_debug("t2dbg", s, ##args)
#define zuf_dbg_t2_rw(s, args ...) zuf_chan_debug("t2grw", s, ##args)
#define zuf_dbg_core(s, args ...) zuf_chan_debug("core ", s, ##args)
new file mode 100644
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Access Control List
+ *
+ * Copyright (c) 2018 NetApp Inc. All rights reserved.
+ *
+ * ZUFS-License: GPL-2.0. See module.c for LICENSE details.
+ *
+ * Authors:
+ * Boaz Harrosh <boazh@netapp.com>
+ */
+
+#include <linux/fs.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/xattr.h>
+#include "zuf.h"
+
+static void _acl_to_value(const struct posix_acl *acl, void *value)
+{
+ int n;
+ struct zuf_acl *macl = value;
+
+ zuf_dbg_acl("acl->count=%d\n", acl->a_count);
+
+ for (n = 0; n < acl->a_count; n++) {
+ const struct posix_acl_entry *entry = &acl->a_entries[n];
+
+ zuf_dbg_acl("aclno=%d tag=0x%x perm=0x%x\n",
+ n, entry->e_tag, entry->e_perm);
+
+ macl->tag = cpu_to_le16(entry->e_tag);
+ macl->perm = cpu_to_le16(entry->e_perm);
+
+ switch (entry->e_tag) {
+ case ACL_USER:
+ macl->id = cpu_to_le32(__kuid_val(entry->e_uid));
+ break;
+ case ACL_GROUP:
+ macl->id = cpu_to_le32(__kgid_val(entry->e_gid));
+ break;
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ break;
+ default:
+ zuf_dbg_err("e_tag=0x%x\n", entry->e_tag);
+ return;
+ }
+ macl++;
+ }
+}
+
+int zuf_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+ char *name = NULL;
+ void *buf;
+ int err;
+ size_t size;
+
+ zuf_dbg_acl("[%ld] acl=%p type=0x%x\n", inode->i_ino, acl, type);
+
+ switch (type) {
+ case ACL_TYPE_ACCESS: {
+ struct zus_inode *zi = ZUII(inode)->zi;
+
+ name = XATTR_POSIX_ACL_ACCESS;
+ if (acl) {
+ err = posix_acl_update_mode(inode, &inode->i_mode,
+ &acl);
+ if (err < 0)
+ return err;
+
+ inode->i_ctime = current_time(inode);
+ timespec_to_mt(&zi->i_ctime, &inode->i_ctime);
+ }
+ zi->i_mode = cpu_to_le16(inode->i_mode);
+ break;
+ }
+ case ACL_TYPE_DEFAULT:
+ name = XATTR_POSIX_ACL_DEFAULT;
+ if (!S_ISDIR(inode->i_mode))
+ return acl ? -EACCES : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ size = acl ? acl->a_count * sizeof(struct zuf_acl) : 0;
+ buf = kmalloc(size, GFP_KERNEL);
+ if (unlikely(!buf))
+ return -ENOMEM;
+
+ if (acl)
+ _acl_to_value(acl, buf);
+
+ err = __zuf_setxattr(inode, ZUF_XF_SYSTEM, name, buf, size, 0);
+ if (!err)
+ set_cached_acl(inode, type, acl);
+
+ kfree(buf);
+ return err;
+}
+
+static struct posix_acl *_value_to_acl(void *value, size_t size)
+{
+ int n, count;
+ struct posix_acl *acl;
+ struct zuf_acl *macl = value;
+ void *end = value + size;
+
+ if (!value)
+ return NULL;
+
+ count = size / sizeof(struct zuf_acl);
+ if (count < 0)
+ return ERR_PTR(-EINVAL);
+ if (count == 0)
+ return NULL;
+
+ acl = posix_acl_alloc(count, GFP_NOFS);
+ if (unlikely(!acl))
+ return ERR_PTR(-ENOMEM);
+
+ for (n = 0; n < count; n++) {
+ if (end < (void *)macl + sizeof(struct zuf_acl))
+ goto fail;
+
+ zuf_dbg_acl("aclno=%d tag=0x%x perm=0x%x id=0x%x\n",
+ n, le16_to_cpu(macl->tag), le16_to_cpu(macl->perm),
+ le32_to_cpu(macl->id));
+
+ acl->a_entries[n].e_tag = le16_to_cpu(macl->tag);
+ acl->a_entries[n].e_perm = le16_to_cpu(macl->perm);
+
+ switch (acl->a_entries[n].e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ macl++;
+ break;
+ case ACL_USER:
+ acl->a_entries[n].e_uid =
+ KUIDT_INIT(le32_to_cpu(macl->id));
+ macl++;
+ if (end < (void *)macl)
+ goto fail;
+ break;
+ case ACL_GROUP:
+ acl->a_entries[n].e_gid =
+ KGIDT_INIT(le32_to_cpu(macl->id));
+ macl++;
+ if (end < (void *)macl)
+ goto fail;
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+ if (macl != end)
+ goto fail;
+ return acl;
+
+fail:
+ posix_acl_release(acl);
+ return ERR_PTR(-EINVAL);
+}
+
+struct posix_acl *zuf_get_acl(struct inode *inode, int type)
+{
+ struct zuf_inode_info *zii = ZUII(inode);
+ char *name = NULL;
+ void *buf;
+ struct posix_acl *acl = NULL;
+ int ret;
+
+ zuf_dbg_acl("[%ld] type=0x%x\n", inode->i_ino, type);
+
+ buf = (void *)__get_free_page(GFP_KERNEL);
+ if (unlikely(!buf))
+ return ERR_PTR(-ENOMEM);
+
+ switch (type) {
+ case ACL_TYPE_ACCESS:
+ name = XATTR_POSIX_ACL_ACCESS;
+ break;
+ case ACL_TYPE_DEFAULT:
+ name = XATTR_POSIX_ACL_DEFAULT;
+ break;
+ default:
+ WARN_ON(1);
+ return ERR_PTR(-EINVAL);
+ }
+
+ zuf_smr_lock(zii);
+
+ ret = __zuf_getxattr(inode, ZUF_XF_SYSTEM, name, buf, PAGE_SIZE);
+ if (likely(ret > 0)) {
+ acl = _value_to_acl(buf, ret);
+ } else if (ret != -ENODATA) {
+ if (ret != 0)
+ zuf_dbg_err("failed to getattr ret=%d\n", ret);
+ acl = ERR_PTR(ret);
+ }
+
+ if (!IS_ERR(acl))
+ set_cached_acl(inode, type, acl);
+
+ zuf_smr_unlock(zii);
+
+ free_page((ulong)buf);
+
+ return acl;
+}
+
+/* Used by creation of new inodes */
+int zuf_acls_create_pre(struct inode *dir, struct inode *inode,
+ struct posix_acl **user_acl)
+{
+ struct posix_acl *acl;
+
+ if (!IS_POSIXACL(dir))
+ return 0;
+
+ zuf_dbg_acl("[%ld] i_ino=%ld i_mode=o%o\n",
+ dir->i_ino, inode->i_ino, inode->i_mode);
+
+ if (S_ISLNK(inode->i_mode))
+ return 0;
+
+ acl = get_acl(dir, ACL_TYPE_DEFAULT);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (!acl)
+ inode->i_mode &= ~current_umask();
+ else
+ *user_acl = acl;
+
+ return 0;
+}
+
+int zuf_acls_create_post(struct inode *dir, struct inode *inode,
+ struct posix_acl *acl)
+{
+ int err;
+
+ zuf_dbg_acl("[%ld] i_ino=%ld i_mode=o%o\n",
+ dir->i_ino, inode->i_ino, inode->i_mode);
+
+ if (S_ISDIR(inode->i_mode)) {
+ err = zuf_set_acl(inode, acl, ACL_TYPE_DEFAULT);
+ if (err)
+ goto cleanup;
+ }
+ err = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
+ if (unlikely(err < 0))
+ return err;
+
+ zus_zi(inode)->i_mode = cpu_to_le16(inode->i_mode);
+ if (err > 0) { /* This is an extended ACL */
+ err = zuf_set_acl(inode, acl, ACL_TYPE_ACCESS);
+ } else {
+ /* NOTE: Boaz think we will cry over this... */
+ struct zufs_ioc_attr ioc_attr = {
+ .hdr.in_len = sizeof(ioc_attr),
+ .hdr.out_len = sizeof(ioc_attr),
+ .hdr.operation = ZUFS_OP_SETATTR,
+ .zus_ii = ZUII(inode)->zus_ii,
+ .zuf_attr = STATX_MODE,
+ };
+
+ err = zufc_dispatch(ZUF_ROOT(SBI(inode->i_sb)),
+ &ioc_attr.hdr, NULL, 0);
+ if (unlikely(err && err != -EINTR))
+ zuf_err("zufc_dispatch failed => %d\n", err);
+ }
+
+cleanup:
+ posix_acl_release(acl);
+ return err;
+}
@@ -521,5 +521,7 @@ const struct inode_operations zuf_file_inode_operations = {
.getattr = zuf_getattr,
.update_time = zuf_update_time,
.fiemap = tozu_fiemap,
+ .get_acl = zuf_get_acl,
+ .set_acl = zuf_set_acl,
.listxattr = zuf_listxattr,
};
@@ -342,6 +342,7 @@ struct inode *zuf_new_inode(struct inode *dir, umode_t mode,
.flags = tmpfile ? ZI_TMPFILE : 0,
.str.len = qstr->len,
};
+ struct posix_acl *acl = NULL;
struct inode *inode;
struct zus_inode *zi = NULL;
struct page *pages[2];
@@ -366,6 +367,10 @@ struct inode *zuf_new_inode(struct inode *dir, umode_t mode,
if (err && err != -EOPNOTSUPP)
goto fail;
+ err = zuf_acls_create_pre(dir, inode, &acl);
+ if (unlikely(err))
+ goto fail;
+
zuf_set_inode_flags(inode, &ioc_new_inode.zi);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
@@ -406,6 +411,12 @@ struct inode *zuf_new_inode(struct inode *dir, umode_t mode,
zuf_dbg_verbose("allocating inode %ld (zi=%p)\n", _zi_ino(zi), zi);
+ if (acl && !symname) {
+ err = zuf_acls_create_post(dir, inode, acl);
+ if (unlikely(err))
+ goto fail;
+ }
+
err = insert_inode_locked(inode);
if (unlikely(err)) {
zuf_dbg_err("[%ld:%s] generation=%lld insert_inode_locked => %d\n",
@@ -420,6 +420,8 @@ const struct inode_operations zuf_dir_inode_operations = {
.setattr = zuf_setattr,
.getattr = zuf_getattr,
.update_time = zuf_update_time,
+ .get_acl = zuf_get_acl,
+ .set_acl = zuf_set_acl,
.listxattr = zuf_listxattr,
};
@@ -427,5 +429,7 @@ const struct inode_operations zuf_special_inode_operations = {
.setattr = zuf_setattr,
.getattr = zuf_getattr,
.update_time = zuf_update_time,
+ .get_acl = zuf_get_acl,
+ .set_acl = zuf_set_acl,
.listxattr = zuf_listxattr,
};
@@ -335,6 +335,12 @@ enum { ZUF_XF_SECURITY = 1,
ZUF_XF_USER = 4,
};
+struct zuf_acl {
+ __le16 tag;
+ __le16 perm;
+ __le32 id;
+} __packed;
+
enum big_alloc_type { ba_stack, ba_kmalloc, ba_vmalloc };
static inline