* [PATCH 2/3] hfsplus: add implementation of the ACLs support
@ 2013-01-29 10:00 Vyacheslav Dubeyko
0 siblings, 0 replies; only message in thread
From: Vyacheslav Dubeyko @ 2013-01-29 10:00 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Hin-Tak Leung
From: Vyacheslav Dubeyko <slava@dubeyko.com>
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@dubeyko.com>
---
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@dubeyko.com>
+ *
+ * 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@dubeyko.com>
+ *
+ * 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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2013-01-29 10:00 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-29 10:00 [PATCH 2/3] hfsplus: add implementation of the ACLs support Vyacheslav Dubeyko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).