Reformat spacing to be even.
[samba.git] / source3 / smbd / posix_acls.c
index 116917d7eb8dd4bf779bda596ec334b08c8d0e00..e59f54c2b12d3e94dbda5917b89b7e255b42cf83 100644 (file)
@@ -1,8 +1,9 @@
 /*
    Unix SMB/CIFS implementation.
    SMB NT Security Descriptor / Unix permission conversion.
-   Copyright (C) Jeremy Allison 1994-2000.
+   Copyright (C) Jeremy Allison 1994-2009.
    Copyright (C) Andreas Gruenbacher 2002.
+   Copyright (C) Simo Sorce <idra@samba.org> 2009.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -47,30 +48,65 @@ typedef struct canon_ace {
        enum ace_owner owner_type;
        enum ace_attribute attr;
        posix_id unix_ug;
-       BOOL inherited;
+       uint8_t ace_flags; /* From windows ACE entry. */
 } canon_ace;
 
 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
 
 /*
  * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
- * attribute on disk.
+ * attribute on disk - version 1.
+ * All values are little endian.
  *
- * |  1   |  1   |   2         |         2           |  .... 
+ * |  1   |  1   |   2         |         2           |  ....
  * +------+------+-------------+---------------------+-------------+--------------------+
  * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
  * +------+------+-------------+---------------------+-------------+--------------------+
+ *
+ * Entry format is :
+ *
+ * |  1   |       4           |
+ * +------+-------------------+
+ * | value|  uid/gid or world |
+ * | type |  value            |
+ * +------+-------------------+
+ *
+ * Version 2 format. Stores extra Windows metadata about an ACL.
+ *
+ * |  1   |  2       |   2         |         2           |  ....
+ * +------+----------+-------------+---------------------+-------------+--------------------+
+ * | vers | ace      | num_entries | num_default_entries | ..entries.. | default_entries... |
+ * |   2  |  type    |             |                     |             |                    |
+ * +------+----------+-------------+---------------------+-------------+--------------------+
+ *
+ * Entry format is :
+ *
+ * |  1   |  1   |       4           |
+ * +------+------+-------------------+
+ * | ace  | value|  uid/gid or world |
+ * | flag | type |  value            |
+ * +------+-------------------+------+
+ *
  */
 
-#define PAI_VERSION_OFFSET     0
-#define PAI_FLAG_OFFSET                1
-#define PAI_NUM_ENTRIES_OFFSET 2
-#define PAI_NUM_DEFAULT_ENTRIES_OFFSET 4
-#define PAI_ENTRIES_BASE       6
+#define PAI_VERSION_OFFSET                     0
+
+#define PAI_V1_FLAG_OFFSET                     1
+#define PAI_V1_NUM_ENTRIES_OFFSET              2
+#define PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET      4
+#define PAI_V1_ENTRIES_BASE                    6
+#define PAI_V1_ACL_FLAG_PROTECTED              0x1
+#define PAI_V1_ENTRY_LENGTH                    5
 
-#define PAI_VERSION            1
-#define PAI_ACL_FLAG_PROTECTED 0x1
-#define PAI_ENTRY_LENGTH       5
+#define PAI_V1_VERSION                         1
+
+#define PAI_V2_TYPE_OFFSET                     1
+#define PAI_V2_NUM_ENTRIES_OFFSET              3
+#define PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET      5
+#define PAI_V2_ENTRIES_BASE                    7
+#define PAI_V2_ENTRY_LENGTH                    6
+
+#define PAI_V2_VERSION                         2
 
 /*
  * In memory format of user.SAMBA_PAI attribute.
@@ -78,12 +114,13 @@ typedef struct canon_ace {
 
 struct pai_entry {
        struct pai_entry *next, *prev;
+       uint8_t ace_flags;
        enum ace_owner owner_type;
        posix_id unix_ug;
 };
 
 struct pai_val {
-       BOOL pai_protected;
+       uint16_t sd_type;
        unsigned int num_entries;
        struct pai_entry *entry_list;
        unsigned int num_def_entries;
@@ -94,19 +131,19 @@ struct pai_val {
  Return a uint32 of the pai_entry principal.
 ************************************************************************/
 
-static uint32 get_pai_entry_val(struct pai_entry *paie)
+static uint32_t get_pai_entry_val(struct pai_entry *paie)
 {
        switch (paie->owner_type) {
                case UID_ACE:
                        DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
-                       return (uint32)paie->unix_ug.uid;
+                       return (uint32_t)paie->unix_ug.uid;
                case GID_ACE:
                        DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
-                       return (uint32)paie->unix_ug.gid;
+                       return (uint32_t)paie->unix_ug.gid;
                case WORLD_ACE:
                default:
                        DEBUG(10,("get_pai_entry_val: world ace\n"));
-                       return (uint32)-1;
+                       return (uint32_t)-1;
        }
 }
 
@@ -114,59 +151,50 @@ static uint32 get_pai_entry_val(struct pai_entry *paie)
  Return a uint32 of the entry principal.
 ************************************************************************/
 
-static uint32 get_entry_val(canon_ace *ace_entry)
+static uint32_t get_entry_val(canon_ace *ace_entry)
 {
        switch (ace_entry->owner_type) {
                case UID_ACE:
                        DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
-                       return (uint32)ace_entry->unix_ug.uid;
+                       return (uint32_t)ace_entry->unix_ug.uid;
                case GID_ACE:
                        DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
-                       return (uint32)ace_entry->unix_ug.gid;
+                       return (uint32_t)ace_entry->unix_ug.gid;
                case WORLD_ACE:
                default:
                        DEBUG(10,("get_entry_val: world ace\n"));
-                       return (uint32)-1;
+                       return (uint32_t)-1;
        }
 }
 
 /************************************************************************
- Count the inherited entries.
-************************************************************************/
-
-static unsigned int num_inherited_entries(canon_ace *ace_list)
-{
-       unsigned int num_entries = 0;
-
-       for (; ace_list; ace_list = ace_list->next)
-               if (ace_list->inherited)
-                       num_entries++;
-       return num_entries;
-}
-
-/************************************************************************
- Create the on-disk format. Caller must free.
+ Create the on-disk format (always v2 now). Caller must free.
 ************************************************************************/
 
-static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL pai_protected, size_t *store_size)
+static char *create_pai_buf_v2(canon_ace *file_ace_list,
+                               canon_ace *dir_ace_list,
+                               uint16_t sd_type,
+                               size_t *store_size)
 {
        char *pai_buf = NULL;
        canon_ace *ace_list = NULL;
        char *entry_offset = NULL;
        unsigned int num_entries = 0;
        unsigned int num_def_entries = 0;
+       unsigned int i;
 
-       for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
-               if (ace_list->inherited)
-                       num_entries++;
+       for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
+               num_entries++;
+       }
 
-       for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
-               if (ace_list->inherited)
-                       num_def_entries++;
+       for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
+               num_def_entries++;
+       }
 
-       DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
+       DEBUG(10,("create_pai_buf_v2: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
 
-       *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
+       *store_size = PAI_V2_ENTRIES_BASE +
+               ((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH);
 
        pai_buf = (char *)SMB_MALLOC(*store_size);
        if (!pai_buf) {
@@ -174,34 +202,48 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, B
        }
 
        /* Set up the header. */
-       memset(pai_buf, '\0', PAI_ENTRIES_BASE);
-       SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
-       SCVAL(pai_buf,PAI_FLAG_OFFSET,(pai_protected ? PAI_ACL_FLAG_PROTECTED : 0));
-       SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
-       SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
+       memset(pai_buf, '\0', PAI_V2_ENTRIES_BASE);
+       SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_V2_VERSION);
+       SSVAL(pai_buf,PAI_V2_TYPE_OFFSET, sd_type);
+       SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
+       SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
 
-       entry_offset = pai_buf + PAI_ENTRIES_BASE;
+       DEBUG(10,("create_pai_buf_v2: sd_type = 0x%x\n",
+                       (unsigned int)sd_type ));
 
-       for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
-               if (ace_list->inherited) {
-                       uint8 type_val = (unsigned char)ace_list->owner_type;
-                       uint32 entry_val = get_entry_val(ace_list);
+       entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
 
-                       SCVAL(entry_offset,0,type_val);
-                       SIVAL(entry_offset,1,entry_val);
-                       entry_offset += PAI_ENTRY_LENGTH;
-               }
+       i = 0;
+       for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
+               uint8_t type_val = (uint8_t)ace_list->owner_type;
+               uint32_t entry_val = get_entry_val(ace_list);
+
+               SCVAL(entry_offset,0,ace_list->ace_flags);
+               SCVAL(entry_offset,1,type_val);
+               SIVAL(entry_offset,2,entry_val);
+               DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
+                       i,
+                       (unsigned int)ace_list->ace_flags,
+                       (unsigned int)type_val,
+                       (unsigned int)entry_val ));
+               i++;
+               entry_offset += PAI_V2_ENTRY_LENGTH;
        }
 
        for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
-               if (ace_list->inherited) {
-                       uint8 type_val = (unsigned char)ace_list->owner_type;
-                       uint32 entry_val = get_entry_val(ace_list);
-
-                       SCVAL(entry_offset,0,type_val);
-                       SIVAL(entry_offset,1,entry_val);
-                       entry_offset += PAI_ENTRY_LENGTH;
-               }
+               uint8_t type_val = (uint8_t)ace_list->owner_type;
+               uint32_t entry_val = get_entry_val(ace_list);
+
+               SCVAL(entry_offset,0,ace_list->ace_flags);
+               SCVAL(entry_offset,1,type_val);
+               SIVAL(entry_offset,2,entry_val);
+               DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
+                       i,
+                       (unsigned int)ace_list->ace_flags,
+                       (unsigned int)type_val,
+                       (unsigned int)entry_val ));
+               i++;
+               entry_offset += PAI_V2_ENTRY_LENGTH;
        }
 
        return pai_buf;
@@ -211,44 +253,40 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, B
  Store the user.SAMBA_PAI attribute on disk.
 ************************************************************************/
 
-static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
-                                       canon_ace *dir_ace_list, BOOL pai_protected)
+static void store_inheritance_attributes(files_struct *fsp,
+                                       canon_ace *file_ace_list,
+                                       canon_ace *dir_ace_list,
+                                       uint16_t sd_type)
 {
        int ret;
        size_t store_size;
        char *pai_buf;
 
-       if (!lp_map_acl_inherit(SNUM(fsp->conn)))
-               return;
-
-       /*
-        * Don't store if this ACL isn't protected and
-        * none of the entries in it are marked as inherited.
-        */
-
-       if (!pai_protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
-               /* Instead just remove the attribute if it exists. */
-               if (fsp->fh->fd != -1)
-                       SMB_VFS_FREMOVEXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME);
-               else
-                       SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
+       if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
                return;
        }
 
-       pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size);
+       pai_buf = create_pai_buf_v2(file_ace_list, dir_ace_list,
+                               sd_type, &store_size);
 
-       if (fsp->fh->fd != -1)
-               ret = SMB_VFS_FSETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
-                               pai_buf, store_size, 0);
-       else
-               ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
+       if (fsp->fh->fd != -1) {
+               ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
                                pai_buf, store_size, 0);
+       } else {
+               ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name->base_name,
+                                      SAMBA_POSIX_INHERITANCE_EA_NAME,
+                                      pai_buf, store_size, 0);
+       }
 
        SAFE_FREE(pai_buf);
 
-       DEBUG(10,("store_inheritance_attribute:%s for file %s\n", pai_protected ? " (protected)" : "", fsp->fsp_name));
-       if (ret == -1 && !no_acl_syscall_error(errno))
+       DEBUG(10,("store_inheritance_attribute: type 0x%x for file %s\n",
+               (unsigned int)sd_type,
+               fsp_str_dbg(fsp)));
+
+       if (ret == -1 && !no_acl_syscall_error(errno)) {
                DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+       }
 }
 
 /************************************************************************
@@ -272,157 +310,362 @@ static void free_inherited_info(struct pai_val *pal)
 }
 
 /************************************************************************
- Was this ACL protected ?
-************************************************************************/
-
-static BOOL get_protected_flag(struct pai_val *pal)
-{
-       if (!pal)
-               return False;
-       return pal->pai_protected;
-}
-
-/************************************************************************
- Was this ACE inherited ?
+ Get any stored ACE flags.
 ************************************************************************/
 
-static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
+static uint16_t get_pai_flags(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
 {
        struct pai_entry *paie;
 
-       if (!pal)
-               return False;
+       if (!pal) {
+               return 0;
+       }
 
        /* If the entry exists it is inherited. */
        for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
                if (ace_entry->owner_type == paie->owner_type &&
                                get_entry_val(ace_entry) == get_pai_entry_val(paie))
-                       return True;
+                       return paie->ace_flags;
        }
-       return False;
+       return 0;
 }
 
 /************************************************************************
- Ensure an attribute just read is valid.
+ Ensure an attribute just read is valid - v1.
 ************************************************************************/
 
-static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
+static bool check_pai_ok_v1(const char *pai_buf, size_t pai_buf_data_size)
 {
        uint16 num_entries;
        uint16 num_def_entries;
 
-       if (pai_buf_data_size < PAI_ENTRIES_BASE) {
+       if (pai_buf_data_size < PAI_V1_ENTRIES_BASE) {
                /* Corrupted - too small. */
-               return False;
+               return false;
        }
 
-       if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
-               return False;
+       if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V1_VERSION) {
+               return false;
+       }
 
-       num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
-       num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+       num_entries = SVAL(pai_buf,PAI_V1_NUM_ENTRIES_OFFSET);
+       num_def_entries = SVAL(pai_buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
 
        /* Check the entry lists match. */
        /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
 
-       if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
-               return False;
+       if (((num_entries + num_def_entries)*PAI_V1_ENTRY_LENGTH) +
+                       PAI_V1_ENTRIES_BASE != pai_buf_data_size) {
+               return false;
+       }
 
-       return True;
+       return true;
+}
+
+/************************************************************************
+ Ensure an attribute just read is valid - v2.
+************************************************************************/
+
+static bool check_pai_ok_v2(const char *pai_buf, size_t pai_buf_data_size)
+{
+       uint16 num_entries;
+       uint16 num_def_entries;
+
+       if (pai_buf_data_size < PAI_V2_ENTRIES_BASE) {
+               /* Corrupted - too small. */
+               return false;
+       }
+
+       if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V2_VERSION) {
+               return false;
+       }
+
+       num_entries = SVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET);
+       num_def_entries = SVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
+
+       /* Check the entry lists match. */
+       /* Each entry is 6 bytes (flags + type + 4 bytes of uid or gid). */
+
+       if (((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH) +
+                       PAI_V2_ENTRIES_BASE != pai_buf_data_size) {
+               return false;
+       }
+
+       return true;
 }
 
+/************************************************************************
+ Decode the owner.
+************************************************************************/
+
+static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
+{
+       paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
+       switch( paie->owner_type) {
+               case UID_ACE:
+                       paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
+                       DEBUG(10,("get_pai_owner_type: uid = %u\n",
+                               (unsigned int)paie->unix_ug.uid ));
+                       break;
+               case GID_ACE:
+                       paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
+                       DEBUG(10,("get_pai_owner_type: gid = %u\n",
+                               (unsigned int)paie->unix_ug.gid ));
+                       break;
+               case WORLD_ACE:
+                       paie->unix_ug.world = -1;
+                       DEBUG(10,("get_pai_owner_type: world ace\n"));
+                       break;
+               default:
+                       DEBUG(10,("get_pai_owner_type: unknown type %u\n",
+                               (unsigned int)paie->owner_type ));
+                       return false;
+       }
+       return true;
+}
 
 /************************************************************************
Convert to in-memory format.
Process v2 entries.
 ************************************************************************/
 
-static struct pai_val *create_pai_val(char *buf, size_t size)
+static const char *create_pai_v1_entries(struct pai_val *paiv,
+                               const char *entry_offset,
+                               bool def_entry)
 {
-       char *entry_offset;
-       struct pai_val *paiv = NULL;
        int i;
 
-       if (!check_pai_ok(buf, size))
+       for (i = 0; i < paiv->num_entries; i++) {
+               struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
+               if (!paie) {
+                       return NULL;
+               }
+
+               paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
+               if (!get_pai_owner_type(paie, entry_offset)) {
+                       return NULL;
+               }
+
+               if (!def_entry) {
+                       DLIST_ADD(paiv->entry_list, paie);
+               } else {
+                       DLIST_ADD(paiv->def_entry_list, paie);
+               }
+               entry_offset += PAI_V1_ENTRY_LENGTH;
+       }
+       return entry_offset;
+}
+
+/************************************************************************
+ Convert to in-memory format from version 1.
+************************************************************************/
+
+static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
+{
+       const char *entry_offset;
+       struct pai_val *paiv = NULL;
+
+       if (!check_pai_ok_v1(buf, size)) {
                return NULL;
+       }
 
        paiv = SMB_MALLOC_P(struct pai_val);
-       if (!paiv)
+       if (!paiv) {
                return NULL;
+       }
 
        memset(paiv, '\0', sizeof(struct pai_val));
 
-       paiv->pai_protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
+       paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
+                       SE_DESC_DACL_PROTECTED : 0;
 
-       paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
-       paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+       paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
+       paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
 
-       entry_offset = buf + PAI_ENTRIES_BASE;
+       entry_offset = buf + PAI_V1_ENTRIES_BASE;
 
-       DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
-                       paiv->pai_protected ? " (pai_protected)" : "", paiv->num_entries, paiv->num_def_entries ));
+       DEBUG(10,("create_pai_val: num_entries = %u, num_def_entries = %u\n",
+                       paiv->num_entries, paiv->num_def_entries ));
 
