diff --git a/source/modules/vfs_zfsacl.c b/source/modules/vfs_zfsacl.c
index b44ac48..6836bdc 100644
--- a/source/modules/vfs_zfsacl.c
+++ b/source/modules/vfs_zfsacl.c
@@ -2,6 +2,8 @@
* Convert ZFS/NFSv4 acls to NT acls and vice versa.
*
* Copyright (C) Jiri Sasek, 2007
+ * Copyright (C) Rafal Szczesniak, 2009
+ *
* based on the foobar.c module which is copyrighted by Volker Lendecke
*
* Many thanks to Axel Apitz for help to fix the special ace's handling
@@ -21,7 +23,7 @@
* along with this program; if not, see .
*
*/
-
+
#include "includes.h"
#include "nfs4_acls.h"
@@ -30,6 +32,245 @@
#define ZFSACL_MODULE_NAME "zfsacl"
+struct ace_params {
+ uint16_t a_type;
+ uint16_t a_flags;
+};
+
+struct ace_params nt_acl_order[] = {
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_OWNER },
+ { ACE_ACCESS_DENIED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ },
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_EVERYONE },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_EVERYONE }
+};
+
+
+struct ace_params zfs_acl_order[] = {
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_OWNER },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER },
+ { ACE_ACCESS_DENIED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ },
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_IDENTIFIER_GROUP },
+ { ACE_ACCESS_DENIED_ACE_TYPE, ACE_EVERYONE },
+ { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_EVERYONE }
+};
+
+
+struct ace_entry {
+ ace_t acebuf;
+ struct ace_entry *prev;
+ struct ace_entry *next;
+};
+
+
+static NTSTATUS ace_entry_append(struct ace_entry **list,
+ const struct ace_entry *entry)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct ace_entry *l = NULL;
+ struct ace_entry *e = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ if (list == NULL &&
+ entry == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ l = *list;
+
+ mem_ctx = talloc_tos();
+ e = talloc_size(mem_ctx, sizeof(struct ace_entry));
+ if (e == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ e->acebuf = entry->acebuf;
+ e->prev = NULL;
+ e->next = NULL;
+
+ DLIST_ADD_END(l, e, struct ace_entry*);
+ *list = l;
+
+done:
+ return status;
+}
+
+
+static NTSTATUS ace_entry_insert(struct ace_entry **list,
+ const struct ace_entry *entry,
+ int position)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct ace_entry *l = NULL;
+ struct ace_entry *prev = NULL;
+ struct ace_entry *e = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ int pos = 0;
+
+ if (list == NULL ||
+ entry == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ l = *list;
+
+ mem_ctx = talloc_tos();
+ e = talloc_size(mem_ctx, sizeof(struct ace_entry));
+ if (e == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ e->acebuf = entry->acebuf;
+ e->prev = NULL;
+ e->next = NULL;
+
+ while (l) {
+ if (pos == position) {
+ e->prev = prev;
+ e->next = l;
+ l->prev = e;
+
+ if (prev) {
+ prev->next = e;
+ }
+
+ goto done;
+ }
+
+ prev = l;
+ l = l->next;
+ pos++;
+ }
+
+done:
+ if (l == NULL ||
+ prev == NULL) {
+ *list = e;
+ }
+
+ return status;
+}
+
+
+static int ace_entries_count(struct ace_entry *list)
+{
+ int count = 0;
+
+ while (list) {
+ count++;
+ list = list->next;
+ }
+
+ return count;
+}
+
+
+static int ace_entry_buf_to_list(ace_t *acebuf,
+ int naces,
+ struct ace_entry **list)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ int i = 0;
+ struct ace_entry *l = NULL;
+ struct ace_entry e;
+
+ if (acebuf == NULL ||
+ naces == 0 ||
+ list == NULL) {
+ goto done;
+ }
+
+ for (i = 0; i < naces; i++) {
+ e.acebuf = acebuf[i];
+ e.next = NULL;
+
+ status = ace_entry_append(&l, &e);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+ }
+
+ *list = l;
+
+done:
+ return i;
+}
+
+
+static int ace_entry_list_to_buf(struct ace_entry *list,
+ ace_t **acebuf)
+{
+ int count = 0;
+ ace_t *aces = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ int i = 0;
+
+ if (list == NULL ||
+ acebuf == NULL) {
+ goto done;
+ }
+
+ mem_ctx = talloc_tos();
+
+ count = ace_entries_count(list);
+ aces = talloc_size(NULL, sizeof(ace_t) * count);
+ if (aces == NULL) {
+ count = 0;
+ goto done;
+ }
+
+ for (i = 0; i < count && list; i++) {
+ aces[i] = list->acebuf;
+ list = list->next;
+ }
+
+done:
+ *acebuf = aces;
+
+ return count;
+}
+
+
+static void ace_entry_list_free(struct ace_entry *list)
+{
+ struct ace_entry *prev = NULL;
+
+ while (list) {
+ prev = list;
+ list = list->next;
+
+ talloc_free(prev);
+ }
+}
+
+
+static void dump_acl(const char *desc, const char *fname,
+ ace_t *acebuf, int naces)
+{
+ int i = 0;
+
+ DEBUG(10, ("%s [%s] (number of ACEs = %d)\n", desc, fname, naces));
+
+ for (i = 0; i < naces; i++) {
+ DEBUGADD(9, ("ace[%02d]: a_who = %d, a_access_mask = 0x%08x, a_flags = 0x%04x, "
+ "a_type = 0x%04x\n", i, (int)acebuf[i].a_who,
+ acebuf[i].a_access_mask, acebuf[i].a_flags,
+ acebuf[i].a_type));
+ }
+}
+
+
/* zfs_get_nt_acl()
* read the local file's acls and return it in NT form
* using the NFSv4 format conversion
@@ -38,7 +279,7 @@ static NTSTATUS zfs_get_nt_acl_common(const char *name,
uint32 security_info,
SMB4ACL_T **ppacl)
{
- int naces, i;
+ int naces, i, j;
ace_t *acebuf;
SMB4ACL_T *pacl;
TALLOC_CTX *mem_ctx;
@@ -55,6 +296,7 @@ static NTSTATUS zfs_get_nt_acl_common(const char *name,
}
return map_nt_error_from_unix(errno);
}
+
/* allocate the field of ZFS aces */
mem_ctx = talloc_tos();
acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces);
@@ -62,50 +304,85 @@ static NTSTATUS zfs_get_nt_acl_common(const char *name,
return NT_STATUS_NO_MEMORY;
}
/* read the aces into the field */
- if(acl(name, ACE_GETACL, naces, acebuf) < 0) {
+ if (acl(name, ACE_GETACL, naces, acebuf) < 0) {
DEBUG(9, ("acl(ACE_GETACL, %s): %s ", name,
strerror(errno)));
return map_nt_error_from_unix(errno);
}
+
/* create SMB4ACL data */
if((pacl = smb_create_smb4acl()) == NULL) {
return NT_STATUS_NO_MEMORY;
}
- for(i=0; ia_type ||
+ (acebuf[i].a_flags & flags_mask) != ace_parms->a_flags) {
+ continue;
+ }
+
+ DEBUGADD(9, ("ACE[%02d]: a_who = %d, a_access_mask = 0x%08x, a_flags = 0x%04x, "
+ "a_type = 0x%04x\n", i, (int)acebuf[i].a_who, acebuf[i].a_access_mask,
+ acebuf[i].a_flags, acebuf[i].a_type));
+
+ aceprop.aceType = (uint32) acebuf[i].a_type;
+ aceprop.aceFlags = (uint32) acebuf[i].a_flags;
+ aceprop.aceMask = (uint32) acebuf[i].a_access_mask;
+ aceprop.who.id = (uint32) acebuf[i].a_who;
+
+ if(aceprop.aceFlags & ACE_OWNER) {
+ aceprop.flags = SMB_ACE4_ID_SPECIAL;
+ aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
+ } else if(aceprop.aceFlags & ACE_GROUP) {
+ aceprop.flags = SMB_ACE4_ID_SPECIAL;
+ aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
+ } else if(aceprop.aceFlags & ACE_EVERYONE) {
+ aceprop.flags = SMB_ACE4_ID_SPECIAL;
+ aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
+ } else {
+ aceprop.flags = 0;
+ }
+
+ if(smb_add_ace4(pacl, &aceprop) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
}
- if(smb_add_ace4(pacl, &aceprop) == NULL)
- return NT_STATUS_NO_MEMORY;
}
*ppacl = pacl;
return NT_STATUS_OK;
}
+
/* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */
static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
{
- int naces = smb_get_naces(smbacl), i;
- ace_t *acebuf;
- SMB4ACE_T *smbace;
- TALLOC_CTX *mem_ctx;
+ int naces = smb_get_naces(smbacl);
+ int res_naces = 0;
+ ace_t *acebuf = NULL;
+ ace_t *res_acebuf = NULL;
+ SMB4ACE_T *smbace = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ bool ret = True;
+ struct ace_entry *new_ace_list = NULL;
+ struct ace_entry *ne = NULL; /* new entry */
+ struct ace_entry *res_ace_list = NULL;
+ struct ace_entry *pe = NULL; /* previous entry */
+ struct ace_entry *re; /* resulting entry */
+ int pos = 0;
+ int i = 0;
+ int j = 0;
/* allocate the field of ZFS aces */
mem_ctx = talloc_tos();
@@ -114,10 +391,11 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
errno = ENOMEM;
return False;
}
+
/* handle all aces */
- for(smbace = smb_first_ace4(smbacl), i = 0;
- smbace!=NULL;
- smbace = smb_next_ace4(smbace), i++) {
+ for (smbace = smb_first_ace4(smbacl), i = 0;
+ smbace!=NULL;
+ smbace = smb_next_ace4(smbace), i++) {
SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
acebuf[i].a_type = aceprop->aceType;
@@ -140,12 +418,112 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
aceprop->who.special_id));
continue; /* don't add it !!! */
}
+
+ /* Either owner, group or everyone are a_who = -1
+ and marked with corresponding flag
+ acebuf[i].a_who = -1; */
}
}
SMB_ASSERT(i == naces);
+ /* dump current acl */
+ dump_acl("Dumping new ACL to be written to ", fsp->fsp_name,
+ acebuf, naces);
+
+ /* create list of entries in new ACL */
+ if (ace_entry_buf_to_list(acebuf, naces, &new_ace_list) != naces) {
+ DEBUG(9, ("Created ACE list length doesn't match number of entries "
+ "in new acebuf"));
+ ret = False;
+ goto cleanup;
+ }
+
+ DEBUG(9, ("Reordering ACL\n"));
+
+ for (j = 0; j < sizeof(zfs_acl_order)/sizeof(zfs_acl_order[0]); j++) {
+ struct ace_params *ace_parms = &zfs_acl_order[j];
+ struct ace_entry entry;
+ uint16_t flags_mask;
+
+ memset(&entry, 0, sizeof(entry));
+ flags_mask = ACE_OWNER | ACE_GROUP | ACE_EVERYONE | ACE_IDENTIFIER_GROUP;
+
+ ne = new_ace_list;
+ while (ne) {
+ if (ne->acebuf.a_type != ace_parms->a_type ||
+ (ne->acebuf.a_flags & flags_mask) != ace_parms->a_flags) {
+ ne = ne->next;
+ continue;
+ }
+
+ DEBUGADD(9, ("ACE[%02d]: a_who = %d, a_access_mask = 0x%08x, a_flags = 0x%04x, "
+ "a_type = 0x%04x\n", i, (int)ne->acebuf.a_who,
+ ne->acebuf.a_access_mask, ne->acebuf.a_flags,
+ ne->acebuf.a_type));
+
+ entry.acebuf.a_who = ne->acebuf.a_who;
+ entry.acebuf.a_type = ne->acebuf.a_type;
+ entry.acebuf.a_flags = ne->acebuf.a_flags;
+ entry.acebuf.a_access_mask = ne->acebuf.a_access_mask;
+
+ status = ace_entry_append(&res_ace_list, &entry);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(9, ("Error when creating ZFS ACL from NT ACL"));
+ ret = False;
+ goto cleanup;
+ }
+
+ ne = ne->next;
+ }
+ }
+
+ pos = 0;
+ re = res_ace_list;
+ pe = NULL;
+
+ /* Make sure each ALLOW entry has a preceding DENY */
+ while (re) {
+ struct ace_entry entry;
+ memset(&entry, 0, sizeof(entry));
+
+ if (re->acebuf.a_type == ACE_ACCESS_ALLOWED_ACE_TYPE &&
+ (pe == NULL ||
+ !(pe->acebuf.a_type == ACE_ACCESS_DENIED_ACE_TYPE &&
+ pe->acebuf.a_who == re->acebuf.a_who &&
+ pe->acebuf.a_flags == re->acebuf.a_flags))) {
+
+ /* Create a DENY entry complementary to existing ALLOW */
+ entry.acebuf.a_who = re->acebuf.a_who;
+ entry.acebuf.a_flags = re->acebuf.a_flags;
+ entry.acebuf.a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ entry.acebuf.a_access_mask = 0;
+
+ status = ace_entry_insert(&res_ace_list, &entry, pos);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(9, ("Error when canonicalising ZFS ACL"));
+ ret = False;
+ goto cleanup;
+ }
+ }
+
+ pos++;
+ pe = re;
+ re = re->next;
+ }
+
+ /* create ace_t buffer with resulting ACEs */
+ res_naces = ace_entry_list_to_buf(res_ace_list, &res_acebuf);
+ if (res_naces < 0) {
+ DEBUG(9, ("Failed to convert ACE list to buffer"));
+ ret = False;
+ goto cleanup;
+ }
+
+ dump_acl("Dumping resulting ACL to be written to file", fsp->fsp_name,
+ res_acebuf, res_naces);
+
/* store acl */
- if(acl(fsp->fsp_name, ACE_SETACL, naces, acebuf)) {
+ if(acl(fsp->fsp_name, ACE_SETACL, res_naces, res_acebuf)) {
if(errno == ENOSYS) {
DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
"supported on the filesystem where the file "
@@ -154,12 +532,32 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp->fsp_name,
strerror(errno)));
}
- return 0;
+
+ ret = False;
+ goto cleanup;
+ }
+
+cleanup:
+ if (new_ace_list) {
+ ace_entry_list_free(new_ace_list);
+ }
+
+ if (res_ace_list) {
+ ace_entry_list_free(res_ace_list);
+ }
+
+ if (acebuf) {
+ talloc_free(acebuf);
+ }
+
+ if (res_acebuf) {
+ talloc_free(res_acebuf);
}
- return True;
+ return ret;
}
+
/* zfs_set_nt_acl()
* set the local file's acls obtaining it in NT form
* using the NFSv4 format conversion