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

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

 



On Sun, Mar 03, 2013 at 07:29:32PM +0400, Vyacheslav Dubeyko wrote:
> From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
> Subject: [PATCH v3 2/3] hfsplus: add implementation of the ACLs support
> 
> This patch adds implementation of the ACLs support for hfsplus driver.

Much of this actually appears to have started as a cut-and-paste of
fs/nfsd/nfs4acl.c.

Could you explain what you needed to change from the nfsd code?

It looks like we should be able to share at least some of this code
instead of copying it.  The posix<->NFSv4 ACL mapping is a bit tricky
and I'd prefer we not have to fix bugs in two different instances of it.

Also: have you looked at the latest rich ACL patches?  That would
provide a native interface to NFSv4-like ACLs, which would really be
much more useful.

--b.

> 
> Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx>
> ---
>  fs/hfsplus/acl.c | 1491 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/hfsplus/acl.h |   90 ++++
>  2 files changed, 1581 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..52f46f4
> --- /dev/null
> +++ b/fs/hfsplus/acl.c
> @@ -0,0 +1,1491 @@
> +/*
> + * 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, "[%s]: found user obj\n", __func__);
> +		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, "[%s]: found group obj\n",
> +								__func__);
> +			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, "[%s]: found other\n", __func__);
> +			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, "[%s]: %#x\n", __func__,
> +						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, "[%s]: %#x\n", __func__,
> +						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, "[%s]: uid %#x\n", __func__, uid);
> +	} else {
> +		dprint(DBG_ACL_MOD, "[%s]: can't convert uid\n", __func__);
> +		return -1;
> +	}
> +
> +	for (i = 0; i < a->n; i++) {
> +		if (a->aces[i].uid == uid) {
> +			dprint(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;
> +
> +	dprint(DBG_ACL_MOD, "[%s]: index %d\n", __func__, 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, "ace_applicable: ",
> +			ace->ace_applicable, HFSPLUS_GUID_SIZE);
> +	dprint(DBG_ACL_MOD, "[%s]: ace_flags %#x, ace_rights %#x\n",
> +			__func__, 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)) {
> +		if (rights & HFSPLUS_VNODE_READ_ATTRIBUTES)
> +			printk(KERN_WARNING "hfs: can't deny read attr\n");
> +		if (rights & HFSPLUS_VNODE_READ_EXTATTRIBUTES)
> +			printk(KERN_WARNING "hfs: can't deny read xattr\n");
> +		if (rights & HFSPLUS_VNODE_READ_SECURITY)
> +			printk(KERN_WARNING "hfs: 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)
> +			printk(KERN_WARNING "hfs: can't deny write attr\n");
> +		if (rights & HFSPLUS_VNODE_WRITE_EXTATTRIBUTES)
> +			printk(KERN_WARNING "hfs: can't deny write xattr\n");
> +		if (rights & HFSPLUS_VNODE_WRITE_SECURITY)
> +			printk(KERN_WARNING "hfs: can't deny write security\n");
> +		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;
> +
> +	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, "[%s]: perm %#x, mode %#x\n",
> +					__func__, 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, "[%s]: owner %#x\n", __func__, pas->owner);
> +	dprint(DBG_ACL_MOD, "[%s]: users %#x\n", __func__, pas->users);
> +	dprint(DBG_ACL_MOD, "[%s]: group %#x\n", __func__, pas->group);
> +	dprint(DBG_ACL_MOD, "[%s]: groups %#x\n", __func__, pas->groups);
> +	dprint(DBG_ACL_MOD, "[%s]: other %#x\n", __func__, pas->other);
> +	dprint(DBG_ACL_MOD, "[%s]: mask %#x\n", __func__, 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, "[%s]: perm %#x, mask %#x\n", __func__, 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, "[%s]: perm %#x, mask %#x\n", __func__, 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, "ace_applicable: ", \
> +				ace->ace_applicable, HFSPLUS_GUID_SIZE); \
> +		dprint(DBG_ACL_MOD, \
> +			"[%s]: ace_flags %#x, ace_rights %#x\n", \
> +				__func__, 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, "ace_applicable: ", \
> +				ace->ace_applicable, HFSPLUS_GUID_SIZE); \
> +		dprint(DBG_ACL_MOD, \
> +			"[%s]: ace_flags %#x, ace_rights %#x\n", \
> +				__func__, 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,
> +			"[%s]: compose owner (%#x) deny ACE\n",
> +			__func__, 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,
> +		"[%s]: compose owner (%#x) permit ACE\n",
> +		__func__, 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,
> +				"[%s]: compose user (%#x) deny ACE\n",
> +				__func__, 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,
> +				"[%s]: compose user (%#x) permit ACE\n",
> +				__func__, 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,
> +		"[%s]: compose group of owner (%#x) permit ACE\n",
> +		__func__, 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,
> +			"[%s]: compose group (%#x) permit ACE\n",
> +			__func__, 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,
> +			"[%s]: compose group of owner (%#x) deny ACE\n",
> +			__func__, 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,
> +			"[%s]: compose group (%#x) deny ACE\n",
> +			__func__, 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, "[%s]: compose EVERYBODY permit ACE\n", __func__);
> +
> +	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, "[%s]: %ld, %ld\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))
> +		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, "[%s]: %ld\n", __func__, 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, "[%s]: %ld, %p, %zd\n",
> +		__func__, 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, "[%s]: %ld, %p, %zd\n",
> +		__func__, 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
--
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