[PATCH v4 4/5] hfsplus: introduce implementation of the ACLs support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
Subject: [PATCH v4 4/5] hfsplus: introduce implementation of the ACLs support

This patch adds implementation of the ACLs support for hfsplus driver.

Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
CC: Trond Myklebust <Trond.Myklebust@xxxxxxxxxx>
CC: "J. Bruce Fields" <bfields@xxxxxxxxxx>
CC: Al Viro <viro@xxxxxxxxxxxxxxxxxx>
CC: Christoph Hellwig <hch@xxxxxxxxxxxxx>
CC: Hin-Tak Leung <htl10@xxxxxxxxxxxxxxxxxxxxx>
---
 fs/hfsplus/acl.c | 1903 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/hfsplus/acl.h |   96 +++
 2 files changed, 1999 insertions(+)
 create mode 100644 fs/hfsplus/acl.c
 create mode 100644 fs/hfsplus/acl.h

diff --git a/fs/hfsplus/acl.c b/fs/hfsplus/acl.c
new file mode 100644
index 0000000..ce6cfb2
--- /dev/null
+++ b/fs/hfsplus/acl.c
@@ -0,0 +1,1903 @@
+/*
+ * linux/fs/hfsplus/acl.c
+ *
+ * Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
+ *
+ * Handler for Posix Access Control Lists (ACLs) support.
+ */
+
+#include <linux/uuid.h>
+
+#include "hfsplus_fs.h"
+#include "xattr.h"
+#include "acl.h"
+
+#define HFSPLUS_ACE_ID_MASK 0xFFFFFFFF
+
+static unsigned char hfsplus_group_fingerprint[] = {0xab, 0xcd, 0xef,
+						    0xab, 0xcd, 0xef,
+						    0xab, 0xcd, 0xef,
+						    0xab, 0xcd, 0xef};
+
+static unsigned char hfsplus_user_fingerprint[] = {0xff, 0xff,
+						   0xee, 0xee,
+						   0xdd, 0xdd,
+						   0xcc, 0xcc,
+						   0xbb, 0xbb,
+						   0xaa, 0xaa};
+
+#define HFSPLUS_FINGERPRINT_SIZE \
+	(HFSPLUS_GUID_SIZE - sizeof(HFSPLUS_ACE_ID_MASK))
+
+#define HFSPLUS_EVERYBODY_ID 0xc
+
+struct hfsplus_nfsv4_mapping_env {
+	struct inode *inode;
+	u8 ace_applicable[HFSPLUS_GUID_SIZE];
+	size_t allocated_acl_size;
+	size_t composed_acl_size;
+};
+
+static uint32_t hfsplus_get_naces(const void *nfsv4_acl)
+{
+	const struct hfsplus_acl_record *raw_acl_rec =
+		(const struct hfsplus_acl_record *)nfsv4_acl;
+	uint32_t entrycount;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl %p\n", __func__, nfsv4_acl);
+
+	entrycount = be32_to_cpu(raw_acl_rec->acl_entrycount);
+
+	hfs_dbg(ACL_MOD, "[%s]: entry_count %u\n",
+			__func__, entrycount);
+
+	return entrycount;
+}
+
+static void *hfsplus_get_ace(const void *nfsv4_acl, uint32_t index)
+{
+	const struct hfsplus_acl_record *raw_acl_rec =
+		(const struct hfsplus_acl_record *)nfsv4_acl;
+	struct hfsplus_acl_entry *ace;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl %p, index %u\n",
+			__func__, nfsv4_acl, index);
+
+	ace = (struct hfsplus_acl_entry *)(raw_acl_rec->acl_ace + index);
+
+	hfs_dbg(ACL_MOD, "[%s]: &ace[0] %p, &ace[index] %p\n",
+			__func__, raw_acl_rec->acl_ace, ace);
+	hfs_dbg_hexdump(ACL_MOD, "ace->ace_applicable: ",
+				ace->ace_applicable, HFSPLUS_GUID_SIZE);
+	hfs_dbg(ACL_MOD, "[%s]: ace->ace_flags %#x, ace->ace_rights %#x\n",
+			__func__,
+			be32_to_cpu(ace->ace_flags),
+			be32_to_cpu(ace->ace_rights));
+
+	return (void *)ace;
+}
+
+static uint32_t hfsplus_get_access_mask(const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, nfsv4_ace);
+
+	return be32_to_cpu(ace->ace_rights);
+}
+
+static bool hfsplus_ace_type_valid(const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	u32 ace_flags = be32_to_cpu(ace->ace_flags);
+	u32 ace_type = ace_flags & HFSPLUS_ACE_KINDMASK;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p, ace_flags %#x, ace_type %#x\n",
+			__func__, nfsv4_ace, ace_flags, ace_type);
+
+	return ace_type == HFSPLUS_ACE_PERMIT || ace_type == HFSPLUS_ACE_DENY;
+}
+
+static bool hfsplus_is_allowed_ace_type(const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	u32 ace_flags = be32_to_cpu(ace->ace_flags);
+	u32 ace_type = ace_flags & HFSPLUS_ACE_KINDMASK;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p, ace_flags %#x, ace_type %#x\n",
+			__func__, nfsv4_ace, ace_flags, ace_type);
+
+	return ace_type == HFSPLUS_ACE_PERMIT;
+}
+
+static unsigned char empty_guid[HFSPLUS_GUID_SIZE] = {0};
+
+static inline int empty_ace(const struct hfsplus_acl_entry *ace)
+{
+	return memcmp(empty_guid, ace->ace_applicable, HFSPLUS_GUID_SIZE) == 0;
+}
+
+#define IS_GROUP_FINGERPRINT(ace_applicable) \
+	(memcmp(ace_applicable, \
+		hfsplus_group_fingerprint, HFSPLUS_FINGERPRINT_SIZE) == 0)
+
+#define IS_USER_FINGERPRINT(ace_applicable) \
+	(memcmp(ace_applicable, \
+		hfsplus_user_fingerprint, HFSPLUS_FINGERPRINT_SIZE) == 0)
+
+static bool hfsplus_is_acl_user_obj(const struct nfsv4_acl_info *nfsv4_acl_info,
+						const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	if (!IS_GROUP_FINGERPRINT(ace->ace_applicable) &&
+			!IS_USER_FINGERPRINT(ace->ace_applicable) &&
+			!empty_ace(ace)) {
+		hfs_dbg(ACL_MOD, "[%s]: found user obj\n", __func__);
+		return true;
+	} else
+		return false;
+}
+
+static bool hfsplus_is_acl_group_obj(
+				const struct nfsv4_acl_info *nfsv4_acl_info,
+				const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_gid_ptr;
+	const struct hfsplus_nfsv4_mapping_env *mapping_env =
+	    (const struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const struct inode *inode = mapping_env->inode;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	if (IS_GROUP_FINGERPRINT(ace->ace_applicable)) {
+		raw_gid_ptr = (__be32 *)&ace->ace_applicable[size];
+		if (be32_to_cpu(*raw_gid_ptr) == i_gid_read(inode)) {
+			hfs_dbg(ACL_MOD, "[%s]: found group obj\n",
+								__func__);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool hfsplus_is_acl_other(const struct nfsv4_acl_info *nfsv4_acl_info,
+						const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	if (IS_GROUP_FINGERPRINT(ace->ace_applicable)) {
+		raw_id_ptr = (__be32 *)&ace->ace_applicable[size];
+		if (be32_to_cpu(*raw_id_ptr) == HFSPLUS_EVERYBODY_ID) {
+			hfs_dbg(ACL_MOD, "[%s]: found other\n", __func__);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool hfsplus_is_acl_user(const struct nfsv4_acl_info *nfsv4_acl_info,
+						const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	if (IS_USER_FINGERPRINT(ace->ace_applicable)) {
+		raw_id_ptr = (__be32 *)&ace->ace_applicable[size];
+		if (be32_to_cpu(*raw_id_ptr) != HFSPLUS_EVERYBODY_ID) {
+			hfs_dbg(ACL_MOD, "[%s]: found user %#x\n",
+					__func__, be32_to_cpu(*raw_id_ptr));
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool hfsplus_is_acl_group(const struct nfsv4_acl_info *nfsv4_acl_info,
+						const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	if (IS_GROUP_FINGERPRINT(ace->ace_applicable)) {
+		raw_id_ptr = (__be32 *)&ace->ace_applicable[size];
+		if (be32_to_cpu(*raw_id_ptr) != HFSPLUS_EVERYBODY_ID) {
+			hfs_dbg(ACL_MOD, "[%s]: found group %#x\n",
+					__func__, be32_to_cpu(*raw_id_ptr));
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool hfsplus_ace_has_unknown_flags(const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	u32 ace_flags = be32_to_cpu(ace->ace_flags);
+	ace_flags &= ~HFSPLUS_ACE_KINDMASK;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p, ace_flags %#x\n",
+			__func__, nfsv4_ace, ace_flags);
+
+	return ace_flags & ~HFSPLUS_ACE_INHERIT_CONTROL_FLAGS;
+}
+
+static bool hfsplus_ace_has_inheritance_flags(const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	u32 ace_flags = be32_to_cpu(ace->ace_flags);
+	ace_flags &= ~HFSPLUS_ACE_KINDMASK;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p, ace_flags %#x\n",
+			__func__, nfsv4_ace, ace_flags);
+
+	return ace_flags & HFSPLUS_ACE_INHERIT_CONTROL_FLAGS;
+}
+
+static bool hfsplus_ace_inherit_only_flag(const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	u32 ace_flags = be32_to_cpu(ace->ace_flags);
+	ace_flags &= ~HFSPLUS_ACE_KINDMASK;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p, ace_flags %#x\n",
+			__func__, nfsv4_ace, ace_flags);
+
+	return ace_flags & HFSPLUS_ACE_ONLY_INHERIT;
+}
+
+static int hfsplus_check_deny(u32 rights, int isowner)
+{
+	hfs_dbg(ACL_MOD, "[%s]: rights %#x, isowner %u\n",
+			__func__, rights, isowner);
+
+	if (rights & (HFSPLUS_VNODE_READ_ATTRIBUTES |
+			HFSPLUS_VNODE_READ_EXTATTRIBUTES |
+			HFSPLUS_VNODE_READ_SECURITY)) {
+		if (rights & HFSPLUS_VNODE_READ_ATTRIBUTES)
+			pr_warn("can't deny read attr\n");
+		if (rights & HFSPLUS_VNODE_READ_EXTATTRIBUTES)
+			pr_warn("can't deny read xattr\n");
+		if (rights & HFSPLUS_VNODE_READ_SECURITY)
+			pr_warn("can't deny read security\n");
+		return -EINVAL;
+	}
+	if (!isowner)
+		return 0;
+	if (rights & (HFSPLUS_VNODE_WRITE_ATTRIBUTES |
+			HFSPLUS_VNODE_WRITE_EXTATTRIBUTES |
+			HFSPLUS_VNODE_WRITE_SECURITY)) {
+		if (rights & HFSPLUS_VNODE_WRITE_ATTRIBUTES)
+			pr_warn("can't deny write attr\n");
+		if (rights & HFSPLUS_VNODE_WRITE_EXTATTRIBUTES)
+			pr_warn("can't deny write xattr\n");
+		if (rights & HFSPLUS_VNODE_WRITE_SECURITY)
+			pr_warn("can't deny write security\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void hfsplus_low_mode_from_nfs4(u32 perm, unsigned short *mode,
+						unsigned int flags)
+{
+	u32 write_mode = HFSPLUS_VNODE_WRITE_DATA;
+
+	hfs_dbg(ACL_MOD, "[%s]: perm %#x, mode ptr %p, flags %#x\n",
+			__func__, perm, mode, flags);
+
+	if (flags & NFS4_ACL_DIR)
+		write_mode |= HFSPLUS_VNODE_DELETE_CHILD;
+	*mode = 0;
+	if ((perm & HFSPLUS_VNODE_READ_DATA) == HFSPLUS_VNODE_READ_DATA)
+		*mode |= ACL_READ;
+	if ((perm & write_mode) == write_mode)
+		*mode |= ACL_WRITE;
+	if ((perm & HFSPLUS_VNODE_EXECUTE) == HFSPLUS_VNODE_EXECUTE)
+		*mode |= ACL_EXECUTE;
+
+	hfs_dbg(ACL_MOD, "[%s]: mode %#x\n", __func__, *mode);
+}
+
+static unsigned int hfsplus_calculate_eflag(unsigned int flags)
+{
+	hfs_dbg(ACL_MOD, "[%s]: flags %#x\n", __func__, flags);
+
+	return (flags & NFS4_ACL_TYPE_DEFAULT) ?
+			HFSPLUS_ACE_INHERIT_CONTROL_FLAGS : 0;
+}
+
+static int hfsplus_find_uid(struct posix_acl_state *state,
+					const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	struct posix_ace_state_array *a = state->users;
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+	kuid_t uid;
+	int i;
+
+	hfs_dbg(ACL_MOD, "[%s]: state %p, ace %p\n",
+			__func__, state, nfsv4_ace);
+
+	if (IS_USER_FINGERPRINT(ace->ace_applicable)) {
+		raw_id_ptr = (__be32 *)&ace->ace_applicable[size];
+		uid = be32_to_cpu(*raw_id_ptr);
+		hfs_dbg(ACL_MOD, "[%s]: uid %#x\n", __func__, uid);
+	} else if (IS_GROUP_FINGERPRINT(ace->ace_applicable)) {
+		pr_warn("can't find group id\n");
+		return -1;
+	} else {
+		hfs_dbg(ACL_MOD, "[%s]: can't convert uid\n", __func__);
+		return -1;
+	}
+
+	for (i = 0; i < a->n; i++) {
+		if (uid_eq(a->aces[i].uid, uid)) {
+			hfs_dbg(ACL_MOD, "[%s]: index %d\n", __func__, i);
+			return i;
+		}
+	}
+	/* Not found: */
+	a->n++;
+	a->aces[i].uid = uid;
+	a->aces[i].perms.allow = state->everyone.allow;
+	a->aces[i].perms.deny  = state->everyone.deny;
+
+	hfs_dbg(ACL_MOD, "[%s]: index %d\n", __func__, i);
+	return i;
+}
+
+static int hfsplus_find_gid(struct posix_acl_state *state,
+					const void *nfsv4_ace)
+{
+	const struct hfsplus_acl_entry *ace =
+		(const struct hfsplus_acl_entry *)nfsv4_ace;
+	struct posix_ace_state_array *a = state->groups;
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+	kgid_t gid;
+	int i;
+
+	hfs_dbg(ACL_MOD, "[%s]: state %p, ace %p\n",
+			__func__, state, nfsv4_ace);
+
+	if (IS_GROUP_FINGERPRINT(ace->ace_applicable)) {
+		raw_id_ptr = (__be32 *)&ace->ace_applicable[size];
+		gid = be32_to_cpu(*raw_id_ptr);
+		hfs_dbg(ACL_MOD, "[%s]: gid %#x\n", __func__, gid);
+	} else if (IS_USER_FINGERPRINT(ace->ace_applicable)) {
+		pr_warn("can't find user id\n");
+		return -1;
+	} else {
+		hfs_dbg(ACL_MOD, "[%s]: can't convert uid\n", __func__);
+		return -1;
+	}
+
+	for (i = 0; i < a->n; i++) {
+		if (gid_eq(a->aces[i].gid, gid)) {
+			hfs_dbg(ACL_MOD, "[%s]: index %d\n", __func__, i);
+			return i;
+		}
+	}
+	/* Not found: */
+	a->n++;
+	a->aces[i].gid = gid;
+	a->aces[i].perms.allow = state->everyone.allow;
+	a->aces[i].perms.deny  = state->everyone.deny;
+
+	hfs_dbg(ACL_MOD, "[%s]: index %d\n", __func__, i);
+	return i;
+}
+
+static int hfsplus_set_posix_ace_uid(struct user_namespace *user_ns,
+					struct posix_acl_entry *pace,
+					kuid_t uid)
+{
+	hfs_dbg(ACL_MOD, "[%s]: pace %p, uid %#x\n",
+			__func__, pace, uid);
+
+	pace->e_uid = make_kuid(user_ns, uid);
+	if (!uid_valid(pace->e_uid)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hfsplus_set_posix_ace_gid(struct user_namespace *user_ns,
+					struct posix_acl_entry *pace,
+					kgid_t gid)
+{
+	hfs_dbg(ACL_MOD, "[%s]: pace %p, gid %#x\n",
+			__func__, pace, gid);
+
+	pace->e_gid = make_kgid(user_ns, gid);
+	if (!gid_valid(pace->e_gid)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define HFSPLUS_ACE_SET_OWNER_USER_ID(ace_applicable) \
+	do { \
+		uuid_be generated_uuid; \
+		uuid_be_gen(&generated_uuid); \
+		memcpy(ace_applicable, \
+			generated_uuid.b, sizeof(generated_uuid)); \
+	} while (0)
+
+#define HFSPLUS_ACE_SET_USER_ID(ace_applicable, id) \
+	do { \
+		memset(ace_applicable, 0, HFSPLUS_GUID_SIZE); \
+		memcpy(&ace_applicable[0], \
+			&hfsplus_user_fingerprint[0], \
+			HFSPLUS_FINGERPRINT_SIZE); \
+		(*((__be32 *)&ace_applicable[HFSPLUS_FINGERPRINT_SIZE]) = \
+			cpu_to_be32(id)); \
+	} while (0)
+
+#define HFSPLUS_ACE_SET_GROUP_ID(ace_applicable, id) \
+	do { \
+		memset(ace_applicable, 0, HFSPLUS_GUID_SIZE); \
+		memcpy(&ace_applicable[0], \
+			&hfsplus_group_fingerprint[0], \
+			HFSPLUS_FINGERPRINT_SIZE); \
+		(*((__be32 *)&ace_applicable[HFSPLUS_FINGERPRINT_SIZE]) = \
+			cpu_to_be32(id)); \
+	} while (0)
+
+#define HFSPLUS_COMPOSE_DENY_ACE(ace, ace_applicable_buf, eflag, perms) \
+	do { \
+		memcpy(ace->ace_applicable, \
+				ace_applicable_buf, HFSPLUS_GUID_SIZE); \
+		ace->ace_flags = cpu_to_be32(HFSPLUS_ACE_DENY | eflag); \
+		ace->ace_rights = cpu_to_be32(perms); \
+		hfs_dbg_hexdump(ACL_MOD, "ace_applicable: ", \
+				ace->ace_applicable, HFSPLUS_GUID_SIZE); \
+		hfs_dbg(ACL_MOD, \
+			"[%s]: ace_flags %#x, ace_rights %#x\n", \
+				__func__, be32_to_cpu(ace->ace_flags), \
+				perms); \
+	} while (0)
+
+#define HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable_buf, eflag, perms) \
+	do { \
+		memcpy(ace->ace_applicable, \
+				ace_applicable_buf, HFSPLUS_GUID_SIZE); \
+		ace->ace_flags = cpu_to_be32(HFSPLUS_ACE_PERMIT | eflag); \
+		ace->ace_rights = cpu_to_be32(perms); \
+		hfs_dbg_hexdump(ACL_MOD, "ace_applicable: ", \
+				ace->ace_applicable, HFSPLUS_GUID_SIZE); \
+		hfs_dbg(ACL_MOD, \
+			"[%s]: ace_flags %#x, ace_rights %#x\n", \
+				__func__, be32_to_cpu(ace->ace_flags), \
+				perms); \
+	} while (0)
+
+static int hfsplus_prepare_nfsv4_acl_mapping(
+				struct nfsv4_acl_info *nfsv4_acl_info,
+				void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl;
+	struct hfsplus_nfsv4_mapping_env *mapping_env;
+	size_t allocated_size;
+	size_t filesec_hdr_size = sizeof(struct hfsplus_filesec);
+
+	hfs_dbg(ACL_MOD, "[%s]: nfsv4_acl_info %p, nfsv4_ace %p\n",
+				__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(!nfsv4_acl_info || !nfsv4_ace);
+
+	fsec_acl = (struct hfsplus_acl_record *)(nfsv4_acl_info->raw_acl);
+	mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)(nfsv4_acl_info->private);
+
+	hfs_dbg(ACL_MOD, "[%s]: fsec_acl %p, mapping_env %p\n",
+				__func__, fsec_acl, mapping_env);
+
+	BUG_ON(*nfsv4_ace != NULL);
+
+	allocated_size = mapping_env->allocated_acl_size;
+
+	hfs_dbg(ACL_MOD, "[%s]: allocated_size %zu\n",
+			__func__, allocated_size);
+
+	if (allocated_size <= 0 ||
+			allocated_size > HFSPLUS_MAX_INLINE_DATA_SIZE) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	if (mapping_env->composed_acl_size != filesec_hdr_size ||
+			mapping_env->composed_acl_size >= allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	*nfsv4_ace = (void *)fsec_acl->acl_ace;
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+	return 0;
+}
+
+static u32 hfsplus_mask_from_posix(struct nfsv4_acl_info *nfsv4_acl_info,
+					unsigned short perm,
+					unsigned int flags)
+{
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	int mask = HFSPLUS_VNODE_ANYONE_MODE;
+
+	if (flags & NFS4_ACL_OWNER)
+		mask |= HFSPLUS_VNODE_OWNER_MODE;
+	if (perm & ACL_READ)
+		mask |= HFSPLUS_VNODE_READ_DATA;
+	if (perm & ACL_WRITE)
+		mask |= HFSPLUS_VNODE_WRITE_MODE;
+	if ((perm & ACL_WRITE) && S_ISDIR(mapping_env->inode->i_mode))
+		mask |= HFSPLUS_VNODE_DELETE_CHILD;
+	if (perm & ACL_EXECUTE)
+		mask |= HFSPLUS_VNODE_EXECUTE;
+
+	hfs_dbg(ACL_MOD, "[%s]: perm %#x, mask %#x\n", __func__, perm, mask);
+
+	return mask;
+}
+
+static u32 hfsplus_deny_mask_from_posix(struct nfsv4_acl_info *nfsv4_acl_info,
+					unsigned short perm,
+					unsigned int flags)
+{
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	u32 mask = 0;
+
+	if (perm & ACL_READ)
+		mask |= HFSPLUS_VNODE_READ_DATA;
+	if (perm & ACL_WRITE)
+		mask |= HFSPLUS_VNODE_WRITE_MODE;
+	if ((perm & ACL_WRITE) && S_ISDIR(mapping_env->inode->i_mode))
+		mask |= HFSPLUS_VNODE_DELETE_CHILD;
+	if (perm & ACL_EXECUTE)
+		mask |= HFSPLUS_VNODE_EXECUTE;
+
+	hfs_dbg(ACL_MOD, "[%s]: perm %#x, mask %#x\n", __func__, perm, mask);
+
+	return mask;
+}
+
+static int hfsplus_map_owner_ace(struct user_namespace *user_ns,
+				    unsigned int eflag,
+				    unsigned int flags,
+				    unsigned short deny,
+				    struct posix_acl_entry *pa,
+				    struct posix_acl_summary *pas,
+				    struct nfsv4_acl_info *nfsv4_acl_info,
+				    void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x, deny %#x\n",
+			__func__, eflag, flags, deny);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p, pas %p\n", __func__, pa, pas);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL || pa == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+
+	HFSPLUS_ACE_SET_OWNER_USER_ID(mapping_env->ace_applicable);
+
+	if (deny) {
+		hfs_dbg(ACL_MOD,
+			"[%s]: compose owner (%#x) deny ACE\n",
+			__func__, i_uid_read(mapping_env->inode));
+
+		if ((mapping_env->composed_acl_size + ace_size) >
+					allocated_size) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, -ENOMEM);
+			return -ENOMEM;
+		}
+
+		rights = hfsplus_deny_mask_from_posix(nfsv4_acl_info,
+							deny, flags);
+		HFSPLUS_COMPOSE_DENY_ACE(ace, mapping_env->ace_applicable,
+						eflag, rights);
+		*nfsv4_ace = (void *)(++ace);
+		ace_entries++;
+		mapping_env->composed_acl_size += ace_size;
+	}
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: compose owner (%#x) permit ACE\n",
+		__func__, i_uid_read(mapping_env->inode));
+
+	if ((mapping_env->composed_acl_size + ace_size) > allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return -ENOMEM;
+	}
+
+	rights = hfsplus_mask_from_posix(nfsv4_acl_info, pa->e_perm,
+						flags | NFS4_ACL_OWNER);
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, mapping_env->ace_applicable,
+						eflag, rights);
+	*nfsv4_ace = (void *)(++ace);
+	ace_entries++;
+	mapping_env->composed_acl_size += ace_size;
+
+	fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+
+	return 0;
+}
+
+static int hfsplus_map_user_ace(struct user_namespace *user_ns,
+				unsigned int eflag,
+				unsigned int flags,
+				unsigned short deny,
+				struct posix_acl_entry *pa,
+				struct posix_acl_summary *pas,
+				struct nfsv4_acl_info *nfsv4_acl_info,
+				void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x, deny %#x\n",
+			__func__, eflag, flags, deny);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p, pas %p\n", __func__, pa, pas);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL || pa == NULL || pas == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+
+	HFSPLUS_ACE_SET_USER_ID(mapping_env->ace_applicable,
+			from_kuid_munged(user_ns, pa->e_id));
+
+	if (deny) {
+		hfs_dbg(ACL_MOD,
+			"[%s]: compose user (%#x) deny ACE\n",
+			__func__, from_kuid_munged(user_ns, pa->e_id));
+
+		if ((mapping_env->composed_acl_size + ace_size) >
+					allocated_size) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, -ENOMEM);
+			return -ENOMEM;
+		}
+
+		rights = hfsplus_deny_mask_from_posix(nfsv4_acl_info,
+							deny, flags);
+		HFSPLUS_COMPOSE_DENY_ACE(ace, mapping_env->ace_applicable,
+							eflag, rights);
+		*nfsv4_ace = (void *)(++ace);
+		ace_entries++;
+		mapping_env->composed_acl_size += ace_size;
+	}
+
+	hfs_dbg(ACL_MOD,
+			"[%s]: compose user (%#x) permit ACE\n",
+			__func__, from_kuid_munged(user_ns, pa->e_id));
+
+	if ((mapping_env->composed_acl_size + ace_size) > allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return -ENOMEM;
+	}
+
+	rights = hfsplus_mask_from_posix(nfsv4_acl_info,
+					pa->e_perm & pas->mask, flags);
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, mapping_env->ace_applicable,
+					eflag, rights);
+	*nfsv4_ace = (void *)(++ace);
+	ace_entries++;
+	mapping_env->composed_acl_size += ace_size;
+
+	fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+
+	return 0;
+}
+
+static int hfsplus_map_group_owner_deny_ace(struct user_namespace *user_ns,
+					unsigned int eflag,
+					unsigned int flags,
+					unsigned short deny,
+					struct posix_acl_entry *pa,
+					struct posix_acl_summary *pas,
+					struct nfsv4_acl_info *nfsv4_acl_info,
+					void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x, deny %#x\n",
+			__func__, eflag, flags, deny);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p, pas %p\n", __func__, pa, pas);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+
+	if (deny) {
+		HFSPLUS_ACE_SET_GROUP_ID(mapping_env->ace_applicable,
+					i_gid_read(mapping_env->inode));
+
+		hfs_dbg(ACL_MOD,
+			"[%s]: compose group of owner (%#x) deny ACE\n",
+			__func__, i_gid_read(mapping_env->inode));
+
+		if ((mapping_env->composed_acl_size + ace_size) >
+					allocated_size) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, -ENOMEM);
+			return -ENOMEM;
+		}
+
+		rights = hfsplus_deny_mask_from_posix(nfsv4_acl_info,
+							deny, flags);
+		HFSPLUS_COMPOSE_DENY_ACE(ace, mapping_env->ace_applicable,
+						eflag, rights);
+		*nfsv4_ace = (void *)(++ace);
+		ace_entries++;
+		mapping_env->composed_acl_size += ace_size;
+		fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+	}
+
+	return 0;
+}
+
+static int hfsplus_map_group_owner_permit_ace(struct user_namespace *user_ns,
+					unsigned int eflag,
+					unsigned int flags,
+					unsigned short deny,
+					struct posix_acl_entry *pa,
+					struct posix_acl_summary *pas,
+					struct nfsv4_acl_info *nfsv4_acl_info,
+					void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x, deny %#x\n",
+			__func__, eflag, flags, deny);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p, pas %p\n", __func__, pa, pas);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL || pas == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+
+	HFSPLUS_ACE_SET_GROUP_ID(mapping_env->ace_applicable,
+				i_gid_read(mapping_env->inode));
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: compose group of owner (%#x) permit ACE\n",
+		__func__, i_gid_read(mapping_env->inode));
+
+	if ((mapping_env->composed_acl_size + ace_size) >
+				allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return -ENOMEM;
+	}
+
+	rights = hfsplus_mask_from_posix(nfsv4_acl_info,
+					pas->group, flags);
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, mapping_env->ace_applicable,
+					eflag, rights);
+	*nfsv4_ace = (void *)(++ace);
+	ace_entries++;
+	mapping_env->composed_acl_size += ace_size;
+
+	fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+
+	return 0;
+}
+
+static int hfsplus_map_group_deny_ace(struct user_namespace *user_ns,
+					unsigned int eflag,
+					unsigned int flags,
+					unsigned short deny,
+					struct posix_acl_entry *pa,
+					struct posix_acl_summary *pas,
+					struct nfsv4_acl_info *nfsv4_acl_info,
+					void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x, deny %#x\n",
+			__func__, eflag, flags, deny);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p, pas %p\n", __func__, pa, pas);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL || pa == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+
+	if (deny) {
+		HFSPLUS_ACE_SET_GROUP_ID(mapping_env->ace_applicable,
+				from_kgid_munged(user_ns, pa->e_id));
+
+		hfs_dbg(ACL_MOD,
+			"[%s]: compose group (%#x) deny ACE\n",
+			__func__, from_kgid_munged(user_ns, pa->e_id));
+
+		if ((mapping_env->composed_acl_size + ace_size) >
+					allocated_size) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, -ENOMEM);
+			return -ENOMEM;
+		}
+
+		rights = hfsplus_deny_mask_from_posix(nfsv4_acl_info,
+							deny, flags);
+		HFSPLUS_COMPOSE_DENY_ACE(ace, mapping_env->ace_applicable,
+						eflag, rights);
+		*nfsv4_ace = (void *)(++ace);
+		ace_entries++;
+		mapping_env->composed_acl_size += ace_size;
+		fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+	}
+
+	return 0;
+}
+
+static int hfsplus_map_group_permit_ace(struct user_namespace *user_ns,
+					unsigned int eflag,
+					unsigned int flags,
+					unsigned short deny,
+					struct posix_acl_entry *pa,
+					struct posix_acl_summary *pas,
+					struct nfsv4_acl_info *nfsv4_acl_info,
+					void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x, deny %#x\n",
+			__func__, eflag, flags, deny);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p, pas %p\n", __func__, pa, pas);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL || pa == NULL || pas == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+
+	HFSPLUS_ACE_SET_GROUP_ID(mapping_env->ace_applicable,
+				from_kgid_munged(user_ns, pa->e_id));
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: compose group (%#x) permit ACE\n",
+		__func__, from_kgid_munged(user_ns, pa->e_id));
+
+	if ((mapping_env->composed_acl_size + ace_size) >
+				allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return -ENOMEM;
+	}
+
+	rights = hfsplus_mask_from_posix(nfsv4_acl_info,
+					pa->e_perm & pas->mask, flags);
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, mapping_env->ace_applicable,
+					eflag, rights);
+	*nfsv4_ace = (void *)(++ace);
+	ace_entries++;
+	mapping_env->composed_acl_size += ace_size;
+
+	fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+
+	return 0;
+}
+
+static int hfsplus_map_everyone_permit_ace(unsigned int eflag,
+					unsigned int flags,
+					struct posix_acl_entry *pa,
+					struct nfsv4_acl_info *nfsv4_acl_info,
+					void **nfsv4_ace)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_acl_entry *ace =
+		*(struct hfsplus_acl_entry **)nfsv4_ace;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	const size_t allocated_size = mapping_env->allocated_acl_size;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+	u32 ace_entries = be32_to_cpu(fsec_acl->acl_entrycount);
+	u32 rights;
+
+	hfs_dbg(ACL_MOD, "[%s]: eflag %#x, flags %#x\n",
+			__func__, eflag, flags);
+	hfs_dbg(ACL_MOD, "[%s]: pa %p\n", __func__, pa);
+	hfs_dbg(ACL_MOD, "[%s]: acl_info %p, nfsv4_ace %p\n",
+			__func__, nfsv4_acl_info, nfsv4_ace);
+
+	BUG_ON(ace == NULL || pa == NULL);
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, *nfsv4_ace);
+	hfs_dbg(ACL_MOD, "[%s]: compose EVERYBODY permit ACE\n", __func__);
+
+	HFSPLUS_ACE_SET_GROUP_ID(mapping_env->ace_applicable,
+					HFSPLUS_EVERYBODY_ID);
+
+	if ((mapping_env->composed_acl_size + ace_size) >
+				allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return -ENOMEM;
+	}
+
+	rights = hfsplus_mask_from_posix(nfsv4_acl_info,
+					pa->e_perm, flags);
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, mapping_env->ace_applicable,
+					eflag, rights);
+	ace_entries++;
+	mapping_env->composed_acl_size += ace_size;
+
+	fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+
+	return 0;
+}
+
+static struct nfsv4_ace_operations hfsplus_ace_ops = {
+	.get_naces = hfsplus_get_naces,
+	.get_ace = hfsplus_get_ace,
+	.get_access_mask = hfsplus_get_access_mask,
+	.ace_type_valid = hfsplus_ace_type_valid,
+	.is_allowed_ace_type = hfsplus_is_allowed_ace_type,
+	.is_acl_user_obj = hfsplus_is_acl_user_obj,
+	.is_acl_group_obj = hfsplus_is_acl_group_obj,
+	.is_acl_other = hfsplus_is_acl_other,
+	.is_acl_user = hfsplus_is_acl_user,
+	.is_acl_group = hfsplus_is_acl_group,
+};
+
+static struct nfsv4_ace_flags_operations hfsplus_ace_flags_ops = {
+	.ace_has_unknown_flags = hfsplus_ace_has_unknown_flags,
+	.ace_has_inheritance_flags = hfsplus_ace_has_inheritance_flags,
+	.ace_inherit_only_flag = hfsplus_ace_inherit_only_flag,
+	.check_deny = hfsplus_check_deny,
+	.low_mode_from_nfs4 = hfsplus_low_mode_from_nfs4,
+	.calculate_eflag = hfsplus_calculate_eflag,
+};
+
+static struct nfsv4_acl_id_operations hfsplus_acl_id_ops = {
+	.find_uid = hfsplus_find_uid,
+	.find_gid = hfsplus_find_gid,
+	.set_posix_ace_uid = hfsplus_set_posix_ace_uid,
+	.set_posix_ace_gid = hfsplus_set_posix_ace_gid,
+};
+
+static struct nfsv4_acl_mapping_operations hfsplus_acl_mapping_ops = {
+	.prepare_nfsv4_acl_mapping = hfsplus_prepare_nfsv4_acl_mapping,
+	.mask_from_posix = hfsplus_mask_from_posix,
+	.deny_mask_from_posix = hfsplus_deny_mask_from_posix,
+	.map_owner_ace = hfsplus_map_owner_ace,
+	.map_user_ace = hfsplus_map_user_ace,
+	.map_group_owner_deny_ace = hfsplus_map_group_owner_deny_ace,
+	.map_group_owner_permit_ace = hfsplus_map_group_owner_permit_ace,
+	.map_group_deny_ace = hfsplus_map_group_deny_ace,
+	.map_group_permit_ace = hfsplus_map_group_permit_ace,
+	.map_everyone_permit_ace = hfsplus_map_everyone_permit_ace,
+};
+
+static struct hfsplus_acl_record *hfsplus_acl_record_from_xattr(
+							void *value,
+							size_t size)
+{
+	struct hfsplus_filesec *filesec_ptr =
+			(struct hfsplus_filesec *)value;
+	struct hfsplus_acl_record *acl_record_ptr = NULL;
+	size_t filesec_hdr_size = offsetof(struct hfsplus_filesec, fsec_acl);
+	size_t acl_record_hdr_size =
+		offsetof(struct hfsplus_acl_record, acl_ace);
+	size_t known_size = filesec_hdr_size;
+	u32 acl_entries_count = 0;
+	u32 acl_entries_size = 0;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: value %p, size %zu\n",
+		__func__, value, size);
+
+	if (unlikely(size < known_size)) {
+		pr_err("filesec hdr corrupted\n");
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (unlikely(be32_to_cpu(filesec_ptr->fsec_magic) !=
+				HFSPLUS_FILESEC_MAGIC)) {
+		pr_err("invalid fsec_magic\n");
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return ERR_PTR(-EINVAL);
+	}
+
+	known_size += acl_record_hdr_size;
+
+	if (unlikely(size < known_size)) {
+		pr_err("acl record hdr corrupted\n");
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return ERR_PTR(-EINVAL);
+	}
+
+	acl_record_ptr = &(filesec_ptr->fsec_acl);
+	acl_entries_count = be32_to_cpu(acl_record_ptr->acl_entrycount);
+	acl_entries_size =
+		acl_entries_count * sizeof(struct hfsplus_acl_entry);
+	known_size += acl_entries_size;
+
+	if (unlikely(size < known_size)) {
+		pr_err("acl entries array corrupted\n");
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return acl_record_ptr;
+}
+
+static uid_t extract_uid_from_ace(struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+	uid_t uid;
+
+	hfs_dbg(ACL_MOD, "[%s]: ace %p\n", __func__, ace);
+
+	if (IS_GROUP_FINGERPRINT(ace->ace_applicable) ||
+			IS_USER_FINGERPRINT(ace->ace_applicable)) {
+		raw_id_ptr = (__be32 *)&ace->ace_applicable[size];
+		uid = be32_to_cpu(*raw_id_ptr);
+		hfs_dbg(ACL_MOD, "[%s]: uid/gid %#x\n", __func__, uid);
+	} else {
+		hfs_dbg(ACL_MOD, "[%s]: uid/gid %#x\n",
+				__func__, ACL_UNDEFINED_ID);
+		return ACL_UNDEFINED_ID;
+	}
+
+	return uid;
+}
+
+/* It is expected that ace is not empty */
+static int compare_ace_type(struct hfsplus_acl_entry *left_ace,
+				struct hfsplus_acl_entry *right_ace)
+{
+	u32 left_ace_flags = be32_to_cpu(left_ace->ace_flags);
+	u32 left_ace_type = left_ace_flags & HFSPLUS_ACE_KINDMASK;
+	u32 right_ace_flags = be32_to_cpu(right_ace->ace_flags);
+	u32 right_ace_type = right_ace_flags & HFSPLUS_ACE_KINDMASK;
+
+	if (left_ace_type == HFSPLUS_ACE_DENY &&
+			right_ace_type == HFSPLUS_ACE_DENY)
+		return 0;
+	else if (left_ace_type == HFSPLUS_ACE_PERMIT &&
+			right_ace_type == HFSPLUS_ACE_PERMIT)
+		return 0;
+	else if (left_ace_type == HFSPLUS_ACE_DENY)
+		return -1;
+	else if (left_ace_type == HFSPLUS_ACE_PERMIT) {
+		if (right_ace_type == HFSPLUS_ACE_DENY)
+			return 1;
+		else
+			return -1;
+	} else if (right_ace_type == HFSPLUS_ACE_DENY ||
+			right_ace_type == HFSPLUS_ACE_PERMIT)
+		return -1;
+
+	return 0;
+}
+
+static int compare_ace(struct nfsv4_acl_info *nfsv4_acl_info,
+			struct hfsplus_acl_entry *left_ace,
+			struct hfsplus_acl_entry *right_ace)
+{
+	uid_t left_uid, right_uid;
+
+	if (empty_ace(left_ace) && empty_ace(right_ace))
+		return 0;
+
+	if (hfsplus_is_acl_user_obj(nfsv4_acl_info, left_ace)) {
+		if (hfsplus_is_acl_user_obj(nfsv4_acl_info, right_ace))
+			return compare_ace_type(left_ace, right_ace);
+		else
+			return -1;
+	} else if (hfsplus_is_acl_user_obj(nfsv4_acl_info, right_ace))
+		return 1;
+
+	if (hfsplus_is_acl_group_obj(nfsv4_acl_info, left_ace)) {
+		if (hfsplus_is_acl_group_obj(nfsv4_acl_info, right_ace))
+			return compare_ace_type(left_ace, right_ace);
+		else
+			return -1;
+	} else if (hfsplus_is_acl_group_obj(nfsv4_acl_info, right_ace))
+		return 1;
+
+	if (hfsplus_is_acl_other(nfsv4_acl_info, left_ace)) {
+		if (hfsplus_is_acl_other(nfsv4_acl_info, right_ace))
+			return compare_ace_type(left_ace, right_ace);
+		else
+			return -1;
+	} else if (hfsplus_is_acl_other(nfsv4_acl_info, right_ace))
+		return 1;
+
+	left_uid = extract_uid_from_ace(left_ace);
+	right_uid = extract_uid_from_ace(right_ace);
+
+	/* ACL_UNDEFINED_ID is greater always */
+	if (left_uid == ACL_UNDEFINED_ID) {
+		if (right_uid == ACL_UNDEFINED_ID)
+			return 0;
+		else
+			return 1;
+	} else if (right_uid == ACL_UNDEFINED_ID)
+		return 1;
+
+	if (left_uid == right_uid)
+		return compare_ace_type(left_ace, right_ace);
+
+	return left_uid < right_uid ? -1 : 1;
+}
+
+/*
+ * Insertion sort.
+ * Algorithm of the method is based on psevdocode from:
+ * http://en.wikipedia.org/wiki/Insertion_sort
+ */
+static int sort_hfsplus_ace(struct nfsv4_acl_info *nfsv4_acl_info)
+{
+	struct hfsplus_acl_record *fsec_acl =
+		(struct hfsplus_acl_record *)nfsv4_acl_info->raw_acl;
+	struct hfsplus_nfsv4_mapping_env *mapping_env =
+		(struct hfsplus_nfsv4_mapping_env *)nfsv4_acl_info->private;
+	struct hfsplus_acl_entry *ace = fsec_acl->acl_ace;
+	struct hfsplus_acl_entry temp_buf;
+	int entries_count = be32_to_cpu(fsec_acl->acl_entrycount);
+	ssize_t calculated_size = sizeof(struct hfsplus_filesec) +
+			(entries_count * sizeof(struct hfsplus_acl_entry));
+	int i;
+
+	if (entries_count == 0)
+		return 0;
+
+	hfs_dbg(ACL_MOD, "[%s]: fsec_acl (%p)\n",
+			__func__, fsec_acl);
+	hfs_dbg(ACL_MOD, "[%s]: calculated_size (%zu)\n",
+			__func__, calculated_size);
+	hfs_dbg(ACL_MOD, "[%s]: composed_acl_size (%zu)\n",
+			__func__, mapping_env->composed_acl_size);
+	hfs_dbg(ACL_MOD, "[%s]: allocated_acl_size (%zu)\n",
+			__func__, mapping_env->allocated_acl_size);
+	hfs_dbg_hexdump(ACL_MOD, "unsorted composed_filesec: ",
+				fsec_acl, mapping_env->composed_acl_size);
+
+	if (calculated_size != mapping_env->composed_acl_size ||
+			calculated_size > mapping_env->allocated_acl_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	for (i = 1; i < entries_count; i++) {
+		int hole_index = i;
+		memcpy(&temp_buf, &ace[i], sizeof(struct hfsplus_acl_entry));
+
+		while (hole_index > 0 &&
+				(compare_ace(nfsv4_acl_info,
+						&ace[hole_index - 1],
+						&temp_buf) > 0)) {
+			/* move hole to next smaller index */
+			memcpy(&ace[hole_index], &ace[hole_index - 1],
+					sizeof(struct hfsplus_acl_entry));
+			hole_index -= 1;
+		}
+
+		memcpy(&ace[hole_index], &temp_buf,
+				sizeof(struct hfsplus_acl_entry));
+	}
+
+	hfs_dbg_hexdump(ACL_MOD, "sorted composed_filesec: ",
+				fsec_acl, mapping_env->composed_acl_size);
+
+	return 0;
+}
+
+struct posix_acl *hfsplus_posix_acl_from_xattr(struct inode *inode,
+					struct user_namespace *user_ns,
+					void *value,
+					size_t size,
+					int type)
+{
+	int err = 0;
+	struct hfsplus_nfsv4_mapping_env mapping_env = {
+		.inode = inode,
+		.composed_acl_size = 0,
+		.allocated_acl_size = size,
+	};
+	struct nfsv4_acl_info acl_info = {
+		.ace_ops = &hfsplus_ace_ops,
+		.flags_ops = &hfsplus_ace_flags_ops,
+		.id_ops = &hfsplus_acl_id_ops,
+		.mapping_ops = &hfsplus_acl_mapping_ops,
+		.raw_acl = NULL,
+		.private = &mapping_env,
+	};
+	struct posix_acl *pacl = NULL, *dpacl = NULL;
+	struct hfsplus_filesec *filesec =
+			(struct hfsplus_filesec *)value;
+	u32 acl_entries_count = 0;
+	unsigned int flags = 0;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, value %p, size %zu, type %#x\n",
+		__func__, inode->i_ino, value, size, type);
+
+	if (!value)
+		return NULL;
+
+	if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_DEFAULT)
+		return NULL;
+
+	acl_info.raw_acl = hfsplus_acl_record_from_xattr(value, size);
+	if (unlikely(IS_ERR(acl_info.raw_acl))) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %ld\n",
+			__FILE__, __LINE__, __func__,
+			PTR_ERR(acl_info.raw_acl));
+		return NULL;
+	}
+
+	acl_entries_count = be32_to_cpu(filesec->fsec_acl.acl_entrycount);
+	mapping_env.composed_acl_size = sizeof(struct hfsplus_filesec) +
+		(acl_entries_count * sizeof(struct hfsplus_acl_entry));
+	err = sort_hfsplus_ace(&acl_info);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+		return ERR_PTR(err);
+	}
+
+	if (S_ISDIR(inode->i_mode))
+		flags |= NFS4_ACL_DIR;
+
+	err = map_nfsv4_acl_to_posix(user_ns, &acl_info, &pacl, &dpacl, flags);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+		goto failed_conversion;
+	}
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		posix_acl_release(dpacl);
+		return pacl;
+
+	case ACL_TYPE_DEFAULT:
+		posix_acl_release(pacl);
+		return dpacl;
+
+	default:
+		BUG();
+	}
+
+failed_conversion:
+	if (pacl)
+		posix_acl_release(pacl);
+	if (dpacl)
+		posix_acl_release(dpacl);
+	return ERR_PTR(err);
+}
+
+static int hfsplus_compose_filesec_from_posix_acl(struct inode *inode,
+						struct user_namespace *user_ns,
+						struct posix_acl *pacl,
+						struct hfsplus_filesec *filesec,
+						size_t allocated_size,
+						int type)
+{
+	int err;
+	struct hfsplus_nfsv4_mapping_env mapping_env = {
+		.inode = inode,
+		.allocated_acl_size = allocated_size,
+		.composed_acl_size = 0,
+	};
+	struct nfsv4_acl_info acl_info = {
+		.ace_ops = &hfsplus_ace_ops,
+		.flags_ops = &hfsplus_ace_flags_ops,
+		.id_ops = &hfsplus_acl_id_ops,
+		.mapping_ops = &hfsplus_acl_mapping_ops,
+		.raw_acl = NULL,
+		.private = &mapping_env,
+	};
+	size_t filesec_hdr_size = sizeof(struct hfsplus_filesec);
+	unsigned int flags = 0;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, pacl %p, filesec %p, alloc_sz %zu, type %#x\n",
+		__func__, inode->i_ino, pacl, filesec, allocated_size, type);
+
+	BUG_ON(!filesec);
+
+	acl_info.raw_acl = &(filesec->fsec_acl);
+
+	if ((mapping_env.composed_acl_size + filesec_hdr_size) >
+				allocated_size) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return -ENOMEM;
+	}
+
+	memset(filesec, 0, sizeof(struct hfsplus_filesec));
+	filesec->fsec_magic = cpu_to_be32(HFSPLUS_FILESEC_MAGIC);
+	mapping_env.composed_acl_size += filesec_hdr_size;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		flags &= ~NFS4_ACL_TYPE_DEFAULT;
+		break;
+
+	case ACL_TYPE_DEFAULT:
+		flags |= NFS4_ACL_TYPE_DEFAULT;
+		break;
+	}
+
+	err = map_posix_acl_to_nfsv4_one(user_ns, pacl, &acl_info, flags);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+		return err;
+	}
+
+	err = sort_hfsplus_ace(&acl_info);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static struct hfsplus_filesec *hfsplus_posix_acl_to_filesec(struct inode *inode,
+						struct user_namespace *user_ns,
+						struct posix_acl *acl,
+						int type)
+{
+	int err = 0;
+	struct hfsplus_filesec *composed_filesec = NULL;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, type %#x, acl %p\n",
+		__func__, inode->i_ino, type, acl);
+
+	if (posix_acl_valid(acl) < 0) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/*
+	 * Mac OS X supports only inline xattr.
+	 * The online xattr can't be greater than
+	 * HFSPLUS_MAX_INLINE_DATA_SIZE (3802) bytes
+	 * in size.
+	 */
+	composed_filesec = kzalloc(HFSPLUS_MAX_INLINE_DATA_SIZE, GFP_KERNEL);
+	if (unlikely(!composed_filesec)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENOMEM);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	err = hfsplus_compose_filesec_from_posix_acl(inode, user_ns,
+						acl, composed_filesec,
+						HFSPLUS_MAX_INLINE_DATA_SIZE,
+						type);
+	if (err) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+		goto failed_conversion;
+	}
+
+	return composed_filesec;
+
+failed_conversion:
+	kfree(composed_filesec);
+	return ERR_PTR(err);
+}
+
+struct posix_acl *hfsplus_get_acl(struct inode *inode, int type)
+{
+	struct posix_acl *acl;
+	char *xattr_name;
+	char *value = NULL;
+	ssize_t size;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, type %#x\n",
+		__func__, inode->i_ino, type);
+
+	acl = get_cached_acl(inode, type);
+	if (acl != ACL_NOT_CACHED)
+		return acl;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+	case ACL_TYPE_DEFAULT:
+		xattr_name = HFSPLUS_XATTR_ACL_NAME;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+
+	size = __hfsplus_getxattr(inode, xattr_name, NULL, 0);
+
+	if (size > 0) {
+		value = kzalloc(size, GFP_NOFS);
+		if (unlikely(!value)) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, -ENOMEM);
+			return ERR_PTR(-ENOMEM);
+		}
+		size = __hfsplus_getxattr(inode, xattr_name, value, size);
+	}
+
+	if (size > 0)
+		acl = hfsplus_posix_acl_from_xattr(inode, &init_user_ns,
+							value, size, type);
+	else if (size == -ENODATA)
+		acl = NULL;
+	else {
+		acl = ERR_PTR(size);
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %zd\n",
+			__FILE__, __LINE__, __func__, size);
+	}
+
+	kfree(value);
+
+	if (!IS_ERR(acl))
+		set_cached_acl(inode, type, acl);
+
+	return acl;
+}
+
+static int hfsplus_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+	int err;
+	char *xattr_name = HFSPLUS_XATTR_ACL_NAME;
+	struct hfsplus_filesec *filesec = NULL;
+	size_t size = 0;
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, type %#x, acl %p\n",
+		__func__, inode->i_ino, type, acl);
+
+	if (S_ISLNK(inode->i_mode)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EOPNOTSUPP);
+		return -EOPNOTSUPP;
+	}
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		if (acl) {
+			err = posix_acl_equiv_mode(acl, &inode->i_mode);
+			if (err < 0) {
+				hfs_dbg(ACL_MOD,
+					"(%s, %d): %s: err %d\n",
+					__FILE__, __LINE__, __func__, err);
+				return err;
+			}
+		}
+		err = 0;
+		break;
+
+	case ACL_TYPE_DEFAULT:
+		if (!S_ISDIR(inode->i_mode))
+			return acl ? -EACCES : 0;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (acl) {
+		filesec = hfsplus_posix_acl_to_filesec(inode, &init_user_ns,
+							acl, type);
+		if (unlikely(!filesec)) {
+			err = -ENOMEM;
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, err);
+			goto end_set_acl;
+		} else if (IS_ERR(filesec)) {
+			err = PTR_ERR(filesec);
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, err);
+			goto end_set_acl;
+		}
+
+		size = sizeof(struct hfsplus_filesec) +
+			(be32_to_cpu(filesec->fsec_acl.acl_entrycount) *
+				ace_size);
+		if (unlikely(size > HFSPLUS_MAX_INLINE_DATA_SIZE)) {
+			err = -ENOMEM;
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, err);
+			goto end_set_acl;
+		}
+	}
+
+	err = __hfsplus_setxattr(inode, xattr_name, filesec, size, 0);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+	}
+
+end_set_acl:
+	kfree(filesec);
+
+	if (!err)
+		set_cached_acl(inode, type, acl);
+
+	return err;
+}
+
+int hfsplus_init_acl(struct inode *inode, struct inode *dir)
+{
+	int err = 0;
+	struct posix_acl *acl = NULL;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, dir->ino %lu\n",
+		__func__, inode->i_ino, dir->i_ino);
+
+	if (S_ISLNK(inode->i_mode))
+		return 0;
+
+	acl = hfsplus_get_acl(dir, ACL_TYPE_DEFAULT);
+	if (IS_ERR(acl)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %ld\n",
+			__FILE__, __LINE__, __func__, PTR_ERR(acl));
+		return PTR_ERR(acl);
+	}
+
+	if (acl) {
+		if (S_ISDIR(inode->i_mode)) {
+			err = hfsplus_set_acl(inode, ACL_TYPE_DEFAULT, acl);
+			if (unlikely(err)) {
+				hfs_dbg(ACL_MOD,
+					"(%s, %d): %s: err %d\n",
+					__FILE__, __LINE__, __func__, err);
+				goto init_acl_cleanup;
+			}
+		}
+
+		err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
+		if (unlikely(err < 0)) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %d\n",
+				__FILE__, __LINE__, __func__, err);
+			return err;
+		}
+
+		if (err > 0) {
+			err = hfsplus_set_acl(inode, ACL_TYPE_ACCESS, acl);
+			if (unlikely(err)) {
+				hfs_dbg(ACL_MOD,
+					"(%s, %d): %s: err %d\n",
+					__FILE__, __LINE__, __func__, err);
+			}
+		}
+	} else
+		inode->i_mode &= ~current_umask();
+
+init_acl_cleanup:
+	posix_acl_release(acl);
+	return err;
+}
+
+int hfsplus_acl_chmod(struct inode *inode)
+{
+	int err;
+	struct posix_acl *acl;
+
+	hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);
+
+	if (S_ISLNK(inode->i_mode)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EOPNOTSUPP);
+		return -EOPNOTSUPP;
+	}
+
+	acl = hfsplus_get_acl(inode, ACL_TYPE_ACCESS);
+	if (IS_ERR(acl) || !acl) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %ld\n",
+			__FILE__, __LINE__, __func__, PTR_ERR(acl));
+		return PTR_ERR(acl);
+	}
+
+	err = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+		return err;
+	}
+
+	err = hfsplus_set_acl(inode, ACL_TYPE_ACCESS, acl);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+	}
+
+	posix_acl_release(acl);
+	return err;
+}
+
+static int hfsplus_xattr_get_acl(struct dentry *dentry,
+					const char *name,
+					void *buffer,
+					size_t size,
+					int type)
+{
+	int res = 0;
+	struct posix_acl *acl;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, buffer %p, size %zu, type %#x\n",
+		__func__, dentry->d_inode->i_ino, buffer, size, type);
+
+	if (strcmp(name, "") != 0) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	acl = hfsplus_get_acl(dentry->d_inode, type);
+	if (IS_ERR(acl)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %ld\n",
+			__FILE__, __LINE__, __func__, PTR_ERR(acl));
+		return PTR_ERR(acl);
+	}
+	if (acl == NULL) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -ENODATA);
+		return -ENODATA;
+	}
+
+	res = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
+	hfs_dbg(ACL_MOD, "[%s]: real_size %d\n", __func__, res);
+
+	posix_acl_release(acl);
+	return res;
+}
+
+static int hfsplus_xattr_set_acl(struct dentry *dentry,
+					const char *name,
+					const void *value,
+					size_t size,
+					int flags,
+					int type)
+{
+	int err = 0;
+	struct inode *inode = dentry->d_inode;
+	struct posix_acl *acl = NULL;
+
+	hfs_dbg(ACL_MOD,
+		"[%s]: ino %lu, value %p, size %zu, flags %#x, type %#x\n",
+		__func__, inode->i_ino, value, size, flags, type);
+
+	if (strcmp(name, "") != 0) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EINVAL);
+		return -EINVAL;
+	}
+
+	if (!inode_owner_or_capable(inode)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, -EPERM);
+		return -EPERM;
+	}
+
+	if (value) {
+		acl = posix_acl_from_xattr(&init_user_ns, value, size);
+		if (IS_ERR(acl)) {
+			hfs_dbg(ACL_MOD,
+				"(%s, %d): %s: err %ld\n",
+				__FILE__, __LINE__, __func__, PTR_ERR(acl));
+			return PTR_ERR(acl);
+		} else if (acl) {
+			err = posix_acl_valid(acl);
+			if (err) {
+				hfs_dbg(ACL_MOD,
+					"(%s, %d): %s: err %d\n",
+					__FILE__, __LINE__, __func__, err);
+				goto end_xattr_set_acl;
+			}
+		}
+	}
+
+	err = hfsplus_set_acl(inode, type, acl);
+	if (unlikely(err)) {
+		hfs_dbg(ACL_MOD,
+			"(%s, %d): %s: err %d\n",
+			__FILE__, __LINE__, __func__, err);
+	}
+
+end_xattr_set_acl:
+	posix_acl_release(acl);
+	return err;
+}
+
+static size_t hfsplus_xattr_list_acl(struct dentry *dentry, char *list,
+		size_t list_size, const char *name, size_t name_len, int type)
+{
+	/*
+	 * This method is not used.
+	 * It is used hfsplus_listxattr() instead of generic_listxattr().
+	 */
+	return -EOPNOTSUPP;
+}
+
+const struct xattr_handler hfsplus_xattr_acl_access_handler = {
+	.prefix	= POSIX_ACL_XATTR_ACCESS,
+	.flags	= ACL_TYPE_ACCESS,
+	.list	= hfsplus_xattr_list_acl,
+	.get	= hfsplus_xattr_get_acl,
+	.set	= hfsplus_xattr_set_acl,
+};
+
+const struct xattr_handler hfsplus_xattr_acl_default_handler = {
+	.prefix	= POSIX_ACL_XATTR_DEFAULT,
+	.flags	= ACL_TYPE_DEFAULT,
+	.list	= hfsplus_xattr_list_acl,
+	.get	= hfsplus_xattr_get_acl,
+	.set	= hfsplus_xattr_set_acl,
+};
diff --git a/fs/hfsplus/acl.h b/fs/hfsplus/acl.h
new file mode 100644
index 0000000..a2efb9d
--- /dev/null
+++ b/fs/hfsplus/acl.h
@@ -0,0 +1,96 @@
+/*
+ * linux/fs/hfsplus/acl.h
+ *
+ * Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
+ *
+ * Handler for Posix Access Control Lists (ACLs) support.
+ */
+
+#ifndef _LINUX_HFSPLUS_ACL_H
+#define _LINUX_HFSPLUS_ACL_H
+
+#include <linux/nfs4acl.h>
+#include <linux/posix_acl_xattr.h>
+
+/* HFS+ Access Control List Entry (ACE) flags */
+#define HFSPLUS_ACE_KINDMASK		0xf
+#define HFSPLUS_ACE_PERMIT		1
+#define HFSPLUS_ACE_DENY		2
+#define HFSPLUS_ACE_AUDIT		3
+#define HFSPLUS_ACE_ALARM		4
+#define HFSPLUS_ACE_INHERITED		(1<<4)
+#define HFSPLUS_ACE_FILE_INHERIT	(1<<5)
+#define HFSPLUS_ACE_DIRECTORY_INHERIT	(1<<6)
+#define HFSPLUS_ACE_LIMIT_INHERIT	(1<<7)
+#define HFSPLUS_ACE_ONLY_INHERIT	(1<<8)
+#define HFSPLUS_ACE_SUCCESS		(1<<9)
+#define HFSPLUS_ACE_FAILURE		(1<<10)
+
+/* All flag bits controlling ACE inheritance */
+#define HFSPLUS_ACE_INHERIT_CONTROL_FLAGS \
+		(HFSPLUS_ACE_FILE_INHERIT | \
+		 HFSPLUS_ACE_DIRECTORY_INHERIT | \
+		 HFSPLUS_ACE_LIMIT_INHERIT | \
+		 HFSPLUS_ACE_ONLY_INHERIT)
+
+/* HFS+ Access Control List Entry (ACE) rights */
+#define HFSPLUS_VNODE_READ_DATA			(1<<1)
+#define HFSPLUS_VNODE_LIST_DIRECTORY		HFSPLUS_VNODE_READ_DATA
+#define HFSPLUS_VNODE_WRITE_DATA		(1<<2)
+#define HFSPLUS_VNODE_ADD_FILE			HFSPLUS_VNODE_WRITE_DATA
+#define HFSPLUS_VNODE_EXECUTE			(1<<3)
+#define HFSPLUS_VNODE_SEARCH			HFSPLUS_VNODE_EXECUTE
+#define HFSPLUS_VNODE_DELETE			(1<<4)
+#define HFSPLUS_VNODE_APPEND_DATA		(1<<5)
+#define HFSPLUS_VNODE_ADD_SUBDIRECTORY		HFSPLUS_VNODE_APPEND_DATA
+#define HFSPLUS_VNODE_DELETE_CHILD		(1<<6)
+#define HFSPLUS_VNODE_READ_ATTRIBUTES		(1<<7)
+#define HFSPLUS_VNODE_WRITE_ATTRIBUTES		(1<<8)
+#define HFSPLUS_VNODE_READ_EXTATTRIBUTES	(1<<9)
+#define HFSPLUS_VNODE_WRITE_EXTATTRIBUTES	(1<<10)
+#define HFSPLUS_VNODE_READ_SECURITY		(1<<11)
+#define HFSPLUS_VNODE_WRITE_SECURITY		(1<<12)
+#define HFSPLUS_VNODE_TAKE_OWNERSHIP		(1<<13)
+
+#define HFSPLUS_ACE_GENERIC_ALL			(1<<21)
+#define HFSPLUS_ACE_GENERIC_EXECUTE		(1<<22)
+#define HFSPLUS_ACE_GENERIC_WRITE		(1<<23)
+#define HFSPLUS_ACE_GENERIC_READ		(1<<24)
+
+#define HFSPLUS_VNODE_ANYONE_MODE (HFSPLUS_VNODE_READ_ATTRIBUTES | \
+					 HFSPLUS_VNODE_READ_EXTATTRIBUTES | \
+					 HFSPLUS_VNODE_READ_SECURITY)
+
+#define HFSPLUS_VNODE_GENERIC_READ_BITS (HFSPLUS_VNODE_READ_DATA | \
+					 HFSPLUS_VNODE_READ_ATTRIBUTES | \
+					 HFSPLUS_VNODE_READ_EXTATTRIBUTES | \
+					 HFSPLUS_VNODE_READ_SECURITY)
+
+#define HFSPLUS_VNODE_OWNER_MODE (HFSPLUS_VNODE_WRITE_ATTRIBUTES | \
+					 HFSPLUS_VNODE_WRITE_EXTATTRIBUTES | \
+					 HFSPLUS_VNODE_WRITE_SECURITY)
+
+#define HFSPLUS_VNODE_WRITE_MODE (HFSPLUS_VNODE_WRITE_DATA | \
+					  HFSPLUS_VNODE_APPEND_DATA | \
+					  HFSPLUS_VNODE_DELETE)
+
+#define HFSPLUS_VNODE_GENERIC_WRITE_BITS (HFSPLUS_VNODE_WRITE_DATA | \
+					  HFSPLUS_VNODE_APPEND_DATA | \
+					  HFSPLUS_VNODE_DELETE | \
+					  HFSPLUS_VNODE_DELETE_CHILD | \
+					  HFSPLUS_VNODE_WRITE_ATTRIBUTES | \
+					  HFSPLUS_VNODE_WRITE_EXTATTRIBUTES | \
+					  HFSPLUS_VNODE_WRITE_SECURITY)
+
+#define HFSPLUS_VNODE_GENERIC_EXECUTE_BITS (HFSPLUS_VNODE_EXECUTE)
+
+#define HFSPLUS_VNODE_GENERIC_ALL_BITS	(HFSPLUS_VNODE_GENERIC_READ_BITS | \
+					 HFSPLUS_VNODE_GENERIC_WRITE_BITS | \
+					 HFSPLUS_VNODE_GENERIC_EXECUTE_BITS)
+
+/* acl.c */
+struct posix_acl *hfsplus_get_acl(struct inode *inode, int type);
+int hfsplus_acl_chmod(struct inode *inode);
+int hfsplus_init_acl(struct inode *, struct inode *);
+
+#endif
-- 
1.7.9.5



--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux