[PATCH 2/3] hfsplus: add implementation of the ACLs support

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

 



From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
Subject: [PATCH 2/3] hfsplus: add implementation of the ACLs support

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

Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
---
 fs/hfsplus/acl.c | 1474 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/hfsplus/acl.h |   90 ++++
 2 files changed, 1564 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..eff8659
--- /dev/null
+++ b/fs/hfsplus/acl.c
@@ -0,0 +1,1474 @@
+/*
+ * 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 posix_ace_state {
+	u32 allow;
+	u32 deny;
+};
+
+struct posix_user_ace_state {
+	uid_t uid;
+	struct posix_ace_state perms;
+};
+
+struct posix_ace_state_array {
+	int n;
+	struct posix_user_ace_state aces[];
+};
+
+struct posix_acl_state {
+	int empty;
+	struct posix_ace_state owner;
+	struct posix_ace_state group;
+	struct posix_ace_state other;
+	struct posix_ace_state everyone;
+	struct posix_ace_state mask; /* Deny unused in this case */
+	struct posix_ace_state_array *users;
+	struct posix_ace_state_array *groups;
+};
+
+static inline void free_state(struct posix_acl_state *state)
+{
+	kfree(state->users);
+	kfree(state->groups);
+}
+
+static int init_state(struct posix_acl_state *state, int cnt)
+{
+	int alloc_size;
+
+	memset(state, 0, sizeof(struct posix_acl_state));
+	state->empty = 1;
+
+	alloc_size = sizeof(struct posix_ace_state_array) +
+			(cnt * sizeof(struct posix_user_ace_state));
+
+	state->users = kzalloc(alloc_size, GFP_KERNEL);
+	if (!state->users)
+		return -ENOMEM;
+
+	state->groups = kzalloc(alloc_size, GFP_KERNEL);
+	if (!state->groups)
+		goto init_state_failed;
+
+	return 0;
+
+init_state_failed:
+	free_state(state);
+	return -ENOMEM;
+}
+
+static const struct hfsplus_acl_record *hfsplus_acl_record_from_xattr(
+							const void *value,
+							size_t size)
+{
+	const struct hfsplus_filesec *filesec_ptr =
+			(const struct hfsplus_filesec *)value;
+	const 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;
+
+	if (unlikely(size < known_size)) {
+		printk(KERN_ERR "hfs: filesec hdr corrupted\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (unlikely(be32_to_cpu(filesec_ptr->fsec_magic) !=
+				HFSPLUS_FILESEC_MAGIC)) {
+		printk(KERN_ERR "hfs: invalid fsec_magic\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	known_size += acl_record_hdr_size;
+
+	if (unlikely(size < known_size)) {
+		printk(KERN_ERR "hfs: acl record hdr corrupted\n");
+		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)) {
+		printk(KERN_ERR "hfs: acl entries array corrupted\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return acl_record_ptr;
+}
+
+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 inline int is_acl_user_obj(const struct hfsplus_acl_entry *ace)
+{
+	if (!IS_GROUP_FINGERPRINT(ace->ace_applicable) &&
+			!IS_USER_FINGERPRINT(ace->ace_applicable) &&
+			!empty_ace(ace)) {
+		dprint(DBG_ACL_MOD, "is_acl_user_obj: found user obj\n");
+		return 1;
+	} else
+		return 0;
+}
+
+static int is_acl_group_obj(struct inode *inode,
+				const struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_gid_ptr;
+
+	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)) {
+			dprint(DBG_ACL_MOD, "is_acl_group_obj: found group obj\n");
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int is_acl_other(struct inode *inode,
+				const struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+
+	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) {
+			dprint(DBG_ACL_MOD, "is_acl_other: found other\n");
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int is_acl_user(const struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+
+	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) {
+			dprint(DBG_ACL_MOD, "is_acl_user: %#x\n",
+				be32_to_cpu(*raw_id_ptr));
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int is_acl_group(const struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+
+	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) {
+			dprint(DBG_ACL_MOD, "is_acl_group: %#x\n",
+				be32_to_cpu(*raw_id_ptr));
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static inline void allow_bits(struct posix_ace_state *astate, u32 rights)
+{
+	astate->allow |= rights & ~astate->deny;
+}
+
+static inline void deny_bits(struct posix_ace_state *astate, u32 rights)
+{
+	astate->deny |= rights & ~astate->allow;
+}
+
+static void allow_bits_array(struct posix_ace_state_array *a, u32 rights)
+{
+	int i;
+
+	for (i = 0; i < a->n; i++)
+		allow_bits(&a->aces[i].perms, rights);
+}
+
+static void deny_bits_array(struct posix_ace_state_array *a, u32 rights)
+{
+	int i;
+
+	for (i = 0; i < a->n; i++)
+		deny_bits(&a->aces[i].perms, rights);
+}
+
+static int find_uid(struct posix_acl_state *state,
+			struct posix_ace_state_array *a,
+			const struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+	uid_t uid;
+	int i;
+
+	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);
+		dprint(DBG_ACL_MOD, "find_uid: uid %#x\n", uid);
+	} else {
+		dprint(DBG_ACL_MOD, "find_uid: can't convert uid\n");
+		return -1;
+	}
+
+	for (i = 0; i < a->n; i++) {
+		if (a->aces[i].uid == uid) {
+			dprint(DBG_ACL_MOD, "find_uid: index %d\n", 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;
+
+	dprint(DBG_ACL_MOD, "find_uid: index %d\n", i);
+	return i;
+}
+
+static void hfsplus_process_one_ace(struct inode *inode,
+					struct posix_acl_state *state,
+					const struct hfsplus_acl_entry *ace)
+{
+	u32 ace_flags = be32_to_cpu(ace->ace_flags);
+	u32 ace_type = ace_flags & HFSPLUS_ACE_KINDMASK;
+	u32 rights = be32_to_cpu(ace->ace_rights);
+	int i;
+
+	dprint_hexdump(DBG_ACL_MOD, "process_ace: ace_applicable: ",
+				ace->ace_applicable, HFSPLUS_GUID_SIZE);
+	dprint(DBG_ACL_MOD, "process_ace: ace_flags %#x, ace_rights %#x\n",
+				ace_flags, rights);
+
+	state->empty = 0;
+
+	if (is_acl_user_obj(ace)) {
+		if (ace_type == HFSPLUS_ACE_PERMIT)
+			allow_bits(&state->owner, rights);
+		else
+			deny_bits(&state->owner, rights);
+	} else if (is_acl_group_obj(inode, ace)) {
+		if (ace_type == HFSPLUS_ACE_PERMIT)
+			allow_bits(&state->group, rights);
+		else {
+			deny_bits(&state->group, rights);
+			rights = state->group.deny;
+			deny_bits(&state->owner, rights);
+			deny_bits(&state->everyone, rights);
+			deny_bits_array(state->users, rights);
+			deny_bits_array(state->groups, rights);
+		}
+	} else if (is_acl_other(inode, ace)) {
+		if (ace_type == HFSPLUS_ACE_PERMIT) {
+			allow_bits(&state->owner, rights);
+			allow_bits(&state->group, rights);
+			allow_bits(&state->other, rights);
+			allow_bits(&state->everyone, rights);
+			allow_bits_array(state->users, rights);
+			allow_bits_array(state->groups, rights);
+		} else {
+			deny_bits(&state->owner, rights);
+			deny_bits(&state->group, rights);
+			deny_bits(&state->other, rights);
+			deny_bits(&state->everyone, rights);
+			deny_bits_array(state->users, rights);
+			deny_bits_array(state->groups, rights);
+		}
+	} else if (is_acl_user(ace)) {
+		i = find_uid(state, state->users, ace);
+		if (i >= 0) {
+			/* ACL_USER */
+			if (ace_type == HFSPLUS_ACE_PERMIT)
+				allow_bits(&state->users->aces[i].perms,
+						rights);
+			else {
+				deny_bits(&state->users->aces[i].perms, rights);
+				rights = state->users->aces[i].perms.deny;
+				deny_bits(&state->owner, rights);
+			}
+		}
+	} else if (is_acl_group(ace)) {
+		i = find_uid(state, state->groups, ace);
+		if (i >= 0) {
+			/* ACL_GROUP */
+			if (ace_type == HFSPLUS_ACE_PERMIT)
+				allow_bits(&state->groups->aces[i].perms,
+						rights);
+			else {
+				deny_bits(&state->groups->aces[i].perms,
+						rights);
+				rights = state->groups->aces[i].perms.deny;
+				deny_bits(&state->owner, rights);
+				deny_bits(&state->group, rights);
+				deny_bits(&state->everyone, rights);
+				deny_bits_array(state->users, rights);
+				deny_bits_array(state->groups, rights);
+			}
+		}
+	}
+}
+
+static inline int check_deny(u32 rights, int isowner)
+{
+	if (rights & (HFSPLUS_VNODE_READ_ATTRIBUTES |
+			HFSPLUS_VNODE_READ_EXTATTRIBUTES |
+			HFSPLUS_VNODE_READ_SECURITY))
+		return -EINVAL;
+	if (!isowner)
+		return 0;
+	if (rights & (HFSPLUS_VNODE_WRITE_ATTRIBUTES |
+			HFSPLUS_VNODE_WRITE_EXTATTRIBUTES |
+			HFSPLUS_VNODE_WRITE_SECURITY))
+		return -EINVAL;
+	return 0;
+}
+
+static void low_mode_from_hfsplus(struct inode *inode,
+					u32 perm,
+					unsigned short *mode)
+{
+	u32 write_mode = HFSPLUS_VNODE_WRITE_DATA/* | HFSPLUS_VNODE_APPEND_DATA*/;
+
+	if (S_ISDIR(inode->i_mode))
+		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;
+
+	dprint(DBG_ACL_MOD,
+		"convert_rights: perm %#x, mode %#x\n", perm, *mode);
+}
+
+static inline void add_to_mask(struct posix_acl_state *state,
+				struct posix_ace_state *astate)
+{
+	state->mask.allow |= astate->allow;
+}
+
+static struct posix_acl *hfsplus_posix_state_to_acl(struct inode *inode,
+					struct user_namespace *user_ns,
+					struct posix_acl_state *state,
+					int type)
+{
+	int err = 0;
+	struct posix_acl_entry *pace;
+	struct posix_acl *pacl;
+	int nace;
+	int i = 0;
+
+	/*
+	 * ACLs with no ACEs are treated differently in the inheritable
+	 * and effective cases: when there are no inheritable ACEs, we
+	 * set a zero-length default posix acl.
+	 */
+	if (state->empty && (type == ACL_TYPE_DEFAULT)) {
+		pacl = posix_acl_alloc(0, GFP_KERNEL);
+		return pacl ? pacl : ERR_PTR(-ENOMEM);
+	}
+
+	/*
+	 * When there are no effective ACEs, the following will end
+	 * up setting a 3-element effective posix ACL with all
+	 * permissions zero.
+	 */
+	nace = 4 + state->users->n + state->groups->n;
+	pacl = posix_acl_alloc(nace, GFP_KERNEL);
+	if (unlikely(!pacl))
+		return ERR_PTR(-ENOMEM);
+
+	pace = pacl->a_entries;
+	pace->e_tag = ACL_USER_OBJ;
+	err = check_deny(state->owner.deny, 1);
+	if (err)
+		goto out_err;
+	low_mode_from_hfsplus(inode, state->owner.allow, &pace->e_perm);
+	pace->e_id = ACL_UNDEFINED_ID;
+
+	for (i=0; i < state->users->n; i++) {
+		pace++;
+		pace->e_tag = ACL_USER;
+		err = check_deny(state->users->aces[i].perms.deny, 0);
+		if (err)
+			goto out_err;
+		low_mode_from_hfsplus(inode,
+					state->users->aces[i].perms.allow,
+					&pace->e_perm);
+		pace->e_uid = make_kuid(user_ns, state->users->aces[i].uid);
+		if (!uid_valid(pace->e_uid))
+			goto out_err;
+		add_to_mask(state, &state->users->aces[i].perms);
+	}
+
+	pace++;
+	pace->e_tag = ACL_GROUP_OBJ;
+	err = check_deny(state->group.deny, 0);
+	if (err)
+		goto out_err;
+	low_mode_from_hfsplus(inode, state->group.allow, &pace->e_perm);
+	pace->e_id = ACL_UNDEFINED_ID;
+	add_to_mask(state, &state->group);
+
+	for (i=0; i < state->groups->n; i++) {
+		pace++;
+		pace->e_tag = ACL_GROUP;
+		err = check_deny(state->groups->aces[i].perms.deny, 0);
+		if (err)
+			goto out_err;
+		low_mode_from_hfsplus(inode,
+					state->groups->aces[i].perms.allow,
+					&pace->e_perm);
+		pace->e_gid = make_kgid(user_ns, state->groups->aces[i].uid);
+		if (!gid_valid(pace->e_gid))
+			goto out_err;
+		add_to_mask(state, &state->groups->aces[i].perms);
+	}
+
+	pace++;
+	pace->e_tag = ACL_MASK;
+	low_mode_from_hfsplus(inode, state->mask.allow, &pace->e_perm);
+	pace->e_id = ACL_UNDEFINED_ID;
+
+	pace++;
+	pace->e_tag = ACL_OTHER;
+	err = check_deny(state->other.deny, 0);
+	if (err)
+		goto out_err;
+	low_mode_from_hfsplus(inode, state->other.allow, &pace->e_perm);
+	pace->e_id = ACL_UNDEFINED_ID;
+
+	return pacl;
+
+out_err:
+	posix_acl_release(pacl);
+	return ERR_PTR(err);
+}
+
+static void sort_pacl_range(struct posix_acl *pacl, int start, int end)
+{
+	int sorted = 0, i;
+	struct posix_acl_entry tmp;
+
+	/*
+	 * We just do a bubble sort; easy to do in place, and we're not
+	 * expecting acl's to be long enough to justify anything more.
+	 */
+	while (!sorted) {
+		sorted = 1;
+		for (i = start; i < end; i++) {
+			if (pacl->a_entries[i].e_id
+					> pacl->a_entries[i+1].e_id) {
+				sorted = 0;
+				tmp = pacl->a_entries[i];
+				pacl->a_entries[i] = pacl->a_entries[i+1];
+				pacl->a_entries[i+1] = tmp;
+			}
+		}
+	}
+}
+
+static void sort_pacl(struct posix_acl *pacl)
+{
+	/*
+	 * The posix_acl_valid requires that users and groups be in order
+	 * by uid/gid.
+	 */
+	int i, j;
+
+	if (pacl->a_count <= 4)
+		return; /* no users or groups */
+	i = 1;
+	while (pacl->a_entries[i].e_tag == ACL_USER)
+		i++;
+	sort_pacl_range(pacl, 1, i-1);
+
+	BUG_ON(pacl->a_entries[i].e_tag != ACL_GROUP_OBJ);
+	j = ++i;
+	while (pacl->a_entries[j].e_tag == ACL_GROUP)
+		j++;
+	sort_pacl_range(pacl, i, j-1);
+	return;
+}
+
+static uid_t extract_uid_from_ace(struct hfsplus_acl_entry *ace)
+{
+	int size = HFSPLUS_FINGERPRINT_SIZE;
+	__be32 *raw_id_ptr;
+	uid_t uid;
+
+	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);
+	} else
+		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 inode *inode,
+			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 (is_acl_user_obj(left_ace)) {
+		if (is_acl_user_obj(right_ace))
+			return compare_ace_type(left_ace, right_ace);
+		else
+			return -1;
+	} else if (is_acl_user_obj(right_ace))
+		return 1;
+
+	if (is_acl_group_obj(inode, left_ace)) {
+		if (is_acl_group_obj(inode, right_ace))
+			return compare_ace_type(left_ace, right_ace);
+		else
+			return -1;
+	} else if (is_acl_group_obj(inode, right_ace))
+		return 1;
+
+	if (is_acl_other(inode, left_ace)) {
+		if (is_acl_other(inode, right_ace))
+			return compare_ace_type(left_ace, right_ace);
+		else
+			return -1;
+	} else if (is_acl_other(inode, 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 inode *inode,
+				struct hfsplus_filesec *filesec,
+				ssize_t size)
+{
+	struct hfsplus_acl_entry *ace = filesec->fsec_acl.acl_ace;
+	struct hfsplus_acl_entry temp_buf;
+	int entries_count = be32_to_cpu(filesec->fsec_acl.acl_entrycount);
+	ssize_t calculated_size = sizeof(struct hfsplus_filesec) +
+			(entries_count * sizeof(struct hfsplus_acl_entry));
+	int i;
+
+	if (be32_to_cpu(filesec->fsec_magic) != HFSPLUS_FILESEC_MAGIC)
+		return -EINVAL;
+
+	if (entries_count == 0)
+		return 0;
+
+	if (calculated_size != size)
+		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(inode,
+						&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));
+	}
+
+	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;
+	struct posix_acl *pacl = NULL, *dpacl = NULL;
+	struct hfsplus_filesec *filesec =
+			(struct hfsplus_filesec *)value;
+	const struct hfsplus_acl_record *raw_acl_rec;
+	struct posix_acl_state effective_acl_state, default_acl_state;
+	const struct hfsplus_acl_entry *ace;
+	u32 acl_entries_count = 0;
+	ssize_t calculated_size = 0;
+
+	if (!value)
+		return NULL;
+
+	if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_DEFAULT)
+		return NULL;
+
+	raw_acl_rec = hfsplus_acl_record_from_xattr(value, size);
+	if (unlikely(IS_ERR(raw_acl_rec)))
+		return NULL;
+
+	acl_entries_count = be32_to_cpu(raw_acl_rec->acl_entrycount);
+
+	calculated_size = sizeof(struct hfsplus_filesec) +
+		(acl_entries_count * sizeof(struct hfsplus_acl_entry));
+	err = sort_hfsplus_ace(inode, filesec, calculated_size);
+	if (unlikely(err))
+		return ERR_PTR(err);
+
+	err = init_state(&effective_acl_state, acl_entries_count);
+	if (unlikely(err))
+		return ERR_PTR(err);
+	err = init_state(&default_acl_state, acl_entries_count);
+	if (unlikely(err))
+		goto free_effective_acl_state;
+
+	err = -EINVAL;
+	for (ace = raw_acl_rec->acl_ace;
+			ace < raw_acl_rec->acl_ace + acl_entries_count;
+					ace++) {
+		u32 ace_flags = be32_to_cpu(ace->ace_flags);
+		u32 ace_type = ace_flags & HFSPLUS_ACE_KINDMASK;
+		ace_flags &= ~HFSPLUS_ACE_KINDMASK;
+
+		if (ace_type != HFSPLUS_ACE_PERMIT &&
+				ace_type != HFSPLUS_ACE_DENY)
+			goto free_default_acl_state;
+
+		if (ace_flags & ~HFSPLUS_ACE_INHERIT_CONTROL_FLAGS)
+			goto free_default_acl_state;
+
+		if ((ace_flags & HFSPLUS_ACE_INHERIT_CONTROL_FLAGS) == 0) {
+			hfsplus_process_one_ace(inode,
+						&effective_acl_state, ace);
+			continue;
+		}
+
+		if (!S_ISDIR(inode->i_mode))
+			goto free_default_acl_state;
+
+		hfsplus_process_one_ace(inode, &default_acl_state, ace);
+
+		if (!(ace_flags & HFSPLUS_ACE_ONLY_INHERIT))
+			hfsplus_process_one_ace(inode,
+						&effective_acl_state, ace);
+	}
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		pacl = hfsplus_posix_state_to_acl(inode,
+						  user_ns,
+						  &effective_acl_state,
+						  type);
+		if (unlikely(IS_ERR(pacl))) {
+			err = PTR_ERR(pacl);
+			pacl = NULL;
+			goto free_default_acl_state;
+		}
+		sort_pacl(pacl);
+		break;
+
+	case ACL_TYPE_DEFAULT:
+		dpacl = hfsplus_posix_state_to_acl(inode,
+						   user_ns,
+						   &default_acl_state,
+						   type);
+		if (unlikely(IS_ERR(dpacl))) {
+			err = PTR_ERR(dpacl);
+			dpacl = NULL;
+			goto free_default_acl_state;
+		}
+		sort_pacl(dpacl);
+		break;
+	}
+
+	err = 0;
+
+free_default_acl_state:
+	free_state(&effective_acl_state);
+
+free_effective_acl_state:
+	free_state(&default_acl_state);
+
+	if (err)
+		goto failed_conversion;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		return pacl;
+
+	case ACL_TYPE_DEFAULT:
+		return dpacl;
+	}
+
+failed_conversion:
+	return ERR_PTR(err);
+}
+
+struct posix_acl_summary {
+	unsigned short owner;
+	unsigned short users;
+	unsigned short group;
+	unsigned short groups;
+	unsigned short other;
+	unsigned short mask;
+};
+
+static void summarize_posix_acl(struct posix_acl *acl,
+				struct posix_acl_summary *pas)
+{
+	struct posix_acl_entry *pa, *pe;
+
+	memset(pas, 0, sizeof(*pas));
+	pas->mask = 07;
+
+	FOREACH_ACL_ENTRY(pa, acl, pe) {
+		switch (pa->e_tag) {
+			case ACL_USER_OBJ:
+				pas->owner = pa->e_perm;
+				break;
+			case ACL_GROUP_OBJ:
+				pas->group = pa->e_perm;
+				break;
+			case ACL_USER:
+				pas->users |= pa->e_perm;
+				break;
+			case ACL_GROUP:
+				pas->groups |= pa->e_perm;
+				break;
+			case ACL_OTHER:
+				pas->other = pa->e_perm;
+				break;
+			case ACL_MASK:
+				pas->mask = pa->e_perm;
+				break;
+		}
+	}
+	/* We'll only care about effective permissions */
+	pas->users &= pas->mask;
+	pas->group &= pas->mask;
+	pas->groups &= pas->mask;
+
+	dprint(DBG_ACL_MOD, "summarize_posix_acl: owner %#x\n", pas->owner);
+	dprint(DBG_ACL_MOD, "summarize_posix_acl: users %#x\n", pas->users);
+	dprint(DBG_ACL_MOD, "summarize_posix_acl: group %#x\n", pas->group);
+	dprint(DBG_ACL_MOD, "summarize_posix_acl: groups %#x\n", pas->groups);
+	dprint(DBG_ACL_MOD, "summarize_posix_acl: other %#x\n", pas->other);
+	dprint(DBG_ACL_MOD, "summarize_posix_acl: mask %#x\n", pas->mask);
+}
+
+static u32 mask_from_posix(struct inode *inode,
+				unsigned short perm,
+				int is_owner)
+{
+	int mask = HFSPLUS_VNODE_ANYONE_MODE;
+
+	if (is_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(inode->i_mode))
+		mask |= HFSPLUS_VNODE_DELETE_CHILD;
+	if (perm & ACL_EXECUTE)
+		mask |= HFSPLUS_VNODE_EXECUTE;
+
+	dprint(DBG_ACL_MOD,
+		"mask_from_posix: perm %#x, mask %#x\n", perm, mask);
+
+	return mask;
+}
+
+static u32 deny_mask_from_posix(struct inode *inode,
+				unsigned short perm)
+{
+	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(inode->i_mode))
+		mask |= HFSPLUS_VNODE_DELETE_CHILD;
+	if (perm & ACL_EXECUTE)
+		mask |= HFSPLUS_VNODE_EXECUTE;
+
+	dprint(DBG_ACL_MOD,
+		"deny_mask_from_posix: perm %#x, mask %#x\n", perm, mask);
+
+	return mask;
+}
+
+#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, eflag, rights) \
+	do { \
+		memcpy(ace->ace_applicable, \
+				ace_applicable, HFSPLUS_GUID_SIZE); \
+		ace->ace_flags = cpu_to_be32(HFSPLUS_ACE_DENY | eflag); \
+		ace->ace_rights = cpu_to_be32(rights); \
+		dprint_hexdump(DBG_ACL_MOD, "compose_ace: ace_applicable: ", \
+				ace->ace_applicable, HFSPLUS_GUID_SIZE); \
+		dprint(DBG_ACL_MOD, \
+			"compose_ace: ace_flags %#x, ace_rights %#x\n", \
+				be32_to_cpu(ace->ace_flags), rights); \
+	} while (0)
+
+#define HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable, eflag, rights) \
+	do { \
+		memcpy(ace->ace_applicable, \
+				ace_applicable, HFSPLUS_GUID_SIZE); \
+		ace->ace_flags = cpu_to_be32(HFSPLUS_ACE_PERMIT | eflag); \
+		ace->ace_rights = cpu_to_be32(rights); \
+		dprint_hexdump(DBG_ACL_MOD, "compose_ace: ace_applicable: ", \
+				ace->ace_applicable, HFSPLUS_GUID_SIZE); \
+		dprint(DBG_ACL_MOD, \
+			"compose_ace: ace_flags %#x, ace_rights %#x\n", \
+				be32_to_cpu(ace->ace_flags), rights); \
+	} while (0)
+
+static int hfsplus_compose_filesec_from_posix_acl(struct inode *inode,
+						struct user_namespace *user_ns,
+						struct posix_acl *acl,
+						struct hfsplus_filesec *filesec,
+						size_t allocated_size,
+						int type)
+{
+	int err;
+	struct posix_acl_entry *pa, *group_owner_entry;
+	struct hfsplus_acl_record *fsec_acl = &(filesec->fsec_acl);
+	struct hfsplus_acl_entry *ace = fsec_acl->acl_ace;
+	struct posix_acl_summary pas;
+	unsigned short deny;
+	int eflag = ((type == ACL_TYPE_DEFAULT) ?
+			HFSPLUS_ACE_INHERIT_CONTROL_FLAGS : 0);
+	u32 ace_entries = 0;
+	u8 ace_applicable[HFSPLUS_GUID_SIZE];
+	size_t calculated_size = 0;
+	size_t filesec_hdr_size = sizeof(struct hfsplus_filesec);
+	size_t ace_size = sizeof(struct hfsplus_acl_entry);
+
+	BUG_ON(acl->a_count < 3);
+	summarize_posix_acl(acl, &pas);
+	pa = acl->a_entries;
+
+	if ((calculated_size + filesec_hdr_size) > allocated_size)
+		return -ENOMEM;
+
+	memset(filesec, 0, sizeof(struct hfsplus_filesec));
+	filesec->fsec_magic = cpu_to_be32(HFSPLUS_FILESEC_MAGIC);
+	calculated_size += filesec_hdr_size;
+
+	/* We could deny everything not granted by the owner */
+	deny = ~pas.owner;
+	/*
+	 * but it is equivalent (and simpler) to deny only what is not
+	 * granted by later entries
+	 */
+	deny &= pas.users | pas.group | pas.groups | pas.other;
+
+	HFSPLUS_ACE_SET_OWNER_USER_ID(ace_applicable);
+
+	if (deny) {
+		dprint(DBG_ACL_MOD,
+			"compose owner (%#x) deny ACE\n", i_uid_read(inode));
+
+		if ((calculated_size + ace_size) > allocated_size)
+			return -ENOMEM;
+		HFSPLUS_COMPOSE_DENY_ACE(ace, ace_applicable, eflag,
+				deny_mask_from_posix(inode, deny));
+		ace++;
+		ace_entries++;
+		calculated_size += ace_size;
+	}
+
+	dprint(DBG_ACL_MOD,
+		"compose owner (%#x) permit ACE\n", i_uid_read(inode));
+
+	if ((calculated_size + ace_size) > allocated_size)
+		return -ENOMEM;
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable, eflag,
+				mask_from_posix(inode, pa->e_perm, 1));
+	ace++;
+	ace_entries++;
+	pa++;
+	calculated_size += ace_size;
+
+	while (pa->e_tag == ACL_USER) {
+		deny = ~(pa->e_perm & pas.mask);
+		deny &= pas.groups | pas.group | pas.other;
+
+		HFSPLUS_ACE_SET_USER_ID(ace_applicable,
+				from_kuid_munged(user_ns, pa->e_id));
+
+		if (deny) {
+			dprint(DBG_ACL_MOD,
+				"compose user (%#x) deny ACE\n",
+				from_kuid_munged(user_ns, pa->e_id));
+
+			if ((calculated_size + ace_size) > allocated_size)
+				return -ENOMEM;
+			HFSPLUS_COMPOSE_DENY_ACE(ace, ace_applicable, eflag,
+					deny_mask_from_posix(inode, deny));
+			ace++;
+			ace_entries++;
+			calculated_size += ace_size;
+		}
+
+		dprint(DBG_ACL_MOD,
+				"compose user (%#x) permit ACE\n",
+				from_kuid_munged(user_ns, pa->e_id));
+
+		if ((calculated_size + ace_size) > allocated_size)
+			return -ENOMEM;
+		HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable, eflag,
+			mask_from_posix(inode, pa->e_perm & pas.mask, 0));
+		ace++;
+		ace_entries++;
+		pa++;
+		calculated_size += ace_size;
+	}
+
+	/* In the case of groups, we apply allow ACEs first, then deny ACEs,
+	 * since a user can be in more than one group.  */
+
+	/* allow ACEs */
+
+	group_owner_entry = pa;
+
+	HFSPLUS_ACE_SET_GROUP_ID(ace_applicable, i_gid_read(inode));
+
+	dprint(DBG_ACL_MOD,
+		"compose group of owner (%#x) permit ACE\n",
+		i_gid_read(inode));
+
+	if ((calculated_size + ace_size) > allocated_size)
+		return -ENOMEM;
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable, eflag,
+			mask_from_posix(inode, pas.group, 0));
+	ace++;
+	ace_entries++;
+	pa++;
+	calculated_size += ace_size;
+
+	while (pa->e_tag == ACL_GROUP) {
+		HFSPLUS_ACE_SET_GROUP_ID(ace_applicable,
+					from_kgid_munged(user_ns, pa->e_id));
+
+		dprint(DBG_ACL_MOD,
+			"compose group (%#x) permit ACE\n",
+			from_kgid_munged(user_ns, pa->e_id));
+
+		if ((calculated_size + ace_size) > allocated_size)
+			return -ENOMEM;
+		HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable, eflag,
+			mask_from_posix(inode, pa->e_perm & pas.mask, 0));
+		ace++;
+		ace_entries++;
+		pa++;
+		calculated_size += ace_size;
+	}
+
+	/* deny ACEs */
+
+	pa = group_owner_entry;
+
+	deny = ~pas.group & pas.other;
+	if (deny) {
+		HFSPLUS_ACE_SET_GROUP_ID(ace_applicable, i_gid_read(inode));
+
+		dprint(DBG_ACL_MOD,
+			"compose group of owner (%#x) deny ACE\n",
+			i_gid_read(inode));
+
+		if ((calculated_size + ace_size) > allocated_size)
+			return -ENOMEM;
+		HFSPLUS_COMPOSE_DENY_ACE(ace, ace_applicable, eflag,
+				deny_mask_from_posix(inode, deny));
+		ace++;
+		ace_entries++;
+		calculated_size += ace_size;
+	}
+	pa++;
+
+	while (pa->e_tag == ACL_GROUP) {
+		deny = ~(pa->e_perm & pas.mask);
+		deny &= pas.other;
+		if (deny) {
+			HFSPLUS_ACE_SET_GROUP_ID(ace_applicable,
+				from_kgid_munged(user_ns, pa->e_id));
+
+		dprint(DBG_ACL_MOD,
+			"compose group (%#x) deny ACE\n",
+			from_kgid_munged(user_ns, pa->e_id));
+
+			if ((calculated_size + ace_size) > allocated_size)
+				return -ENOMEM;
+			HFSPLUS_COMPOSE_DENY_ACE(ace, ace_applicable, eflag,
+					deny_mask_from_posix(inode, deny));
+			ace++;
+			ace_entries++;
+			calculated_size += ace_size;
+		}
+		pa++;
+	}
+
+	if (pa->e_tag == ACL_MASK)
+		pa++;
+
+	HFSPLUS_ACE_SET_GROUP_ID(ace_applicable, HFSPLUS_EVERYBODY_ID);
+
+	dprint(DBG_ACL_MOD, "compose EVERYBODY permit ACE\n");
+
+	if ((calculated_size + ace_size) > allocated_size)
+		return -ENOMEM;
+	HFSPLUS_COMPOSE_PERMIT_ACE(ace, ace_applicable, eflag,
+			mask_from_posix(inode, pa->e_perm, 0));
+	ace_entries++;
+	calculated_size += ace_size;
+
+	fsec_acl->acl_entrycount = cpu_to_be32(ace_entries);
+
+	err = sort_hfsplus_ace(inode, filesec, calculated_size);
+	if (err)
+		return err;
+
+	dprint_hexdump(DBG_ACL_MOD, "composed_filesec: ",
+				filesec, calculated_size);
+
+	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;
+
+	if (posix_acl_valid(acl) < 0)
+		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))
+		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)
+		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;
+
+	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))
+			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);
+
+	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);
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+
+	switch(type) {
+	case ACL_TYPE_ACCESS:
+		if (acl) {
+			err = posix_acl_equiv_mode(acl, &inode->i_mode);
+			if (err < 0)
+				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;
+			goto end_set_acl;
+		} else if (IS_ERR(filesec)) {
+			err = PTR_ERR(filesec);
+			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;
+			goto end_set_acl;
+		}
+	}
+
+	err = __hfsplus_setxattr(inode, xattr_name, filesec, size, 0);
+
+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;
+
+	dprint(DBG_ACL_MOD, "init_acl: %ld, %ld\n",
+		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))
+		return PTR_ERR(acl);
+
+	if (acl) {
+		if (S_ISDIR(inode->i_mode)) {
+			err = hfsplus_set_acl(inode, ACL_TYPE_DEFAULT, acl);
+			if (unlikely(err))
+				goto init_acl_cleanup;
+		}
+
+		err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
+		if (unlikely(err < 0))
+			return err;
+
+		if (err > 0)
+			err = hfsplus_set_acl(inode, ACL_TYPE_ACCESS, acl);
+	} 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;
+
+	dprint(DBG_ACL_MOD, "acl_chmod: %ld\n", inode->i_ino);
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+
+	acl = hfsplus_get_acl(inode, ACL_TYPE_ACCESS);
+	if (IS_ERR(acl) || !acl)
+		return PTR_ERR(acl);
+
+	err = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
+	if (unlikely(err))
+		return err;
+
+	err = hfsplus_set_acl(inode, ACL_TYPE_ACCESS, acl);
+	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;
+
+	dprint(DBG_ACL_MOD, "xattr_get_acl: %ld, %p, %ld\n",
+		dentry->d_inode->i_ino, buffer, size);
+
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+
+	acl = hfsplus_get_acl(dentry->d_inode, type);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl == NULL)
+		return -ENODATA;
+
+	res = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
+	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;
+
+	dprint(DBG_ACL_MOD, "xattr_set_acl: %ld, %p, %ld\n",
+		inode->i_ino, value, size);
+
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+
+	if (!inode_owner_or_capable(inode))
+		return -EPERM;
+
+	if (value) {
+		acl = posix_acl_from_xattr(&init_user_ns, value, size);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		else if (acl) {
+			err = posix_acl_valid(acl);
+			if (err)
+				goto end_xattr_set_acl;
+		}
+	}
+
+	err = hfsplus_set_acl(inode, type, acl);
+
+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..ce41923
--- /dev/null
+++ b/fs/hfsplus/acl.h
@@ -0,0 +1,90 @@
+/*
+ * linux/fs/hfsplus/acl.h
+ *
+ * Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
+ *
+ * Handler for Posix Access Control Lists (ACLs) support.
+ */
+
+#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 *);
-- 
1.7.9.5



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


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux