|
@@ -0,0 +1,378 @@
|
|
|
+/* -*- mode: c; c-basic-offset: 8; -*-
|
|
|
+ * vim: noexpandtab sw=8 ts=8 sts=0:
|
|
|
+ *
|
|
|
+ * acl.c
|
|
|
+ *
|
|
|
+ * Copyright (C) 2004, 2008 Oracle. All rights reserved.
|
|
|
+ *
|
|
|
+ * CREDITS:
|
|
|
+ * Lots of code in this file is copy from linux/fs/ext3/acl.c.
|
|
|
+ * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or
|
|
|
+ * modify it under the terms of the GNU General Public
|
|
|
+ * License version 2 as published by the Free Software Foundation.
|
|
|
+ *
|
|
|
+ * 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/init.h>
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/string.h>
|
|
|
+
|
|
|
+#define MLOG_MASK_PREFIX ML_INODE
|
|
|
+#include <cluster/masklog.h>
|
|
|
+
|
|
|
+#include "ocfs2.h"
|
|
|
+#include "alloc.h"
|
|
|
+#include "dlmglue.h"
|
|
|
+#include "file.h"
|
|
|
+#include "ocfs2_fs.h"
|
|
|
+
|
|
|
+#include "xattr.h"
|
|
|
+#include "acl.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * Convert from xattr value to acl struct.
|
|
|
+ */
|
|
|
+static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size)
|
|
|
+{
|
|
|
+ int n, count;
|
|
|
+ struct posix_acl *acl;
|
|
|
+
|
|
|
+ if (!value)
|
|
|
+ return NULL;
|
|
|
+ if (size < sizeof(struct posix_acl_entry))
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ count = size / sizeof(struct posix_acl_entry);
|
|
|
+ if (count < 0)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ if (count == 0)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ acl = posix_acl_alloc(count, GFP_NOFS);
|
|
|
+ if (!acl)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+ for (n = 0; n < count; n++) {
|
|
|
+ struct ocfs2_acl_entry *entry =
|
|
|
+ (struct ocfs2_acl_entry *)value;
|
|
|
+
|
|
|
+ acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
|
|
|
+ acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
|
|
|
+ acl->a_entries[n].e_id = le32_to_cpu(entry->e_id);
|
|
|
+ value += sizeof(struct posix_acl_entry);
|
|
|
+
|
|
|
+ }
|
|
|
+ return acl;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Convert acl struct to xattr value.
|
|
|
+ */
|
|
|
+static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size)
|
|
|
+{
|
|
|
+ struct ocfs2_acl_entry *entry = NULL;
|
|
|
+ char *ocfs2_acl;
|
|
|
+ size_t n;
|
|
|
+
|
|
|
+ *size = acl->a_count * sizeof(struct posix_acl_entry);
|
|
|
+
|
|
|
+ ocfs2_acl = kmalloc(*size, GFP_NOFS);
|
|
|
+ if (!ocfs2_acl)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ entry = (struct ocfs2_acl_entry *)ocfs2_acl;
|
|
|
+ for (n = 0; n < acl->a_count; n++, entry++) {
|
|
|
+ entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
|
|
|
+ entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
|
|
|
+ entry->e_id = cpu_to_le32(acl->a_entries[n].e_id);
|
|
|
+ }
|
|
|
+ return ocfs2_acl;
|
|
|
+}
|
|
|
+
|
|
|
+static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode,
|
|
|
+ int type,
|
|
|
+ struct buffer_head *di_bh)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
|
|
+ int name_index;
|
|
|
+ char *value = NULL;
|
|
|
+ struct posix_acl *acl;
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case ACL_TYPE_ACCESS:
|
|
|
+ name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
|
|
+ break;
|
|
|
+ case ACL_TYPE_DEFAULT:
|
|
|
+ name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index, "", NULL, 0);
|
|
|
+ if (retval > 0) {
|
|
|
+ value = kmalloc(retval, GFP_NOFS);
|
|
|
+ if (!value)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+ retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index,
|
|
|
+ "", value, retval);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (retval > 0)
|
|
|
+ acl = ocfs2_acl_from_xattr(value, retval);
|
|
|
+ else if (retval == -ENODATA || retval == 0)
|
|
|
+ acl = NULL;
|
|
|
+ else
|
|
|
+ acl = ERR_PTR(retval);
|
|
|
+
|
|
|
+ kfree(value);
|
|
|
+
|
|
|
+ return acl;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Get posix acl.
|
|
|
+ */
|
|
|
+static struct posix_acl *ocfs2_get_acl(struct inode *inode, int type)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
|
|
+ struct buffer_head *di_bh = NULL;
|
|
|
+ struct posix_acl *acl;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ ret = ocfs2_inode_lock(inode, &di_bh, 0);
|
|
|
+ if (ret < 0) {
|
|
|
+ mlog_errno(ret);
|
|
|
+ acl = ERR_PTR(ret);
|
|
|
+ return acl;
|
|
|
+ }
|
|
|
+
|
|
|
+ acl = ocfs2_get_acl_nolock(inode, type, di_bh);
|
|
|
+
|
|
|
+ ocfs2_inode_unlock(inode, 0);
|
|
|
+
|
|
|
+ brelse(di_bh);
|
|
|
+
|
|
|
+ return acl;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Set the access or default ACL of an inode.
|
|
|
+ */
|
|
|
+static int ocfs2_set_acl(handle_t *handle,
|
|
|
+ struct inode *inode,
|
|
|
+ struct buffer_head *di_bh,
|
|
|
+ int type,
|
|
|
+ struct posix_acl *acl,
|
|
|
+ struct ocfs2_alloc_context *meta_ac,
|
|
|
+ struct ocfs2_alloc_context *data_ac)
|
|
|
+{
|
|
|
+ int name_index;
|
|
|
+ void *value = NULL;
|
|
|
+ size_t size = 0;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (S_ISLNK(inode->i_mode))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case ACL_TYPE_ACCESS:
|
|
|
+ name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
|
|
+ if (acl) {
|
|
|
+ mode_t mode = inode->i_mode;
|
|
|
+ ret = posix_acl_equiv_mode(acl, &mode);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ else {
|
|
|
+ inode->i_mode = mode;
|
|
|
+ if (ret == 0)
|
|
|
+ acl = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case ACL_TYPE_DEFAULT:
|
|
|
+ name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
|
|
|
+ if (!S_ISDIR(inode->i_mode))
|
|
|
+ return acl ? -EACCES : 0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (acl) {
|
|
|
+ value = ocfs2_acl_to_xattr(acl, &size);
|
|
|
+ if (IS_ERR(value))
|
|
|
+ return (int)PTR_ERR(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (handle)
|
|
|
+ ret = ocfs2_xattr_set_handle(handle, inode, di_bh, name_index,
|
|
|
+ "", value, size, 0,
|
|
|
+ meta_ac, data_ac);
|
|
|
+ else
|
|
|
+ ret = ocfs2_xattr_set(inode, name_index, "", value, size, 0);
|
|
|
+
|
|
|
+ kfree(value);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t ocfs2_xattr_list_acl_access(struct inode *inode,
|
|
|
+ char *list,
|
|
|
+ size_t list_len,
|
|
|
+ const char *name,
|
|
|
+ size_t name_len)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
|
|
+ const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
|
|
|
+
|
|
|
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (list && size <= list_len)
|
|
|
+ memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
|
|
|
+ return size;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t ocfs2_xattr_list_acl_default(struct inode *inode,
|
|
|
+ char *list,
|
|
|
+ size_t list_len,
|
|
|
+ const char *name,
|
|
|
+ size_t name_len)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
|
|
+ const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
|
|
|
+
|
|
|
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (list && size <= list_len)
|
|
|
+ memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
|
|
|
+ return size;
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_xattr_get_acl(struct inode *inode,
|
|
|
+ int type,
|
|
|
+ void *buffer,
|
|
|
+ size_t size)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
|
|
+ struct posix_acl *acl;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ acl = ocfs2_get_acl(inode, type);
|
|
|
+ if (IS_ERR(acl))
|
|
|
+ return PTR_ERR(acl);
|
|
|
+ if (acl == NULL)
|
|
|
+ return -ENODATA;
|
|
|
+ ret = posix_acl_to_xattr(acl, buffer, size);
|
|
|
+ posix_acl_release(acl);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_xattr_get_acl_access(struct inode *inode,
|
|
|
+ const char *name,
|
|
|
+ void *buffer,
|
|
|
+ size_t size)
|
|
|
+{
|
|
|
+ if (strcmp(name, "") != 0)
|
|
|
+ return -EINVAL;
|
|
|
+ return ocfs2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_xattr_get_acl_default(struct inode *inode,
|
|
|
+ const char *name,
|
|
|
+ void *buffer,
|
|
|
+ size_t size)
|
|
|
+{
|
|
|
+ if (strcmp(name, "") != 0)
|
|
|
+ return -EINVAL;
|
|
|
+ return ocfs2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_xattr_set_acl(struct inode *inode,
|
|
|
+ int type,
|
|
|
+ const void *value,
|
|
|
+ size_t size)
|
|
|
+{
|
|
|
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
|
|
+ struct posix_acl *acl;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if (!is_owner_or_cap(inode))
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ if (value) {
|
|
|
+ acl = posix_acl_from_xattr(value, size);
|
|
|
+ if (IS_ERR(acl))
|
|
|
+ return PTR_ERR(acl);
|
|
|
+ else if (acl) {
|
|
|
+ ret = posix_acl_valid(acl);
|
|
|
+ if (ret)
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ acl = NULL;
|
|
|
+
|
|
|
+ ret = ocfs2_set_acl(NULL, inode, NULL, type, acl, NULL, NULL);
|
|
|
+
|
|
|
+cleanup:
|
|
|
+ posix_acl_release(acl);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_xattr_set_acl_access(struct inode *inode,
|
|
|
+ const char *name,
|
|
|
+ const void *value,
|
|
|
+ size_t size,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ if (strcmp(name, "") != 0)
|
|
|
+ return -EINVAL;
|
|
|
+ return ocfs2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_xattr_set_acl_default(struct inode *inode,
|
|
|
+ const char *name,
|
|
|
+ const void *value,
|
|
|
+ size_t size,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ if (strcmp(name, "") != 0)
|
|
|
+ return -EINVAL;
|
|
|
+ return ocfs2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
|
|
|
+}
|
|
|
+
|
|
|
+struct xattr_handler ocfs2_xattr_acl_access_handler = {
|
|
|
+ .prefix = POSIX_ACL_XATTR_ACCESS,
|
|
|
+ .list = ocfs2_xattr_list_acl_access,
|
|
|
+ .get = ocfs2_xattr_get_acl_access,
|
|
|
+ .set = ocfs2_xattr_set_acl_access,
|
|
|
+};
|
|
|
+
|
|
|
+struct xattr_handler ocfs2_xattr_acl_default_handler = {
|
|
|
+ .prefix = POSIX_ACL_XATTR_DEFAULT,
|
|
|
+ .list = ocfs2_xattr_list_acl_default,
|
|
|
+ .get = ocfs2_xattr_get_acl_default,
|
|
|
+ .set = ocfs2_xattr_set_acl_default,
|
|
|
+};
|