-       for (i = 0; i < paiv->num_entries; i++) {
-               struct pai_entry *paie;
+       entry_offset = create_pai_v1_entries(paiv, entry_offset, false);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+       entry_offset = create_pai_v1_entries(paiv, entry_offset, true);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+
+       return paiv;
+}
+
+/************************************************************************
+ Process v2 entries.
+************************************************************************/
+
+static const char *create_pai_v2_entries(struct pai_val *paiv,
+                               unsigned int num_entries,
+                               const char *entry_offset,
+                               bool def_entry)
+{
+       unsigned int i;
 
-               paie = SMB_MALLOC_P(struct pai_entry);
+       for (i = 0; i < num_entries; i++) {
+               struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
                if (!paie) {
-                       free_inherited_info(paiv);
                        return NULL;
                }
 
-               paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
-               switch( paie->owner_type) {
-                       case UID_ACE:
-                               paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
-                               DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
-                               break;
-                       case GID_ACE:
-                               paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
-                               DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
-                               break;
-                       case WORLD_ACE:
-                               paie->unix_ug.world = -1;
-                               DEBUG(10,("create_pai_val: world ace\n"));
-                               break;
-                       default:
-                               free_inherited_info(paiv);
-                               return NULL;
+               paie->ace_flags = CVAL(entry_offset,0);
+
+               if (!get_pai_owner_type(paie, entry_offset+1)) {
+                       return NULL;
                }
-               entry_offset += PAI_ENTRY_LENGTH;
-               DLIST_ADD(paiv->entry_list, paie);
+               if (!def_entry) {
+                       DLIST_ADD(paiv->entry_list, paie);
+               } else {
+                       DLIST_ADD(paiv->def_entry_list, paie);
+               }
+               entry_offset += PAI_V2_ENTRY_LENGTH;
        }
+       return entry_offset;
+}
 
-       for (i = 0; i < paiv->num_def_entries; i++) {
-               struct pai_entry *paie;
+/************************************************************************
+ Convert to in-memory format from version 2.
+************************************************************************/
 
-               paie = SMB_MALLOC_P(struct pai_entry);
-               if (!paie) {
-                       free_inherited_info(paiv);
-                       return NULL;
+static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
+{
+       const char *entry_offset;
+       struct pai_val *paiv = NULL;
+
+       if (!check_pai_ok_v2(buf, size)) {
+               return NULL;
+       }
+
+       paiv = SMB_MALLOC_P(struct pai_val);
+       if (!paiv) {
+               return NULL;
+       }
+
+       memset(paiv, '\0', sizeof(struct pai_val));
+
+       paiv->sd_type = SVAL(buf,PAI_V2_TYPE_OFFSET);
+
+       paiv->num_entries = SVAL(buf,PAI_V2_NUM_ENTRIES_OFFSET);
+       paiv->num_def_entries = SVAL(buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
+
+       entry_offset = buf + PAI_V2_ENTRIES_BASE;
+
+       DEBUG(10,("create_pai_val_v2: sd_type = 0x%x num_entries = %u, num_def_entries = %u\n",
+                       (unsigned int)paiv->sd_type,
+                       paiv->num_entries, paiv->num_def_entries ));
+
+       entry_offset = create_pai_v2_entries(paiv, paiv->num_entries,
+                               entry_offset, false);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+       entry_offset = create_pai_v2_entries(paiv, paiv->num_def_entries,
+                               entry_offset, true);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+
+       return paiv;
+}
+
+/************************************************************************
+ Convert to in-memory format - from either version 1 or 2.
+************************************************************************/
+
+static struct pai_val *create_pai_val(const char *buf, size_t size)
+{
+       if (size < 1) {
+               return NULL;
+       }
+       if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V1_VERSION) {
+               return create_pai_val_v1(buf, size);
+       } else if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V2_VERSION) {
+               return create_pai_val_v2(buf, size);
+       } else {
+               return NULL;
+       }
+}
+
+/************************************************************************
+ Load the user.SAMBA_PAI attribute.
+************************************************************************/
+
+static struct pai_val *fload_inherited_info(files_struct *fsp)
+{
+       char *pai_buf;
+       size_t pai_buf_size = 1024;
+       struct pai_val *paiv = NULL;
+       ssize_t ret;
+
+       if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
+               return NULL;
+       }
+
+       if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
+               return NULL;
+       }
+
+       do {
+               if (fsp->fh->fd != -1) {
+                       ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
+                                       pai_buf, pai_buf_size);
+               } else {
+                       ret = SMB_VFS_GETXATTR(fsp->conn,
+                                              fsp->fsp_name->base_name,
+                                              SAMBA_POSIX_INHERITANCE_EA_NAME,
+                                              pai_buf, pai_buf_size);
                }
 
-               paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
-               switch( paie->owner_type) {
-                       case UID_ACE:
-                               paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
-                               DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
-                               break;
-                       case GID_ACE:
-                               paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
-                               DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
-                               break;
-                       case WORLD_ACE:
-                               paie->unix_ug.world = -1;
-                               DEBUG(10,("create_pai_val: (def) world ace\n"));
+               if (ret == -1) {
+                       if (errno != ERANGE) {
                                break;
-                       default:
-                               free_inherited_info(paiv);
+                       }
+                       /* Buffer too small - enlarge it. */
+                       pai_buf_size *= 2;
+                       SAFE_FREE(pai_buf);
+                       if (pai_buf_size > 1024*1024) {
+                               return NULL; /* Limit malloc to 1mb. */
+                       }
+                       if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
                                return NULL;
                }
-               entry_offset += PAI_ENTRY_LENGTH;
-               DLIST_ADD(paiv->def_entry_list, paie);
+       } while (ret == -1);
+
+       DEBUG(10,("load_inherited_info: ret = %lu for file %s\n",
+                 (unsigned long)ret, fsp_str_dbg(fsp)));
+
+       if (ret == -1) {
+               /* No attribute or not supported. */
+#if defined(ENOATTR)
+               if (errno != ENOATTR)
+                       DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
+#else
+               if (errno != ENOSYS)
+                       DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
+#endif
+               SAFE_FREE(pai_buf);
+               return NULL;
        }
 
+       paiv = create_pai_val(pai_buf, ret);
+
+       if (paiv) {
+               DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
+                         (unsigned int)paiv->sd_type, fsp_str_dbg(fsp)));
+       }
+
+       SAFE_FREE(pai_buf);
        return paiv;
 }
 
@@ -430,26 +673,26 @@ static struct pai_val *create_pai_val(char *buf, size_t size)
  Load the user.SAMBA_PAI attribute.
 ************************************************************************/
 
-static struct pai_val *load_inherited_info(files_struct *fsp)
+static struct pai_val *load_inherited_info(const struct connection_struct *conn,
+                                          const char *fname)
 {
        char *pai_buf;
        size_t pai_buf_size = 1024;
        struct pai_val *paiv = NULL;
        ssize_t ret;
 
-       if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+       if (!lp_map_acl_inherit(SNUM(conn))) {
                return NULL;
+       }
 
-       if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
+       if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
                return NULL;
+       }
 
        do {
-               if (fsp->fh->fd != -1)
-                       ret = SMB_VFS_FGETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
-                                       pai_buf, pai_buf_size);
-               else
-                       ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
-                                       pai_buf, pai_buf_size);
+               ret = SMB_VFS_GETXATTR(conn, fname,
+                                      SAMBA_POSIX_INHERITANCE_EA_NAME,
+                                      pai_buf, pai_buf_size);
 
                if (ret == -1) {
                        if (errno != ERANGE) {
@@ -466,7 +709,7 @@ static struct pai_val *load_inherited_info(files_struct *fsp)
                }
        } while (ret == -1);
 
-       DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
+       DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fname));
 
        if (ret == -1) {
                /* No attribute or not supported. */
@@ -483,8 +726,11 @@ static struct pai_val *load_inherited_info(files_struct *fsp)
 
        paiv = create_pai_val(pai_buf, ret);
 
-       if (paiv && paiv->pai_protected)
-               DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
+       if (paiv) {
+               DEBUG(10,("load_inherited_info: ACL type 0x%x for file %s\n",
+                       (unsigned int)paiv->sd_type,
+                       fname));
+       }
 
        SAFE_FREE(pai_buf);
        return paiv;
@@ -498,12 +744,12 @@ static struct pai_val *load_inherited_info(files_struct *fsp)
  Count a linked list of canonical ACE entries.
 ****************************************************************************/
 
-static size_t count_canon_ace_list( canon_ace *list_head )
+static size_t count_canon_ace_list( canon_ace *l_head )
 {
        size_t count = 0;
        canon_ace *ace;
 
-       for (ace = list_head; ace; ace = ace->next)
+       for (ace = l_head; ace; ace = ace->next)
                count++;
 
        return count;
@@ -513,13 +759,13 @@ static size_t count_canon_ace_list( canon_ace *list_head )
  Free a linked list of canonical ACE entries.
 ****************************************************************************/
 
-static void free_canon_ace_list( canon_ace *list_head )
+static void free_canon_ace_list( canon_ace *l_head )
 {
        canon_ace *list, *next;
 
-       for (list = list_head; list; list = next) {
+       for (list = l_head; list; list = next) {
                next = list->next;
-               DLIST_REMOVE(list_head, list);
+               DLIST_REMOVE(l_head, list);
                SAFE_FREE(list);
        }
 }
@@ -546,10 +792,8 @@ static canon_ace *dup_canon_ace( canon_ace *src_ace)
 
 static void print_canon_ace(canon_ace *pace, int num)
 {
-       fstring str;
-
        dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
-       dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
+       dbgtext( "SID = %s ", sid_string_dbg(&pace->trustee));
        if (pace->owner_type == UID_ACE) {
                const char *u_name = uidtoname(pace->unix_ug.uid);
                dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
@@ -578,8 +822,8 @@ static void print_canon_ace(canon_ace *pace, int num)
                        dbgtext( "MASK " );
                        break;
        }
-       if (pace->inherited)
-               dbgtext( "(inherited) ");
+
+       dbgtext( "ace_flags = 0x%x ", (unsigned int)pace->ace_flags);
        dbgtext( "perms ");
        dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
        dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
@@ -662,31 +906,10 @@ static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_AC
  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
 ****************************************************************************/
 
-static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
-{
-       uid_to_sid( powner_sid, psbuf->st_uid );
-       gid_to_sid( pgroup_sid, psbuf->st_gid );
-}
-
-/****************************************************************************
- Is the identity in two ACEs equal ? Check both SID and uid/gid.
-****************************************************************************/
-
-static BOOL identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
+void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
 {
-       if (sid_equal(&ace1->trustee, &ace2->trustee)) {
-               return True;
-       }
-       if (ace1->owner_type == ace2->owner_type) {
-               if (ace1->owner_type == UID_ACE &&
-                               ace1->unix_ug.uid == ace2->unix_ug.uid) {
-                       return True;
-               } else if (ace1->owner_type == GID_ACE &&
-                               ace1->unix_ug.gid == ace2->unix_ug.gid) {
-                       return True;
-               }
-       }
-       return False;
+       uid_to_sid( powner_sid, psbuf->st_ex_uid );
+       gid_to_sid( pgroup_sid, psbuf->st_ex_gid );
 }
 
 /****************************************************************************
@@ -695,9 +918,9 @@ static BOOL identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
  if the permissions become zero, delete the deny if the permissions are non zero.
 ****************************************************************************/
 
-static void merge_aces( canon_ace **pp_list_head )
+static void merge_aces( canon_ace **pp_list_head, bool dir_acl)
 {
-       canon_ace *list_head = *pp_list_head;
+       canon_ace *l_head = *pp_list_head;
        canon_ace *curr_ace_outer;
        canon_ace *curr_ace_outer_next;
 
@@ -706,19 +929,31 @@ static void merge_aces( canon_ace **pp_list_head )
         * with identical SIDs.
         */
 
-       for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+       for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
                canon_ace *curr_ace;
                canon_ace *curr_ace_next;
 
                curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
 
                for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+                       bool can_merge = false;
 
                        curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
 
-                       if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
-                               (curr_ace->attr == curr_ace_outer->attr)) {
+                       /* For file ACLs we can merge if the SIDs and ALLOW/DENY
+                        * types are the same. For directory acls we must also
+                        * ensure the POSIX ACL types are the same. */
+
+                       if (!dir_acl) {
+                               can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                                               (curr_ace->attr == curr_ace_outer->attr));
+                       } else {
+                               can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                                               (curr_ace->type == curr_ace_outer->type) &&
+                                               (curr_ace->attr == curr_ace_outer->attr));
+                       }
 
+                       if (can_merge) {
                                if( DEBUGLVL( 10 )) {
                                        dbgtext("merge_aces: Merging ACE's\n");
                                        print_canon_ace( curr_ace_outer, 0);
@@ -727,8 +962,14 @@ static void merge_aces( canon_ace **pp_list_head )
 
                                /* Merge two allow or two deny ACE's. */
 
+                               /* Theoretically we shouldn't merge a dir ACE if
+                                * one ACE has the CI flag set, and the other
+                                * ACE has the OI flag set, but this is rare
+                                * enough we can ignore it. */
+
                                curr_ace_outer->perms |= curr_ace->perms;
-                               DLIST_REMOVE(list_head, curr_ace);
+                               curr_ace_outer->ace_flags |= curr_ace->ace_flags;
+                               DLIST_REMOVE(l_head, curr_ace);
                                SAFE_FREE(curr_ace);
                                curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
                        }
@@ -741,7 +982,7 @@ static void merge_aces( canon_ace **pp_list_head )
         * appears only once in the list.
         */
 
-       for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+       for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
                canon_ace *curr_ace;
                canon_ace *curr_ace_next;
 
@@ -756,7 +997,7 @@ static void merge_aces( canon_ace **pp_list_head )
                         * we've put on the ACL, we know the deny must be the first one.
                         */
 
-                       if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
+                       if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
                                (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
 
                                if( DEBUGLVL( 10 )) {
@@ -773,7 +1014,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                         * The deny overrides the allow. Remove the allow.
                                         */
 
-                                       DLIST_REMOVE(list_head, curr_ace);
+                                       DLIST_REMOVE(l_head, curr_ace);
                                        SAFE_FREE(curr_ace);
                                        curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
 
@@ -789,7 +1030,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                         * before we can get to an allow ace.
                                         */
 
-                                       DLIST_REMOVE(list_head, curr_ace_outer);
+                                       DLIST_REMOVE(l_head, curr_ace_outer);
                                        SAFE_FREE(curr_ace_outer);
                                        break;
                                }
@@ -800,14 +1041,14 @@ static void merge_aces( canon_ace **pp_list_head )
 
        /* We may have modified the list. */
 
-       *pp_list_head = list_head;
+       *pp_list_head = l_head;
 }
 
 /****************************************************************************
  Check if we need to return NT4.x compatible ACL entries.
 ****************************************************************************/
 
-static BOOL nt4_compatible_acls(void)
+bool nt4_compatible_acls(void)
 {
        int compat = lp_acl_compatibility();
 
@@ -827,13 +1068,12 @@ static BOOL nt4_compatible_acls(void)
  not get. Deny entries are implicit on get with ace->perms = 0.
 ****************************************************************************/
 
-static SEC_ACCESS map_canon_ace_perms(int snum,
-                               int *pacl_type,
+uint32_t map_canon_ace_perms(int snum,
+                               enum security_ace_type *pacl_type,
                                mode_t perms,
-                               BOOL directory_ace)
+                               bool directory_ace)
 {
-       SEC_ACCESS sa;
-       uint32 nt_mask = 0;
+       uint32_t nt_mask = 0;
 
        *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
 
@@ -841,7 +1081,7 @@ static SEC_ACCESS map_canon_ace_perms(int snum,
                if (directory_ace) {
                        nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
                } else {
-                       nt_mask = UNIX_ACCESS_RWX;
+                       nt_mask = (UNIX_ACCESS_RWX & ~DELETE_ACCESS);
                }
        } else if ((perms & ALL_ACE_PERMS) == (mode_t)0) {
                /*
@@ -869,19 +1109,22 @@ static SEC_ACCESS map_canon_ace_perms(int snum,
                }
        }
 
+       if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
+               nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
+       }
+
        DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
                        (unsigned int)perms, (unsigned int)nt_mask ));
 
-       init_sec_access(&sa,nt_mask);
-       return sa;
+       return nt_mask;
 }
 
 /****************************************************************************
  Map NT perms to a UNIX mode_t.
 ****************************************************************************/
 
-#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
-#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA)
 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
 
 static mode_t map_nt_perms( uint32 *mask, int type)
@@ -925,7 +1168,7 @@ static mode_t map_nt_perms( uint32 *mask, int type)
  Unpack a SEC_DESC into a UNIX owner and group.
 ****************************************************************************/
 
-NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, const SEC_DESC *psd)
 {
        DOM_SID owner_sid;
        DOM_SID grp_sid;
@@ -962,7 +1205,7 @@ NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_i
                        } else {
                                DEBUG(3,("unpack_nt_owners: unable to validate"
                                         " owner sid for %s\n",
-                                        sid_string_static(&owner_sid)));
+                                        sid_string_dbg(&owner_sid)));
                                return NT_STATUS_INVALID_OWNER;
                        }
                }
@@ -1001,20 +1244,21 @@ NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_i
  Ensure the enforced permissions for this share apply.
 ****************************************************************************/
 
-static void apply_default_perms(const files_struct *fsp, canon_ace *pace, mode_t type)
+static void apply_default_perms(const struct share_params *params,
+                               const bool is_directory, canon_ace *pace,
+                               mode_t type)
 {
-       int snum = SNUM(fsp->conn);
        mode_t and_bits = (mode_t)0;
        mode_t or_bits = (mode_t)0;
 
        /* Get the initial bits to apply. */
 
-       if (fsp->is_directory) {
-               and_bits = lp_dir_security_mask(snum);
-               or_bits = lp_force_dir_security_mode(snum);
+       if (is_directory) {
+               and_bits = lp_dir_security_mask(params->service);
+               or_bits = lp_force_dir_security_mode(params->service);
        } else {
-               and_bits = lp_security_mask(snum);
-               or_bits = lp_force_security_mode(snum);
+               and_bits = lp_security_mask(params->service);
+               or_bits = lp_force_security_mode(params->service);
        }
 
        /* Now bounce them into the S_USR space. */     
@@ -1022,7 +1266,7 @@ static void apply_default_perms(const files_struct *fsp, canon_ace *pace, mode_t
        case S_IRUSR:
                /* Ensure owner has read access. */
                pace->perms |= S_IRUSR;
-               if (fsp->is_directory)
+               if (is_directory)
                        pace->perms |= (S_IWUSR|S_IXUSR);
                and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
                or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
@@ -1045,7 +1289,7 @@ static void apply_default_perms(const files_struct *fsp, canon_ace *pace, mode_t
  expensive and will need optimisation. A *lot* of optimisation :-). JRA.
 ****************************************************************************/
 
-static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
+static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
 {
        const char *u_name = NULL;
 
@@ -1054,16 +1298,36 @@ static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
        if (sid_equal(&group_ace->trustee, &global_sid_World))
                return True;
 
-       /* Assume that the current user is in the current group (force group) */
+       /*
+        * if it's the current user, we already have the unix token
+        * and don't need to do the complex user_in_group_sid() call
+        */
+       if (uid_ace->unix_ug.uid == current_user.ut.uid) {
+               size_t i;
+
+               if (group_ace->unix_ug.gid == current_user.ut.gid) {
+                       return True;
+               }
 
-       if (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid)
-               return True;
+               for (i=0; i < current_user.ut.ngroups; i++) {
+                       if (group_ace->unix_ug.gid == current_user.ut.groups[i]) {
+                               return True;
+                       }
+               }
+       }
 
        /* u_name talloc'ed off tos. */
        u_name = uidtoname(uid_ace->unix_ug.uid);
        if (!u_name) {
                return False;
        }
+
+       /*
+        * user_in_group_sid() uses create_token_from_username()
+        * which creates an artificial NT token given just a username,
+        * so this is not reliable for users from foreign domains
+        * exported by winbindd!
+        */
        return user_in_group_sid(u_name, &group_ace->trustee);
 }
 
@@ -1077,24 +1341,25 @@ static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
  type.
 ****************************************************************************/
 
-static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
-                                                       const files_struct *fsp,
-                                                       const DOM_SID *pfile_owner_sid,
-                                                       const DOM_SID *pfile_grp_sid,
-                                                       SMB_STRUCT_STAT *pst,
-                                                       BOOL setting_acl)
+static bool ensure_canon_entry_valid(canon_ace **pp_ace,
+                               const struct share_params *params,
+                               const bool is_directory,
+                               const DOM_SID *pfile_owner_sid,
+                               const DOM_SID *pfile_grp_sid,
+                               const SMB_STRUCT_STAT *pst,
+                               bool setting_acl)
 {
        canon_ace *pace;
-       BOOL got_user = False;
-       BOOL got_grp = False;
-       BOOL got_other = False;
+       bool got_user = False;
+       bool got_grp = False;
+       bool got_other = False;
        canon_ace *pace_other = NULL;
 
        for (pace = *pp_ace; pace; pace = pace->next) {
                if (pace->type == SMB_ACL_USER_OBJ) {
 
                        if (setting_acl)
-                               apply_default_perms(fsp, pace, S_IRUSR);
+                               apply_default_perms(params, is_directory, pace, S_IRUSR);
                        got_user = True;
 
                } else if (pace->type == SMB_ACL_GROUP_OBJ) {
@@ -1104,7 +1369,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                         */
 
                        if (setting_acl)
-                               apply_default_perms(fsp, pace, S_IRGRP);
+                               apply_default_perms(params, is_directory, pace, S_IRGRP);
                        got_grp = True;
 
                } else if (pace->type == SMB_ACL_OTHER) {
@@ -1114,7 +1379,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                         */
 
                        if (setting_acl)
-                               apply_default_perms(fsp, pace, S_IROTH);
+                               apply_default_perms(params, is_directory, pace, S_IROTH);
                        got_other = True;
                        pace_other = pace;
                }
@@ -1129,37 +1394,40 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                ZERO_STRUCTP(pace);
                pace->type = SMB_ACL_USER_OBJ;
                pace->owner_type = UID_ACE;
-               pace->unix_ug.uid = pst->st_uid;
+               pace->unix_ug.uid = pst->st_ex_uid;
                pace->trustee = *pfile_owner_sid;
                pace->attr = ALLOW_ACE;
+               /* Start with existing permissions, principle of least
+                  surprises for the user. */
+               pace->perms = pst->st_ex_mode;
 
                if (setting_acl) {
                        /* See if the owning user is in any of the other groups in
-                          the ACE. If so, OR in the permissions from that group. */
+                          the ACE, or if there's a matching user entry.
+                          If so, OR in the permissions from that entry. */
 
-                       BOOL group_matched = False;
                        canon_ace *pace_iter;
 
                        for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
-                               if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
+                               if (pace_iter->type == SMB_ACL_USER &&
+                                               pace_iter->unix_ug.uid == pace->unix_ug.uid) {
+                                       pace->perms |= pace_iter->perms;
+                               } else if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
                                        if (uid_entry_in_group(pace, pace_iter)) {
                                                pace->perms |= pace_iter->perms;
-                                               group_matched = True;
                                        }
                                }
                        }
 
-                       /* If we only got an "everyone" perm, just use that. */
-                       if (!group_matched) {
+                       if (pace->perms == 0) {
+                               /* If we only got an "everyone" perm, just use that. */
                                if (got_other)
                                        pace->perms = pace_other->perms;
-                               else
-                                       pace->perms = 0;
                        }
 
-                       apply_default_perms(fsp, pace, S_IRUSR);
+                       apply_default_perms(params, is_directory, pace, S_IRUSR);
                } else {
-                       pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
+                       pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRUSR, S_IWUSR, S_IXUSR);
                }
 
                DLIST_ADD(*pp_ace, pace);
@@ -1174,7 +1442,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                ZERO_STRUCTP(pace);
                pace->type = SMB_ACL_GROUP_OBJ;
                pace->owner_type = GID_ACE;
-               pace->unix_ug.uid = pst->st_gid;
+               pace->unix_ug.uid = pst->st_ex_gid;
                pace->trustee = *pfile_grp_sid;
                pace->attr = ALLOW_ACE;
                if (setting_acl) {
@@ -1183,9 +1451,9 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                                pace->perms = pace_other->perms;
                        else
                                pace->perms = 0;
-                       apply_default_perms(fsp, pace, S_IRGRP);
+                       apply_default_perms(params, is_directory, pace, S_IRGRP);
                } else {
-                       pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
+                       pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRGRP, S_IWGRP, S_IXGRP);
                }
 
                DLIST_ADD(*pp_ace, pace);
@@ -1205,9 +1473,9 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                pace->attr = ALLOW_ACE;
                if (setting_acl) {
                        pace->perms = 0;
-                       apply_default_perms(fsp, pace, S_IROTH);
+                       apply_default_perms(params, is_directory, pace, S_IROTH);
                } else
-                       pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
+                       pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IROTH, S_IWOTH, S_IXOTH);
 
                DLIST_ADD(*pp_ace, pace);
        }
@@ -1219,11 +1487,12 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
  Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
  If it does not have them, check if there are any entries where the trustee is the
  file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
+ Note we must not do this to default directory ACLs.
 ****************************************************************************/
 
 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
 {
-       BOOL got_user_obj, got_group_obj;
+       bool got_user_obj, got_group_obj;
        canon_ace *current_ace;
        int i, entries;
 
@@ -1264,18 +1533,20 @@ static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID
  Unpack a SEC_DESC into two canonical ace lists.
 ****************************************************************************/
 
-static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
-                                                       DOM_SID *pfile_owner_sid,
-                                                       DOM_SID *pfile_grp_sid,
-                                                       canon_ace **ppfile_ace, canon_ace **ppdir_ace,
-                                                       SEC_ACL *dacl)
+static bool create_canon_ace_lists(files_struct *fsp,
+                                       const SMB_STRUCT_STAT *pst,
+                                       DOM_SID *pfile_owner_sid,
+                                       DOM_SID *pfile_grp_sid,
+                                       canon_ace **ppfile_ace,
+                                       canon_ace **ppdir_ace,
+                                       const SEC_ACL *dacl)
 {
-       BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
+       bool all_aces_are_inherit_only = (fsp->is_directory ? True : False);
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
        canon_ace *current_ace = NULL;
-       BOOL got_dir_allow = False;
-       BOOL got_file_allow = False;
+       bool got_dir_allow = False;
+       bool got_file_allow = False;
        int i, j;
 
        *ppfile_ace = NULL;
@@ -1343,12 +1614,12 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
 
                                psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
                                psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
-                               
+
                        } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
 
                                psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
                                psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
-                               
+
                        }
                }
        }
@@ -1383,54 +1654,73 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                        current_ace->type = SMB_ACL_OTHER;
                } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
                        current_ace->owner_type = UID_ACE;
-                       current_ace->unix_ug.uid = pst->st_uid;
+                       current_ace->unix_ug.uid = pst->st_ex_uid;
                        current_ace->type = SMB_ACL_USER_OBJ;
 
                        /*
                         * The Creator Owner entry only specifies inheritable permissions,
                         * never access permissions. WinNT doesn't always set the ACE to
-                        *INHERIT_ONLY, though.
+                        * INHERIT_ONLY, though.
                         */
 
-                       if (nt4_compatible_acls())
-                               psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                       psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
                } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
                        current_ace->owner_type = GID_ACE;
-                       current_ace->unix_ug.gid = pst->st_gid;
+                       current_ace->unix_ug.gid = pst->st_ex_gid;
                        current_ace->type = SMB_ACL_GROUP_OBJ;
 
                        /*
                         * The Creator Group entry only specifies inheritable permissions,
                         * never access permissions. WinNT doesn't always set the ACE to
-                        *INHERIT_ONLY, though.
+                        * INHERIT_ONLY, though.
                         */
-                       if (nt4_compatible_acls())
-                               psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                       psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
 
                } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
                        current_ace->owner_type = UID_ACE;
-                       current_ace->type = SMB_ACL_USER;
+                       /* If it's the owning user, this is a user_obj, not
+                        * a user. */
+                       if (current_ace->unix_ug.uid == pst->st_ex_uid) {
+                               current_ace->type = SMB_ACL_USER_OBJ;
+                       } else {
+                               current_ace->type = SMB_ACL_USER;
+                       }
                } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
                        current_ace->owner_type = GID_ACE;
-                       current_ace->type = SMB_ACL_GROUP;
+                       /* If it's the primary group, this is a group_obj, not
+                        * a group. */
+                       if (current_ace->unix_ug.gid == pst->st_ex_gid) {
+                               current_ace->type = SMB_ACL_GROUP_OBJ;
+                       } else {
+                               current_ace->type = SMB_ACL_GROUP;
+                       }
                } else {
-                       fstring str;
-
                        /*
                         * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
                         */
 
                        if (non_mappable_sid(&psa->trustee)) {
-                               DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
-                                       sid_to_string(str, &psa->trustee) ));
+                               DEBUG(10, ("create_canon_ace_lists: ignoring "
+                                          "non-mappable SID %s\n",
+                                          sid_string_dbg(&psa->trustee)));
                                SAFE_FREE(current_ace);
                                continue;
                        }
 
+                       if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
+                               DEBUG(10, ("create_canon_ace_lists: ignoring "
+                                       "unknown or foreign SID %s\n",
+                                       sid_string_dbg(&psa->trustee)));
+                                       SAFE_FREE(current_ace);
+                               continue;
+                       }
+
                        free_canon_ace_list(file_ace);
                        free_canon_ace_list(dir_ace);
-                       DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
-                               sid_to_string(str, &current_ace->trustee) ));
+                       DEBUG(0, ("create_canon_ace_lists: unable to map SID "
+                                 "%s to uid or gid.\n",
+                                 sid_string_dbg(&current_ace->trustee)));
                        SAFE_FREE(current_ace);
                        return False;
                }
@@ -1442,7 +1732,9 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
 
                current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
                current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
-               current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
+
+               /* Store the ace_flag. */
+               current_ace->ace_flags = psa->flags;
 
                /*
                 * Now add the created ace to either the file list, the directory
@@ -1460,6 +1752,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                        if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
                                (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
 
+                               canon_ace *current_dir_ace = current_ace;
                                DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
 
                                /*
@@ -1471,8 +1764,12 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                                        got_dir_allow = True;
 
                                if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
-                                       DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
-Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+                                       DEBUG(0,("create_canon_ace_lists: "
+                                                "malformed ACL in "
+                                                "inheritable ACL! Deny entry "
+                                                "after Allow entry. Failing "
+                                                "to set on file %s.\n",
+                                                fsp_str_dbg(fsp)));
                                        free_canon_ace_list(file_ace);
                                        free_canon_ace_list(dir_ace);
                                        return False;
@@ -1503,6 +1800,13 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                                         * pointer is now owned by the dir_ace list.
                                         */
                                        current_ace = dup_ace;
+                                       /* We've essentially split this ace into two,
+                                        * and added the ace with inheritance request
+                                        * bits to the directory ACL. Drop those bits for
+                                        * the ACE we're adding to the file list. */
+                                       current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
+                                                               SEC_ACE_FLAG_CONTAINER_INHERIT|
+                                                               SEC_ACE_FLAG_INHERIT_ONLY);
                                } else {
                                        /*
                                         * We must not free current_ace here as its
@@ -1510,6 +1814,43 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                                         */
                                        current_ace = NULL;
                                }
+
+                               /*
+                                * current_ace is now either owned by file_ace
+                                * or is NULL. We can safely operate on current_dir_ace
+                                * to treat mapping for default acl entries differently
+                                * than access acl entries.
+                                */
+
+                               if (current_dir_ace->owner_type == UID_ACE) {
+                                       /*
+                                        * We already decided above this is a uid,
+                                        * for default acls ace's only CREATOR_OWNER
+                                        * maps to ACL_USER_OBJ. All other uid
+                                        * ace's are ACL_USER.
+                                        */
+                                       if (sid_equal(&current_dir_ace->trustee,
+                                                       &global_sid_Creator_Owner)) {
+                                               current_dir_ace->type = SMB_ACL_USER_OBJ;
+                                       } else {
+                                               current_dir_ace->type = SMB_ACL_USER;
+                                       }
+                               }
+
+                               if (current_dir_ace->owner_type == GID_ACE) {
+                                       /*
+                                        * We already decided above this is a gid,
+                                        * for default acls ace's only CREATOR_GROUP
+                                        * maps to ACL_GROUP_OBJ. All other uid
+                                        * ace's are ACL_GROUP.
+                                        */
+                                       if (sid_equal(&current_dir_ace->trustee,
+                                                       &global_sid_Creator_Group)) {
+                                               current_dir_ace->type = SMB_ACL_GROUP_OBJ;
+                                       } else {
+                                               current_dir_ace->type = SMB_ACL_GROUP;
+                                       }
+                               }
                        }
                }
 
@@ -1529,8 +1870,10 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                                got_file_allow = True;
 
                        if ((current_ace->attr == DENY_ACE) && got_file_allow) {
-                               DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
-Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+                               DEBUG(0,("create_canon_ace_lists: malformed "
+                                        "ACL in file ACL ! Deny entry after "
+                                        "Allow entry. Failing to set on file "
+                                        "%s.\n", fsp_str_dbg(fsp)));
                                free_canon_ace_list(file_ace);
                                free_canon_ace_list(dir_ace);
                                return False;
@@ -1569,17 +1912,15 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                dir_ace = NULL;
        } else {
                /*
-                * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
-                * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
-                * entries can be converted to *_OBJ. Usually we will already have these
-                * entries in the Default ACL, and the Access ACL will not have them.
+                * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in
+                * the file ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
+                * entries can be converted to *_OBJ. Don't do this for the default
+                * ACL, we will create them separately for this if needed inside
+                * ensure_canon_entry_valid().
                 */
                if (file_ace) {
                        check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
                }
-               if (dir_ace) {
-                       check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
-               }
        }
 
        *ppfile_ace = file_ace;
@@ -1900,51 +2241,19 @@ static void process_deny_list( canon_ace **pp_ace_list )
        *pp_ace_list = ace_list;
 }
 
-/****************************************************************************
- Create a default mode that will be used if a security descriptor entry has
- no user/group/world entries.
-****************************************************************************/
-
-static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
-{
-       int snum = SNUM(fsp->conn);
-       mode_t and_bits = (mode_t)0;
-       mode_t or_bits = (mode_t)0;
-       mode_t mode = interitable_mode
-               ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name,
-                            NULL )
-               : S_IRUSR;
-
-       if (fsp->is_directory)
-               mode |= (S_IWUSR|S_IXUSR);
-
-       /*
-        * Now AND with the create mode/directory mode bits then OR with the
-        * force create mode/force directory mode bits.
-        */
-
-       if (fsp->is_directory) {
-               and_bits = lp_dir_security_mask(snum);
-               or_bits = lp_force_dir_security_mode(snum);
-       } else {
-               and_bits = lp_security_mask(snum);
-               or_bits = lp_force_security_mode(snum);
-       }
-
-       return ((mode & and_bits)|or_bits);
-}
-
 /****************************************************************************
  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
  succeeding.
 ****************************************************************************/
 
-static BOOL unpack_canon_ace(files_struct *fsp, 
-                                                       SMB_STRUCT_STAT *pst,
-                                                       DOM_SID *pfile_owner_sid,
-                                                       DOM_SID *pfile_grp_sid,
-                                                       canon_ace **ppfile_ace, canon_ace **ppdir_ace,
-                                                       uint32 security_info_sent, SEC_DESC *psd)
+static bool unpack_canon_ace(files_struct *fsp,
+                               const SMB_STRUCT_STAT *pst,
+                               DOM_SID *pfile_owner_sid,
+                               DOM_SID *pfile_grp_sid,
+                               canon_ace **ppfile_ace,
+                               canon_ace **ppdir_ace,
+                               uint32 security_info_sent,
+                               const SEC_DESC *psd)
 {
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
@@ -1985,10 +2294,10 @@ static BOOL unpack_canon_ace(files_struct *fsp,
         */
 
        print_canon_ace_list( "file ace - before merge", file_ace);
-       merge_aces( &file_ace );
+       merge_aces( &file_ace, false);
 
        print_canon_ace_list( "dir ace - before merge", dir_ace);
-       merge_aces( &dir_ace );
+       merge_aces( &dir_ace, true);
 
        /*
         * NT ACLs are order dependent. Go through the acl lists and
@@ -2009,14 +2318,8 @@ static BOOL unpack_canon_ace(files_struct *fsp,
 
        print_canon_ace_list( "file ace - before valid", file_ace);
 
-       /*
-        * A default 3 element mode entry for a file should be r-- --- ---.
-        * A default 3 element mode entry for a directory should be rwx --- ---.
-        */
-
-       pst->st_mode = create_default_mode(fsp, False);
-
-       if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+       if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2024,15 +2327,8 @@ static BOOL unpack_canon_ace(files_struct *fsp,
 
        print_canon_ace_list( "dir ace - before valid", dir_ace);
 
-       /*
-        * A default inheritable 3 element mode entry for a directory should be the
-        * mode Samba will use to create a file within. Ensure user rwx bits are set if
-        * it's a directory.
-        */
-
-       pst->st_mode = create_default_mode(fsp, True);
-
-       if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+       if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2066,14 +2362,14 @@ static BOOL unpack_canon_ace(files_struct *fsp,
  But NT cannot display this in their ACL editor !
 ********************************************************************************/
 
-static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
+static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
 {
-       canon_ace *list_head = *pp_list_head;
+       canon_ace *l_head = *pp_list_head;
        canon_ace *owner_ace = NULL;
        canon_ace *other_ace = NULL;
        canon_ace *ace = NULL;
 
-       for (ace = list_head; ace; ace = ace->next) {
+       for (ace = l_head; ace; ace = ace->next) {
                if (ace->type == SMB_ACL_USER_OBJ)
                        owner_ace = ace;
                else if (ace->type == SMB_ACL_OTHER) {
@@ -2081,7 +2377,7 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
                        other_ace = ace;
                }
        }
-               
+
        if (!owner_ace || !other_ace) {
                DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
                        filename ));
@@ -2094,28 +2390,29 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
         */
 
        if (owner_ace) {
-               DLIST_PROMOTE(list_head, owner_ace);
+               DLIST_PROMOTE(l_head, owner_ace);
        }
 
        if (other_ace) {
-               DLIST_DEMOTE(list_head, other_ace, canon_ace *);
+               DLIST_DEMOTE(l_head, other_ace, canon_ace *);
        }
 
        /* We have probably changed the head of the list. */
 
-       *pp_list_head = list_head;
+       *pp_list_head = l_head;
 }
-               
+
 /****************************************************************************
  Create a linked list of canonical ACE entries.
 ****************************************************************************/
 
-static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
-                                       const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
+static canon_ace *canonicalise_acl(struct connection_struct *conn,
+                                  const char *fname, SMB_ACL_T posix_acl,
+                                  const SMB_STRUCT_STAT *psbuf,
+                                  const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
 {
-       connection_struct *conn = fsp->conn;
        mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
-       canon_ace *list_head = NULL;
+       canon_ace *l_head = NULL;
        canon_ace *ace = NULL;
        canon_ace *next_ace = NULL;
        int entry_id = SMB_ACL_FIRST_ENTRY;
@@ -2129,9 +2426,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                posix_id unix_ug;
                enum ace_owner owner_type;
 
-               /* get_next... */
-               if (entry_id == SMB_ACL_FIRST_ENTRY)
-                       entry_id = SMB_ACL_NEXT_ENTRY;
+               entry_id = SMB_ACL_NEXT_ENTRY;
 
                /* Is this a MASK entry ? */
                if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
@@ -2145,7 +2440,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                        case SMB_ACL_USER_OBJ:
                                /* Get the SID from the owner. */
                                sid_copy(&sid, powner);
-                               unix_ug.uid = psbuf->st_uid;
+                               unix_ug.uid = psbuf->st_ex_uid;
                                owner_type = UID_ACE;
                                break;
                        case SMB_ACL_USER:
@@ -2155,17 +2450,6 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                                                DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
                                                continue;
                                        }
-                                       /*
-                                        * A SMB_ACL_USER entry for the owner is shadowed by the
-                                        * SMB_ACL_USER_OBJ entry and Windows also cannot represent
-                                        * that entry, so we ignore it. We also don't create such
-                                        * entries out of the blue when setting ACLs, so a get/set
-                                        * cycle will drop them.
-                                        */
-                                       if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
-                                               SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
-                                               continue;
-                                       }
                                        uid_to_sid( &sid, *puid);
                                        unix_ug.uid = *puid;
                                        owner_type = UID_ACE;
@@ -2175,7 +2459,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                        case SMB_ACL_GROUP_OBJ:
                                /* Get the SID from the owning group. */
                                sid_copy(&sid, pgroup);
-                               unix_ug.gid = psbuf->st_gid;
+                               unix_ug.gid = psbuf->st_ex_gid;
                                owner_type = GID_ACE;
                                break;
                        case SMB_ACL_GROUP:
@@ -2219,16 +2503,18 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                ace->trustee = sid;
                ace->unix_ug = unix_ug;
                ace->owner_type = owner_type;
-               ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
+               ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
 
-               DLIST_ADD(list_head, ace);
+               DLIST_ADD(l_head, ace);
        }
 
        /*
         * This next call will ensure we have at least a user/group/world set.
         */
 
-       if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
+       if (!ensure_canon_entry_valid(&l_head, conn->params,
+                                     S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
+                                     psbuf, False))
                goto fail;
 
        /*
@@ -2238,7 +2524,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
 
        DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
 
-       for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
+       for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
                next_ace = ace->next;
 
                /* Masks are only applied to entries other than USER_OBJ and OTHER. */
@@ -2246,7 +2532,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                        ace->perms &= acl_mask;
 
                if (ace->perms == 0) {
-                       DLIST_PROMOTE(list_head, ace);
+                       DLIST_PROMOTE(l_head, ace);
                }
 
                if( DEBUGLVL( 10 ) ) {
@@ -2254,15 +2540,15 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
                }
        }
 
-       arrange_posix_perms(fsp->fsp_name,&list_head );
+       arrange_posix_perms(fname,&l_head );
 
-       print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
+       print_canon_ace_list( "canonicalise_acl: ace entries after arrange", l_head );
 
-       return list_head;
+       return l_head;
 
   fail:
 
-       free_canon_ace_list(list_head);
+       free_canon_ace_list(l_head);
        return NULL;
 }
 
@@ -2270,7 +2556,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl
  Check if the current user group list contains a given group.
 ****************************************************************************/
 
-static BOOL current_user_in_group(gid_t gid)
+bool current_user_in_group(gid_t gid)
 {
        int i;
 
@@ -2284,40 +2570,58 @@ static BOOL current_user_in_group(gid_t gid)
 }
 
 /****************************************************************************
- Should we override a deny ?  Check deprecated 'acl group control'
- and 'dos filemode'
+ Should we override a deny ? Check 'acl group control' and 'dos filemode'.
 ****************************************************************************/
 
-static BOOL acl_group_override(connection_struct *conn, gid_t prim_gid)
+static bool acl_group_override(connection_struct *conn,
+                              const struct smb_filename *smb_fname)
 {
-       if ( (errno == EACCES || errno == EPERM) 
-               && (lp_acl_group_control(SNUM(conn)) || lp_dos_filemode(SNUM(conn)))
-               && current_user_in_group(prim_gid)) 
-       {
-               return True;
-       } 
+       if ((errno != EPERM) && (errno != EACCES)) {
+               return false;
+       }
 
-       return False;
+       /* file primary group == user primary or supplementary group */
+       if (lp_acl_group_control(SNUM(conn)) &&
+           current_user_in_group(smb_fname->st.st_ex_gid)) {
+               return true;
+       }
+
+       /* user has writeable permission */
+       if (lp_dos_filemode(SNUM(conn)) &&
+           can_write_to_file(conn, smb_fname)) {
+               return true;
+       }
+
+       return false;
 }
 
 /****************************************************************************
  Attempt to apply an ACL to a file or directory.
 ****************************************************************************/
 
-static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, gid_t prim_gid, BOOL *pacl_set_support)
+static bool set_canon_ace_list(files_struct *fsp,
+                               canon_ace *the_ace,
+                               bool default_ace,
+                               const SMB_STRUCT_STAT *psbuf,
+                               bool *pacl_set_support)
 {
        connection_struct *conn = fsp->conn;
-       BOOL ret = False;
+       bool ret = False;
        SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
        canon_ace *p_ace;
        int i;
        SMB_ACL_ENTRY_T mask_entry;
-       BOOL got_mask_entry = False;
+       bool got_mask_entry = False;
        SMB_ACL_PERMSET_T mask_permset;
        SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
-       BOOL needs_mask = False;
+       bool needs_mask = False;
        mode_t mask_perms = 0;
 
+       /* Use the psbuf that was passed in. */
+       if (psbuf != &fsp->fsp_name->st) {
+               fsp->fsp_name->st = *psbuf;
+       }
+
 #if defined(POSIX_ACL_NEEDS_MASK)
        /* HP-UX always wants to have a mask (called "class" there). */
        needs_mask = True;
@@ -2334,7 +2638,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
                                default_ace ? "default" : "file", strerror(errno) ));
                }
                *pacl_set_support = False;
-               return False;
+               goto fail;
        }
 
        if( DEBUGLVL( 10 )) {
@@ -2474,7 +2778,8 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
         */
 
        if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
-               if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
+               if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name->base_name,
+                                            the_acl_type, the_acl) == -1) {
                        /*
                         * Some systems allow all the above calls and only fail with no ACL support
                         * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
@@ -2483,14 +2788,18 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
                                *pacl_set_support = False;
                        }
 
-                       if (acl_group_override(conn, prim_gid)) {
+                       if (acl_group_override(conn, fsp->fsp_name)) {
                                int sret;
 
-                               DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
-                                       fsp->fsp_name ));
+                               DEBUG(5,("set_canon_ace_list: acl group "
+                                        "control on and current user in file "
+                                        "%s primary group.\n",
+                                        fsp_str_dbg(fsp)));
 
                                become_root();
-                               sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl);
+                               sret = SMB_VFS_SYS_ACL_SET_FILE(conn,
+                                   fsp->fsp_name->base_name, the_acl_type,
+                                   the_acl);
                                unbecome_root();
                                if (sret == 0) {
                                        ret = True;     
@@ -2498,14 +2807,17 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
                        }
 
                        if (ret == False) {
-                               DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
-                                               the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
-                                               fsp->fsp_name, strerror(errno) ));
+                               DEBUG(2,("set_canon_ace_list: "
+                                        "sys_acl_set_file type %s failed for "
+                                        "file %s (%s).\n",
+                                        the_acl_type == SMB_ACL_TYPE_DEFAULT ?
+                                        "directory default" : "file",
+                                        fsp_str_dbg(fsp), strerror(errno)));
                                goto fail;
                        }
                }
        } else {
-               if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl) == -1) {
+               if (SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl) == -1) {
                        /*
                         * Some systems allow all the above calls and only fail with no ACL support
                         * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
@@ -2514,14 +2826,16 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
                                *pacl_set_support = False;
                        }
 
-                       if (acl_group_override(conn, prim_gid)) {
+                       if (acl_group_override(conn, fsp->fsp_name)) {
                                int sret;
 
-                               DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
-                                       fsp->fsp_name ));
+                               DEBUG(5,("set_canon_ace_list: acl group "
+                                        "control on and current user in file "
+                                        "%s primary group.\n",
+                                        fsp_str_dbg(fsp)));
 
                                become_root();
-                               sret = SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl);
+                               sret = SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl);
                                unbecome_root();
                                if (sret == 0) {
                                        ret = True;
@@ -2529,8 +2843,10 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
                        }
 
                        if (ret == False) {
-                               DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
-                                               fsp->fsp_name, strerror(errno) ));
+                               DEBUG(2,("set_canon_ace_list: "
+                                        "sys_acl_set_file failed for file %s "
+                                        "(%s).\n",
+                                        fsp_str_dbg(fsp), strerror(errno)));
                                goto fail;
                        }
                }
@@ -2586,7 +2902,7 @@ SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
 
 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
 
-static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
+static bool convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
 {
        int snum = SNUM(fsp->conn);
        size_t ace_count = count_canon_ace_list(file_ace_list);
@@ -2598,8 +2914,9 @@ static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file
        mode_t or_bits;
 
        if (ace_count != 3) {
-               DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
-posix perms.\n", fsp->fsp_name ));
+               DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE "
+                        "entries for file %s to convert to posix perms.\n",
+                        fsp_str_dbg(fsp)));
                return False;
        }
 
@@ -2613,8 +2930,8 @@ posix perms.\n", fsp->fsp_name ));
        }
 
        if (!owner_ace || !group_ace || !other_ace) {
-               DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
-                               fsp->fsp_name ));
+               DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get "
+                        "standard entries for file %s.\n", fsp_str_dbg(fsp)));
                return False;
        }
 
@@ -2648,9 +2965,10 @@ posix perms.\n", fsp->fsp_name ));
 
        *posix_perms = (((*posix_perms) & and_bits)|or_bits);
 
-       DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
-               (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
-               fsp->fsp_name ));
+       DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o "
+                 "to perm=0%o for file %s.\n", (int)owner_ace->perms,
+                 (int)group_ace->perms, (int)other_ace->perms,
+                 (int)*posix_perms, fsp_str_dbg(fsp)));
 
        return True;
 }
@@ -2670,8 +2988,8 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
                for (j = i+1; j < num_aces; j++) {
                        uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
                        uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
-                       BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
-                       BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
+                       bool i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
+                       bool j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
 
                        /* We know the lower number ACE's are file entries. */
                        if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
@@ -2721,6 +3039,43 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
 
        return num_aces;
 }
+
+/*
+ * Add or Replace ACE entry.
+ * In some cases we need to add a specific ACE for compatibility reasons.
+ * When doing that we must make sure we are not actually creating a duplicate
+ * entry. So we need to search whether an ACE entry already exist and eventually
+ * replacce the access mask, or add a completely new entry if none was found.
+ *
+ * This function assumes the array has enough space to add a new entry without
+ * any reallocation of memory.
+ */
+
+static void add_or_replace_ace(SEC_ACE *nt_ace_list, size_t *num_aces,
+                               const DOM_SID *sid, enum security_ace_type type,
+                               uint32_t mask, uint8_t flags)
+{
+       int i;
+
+       /* first search for a duplicate */
+       for (i = 0; i < *num_aces; i++) {
+               if (sid_equal(&nt_ace_list[i].trustee, sid) &&
+                   (nt_ace_list[i].flags == flags)) break;
+       }
+
+       if (i < *num_aces) { /* found */
+               nt_ace_list[i].type = type;
+               nt_ace_list[i].access_mask = mask;
+               DEBUG(10, ("Replacing ACE %d with SID %s and flags %02x\n",
+                          i, sid_string_dbg(sid), flags));
+               return;
+       }
+
+       /* not found, append it */
+       init_sec_ace(&nt_ace_list[(*num_aces)++], sid, type, mask, flags);
+}
+
+
 /****************************************************************************
  Reply to query a security descriptor from an fsp. If it succeeds it allocates
  the space for the return elements and returns the size needed to return the
@@ -2728,11 +3083,15 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
  the UNIX style get ACL.
 ****************************************************************************/
 
-size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
+static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
+                                     const char *name,
+                                     const SMB_STRUCT_STAT *sbuf,
+                                     struct pai_val *pal,
+                                     SMB_ACL_T posix_acl,
+                                     SMB_ACL_T def_acl,
+                                     uint32_t security_info,
+                                     SEC_DESC **ppdesc)
 {
-       connection_struct *conn = fsp->conn;
-       SMB_STRUCT_STAT sbuf;
-       SEC_ACE *nt_ace_list = NULL;
        DOM_SID owner_sid;
        DOM_SID group_sid;
        size_t sd_size = 0;
@@ -2740,68 +3099,26 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
        size_t num_acls = 0;
        size_t num_def_acls = 0;
        size_t num_aces = 0;
-       SMB_ACL_T posix_acl = NULL;
-       SMB_ACL_T def_acl = NULL;
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
+       SEC_ACE *nt_ace_list = NULL;
        size_t num_profile_acls = 0;
-       struct pai_val *pal = NULL;
+       DOM_SID orig_owner_sid;
        SEC_DESC *psd = NULL;
-
-       *ppdesc = NULL;
-
-       DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
-
-       if(fsp->is_directory || fsp->fh->fd == -1) {
-
-               /* Get the stat struct for the owner info. */
-               if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
-                       return 0;
-               }
-               /*
-                * Get the ACL from the path.
-                */
-
-               posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
-
-               /*
-                * If it's a directory get the default POSIX ACL.
-                */
-
-               if(fsp->is_directory) {
-                       def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
-                       def_acl = free_empty_sys_acl(conn, def_acl);
-               }
-
-       } else {
-
-               /* Get the stat struct for the owner info. */
-               if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) {
-                       return 0;
-               }
-               /*
-                * Get the ACL from the fd.
-                */
-               posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
-       }
-
-       DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
-                       posix_acl ? "present" :  "absent",
-                       def_acl ? "present" :  "absent" ));
-
-       pal = load_inherited_info(fsp);
+       int i;
 
        /*
         * Get the owner, group and world SIDs.
         */
 
+       create_file_sids(sbuf, &owner_sid, &group_sid);
+
        if (lp_profile_acls(SNUM(conn))) {
                /* For WXP SP1 the owner must be administrators. */
+               sid_copy(&orig_owner_sid, &owner_sid);
                sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
                sid_copy(&group_sid, &global_sid_Builtin_Users);
-               num_profile_acls = 2;
-       } else {
-               create_file_sids(&sbuf, &owner_sid, &group_sid);
+               num_profile_acls = 3;
        }
 
        if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
@@ -2816,19 +3133,23 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
                 */
 
                /* Create the canon_ace lists. */
-               file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
+               file_ace = canonicalise_acl(conn, name, posix_acl, sbuf,
+                                           &owner_sid, &group_sid, pal,
+                                           SMB_ACL_TYPE_ACCESS);
 
                /* We must have *some* ACLS. */
        
                if (count_canon_ace_list(file_ace) == 0) {
-                       DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
+                       DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", name));
                        goto done;
                }
 
-               if (fsp->is_directory && def_acl) {
-                       dir_ace = canonicalise_acl(fsp, def_acl, &sbuf,
-                                       &global_sid_Creator_Owner,
-                                       &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
+               if (S_ISDIR(sbuf->st_ex_mode) && def_acl) {
+                       dir_ace = canonicalise_acl(conn, name, def_acl,
+                                                  sbuf,
+                                                  &global_sid_Creator_Owner,
+                                                  &global_sid_Creator_Group,
+                                                  pal, SMB_ACL_TYPE_DEFAULT);
                }
 
                /*
@@ -2837,8 +3158,7 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
 
                {
                        canon_ace *ace;
-                       int nt_acl_type;
-                       int i;
+                       enum security_ace_type nt_acl_type;
 
                        if (nt4_compatible_acls() && dir_ace) {
                                /*
@@ -2904,64 +3224,52 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
                         * Create the NT ACE list from the canonical ace lists.
                         */
 
-                       ace = file_ace;
-
-                       for (i = 0; i < num_acls; i++, ace = ace->next) {
-                               SEC_ACCESS acc;
-
-                               acc = map_canon_ace_perms(SNUM(conn),
+                       for (ace = file_ace; ace != NULL; ace = ace->next) {
+                               uint32_t acc = map_canon_ace_perms(SNUM(conn),
                                                &nt_acl_type,
                                                ace->perms,
-                                               fsp->is_directory);
+                                               S_ISDIR(sbuf->st_ex_mode));
                                init_sec_ace(&nt_ace_list[num_aces++],
                                        &ace->trustee,
                                        nt_acl_type,
                                        acc,
-                                       ace->inherited ?
-                                               SEC_ACE_FLAG_INHERITED_ACE : 0);
+                                       ace->ace_flags);
                        }
 
                        /* The User must have access to a profile share - even
                         * if we can't map the SID. */
                        if (lp_profile_acls(SNUM(conn))) {
-                               SEC_ACCESS acc;
-
-                               init_sec_access(&acc,FILE_GENERIC_ALL);
-                               init_sec_ace(&nt_ace_list[num_aces++],
-                                               &global_sid_Builtin_Users,
-                                               SEC_ACE_TYPE_ACCESS_ALLOWED,
-                                               acc, 0);
+                               add_or_replace_ace(nt_ace_list, &num_aces,
+                                                  &global_sid_Builtin_Users,
+                                                  SEC_ACE_TYPE_ACCESS_ALLOWED,
+                                                  FILE_GENERIC_ALL, 0);
                        }
 
-                       ace = dir_ace;
-
-                       for (i = 0; i < num_def_acls; i++, ace = ace->next) {
-                               SEC_ACCESS acc;
-
-                               acc = map_canon_ace_perms(SNUM(conn),
+                       for (ace = dir_ace; ace != NULL; ace = ace->next) {
+                               uint32_t acc = map_canon_ace_perms(SNUM(conn),
                                                &nt_acl_type,
                                                ace->perms,
-                                               fsp->is_directory);
+                                               S_ISDIR(sbuf->st_ex_mode));
                                init_sec_ace(&nt_ace_list[num_aces++],
                                        &ace->trustee,
                                        nt_acl_type,
                                        acc,
+                                       ace->ace_flags |
                                        SEC_ACE_FLAG_OBJECT_INHERIT|
                                        SEC_ACE_FLAG_CONTAINER_INHERIT|
-                                       SEC_ACE_FLAG_INHERIT_ONLY|
-                                       (ace->inherited ?
-                                          SEC_ACE_FLAG_INHERITED_ACE : 0));
+                                       SEC_ACE_FLAG_INHERIT_ONLY);
                        }
 
                        /* The User must have access to a profile share - even
                         * if we can't map the SID. */
                        if (lp_profile_acls(SNUM(conn))) {
-                               SEC_ACCESS acc;
-
-                               init_sec_access(&acc,FILE_GENERIC_ALL);
-                               init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
-                                               SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
-                                               SEC_ACE_FLAG_INHERIT_ONLY|0);
+                               add_or_replace_ace(nt_ace_list, &num_aces,
+                                               &global_sid_Builtin_Users,
+                                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                                               FILE_GENERIC_ALL,
+                                               SEC_ACE_FLAG_OBJECT_INHERIT |
+                                               SEC_ACE_FLAG_CONTAINER_INHERIT |
+                                               SEC_ACE_FLAG_INHERIT_ONLY);
                        }
 
                        /*
@@ -2972,6 +3280,18 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
 
                        num_aces = merge_default_aces(nt_ace_list, num_aces);
 
+                       if (lp_profile_acls(SNUM(conn))) {
+                               for (i = 0; i < num_aces; i++) {
+                                       if (sid_equal(&nt_ace_list[i].trustee, &owner_sid)) {
+                                               add_or_replace_ace(nt_ace_list, &num_aces,
+                                                                  &orig_owner_sid,
+                                                                  nt_ace_list[i].type,
+                                                                  nt_ace_list[i].access_mask,
+                                                                  nt_ace_list[i].flags);
+                                               break;
+                                       }
+                               }
+                       }
                }
 
                if (num_aces) {
@@ -3004,8 +3324,10 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
         * flag doesn't seem to bother Windows NT.
         * Always set this if map acl inherit is turned off.
         */
-       if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
-               psd->type |= SE_DESC_DACL_PROTECTED;
+       if (pal == NULL || !lp_map_acl_inherit(SNUM(conn))) {
+               psd->type |= SEC_DESC_DACL_PROTECTED;
+       } else {
+               psd->type |= pal->sd_type;
        }
 
        if (psd->dacl) {
@@ -3027,7 +3349,83 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
        free_inherited_info(pal);
        SAFE_FREE(nt_ace_list);
 
-       return sd_size;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
+                          SEC_DESC **ppdesc)
+{
+       SMB_STRUCT_STAT sbuf;
+       SMB_ACL_T posix_acl = NULL;
+       struct pai_val *pal;
+
+       *ppdesc = NULL;
+
+       DEBUG(10,("posix_fget_nt_acl: called for file %s\n",
+                 fsp_str_dbg(fsp)));
+
+       /* can it happen that fsp_name == NULL ? */
+       if (fsp->is_directory ||  fsp->fh->fd == -1) {
+               return posix_get_nt_acl(fsp->conn, fsp->fsp_name->base_name,
+                                       security_info, ppdesc);
+       }
+
+       /* Get the stat struct for the owner info. */
+       if(SMB_VFS_FSTAT(fsp, &sbuf) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       /* Get the ACL from the fd. */
+       posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
+
+       pal = fload_inherited_info(fsp);
+
+       return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name->base_name,
+                                      &sbuf, pal, posix_acl, NULL,
+                                      security_info, ppdesc);
+}
+
+NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
+                         uint32_t security_info, SEC_DESC **ppdesc)
+{
+       SMB_ACL_T posix_acl = NULL;
+       SMB_ACL_T def_acl = NULL;
+       struct pai_val *pal;
+       struct smb_filename smb_fname;
+       int ret;
+
+       *ppdesc = NULL;
+
+       DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
+
+       ZERO_STRUCT(smb_fname);
+       smb_fname.base_name = discard_const_p(char, name);
+
+       /* Get the stat struct for the owner info. */
+       if (lp_posix_pathnames()) {
+               ret = SMB_VFS_LSTAT(conn, &smb_fname);
+       } else {
+               ret = SMB_VFS_STAT(conn, &smb_fname);
+       }
+
+       if (ret == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       /* Get the ACL from the path. */
+       posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
+
+       /* If it's a directory get the default POSIX ACL. */
+       if(S_ISDIR(smb_fname.st.st_ex_mode)) {
+               def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
+               def_acl = free_empty_sys_acl(conn, def_acl);
+       }
+
+       pal = load_inherited_info(conn, name);
+
+       return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
+                                      posix_acl, def_acl, security_info,
+                                      ppdesc);
 }
 
 /****************************************************************************
@@ -3040,11 +3438,11 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
      then allow chown to the currently authenticated user.
 ****************************************************************************/
 
-int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
+int try_chown(connection_struct *conn, struct smb_filename *smb_fname,
+             uid_t uid, gid_t gid)
 {
        int ret;
        files_struct *fsp;
-       SMB_STRUCT_STAT st;
 
        if(!CAN_WRITE(conn)) {
                return -1;
@@ -3052,16 +3450,21 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
 
        /* Case (1). */
        /* try the direct way first */
-       ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
+       if (lp_posix_pathnames()) {
+               ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid, gid);
+       } else {
+               ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid, gid);
+       }
+
        if (ret == 0)
                return 0;
 
        /* Case (2) / (3) */
        if (lp_enable_privileges()) {
 
-               BOOL has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
+               bool has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
                                                              &se_take_ownership);
-               BOOL has_restore_priv = user_has_privileges(current_user.nt_user_token,
+               bool has_restore_priv = user_has_privileges(current_user.nt_user_token,
                                                       &se_restore);
 
                /* Case (2) */
@@ -3071,7 +3474,13 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
 
                        become_root();
                        /* Keep the current file gid the same - take ownership doesn't imply group change. */
-                       ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1);
+                       if (lp_posix_pathnames()) {
+                               ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid,
+                                                   (gid_t)-1);
+                       } else {
+                               ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid,
+                                                   (gid_t)-1);
+                       }
                        unbecome_root();
                        return ret;
                }
@@ -3083,161 +3492,139 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
                return -1;
        }
 
-       if (SMB_VFS_STAT(conn,fname,&st)) {
+       /* only allow chown to the current user. This is more secure,
+          and also copes with the case where the SID in a take ownership ACL is
+          a local SID on the users workstation
+       */
+       if (uid != current_user.ut.uid) {
+               errno = EPERM;
                return -1;
        }
 
-       if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,&st,&fsp))) {
+       if (lp_posix_pathnames()) {
+               ret = SMB_VFS_LSTAT(conn, smb_fname);
+       } else {
+               ret = SMB_VFS_STAT(conn, smb_fname);
+       }
+
+       if (ret == -1) {
                return -1;
        }
 
-       /* only allow chown to the current user. This is more secure,
-          and also copes with the case where the SID in a take ownership ACL is
-          a local SID on the users workstation 
-       */
-       uid = current_user.ut.uid;
+       if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname, &fsp))) {
+               return -1;
+       }
 
        become_root();
        /* Keep the current file gid the same. */
-       ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, uid, (gid_t)-1);
+       if (fsp->fh->fd == -1) {
+               if (lp_posix_pathnames()) {
+                       ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid,
+                                           (gid_t)-1);
+               } else {
+                       ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid,
+                                           (gid_t)-1);
+               }
+       } else {
+               ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+       }
        unbecome_root();
 
-       close_file_fchmod(fsp);
+       close_file(NULL, fsp, NORMAL_CLOSE);
 
        return ret;
 }
 
-static NTSTATUS append_ugw_ace(files_struct *fsp,
-                       SMB_STRUCT_STAT *psbuf,
-                       mode_t unx_mode,
-                       int ugw,
-                       SEC_ACE *se)
-{
-       mode_t perms;
-       SEC_ACCESS acc;
-       int nt_acl_type;
-       DOM_SID trustee;
-
-       switch (ugw) {
-               case S_IRUSR:
-                       perms = unix_perms_to_acl_perms(unx_mode,
-                                                       S_IRUSR,
-                                                       S_IWUSR,
-                                                       S_IXUSR);
-                       uid_to_sid(&trustee, psbuf->st_uid );
-                       break;
-               case S_IRGRP:
-                       perms = unix_perms_to_acl_perms(unx_mode,
-                                                       S_IRGRP,
-                                                       S_IWGRP,
-                                                       S_IXGRP);
-                       gid_to_sid(&trustee, psbuf->st_gid );
-                       break;
-               case S_IROTH:
-                       perms = unix_perms_to_acl_perms(unx_mode,
-                                                       S_IROTH,
-                                                       S_IWOTH,
-                                                       S_IXOTH);
-                       sid_copy(&trustee, &global_sid_World);
-                       break;
-               default:
-                       return NT_STATUS_INVALID_PARAMETER;
-       }
-       acc = map_canon_ace_perms(SNUM(fsp->conn),
-                               &nt_acl_type,
-                               perms,
-                               fsp->is_directory);
-
-       init_sec_ace(se,
-               &trustee,
-               nt_acl_type,
-               acc,
-               0);
-       return NT_STATUS_OK;
-}
+#if 0
+/* Disable this - prevents ACL inheritance from the ACL editor. JRA. */
 
 /****************************************************************************
- If this is an
+ Take care of parent ACL inheritance.
 ****************************************************************************/
 
-static NTSTATUS append_parent_acl(files_struct *fsp,
-                               SMB_STRUCT_STAT *psbuf,
-                               SEC_DESC *psd,
+NTSTATUS append_parent_acl(files_struct *fsp,
+                               const SEC_DESC *pcsd,
                                SEC_DESC **pp_new_sd)
 {
+       struct smb_filename *smb_dname = NULL;
        SEC_DESC *parent_sd = NULL;
        files_struct *parent_fsp = NULL;
-       TALLOC_CTX *mem_ctx = talloc_parent(psd);
+       TALLOC_CTX *mem_ctx = talloc_tos();
        char *parent_name = NULL;
        SEC_ACE *new_ace = NULL;
-       unsigned int num_aces = psd->dacl->num_aces;
-       SMB_STRUCT_STAT sbuf;
+       unsigned int num_aces = pcsd->dacl->num_aces;
        NTSTATUS status;
        int info;
-       size_t sd_size;
        unsigned int i, j;
-       mode_t unx_mode;
-
-       ZERO_STRUCT(sbuf);
+       SEC_DESC *psd = dup_sec_desc(talloc_tos(), pcsd);
+       bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
 
-       if (mem_ctx == NULL) {
+       if (psd == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       if (!parent_dirname_talloc(mem_ctx,
-                               fsp->fsp_name,
-                               &parent_name,
-                               NULL)) {
+       if (!parent_dirname(mem_ctx, fsp->fsp_name->base_name, &parent_name,
+                           NULL)) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* Create a default mode for u/g/w. */
-       unx_mode = unix_mode(fsp->conn,
-                       aARCH | (fsp->is_directory ? aDIR : 0),
-                       fsp->fsp_name,
-                       parent_name);
-
-       status = open_directory(fsp->conn,
-                               NULL,
-                               parent_name,
-                               &sbuf,
-                               FILE_READ_ATTRIBUTES, /* Just a stat open */
-                               FILE_SHARE_NONE, /* Ignored for stat opens */
-                               FILE_OPEN,
-                               0,
-                               INTERNAL_OPEN_ONLY,
-                               &info,
-                               &parent_fsp);
+       status = create_synthetic_smb_fname(mem_ctx, parent_name, NULL, NULL,
+                                           &smb_dname);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       status = SMB_VFS_CREATE_FILE(
+               fsp->conn,                              /* conn */
+               NULL,                                   /* req */
+               0,                                      /* root_dir_fid */
+               smb_dname,                              /* fname */
+               FILE_READ_ATTRIBUTES,                   /* access_mask */
+               FILE_SHARE_NONE,                        /* share_access */
+               FILE_OPEN,                              /* create_disposition*/
+               FILE_DIRECTORY_FILE,                    /* create_options */
+               0,                                      /* file_attributes */
+               INTERNAL_OPEN_ONLY,                     /* oplock_request */
+               0,                                      /* allocation_size */
+               NULL,                                   /* sd */
+               NULL,                                   /* ea_list */
+               &parent_fsp,                            /* result */
+               &info);                                 /* pinfo */
 
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb_dname);
                return status;
        }
 
-       sd_size = SMB_VFS_GET_NT_ACL(parent_fsp, parent_fsp->fsp_name,
-                       DACL_SECURITY_INFORMATION, &parent_sd );
+       status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, smb_dname->base_name,
+                                   DACL_SECURITY_INFORMATION, &parent_sd );
 
-       close_file(parent_fsp, NORMAL_CLOSE);
+       close_file(NULL, parent_fsp, NORMAL_CLOSE);
+       TALLOC_FREE(smb_dname);
 
-       if (!sd_size) {
-               return NT_STATUS_ACCESS_DENIED;
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        /*
         * Make room for potentially all the ACLs from
-        * the parent, plus the user/group/other triple.
+        * the parent. We used to add the ugw triple here,
+        * as we knew we were dealing with POSIX ACLs.
+        * We no longer need to do so as we can guarentee
+        * that a default ACL from the parent directory will
+        * be well formed for POSIX ACLs if it came from a
+        * POSIX ACL source, and if we're not writing to a
+        * POSIX ACL sink then we don't care if it's not well
+        * formed. JRA.
         */
 
-       num_aces += parent_sd->dacl->num_aces + 3;
+       num_aces += parent_sd->dacl->num_aces;
 
        if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
                                        num_aces)) == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       DEBUG(10,("append_parent_acl: parent ACL has %u entries. New "
-               "ACL has %u entries\n",
-               parent_sd->dacl->num_aces, num_aces ));
-
        /* Start by copying in all the given ACE entries. */
        for (i = 0; i < psd->dacl->num_aces; i++) {
                sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
@@ -3248,57 +3635,106 @@ static NTSTATUS append_parent_acl(files_struct *fsp,
         * as that really only applies to newly created files. JRA.
         */
 
-        /*
-         * Append u/g/w.
-         */
-
-       status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRUSR, &new_ace[i++]);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-       status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRGRP, &new_ace[i++]);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-       status = append_ugw_ace(fsp, psbuf, unx_mode, S_IROTH, &new_ace[i++]);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
        /* Finally append any inherited ACEs. */
        for (j = 0; j < parent_sd->dacl->num_aces; j++) {
                SEC_ACE *se = &parent_sd->dacl->aces[j];
-               uint32 i_flags = se->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
-                                       SEC_ACE_FLAG_CONTAINER_INHERIT|
-                                       SEC_ACE_FLAG_INHERIT_ONLY);
 
                if (fsp->is_directory) {
-                       if (i_flags == SEC_ACE_FLAG_OBJECT_INHERIT) {
-                               /* Should only apply to a file - ignore. */
+                       if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+                               /* Doesn't apply to a directory - ignore. */
+                               DEBUG(10,("append_parent_acl: directory %s "
+                                       "ignoring non container "
+                                       "inherit flags %u on ACE with sid %s "
+                                       "from parent %s\n",
+                                       fsp_str_dbg(fsp),
+                                       (unsigned int)se->flags,
+                                       sid_string_dbg(&se->trustee),
+                                       parent_name));
                                continue;
                        }
                } else {
-                       if ((i_flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
-                                       SEC_ACE_FLAG_INHERIT_ONLY)) !=
-                                       SEC_ACE_FLAG_OBJECT_INHERIT) {
-                               /* Should not apply to a file - ignore. */
+                       if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
+                               /* Doesn't apply to a file - ignore. */
+                               DEBUG(10,("append_parent_acl: file %s "
+                                       "ignoring non object "
+                                       "inherit flags %u on ACE with sid %s "
+                                       "from parent %s\n",
+                                       fsp_str_dbg(fsp),
+                                       (unsigned int)se->flags,
+                                       sid_string_dbg(&se->trustee),
+                                       parent_name));
                                continue;
                        }
                }
-               sec_ace_copy(&new_ace[i], se);
-               if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
-                       new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
+
+               if (is_dacl_protected) {
+                       /* If the DACL is protected it means we must
+                        * not overwrite an existing ACE entry with the
+                        * same SID. This is order N^2. Ouch :-(. JRA. */
+                       unsigned int k;
+                       for (k = 0; k < psd->dacl->num_aces; k++) {
+                               if (sid_equal(&psd->dacl->aces[k].trustee,
+                                               &se->trustee)) {
+                                       break;
+                               }
+                       }
+                       if (k < psd->dacl->num_aces) {
+                               /* SID matched. Ignore. */
+                               DEBUG(10,("append_parent_acl: path %s "
+                                       "ignoring ACE with protected sid %s "
+                                       "from parent %s\n",
+                                       fsp_str_dbg(fsp),
+                                       sid_string_dbg(&se->trustee),
+                                       parent_name));
+                               continue;
+                       }
+               }
+
+               sec_ace_copy(&new_ace[i], se);
+               if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+                       new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
                }
                new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
+
+               if (fsp->is_directory) {
+                       /*
+                        * Strip off any inherit only. It's applied.
+                        */
+                       new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY);
+                       if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+                               /* No further inheritance. */
+                               new_ace[i].flags &=
+                                       ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
+                                       SEC_ACE_FLAG_OBJECT_INHERIT);
+                       }
+               } else {
+                       /*
+                        * Strip off any container or inherit
+                        * flags, they can't apply to objects.
+                        */
+                       new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
+                                               SEC_ACE_FLAG_INHERIT_ONLY|
+                                               SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
+               }
                i++;
+
+               DEBUG(10,("append_parent_acl: path %s "
+                       "inheriting ACE with sid %s "
+                       "from parent %s\n",
+                       fsp_str_dbg(fsp),
+                       sid_string_dbg(&se->trustee),
+                       parent_name));
        }
 
-       parent_sd->dacl->aces = new_ace;
-       parent_sd->dacl->num_aces = i;
+       psd->dacl->aces = new_ace;
+       psd->dacl->num_aces = i;
+       psd->type &= ~(SE_DESC_DACL_AUTO_INHERITED|
+                         SE_DESC_DACL_AUTO_INHERIT_REQ);
 
-       *pp_new_sd = parent_sd;
+       *pp_new_sd = psd;
        return status;
 }
+#endif
 
 /****************************************************************************
  Reply to set a security descriptor on an fsp. security_info_sent is the
@@ -3306,63 +3742,87 @@ static NTSTATUS append_parent_acl(files_struct *fsp,
  This should be the only external function needed for the UNIX style set ACL.
 ****************************************************************************/
 
-NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd_orig)
 {
        connection_struct *conn = fsp->conn;
        uid_t user = (uid_t)-1;
        gid_t grp = (gid_t)-1;
-       SMB_STRUCT_STAT sbuf;
        DOM_SID file_owner_sid;
        DOM_SID file_grp_sid;
        canon_ace *file_ace_list = NULL;
        canon_ace *dir_ace_list = NULL;
-       BOOL acl_perms = False;
+       bool acl_perms = False;
        mode_t orig_mode = (mode_t)0;
        NTSTATUS status;
+       bool set_acl_as_root = false;
+       bool acl_set_support = false;
+       bool ret = false;
+       SEC_DESC *psd = NULL;
 
-       DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
+       DEBUG(10,("set_nt_acl: called for file %s\n",
+                 fsp_str_dbg(fsp)));
 
        if (!CAN_WRITE(conn)) {
                DEBUG(10,("set acl rejected on read-only share\n"));
                return NT_STATUS_MEDIA_WRITE_PROTECTED;
        }
 
+       if (!psd_orig) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       psd = dup_sec_desc(talloc_tos(), psd_orig);
+       if (!psd) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        /*
         * Get the current state of the file.
         */
 
-       if(fsp->is_directory || fsp->fh->fd == -1) {
-               if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
-                       return map_nt_error_from_unix(errno);
-       } else {
-               if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0)
-                       return map_nt_error_from_unix(errno);
+       status = vfs_stat_fsp(fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       /* Save the original elements we check against. */
-       orig_mode = sbuf.st_mode;
+       /* Save the original element we check against. */
+       orig_mode = fsp->fsp_name->st.st_ex_mode;
 
        /*
         * Unpack the user/group/world id's.
         */
 
+       /* POSIX can't cope with missing owner/group. */
+       if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
+               security_info_sent &= ~SECINFO_OWNER;
+       }
+       if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
+               security_info_sent &= ~SECINFO_GROUP;
+       }
+
        status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        /*
-        * Do we need to chown ?
+        * Do we need to chown ? If so this must be done first as the incoming
+        * CREATOR_OWNER acl will be relative to the *new* owner, not the old.
+        * Noticed by Simo.
         */
 
-       if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) {
+       if (((user != (uid_t)-1) && (fsp->fsp_name->st.st_ex_uid != user)) ||
+           (( grp != (gid_t)-1) && (fsp->fsp_name->st.st_ex_gid != grp))) {
 
                DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
-                               fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
-
-               if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
-                       DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
-                               fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+                        fsp_str_dbg(fsp), (unsigned int)user,
+                        (unsigned int)grp));
+
+               if(try_chown(fsp->conn, fsp->fsp_name, user, grp) == -1) {
+                       DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error "
+                                "= %s.\n", fsp_str_dbg(fsp),
+                                (unsigned int)user, (unsigned int)grp,
+                                strerror(errno)));
                        if (errno == EPERM) {
                                return NT_STATUS_INVALID_OWNER;
                        }
@@ -3374,168 +3834,243 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                 * (suid/sgid bits, for instance)
                 */
 
-               if(fsp->is_directory) {
-                       if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
-                               return map_nt_error_from_unix(errno);
-                       }
-               } else {
-
-                       int ret;
+               status = vfs_stat_fsp(fsp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
 
-                       if(fsp->fh->fd == -1)
-                               ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
-                       else
-                               ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf);
+               /* Save the original element we check against. */
+               orig_mode = fsp->fsp_name->st.st_ex_mode;
 
-                       if(ret != 0)
-                               return map_nt_error_from_unix(errno);
+               /* If we successfully chowned, we know we must
+                * be able to set the acl, so do it as root.
+                */
+               set_acl_as_root = true;
+       }
+
+       create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
+
+       if((security_info_sent & SECINFO_DACL) &&
+                       (psd->type & SEC_DESC_DACL_PRESENT) &&
+                       (psd->dacl == NULL)) {
+               SEC_ACE ace[3];
+
+               /* We can't have NULL DACL in POSIX.
+                  Use owner/group/Everyone -> full access. */
+
+               init_sec_ace(&ace[0],
+                               &file_owner_sid,
+                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               GENERIC_ALL_ACCESS,
+                               0);
+               init_sec_ace(&ace[1],
+                               &file_grp_sid,
+                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               GENERIC_ALL_ACCESS,
+                               0);
+               init_sec_ace(&ace[2],
+                               &global_sid_World,
+                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               GENERIC_ALL_ACCESS,
+                               0);
+               psd->dacl = make_sec_acl(talloc_tos(),
+                                       NT4_ACL_REVISION,
+                                       3,
+                                       ace);
+               if (psd->dacl == NULL) {
+                       return NT_STATUS_NO_MEMORY;
                }
-
-               /* Save the original elements we check against. */
-               orig_mode = sbuf.st_mode;
+               security_acl_map_generic(psd->dacl, &file_generic_mapping);
        }
 
-       create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
+       acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
+                                    &file_grp_sid, &file_ace_list,
+                                    &dir_ace_list, security_info_sent, psd);
 
-       if ((security_info_sent & DACL_SECURITY_INFORMATION) &&
-               psd->dacl != NULL &&
-               (psd->type & (SE_DESC_DACL_AUTO_INHERITED|
-                             SE_DESC_DACL_AUTO_INHERIT_REQ))==
-                       (SE_DESC_DACL_AUTO_INHERITED|
-                        SE_DESC_DACL_AUTO_INHERIT_REQ) ) {
-               status = append_parent_acl(fsp, &sbuf, psd, &psd);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
+       /* Ignore W2K traverse DACL set. */
+       if (!file_ace_list && !dir_ace_list) {
+               return NT_STATUS_OK;
        }
 
-       acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
-                                       &file_ace_list, &dir_ace_list, security_info_sent, psd);
+       if (!acl_perms) {
+               DEBUG(3,("set_nt_acl: cannot set permissions\n"));
+               free_canon_ace_list(file_ace_list);
+               free_canon_ace_list(dir_ace_list);
+               return NT_STATUS_ACCESS_DENIED;
+       }
 
-       /* Ignore W2K traverse DACL set. */
-       if (file_ace_list || dir_ace_list) {
+       /*
+        * Only change security if we got a DACL.
+        */
 
-               if (!acl_perms) {
-                       DEBUG(3,("set_nt_acl: cannot set permissions\n"));
-                       free_canon_ace_list(file_ace_list);
-                       free_canon_ace_list(dir_ace_list); 
-                       return NT_STATUS_ACCESS_DENIED;
-               }
+       if(!(security_info_sent & DACL_SECURITY_INFORMATION) || (psd->dacl == NULL)) {
+               free_canon_ace_list(file_ace_list);
+               free_canon_ace_list(dir_ace_list);
+               return NT_STATUS_OK;
+       }
 
-               /*
-                * Only change security if we got a DACL.
-                */
+       /*
+        * Try using the POSIX ACL set first. Fall back to chmod if
+        * we have no ACL support on this filesystem.
+        */
 
-               if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
+       if (acl_perms && file_ace_list) {
+               if (set_acl_as_root) {
+                       become_root();
+               }
+               ret = set_canon_ace_list(fsp, file_ace_list, false,
+                                        &fsp->fsp_name->st, &acl_set_support);
+               if (set_acl_as_root) {
+                       unbecome_root();
+               }
+               if (acl_set_support && ret == false) {
+                       DEBUG(3,("set_nt_acl: failed to set file acl on file "
+                                "%s (%s).\n", fsp_str_dbg(fsp),
+                                strerror(errno)));
+                       free_canon_ace_list(file_ace_list);
+                       free_canon_ace_list(dir_ace_list);
+                       return map_nt_error_from_unix(errno);
+               }
+       }
 
-                       BOOL acl_set_support = False;
-                       BOOL ret = False;
+       if (acl_perms && acl_set_support && fsp->is_directory) {
+               if (dir_ace_list) {
+                       if (set_acl_as_root) {
+                               become_root();
+                       }
+                       ret = set_canon_ace_list(fsp, dir_ace_list, true,
+                                                &fsp->fsp_name->st,
+                                                &acl_set_support);
+                       if (set_acl_as_root) {
+                               unbecome_root();
+                       }
+                       if (ret == false) {
+                               DEBUG(3,("set_nt_acl: failed to set default "
+                                        "acl on directory %s (%s).\n",
+                                        fsp_str_dbg(fsp), strerror(errno)));
+                               free_canon_ace_list(file_ace_list);
+                               free_canon_ace_list(dir_ace_list);
+                               return map_nt_error_from_unix(errno);
+                       }
+               } else {
+                       int sret = -1;
 
                        /*
-                        * Try using the POSIX ACL set first. Fall back to chmod if
-                        * we have no ACL support on this filesystem.
+                        * No default ACL - delete one if it exists.
                         */
 
-                       if (acl_perms && file_ace_list) {
-                               ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
-                               if (acl_set_support && ret == False) {
-                                       DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+                       if (set_acl_as_root) {
+                               become_root();
+                       }
+                       sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn,
+                           fsp->fsp_name->base_name);
+                       if (set_acl_as_root) {
+                               unbecome_root();
+                       }
+                       if (sret == -1) {
+                               if (acl_group_override(conn, fsp->fsp_name)) {
+                                       DEBUG(5,("set_nt_acl: acl group "
+                                                "control on and current user "
+                                                "in file %s primary group. "
+                                                "Override delete_def_acl\n",
+                                                fsp_str_dbg(fsp)));
+
+                                       become_root();
+                                       sret =
+                                           SMB_VFS_SYS_ACL_DELETE_DEF_FILE(
+                                                   conn,
+                                                   fsp->fsp_name->base_name);
+                                       unbecome_root();
+                               }
+
+                               if (sret == -1) {
+                                       DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
                                        free_canon_ace_list(file_ace_list);
-                                       free_canon_ace_list(dir_ace_list); 
+                                       free_canon_ace_list(dir_ace_list);
                                        return map_nt_error_from_unix(errno);
                                }
                        }
+               }
+       }
 
-                       if (acl_perms && acl_set_support && fsp->is_directory) {
-                               if (dir_ace_list) {
-                                       if (!set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support)) {
-                                               DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
-                                               free_canon_ace_list(file_ace_list);
-                                               free_canon_ace_list(dir_ace_list); 
-                                               return map_nt_error_from_unix(errno);
-                                       }
-                               } else {
+       if (acl_set_support) {
+               if (set_acl_as_root) {
+                       become_root();
+               }
+               store_inheritance_attributes(fsp,
+                               file_ace_list,
+                               dir_ace_list,
+                               psd->type);
+               if (set_acl_as_root) {
+                       unbecome_root();
+               }
+       }
 
-                                       /*
-                                        * No default ACL - delete one if it exists.
-                                        */
+       /*
+        * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
+        */
 
-                                       if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
-                                               int sret = -1;
-
-                                               if (acl_group_override(conn, sbuf.st_gid)) {
-                                                       DEBUG(5,("set_nt_acl: acl group control on and "
-                                                               "current user in file %s primary group. Override delete_def_acl\n",
-                                                               fsp->fsp_name ));
-
-                                                       become_root();
-                                                       sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
-                                                       unbecome_root();
-                                               }
-
-                                               if (sret == -1) {
-                                                       DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
-                                                       free_canon_ace_list(file_ace_list);
-                                                       free_canon_ace_list(dir_ace_list);
-                                                       return map_nt_error_from_unix(errno);
-                                               }
-                                       }
-                               }
-                       }
+       if(!acl_set_support && acl_perms) {
+               mode_t posix_perms;
 
-                       if (acl_set_support) {
-                               store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
-                                               (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
-                       }
+               if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
+                       free_canon_ace_list(file_ace_list);
+                       free_canon_ace_list(dir_ace_list);
+                       DEBUG(3,("set_nt_acl: failed to convert file acl to "
+                                "posix permissions for file %s.\n",
+                                fsp_str_dbg(fsp)));
+                       return NT_STATUS_ACCESS_DENIED;
+               }
 
-                       /*
-                        * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
-                        */
+               if (orig_mode != posix_perms) {
+                       int sret = -1;
 
-                       if(!acl_set_support && acl_perms) {
-                               mode_t posix_perms;
+                       DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
+                                fsp_str_dbg(fsp), (unsigned int)posix_perms));
 
-                               if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
-                                       free_canon_ace_list(file_ace_list);
-                                       free_canon_ace_list(dir_ace_list);
-                                       DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
-                                               fsp->fsp_name ));
-                                       return NT_STATUS_ACCESS_DENIED;
+                       if (set_acl_as_root) {
+                               become_root();
+                       }
+                       sret = SMB_VFS_CHMOD(conn, fsp->fsp_name->base_name,
+                                            posix_perms);
+                       if (set_acl_as_root) {
+                               unbecome_root();
+                       }
+                       if(sret == -1) {
+                               if (acl_group_override(conn, fsp->fsp_name)) {
+                                       DEBUG(5,("set_nt_acl: acl group "
+                                                "control on and current user "
+                                                "in file %s primary group. "
+                                                "Override chmod\n",
+                                                fsp_str_dbg(fsp)));
+
+                                       become_root();
+                                       sret = SMB_VFS_CHMOD(conn,
+                                           fsp->fsp_name->base_name,
+                                           posix_perms);
+                                       unbecome_root();
                                }
 
-                               if (orig_mode != posix_perms) {
-
-                                       DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
-                                               fsp->fsp_name, (unsigned int)posix_perms ));
-
-                                       if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
-                                               int sret = -1;
-                                               if (acl_group_override(conn, sbuf.st_gid)) {
-                                                       DEBUG(5,("set_nt_acl: acl group control on and "
-                                                               "current user in file %s primary group. Override chmod\n",
-                                                               fsp->fsp_name ));
-
-                                                       become_root();
-                                                       sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
-                                                       unbecome_root();
-                                               }
-
-                                               if (sret == -1) {
-                                                       DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
-                                                               fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
-                                                       free_canon_ace_list(file_ace_list);
-                                                       free_canon_ace_list(dir_ace_list);
-                                                       return map_nt_error_from_unix(errno);
-                                               }
-                                       }
+                               if (sret == -1) {
+                                       DEBUG(3,("set_nt_acl: chmod %s, 0%o "
+                                                "failed. Error = %s.\n",
+                                                fsp_str_dbg(fsp),
+                                                (unsigned int)posix_perms,
+                                                strerror(errno)));
+                                       free_canon_ace_list(file_ace_list);
+                                       free_canon_ace_list(dir_ace_list);
+                                       return map_nt_error_from_unix(errno);
                                }
                        }
                }
-
-               free_canon_ace_list(file_ace_list);
-               free_canon_ace_list(dir_ace_list); 
        }
 
+       free_canon_ace_list(file_ace_list);
+       free_canon_ace_list(dir_ace_list);
+
+       /* Ensure the stat struct in the fsp is correct. */
+       status = vfs_stat_fsp(fsp);
+
        return NT_STATUS_OK;
 }
 
@@ -3560,9 +4095,7 @@ int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode
                SMB_ACL_TAG_T tagtype;
                SMB_ACL_PERMSET_T permset;
 
-               /* get_next... */
-               if (entry_id == SMB_ACL_FIRST_ENTRY)
-                       entry_id = SMB_ACL_NEXT_ENTRY;
+               entry_id = SMB_ACL_NEXT_ENTRY;
 
                if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
                        break;
@@ -3600,9 +4133,7 @@ static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mo
                SMB_ACL_PERMSET_T permset;
                mode_t perms;
 
-               /* get_next... */
-               if (entry_id == SMB_ACL_FIRST_ENTRY)
-                       entry_id = SMB_ACL_NEXT_ENTRY;
+               entry_id = SMB_ACL_NEXT_ENTRY;
 
                if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
                        return -1;
@@ -3659,7 +4190,7 @@ static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mo
  resulting ACL on TO.  Note that name is in UNIX character set.
 ****************************************************************************/
 
-static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
+static int copy_access_posix_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
 {
        SMB_ACL_T posix_acl = NULL;
        int ret = -1;
@@ -3686,7 +4217,27 @@ static int copy_access_acl(connection_struct *conn, const char *from, const char
 
 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
 {
-       return copy_access_acl(conn, name, name, mode);
+       return copy_access_posix_acl(conn, name, name, mode);
+}
+
+/****************************************************************************
+ Check for an existing default POSIX ACL on a directory.
+****************************************************************************/
+
+static bool directory_has_default_posix_acl(connection_struct *conn, const char *fname)
+{
+       SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
+       bool has_acl = False;
+       SMB_ACL_ENTRY_T entry;
+
+       if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
+               has_acl = True;
+       }
+
+       if (def_acl) {
+               SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
+       }
+        return has_acl;
 }
 
 /****************************************************************************
@@ -3694,13 +4245,13 @@ int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
  inherit this Access ACL to file name.
 ****************************************************************************/
 
-int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir,
+int inherit_access_posix_acl(connection_struct *conn, const char *inherit_from_dir,
                       const char *name, mode_t mode)
 {
-       if (directory_has_default_acl(conn, inherit_from_dir))
+       if (directory_has_default_posix_acl(conn, inherit_from_dir))
                return 0;
 
-       return copy_access_acl(conn, inherit_from_dir, name, mode);
+       return copy_access_posix_acl(conn, inherit_from_dir, name, mode);
 }
 
 /****************************************************************************
@@ -3708,19 +4259,19 @@ int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir,
  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
 ****************************************************************************/
 
-int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
+int fchmod_acl(files_struct *fsp, mode_t mode)
 {
        connection_struct *conn = fsp->conn;
        SMB_ACL_T posix_acl = NULL;
        int ret = -1;
 
-       if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
+       if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp)) == NULL)
                return -1;
 
        if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
                goto done;
 
-       ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
+       ret = SMB_VFS_SYS_ACL_SET_FD(fsp, posix_acl);
 
   done:
 
@@ -3728,31 +4279,11 @@ int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
        return ret;
 }
 
-/****************************************************************************
- Check for an existing default POSIX ACL on a directory.
-****************************************************************************/
-
-BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
-{
-       SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
-       BOOL has_acl = False;
-       SMB_ACL_ENTRY_T entry;
-
-       if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
-               has_acl = True;
-       }
-
-       if (def_acl) {
-               SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
-       }
-        return has_acl;
-}
-
 /****************************************************************************
  Map from wire type to permset.
 ****************************************************************************/
 
-static BOOL unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
+static bool unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
 {
        if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
                return False;
@@ -3784,7 +4315,7 @@ static BOOL unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_
  Map from wire type to tagtype.
 ****************************************************************************/
 
-static BOOL unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
+static bool unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
 {
        switch (wire_tt) {
                case SMB_POSIX_ACL_USER_OBJ:
@@ -3907,15 +4438,19 @@ static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_
  on the directory.
 ****************************************************************************/
 
-BOOL set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
+bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, const SMB_STRUCT_STAT *psbuf,
                                uint16 num_def_acls, const char *pdata)
 {
        SMB_ACL_T def_acl = NULL;
 
-       if (num_def_acls && !S_ISDIR(psbuf->st_mode)) {
-               DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
-               errno = EISDIR;
-               return False;
+       if (!S_ISDIR(psbuf->st_ex_mode)) {
+               if (num_def_acls) {
+                       DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
+                       errno = EISDIR;
+                       return False;
+               } else {
+                       return True;
+               }
        }
 
        if (!num_def_acls) {
@@ -3952,12 +4487,12 @@ BOOL set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_
  FIXME ! How does the share mask/mode fit into this.... ?
 ****************************************************************************/
 
-static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
+static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
 {
        SMB_ACL_T file_acl = NULL;
        int entry_id = SMB_ACL_FIRST_ENTRY;
        SMB_ACL_ENTRY_T entry;
-       BOOL ret = False;
+       bool ret = False;
        /* Create a new ACL with only 3 entries, u/g/w. */
        SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
        SMB_ACL_ENTRY_T user_ent = NULL;
@@ -4005,7 +4540,7 @@ static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const c
 
        /* Get the current file ACL. */
        if (fsp && fsp->fh->fd != -1) {
-               file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
+               file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
        } else {
                file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
        }
@@ -4022,9 +4557,7 @@ static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const c
                SMB_ACL_TAG_T tagtype;
                SMB_ACL_PERMSET_T permset;
 
-               /* get_next... */
-               if (entry_id == SMB_ACL_FIRST_ENTRY)
-                       entry_id = SMB_ACL_NEXT_ENTRY;
+               entry_id = SMB_ACL_NEXT_ENTRY;
 
                if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
                        DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
@@ -4058,7 +4591,7 @@ static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const c
 
        /* Set the new empty file ACL. */
        if (fsp && fsp->fh->fd != -1) {
-               if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, new_file_acl) == -1) {
+               if (SMB_VFS_SYS_ACL_SET_FD(fsp, new_file_acl) == -1) {
                        DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
                                fname, strerror(errno) ));
                        goto done;
@@ -4090,7 +4623,7 @@ static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const c
  except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
 ****************************************************************************/
 
-BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
+bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
 {
        SMB_ACL_T file_acl = NULL;
 
@@ -4105,7 +4638,7 @@ BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *
 
        if (fsp && fsp->fh->fd != -1) {
                /* The preferred way - use an open fd. */
-               if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, file_acl) == -1) {
+               if (SMB_VFS_SYS_ACL_SET_FD(fsp, file_acl) == -1) {
                        DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
                                fname, strerror(errno) ));
                        SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
@@ -4125,448 +4658,6 @@ BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *
        return True;
 }
 
-/****************************************************************************
- Check for POSIX group ACLs. If none use stat entry.
- Return -1 if no match, 0 if match and denied, 1 if match and allowed.
-****************************************************************************/
-
-static int check_posix_acl_group_access(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
-{
-       SMB_ACL_T posix_acl = NULL;
-       int entry_id = SMB_ACL_FIRST_ENTRY;
-       SMB_ACL_ENTRY_T entry;
-       int i;
-       BOOL seen_mask = False;
-       BOOL seen_owning_group = False;
-       int ret = -1;
-       gid_t cu_gid;
-
-       DEBUG(10,("check_posix_acl_group_access: requesting 0x%x on file %s\n",
-               (unsigned int)access_mask, fname ));
-
-       if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS)) == NULL) {
-               goto check_stat;
-       }
-
-       /* First ensure the group mask allows group access. */
-       /* Also check any user entries (these take preference over group). */
-
-       while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
-               SMB_ACL_TAG_T tagtype;
-               SMB_ACL_PERMSET_T permset;
-               int have_write = -1;
-               int have_read = -1;
-
-               /* get_next... */
-               if (entry_id == SMB_ACL_FIRST_ENTRY)
-                       entry_id = SMB_ACL_NEXT_ENTRY;
-
-               if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
-                       goto check_stat;
-               }
-
-               if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
-                       goto check_stat;
-               }
-
-               have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ);
-               if (have_read == -1) {
-                       goto check_stat;
-               }
-
-               have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
-               if (have_write == -1) {
-                       goto check_stat;
-               }
-
-               /*
-                * Solaris returns 2 for this if write is available.
-                * canonicalize to 0 or 1.
-                */     
-               have_write = (have_write ? 1 : 0);
-               have_read = (have_read ? 1 : 0);
-
-               switch(tagtype) {
-                       case SMB_ACL_MASK:
-                               seen_mask = True;
-                               switch (access_mask) {
-                                       case FILE_READ_DATA:
-                                               if (!have_read) {
-                                                       ret = -1;
-                                                       DEBUG(10,("check_posix_acl_group_access: file %s "
-                                                               "refusing read due to mask.\n", fname));
-                                                       goto done;
-                                               }
-                                               break;
-                                       case FILE_WRITE_DATA:
-                                               if (!have_write) {
-                                                       ret = -1;
-                                                       DEBUG(10,("check_posix_acl_group_access: file %s "
-                                                               "refusing write due to mask.\n", fname));
-                                                       goto done;
-                                               }
-                                               break;
-                                       default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-                                               if (!have_write || !have_read) {
-                                                       ret = -1;
-                                                       DEBUG(10,("check_posix_acl_group_access: file %s "
-                                                               "refusing read/write due to mask.\n", fname));
-                                                       goto done;
-                                               }
-                                               break;
-                               }
-                               break;
-                       case SMB_ACL_USER:
-                       {
-                               /* Check against current_user.ut.uid. */
-                               uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
-                               if (puid == NULL) {
-                                       goto check_stat;
-                               }
-                               if (current_user.ut.uid == *puid) {
-                                       /* We have a uid match but we must ensure we have seen the acl mask. */
-                                       switch (access_mask) {
-                                               case FILE_READ_DATA:
-                                                       ret = have_read;
-                                                       break;
-                                               case FILE_WRITE_DATA:
-                                                       ret = have_write;
-                                                       break;
-                                               default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-                                                       ret = (have_write & have_read);
-                                                       break;
-                                       }
-                                       DEBUG(10,("check_posix_acl_group_access: file %s "
-                                               "match on user %u -> %s.\n",
-                                               fname, (unsigned int)*puid,
-                                               ret ? "can access" : "cannot access"));
-                                       if (seen_mask) {
-                                               goto done;
-                                       }
-                               }
-                               break;
-                       }
-                       default:
-                               continue;
-               }
-       }
-
-       /* If ret is anything other than -1 we matched on a user entry. */
-       if (ret != -1) {
-               goto done;
-       }
-
-       /* Next check all group entries. */
-       entry_id = SMB_ACL_FIRST_ENTRY;
-       while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
-               SMB_ACL_TAG_T tagtype;
-               SMB_ACL_PERMSET_T permset;
-               int have_write = -1;
-               int have_read = -1;
-
-               /* get_next... */
-               if (entry_id == SMB_ACL_FIRST_ENTRY)
-                       entry_id = SMB_ACL_NEXT_ENTRY;
-
-               if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
-                       goto check_stat;
-               }
-
-               if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
-                       goto check_stat;
-               }
-
-               have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ);
-               if (have_read == -1) {
-                       goto check_stat;
-               }
-
-               have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
-               if (have_write == -1) {
-                       goto check_stat;
-               }
-
-               /*
-                * Solaris returns 2 for this if write is available.
-                * canonicalize to 0 or 1.
-                */     
-               have_write = (have_write ? 1 : 0);
-               have_read = (have_read ? 1 : 0);
-
-               switch(tagtype) {
-                       case SMB_ACL_GROUP:
-                       case SMB_ACL_GROUP_OBJ:
-                       {
-                               gid_t *pgid = NULL;
-
-                               if (tagtype == SMB_ACL_GROUP) {
-                                       pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
-                               } else {
-                                       seen_owning_group = True;
-                                       pgid = &psbuf->st_gid;
-                               }
-                               if (pgid == NULL) {
-                                       goto check_stat;
-                               }
-
-                               /*
-                                * Does it match the current effective group
-                                * or supplementary groups ?
-                                */
-                               for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1;
-                                                       cu_gid = get_current_user_gid_next(&i)) {
-                                       if (cu_gid == *pgid) {
-                                               switch (access_mask) {
-                                                       case FILE_READ_DATA:
-                                                               ret = have_read;
-                                                               break;
-                                                       case FILE_WRITE_DATA:
-                                                               ret = have_write;
-                                                               break;
-                                                       default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-                                                               ret = (have_write & have_read);
-                                                               break;
-                                               }
-
-                                               DEBUG(10,("check_posix_acl_group_access: file %s "
-                                                       "match on group %u -> can access.\n",
-                                                       fname, (unsigned int)cu_gid ));
-
-                                               /* If we don't have access permission this entry doesn't
-                                                       terminate the enumeration of the entries. */
-                                               if (ret) {
-                                                       goto done;
-                                               }
-                                               /* But does terminate the group iteration. */
-                                               break;
-                                       }
-                               }
-                               break;
-                       }
-                       default:
-                               continue;
-               }
-       }
-
-       /* If ret is -1 here we didn't match on the user entry or
-          supplemental group entries. */
-       
-       DEBUG(10,("check_posix_acl_group_access: ret = %d before check_stat:\n", ret));
-
-  check_stat:
-
-       /*
-        * We only check the S_I[RW]GRP permissions if we haven't already
-        * seen an owning group SMB_ACL_GROUP_OBJ ace entry. If there is an
-        * SMB_ACL_GROUP_OBJ ace entry then the group bits in st_gid are
-        * the same as the SMB_ACL_MASK bits, not the SMB_ACL_GROUP_OBJ
-        * bits. Thanks to Marc Cousin <mcousin@sigma.fr> for pointing
-        * this out. JRA.
-        */
-
-       if (!seen_owning_group) {
-               /* Do we match on the owning group entry ? */
-               /*
-                * Does it match the current effective group
-                * or supplementary groups ?
-                */
-               for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1;
-                                               cu_gid = get_current_user_gid_next(&i)) {
-                       if (cu_gid == psbuf->st_gid) {
-                               switch (access_mask) {
-                                       case FILE_READ_DATA:
-                                               ret = (psbuf->st_mode & S_IRGRP) ? 1 : 0;
-                                               break;
-                                       case FILE_WRITE_DATA:
-                                               ret = (psbuf->st_mode & S_IWGRP) ? 1 : 0;
-                                               break;
-                                       default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-                                               if ((psbuf->st_mode & (S_IWGRP|S_IRGRP)) == (S_IWGRP|S_IRGRP)) {
-                                                       ret = 1;
-                                               } else {
-                                                       ret = 0;
-                                               }
-                                               break;
-                               }
-                               DEBUG(10,("check_posix_acl_group_access: file %s "
-                                       "match on owning group %u -> %s.\n",
-                                       fname, (unsigned int)psbuf->st_gid,
-                                       ret ? "can access" : "cannot access"));
-                               break;
-                       }
-               }
-
-               if (cu_gid == (gid_t)-1) {
-                       DEBUG(10,("check_posix_acl_group_access: file %s "
-                               "failed to match on user or group in token (ret = %d).\n",
-                               fname, ret ));
-               }
-       }
-
-  done:
-
-       if (posix_acl) {
-               SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
-       }
-
-       DEBUG(10,("check_posix_acl_group_access: file %s returning (ret = %d).\n", fname, ret ));
-       return ret;
-}
-
-/****************************************************************************
- Actually emulate the in-kernel access checking for delete access. We need
- this to successfully return ACCESS_DENIED on a file open for delete access.
-****************************************************************************/
-
-BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
-{
-       SMB_STRUCT_STAT sbuf;
-       TALLOC_CTX *ctx = talloc_tos();
-       char *dname = NULL;
-       int ret;
-
-       if (!CAN_WRITE(conn)) {
-               return False;
-       }
-
-       /* Get the parent directory permission mask and owners. */
-       if (!parent_dirname_talloc(ctx,
-                               fname,
-                               &dname,
-                               NULL)) {
-               return False;
-       }
-       if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) {
-               return False;
-       }
-       if (!S_ISDIR(sbuf.st_mode)) {
-               return False;
-       }
-       if (current_user.ut.uid == 0 || conn->admin_user) {
-               /* I'm sorry sir, I didn't know you were root... */
-               return True;
-       }
-
-       /* Check primary owner write access. */
-       if (current_user.ut.uid == sbuf.st_uid) {
-               return (sbuf.st_mode & S_IWUSR) ? True : False;
-       }
-
-#ifdef S_ISVTX
-       /* sticky bit means delete only by owner or root. */
-       if (sbuf.st_mode & S_ISVTX) {
-               SMB_STRUCT_STAT sbuf_file;
-               if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) {
-                       if (errno == ENOENT) {
-                               /* If the file doesn't already exist then
-                                * yes we'll be able to delete it. */
-                               return True;
-                       }
-                       return False;
-               }
-               /*
-                * Patch from SATOH Fumiyasu <fumiyas@miraclelinux.com>
-                * for bug #3348. Don't assume owning sticky bit
-                * directory means write access allowed.
-                */
-               if (current_user.ut.uid != sbuf_file.st_uid) {
-                       return False;
-               }
-       }
-#endif
-
-       /* Check group or explicit user acl entry write access. */
-       ret = check_posix_acl_group_access(conn, dname, &sbuf, FILE_WRITE_DATA);
-       if (ret == 0 || ret == 1) {
-               return ret ? True : False;
-       }
-
-       /* Finally check other write access. */
-       return (sbuf.st_mode & S_IWOTH) ? True : False;
-}
-
-/****************************************************************************
- Actually emulate the in-kernel access checking for read/write access. We need
- this to successfully check for ability to write for dos filetimes.
- Note this doesn't take into account share write permissions.
-****************************************************************************/
-
-BOOL can_access_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
-{
-       int ret;
-
-       if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) {
-               return False;
-       }
-       access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA);
-
-       DEBUG(10,("can_access_file: requesting 0x%x on file %s\n",
-               (unsigned int)access_mask, fname ));
-
-       if (current_user.ut.uid == 0 || conn->admin_user) {
-               /* I'm sorry sir, I didn't know you were root... */
-               return True;
-       }
-
-       if (!VALID_STAT(*psbuf)) {
-               /* Get the file permission mask and owners. */
-               if(SMB_VFS_STAT(conn, fname, psbuf) != 0) {
-                       return False;
-               }
-       }
-
-       /* Check primary owner access. */
-       if (current_user.ut.uid == psbuf->st_uid) {
-               switch (access_mask) {
-                       case FILE_READ_DATA:
-                               return (psbuf->st_mode & S_IRUSR) ? True : False;
-
-                       case FILE_WRITE_DATA:
-                               return (psbuf->st_mode & S_IWUSR) ? True : False;
-
-                       default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-
-                               if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) {
-                                       return True;
-                               } else {
-                                       return False;
-                               }
-               }
-       }
-
-       /* Check group or explicit user acl entry access. */
-       ret = check_posix_acl_group_access(conn, fname, psbuf, access_mask);
-       if (ret == 0 || ret == 1) {
-               return ret ? True : False;
-       }
-
-       /* Finally check other access. */
-       switch (access_mask) {
-               case FILE_READ_DATA:
-                       return (psbuf->st_mode & S_IROTH) ? True : False;
-
-               case FILE_WRITE_DATA:
-                       return (psbuf->st_mode & S_IWOTH) ? True : False;
-
-               default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-
-                       if ((psbuf->st_mode & (S_IWOTH|S_IROTH)) == (S_IWOTH|S_IROTH)) {
-                               return True;
-                       }
-       }
-       return False;
-}
-
-/****************************************************************************
- Userspace check for write access.
- Note this doesn't take into account share write permissions.
-****************************************************************************/
-
-BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
-{
-       return can_access_file(conn, fname, psbuf, FILE_WRITE_DATA);
-}
-
 /********************************************************************
  Pull the NT ACL from a file on disk or the OpenEventlog() access
  check.  Caller is responsible for freeing the returned security
@@ -4580,30 +4671,30 @@ BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_ST
 SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
 {
        SEC_DESC *psd, *ret_sd;
-       connection_struct conn;
+       connection_struct *conn;
        files_struct finfo;
        struct fd_handle fh;
+       NTSTATUS status;
 
-       ZERO_STRUCT( conn );
-
-       if ( !(conn.mem_ctx = talloc_init( "novfs_get_nt_acl" )) ) {
-               DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
+       conn = TALLOC_ZERO_P(ctx, connection_struct);
+       if (conn == NULL) {
+               DEBUG(0, ("talloc failed\n"));
                return NULL;
        }
 
-       if (!(conn.params = TALLOC_P(conn.mem_ctx, struct share_params))) {
+       if (!(conn->params = TALLOC_P(conn, struct share_params))) {
                DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
-               TALLOC_FREE(conn.mem_ctx);
+               TALLOC_FREE(conn);
                return NULL;
        }
 
-       conn.params->service = -1;
+       conn->params->service = -1;
 
-       set_conn_connectpath(&conn, "/");
+       set_conn_connectpath(conn, "/");
 
-       if (!smbd_vfs_init(&conn)) {
+       if (!smbd_vfs_init(conn)) {
                DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
-               conn_free_internal( &conn );
+               conn_free(conn);
                return NULL;
         }
 
@@ -4611,20 +4702,138 @@ SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
        ZERO_STRUCT( fh );
 
        finfo.fnum = -1;
-       finfo.conn = &conn;
+       finfo.conn = conn;
        finfo.fh = &fh;
        finfo.fh->fd = -1;
-       finfo.fsp_name = CONST_DISCARD(char *,fname);
 
-       if (get_nt_acl( &finfo, DACL_SECURITY_INFORMATION, &psd ) == 0) {
+       status = create_synthetic_smb_fname(talloc_tos(), fname, NULL, NULL,
+                                           &finfo.fsp_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               conn_free(conn);
+               return NULL;
+       }
+
+       if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, DACL_SECURITY_INFORMATION, &psd))) {
                DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
-               conn_free_internal( &conn );
+               TALLOC_FREE(finfo.fsp_name);
+               conn_free(conn);
                return NULL;
        }
 
        ret_sd = dup_sec_desc( ctx, psd );
 
-       conn_free_internal( &conn );
+       TALLOC_FREE(finfo.fsp_name);
+       conn_free(conn);
 
        return ret_sd;
 }
+
+/* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
+
+NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
+                                       const char *name,
+                                       SMB_STRUCT_STAT *psbuf,
+                                       SEC_DESC **ppdesc)
+{
+       struct dom_sid owner_sid, group_sid;
+       size_t size = 0;
+       SEC_ACE aces[4];
+       uint32_t access_mask = 0;
+       mode_t mode = psbuf->st_ex_mode;
+       SEC_ACL *new_dacl = NULL;
+       int idx = 0;
+
+       DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
+               name, (int)mode ));
+
+       uid_to_sid(&owner_sid, psbuf->st_ex_uid);
+       gid_to_sid(&group_sid, psbuf->st_ex_gid);
+
+       /*
+        We provide up to 4 ACEs
+               - Owner
+               - Group
+               - Everyone
+               - NT System
+       */
+
+       if (mode & S_IRUSR) {
+               if (mode & S_IWUSR) {
+                       access_mask |= SEC_RIGHTS_FILE_ALL;
+               } else {
+                       access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+               }
+       }
+       if (mode & S_IWUSR) {
+               access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+       }
+
+       init_sec_ace(&aces[idx],
+                       &owner_sid,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       access_mask,
+                       0);
+       idx++;
+
+       access_mask = 0;
+       if (mode & S_IRGRP) {
+               access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+       }
+       if (mode & S_IWGRP) {
+               /* note that delete is not granted - this matches posix behaviour */
+               access_mask |= SEC_RIGHTS_FILE_WRITE;
+       }
+       if (access_mask) {
+               init_sec_ace(&aces[idx],
+                       &group_sid,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       access_mask,
+                       0);
+               idx++;
+       }
+
+       access_mask = 0;
+       if (mode & S_IROTH) {
+               access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+       }
+       if (mode & S_IWOTH) {
+               access_mask |= SEC_RIGHTS_FILE_WRITE;
+       }
+       if (access_mask) {
+               init_sec_ace(&aces[idx],
+                       &global_sid_World,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       access_mask,
+                       0);
+               idx++;
+       }
+
+       init_sec_ace(&aces[idx],
+                       &global_sid_System,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       SEC_RIGHTS_FILE_ALL,
+                       0);
+       idx++;
+
+       new_dacl = make_sec_acl(ctx,
+                       NT4_ACL_REVISION,
+                       idx,
+                       aces);
+
+       if (!new_dacl) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       *ppdesc = make_sec_desc(ctx,
+                       SECURITY_DESCRIPTOR_REVISION_1,
+                       SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
+                       &owner_sid,
+                       &group_sid,
+                       NULL,
+                       new_dacl,
+                       &size);
+       if (!*ppdesc) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       return NT_STATUS_OK;
+}