s3 file_access: Convert some more functions over to use smb_filneame
[ira/wip.git] / source3 / smbd / posix_acls.c
index 951046c56253034882f4121894088a6e632fb1d8..627ca2e171f2288ba404fa1124222fcde1385d5d 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_V1_VERSION                         1
 
-#define PAI_VERSION            1
-#define PAI_ACL_FLAG_PROTECTED 0x1
-#define PAI_ENTRY_LENGTH       5
+#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,41 +151,30 @@ 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;
@@ -156,17 +182,18 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, b
        unsigned int num_entries = 0;
        unsigned int num_def_entries = 0;
 
-       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 +201,32 @@ 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;
+       entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
 
        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);
+               uint8_t type_val = (uint8_t)ace_list->owner_type;
+               uint32_t entry_val = get_entry_val(ace_list);
 
-                       SCVAL(entry_offset,0,type_val);
-                       SIVAL(entry_offset,1,entry_val);
-                       entry_offset += PAI_ENTRY_LENGTH;
-               }
+               SCVAL(entry_offset,0,ace_list->ace_flags);
+               SCVAL(entry_offset,1,type_val);
+               SIVAL(entry_offset,2,entry_val);
+               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);
+               uint8_t type_val = (uint8_t)ace_list->owner_type;
+               uint32_t entry_val = get_entry_val(ace_list);
 
-                       SCVAL(entry_offset,0,type_val);
-                       SIVAL(entry_offset,1,entry_val);
-                       entry_offset += PAI_ENTRY_LENGTH;
-               }
+               SCVAL(entry_offset,0,ace_list->ace_flags);
+               SCVAL(entry_offset,1,type_val);
+               SIVAL(entry_offset,2,entry_val);
+               entry_offset += PAI_V2_ENTRY_LENGTH;
        }
 
        return pai_buf;
@@ -211,44 +236,39 @@ 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, 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)
+       if (fsp->fh->fd != -1) {
                ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
                                pai_buf, store_size, 0);
-       else
+       } else {
                ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_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->fsp_name));
+
+       if (ret == -1 && !no_acl_syscall_error(errno)) {
                DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+       }
 }
 
 /************************************************************************
@@ -272,160 +292,289 @@ static void free_inherited_info(struct pai_val *pal)
 }
 
 /************************************************************************
- Was this ACL protected ?
+ Get any stored ACE flags.
 ************************************************************************/
 
-static bool get_protected_flag(struct pai_val *pal)
-{
-       if (!pal)
-               return False;
-       return pal->pai_protected;
-}
-
-/************************************************************************
- Was this ACE inherited ?
-************************************************************************/
-
-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;
+}
 
 /************************************************************************
Convert to in-memory format.
Decode the owner.
 ************************************************************************/
 
-static struct pai_val *create_pai_val(char *buf, size_t size)
+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:
+                       return false;
+       }
+       return true;
+}
+
+/************************************************************************
+ Process v2 entries.
+************************************************************************/
+
+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.
+************************************************************************/
 
-               paie = SMB_MALLOC_P(struct pai_entry);
+static const char *create_pai_v2_entries(struct pai_val *paiv,
+                               const char *entry_offset,
+                               bool def_entry)
+{
+       int i;
+
+       for (i = 0; i < paiv->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;
-               }
-               entry_offset += PAI_ENTRY_LENGTH;
-               DLIST_ADD(paiv->entry_list, paie);
-       }
+               paie->ace_flags = CVAL(entry_offset,0);
 
-       for (i = 0; i < paiv->num_def_entries; i++) {
-               struct pai_entry *paie;
+               entry_offset++;
 
-               paie = SMB_MALLOC_P(struct pai_entry);
-               if (!paie) {
-                       free_inherited_info(paiv);
+               if (!get_pai_owner_type(paie, entry_offset)) {
                        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: (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"));
-                               break;
-                       default:
-                               free_inherited_info(paiv);
-                               return NULL;
+               if (!def_entry) {
+                       DLIST_ADD(paiv->entry_list, paie);
+               } else {
+                       DLIST_ADD(paiv->def_entry_list, paie);
                }
-               entry_offset += PAI_ENTRY_LENGTH;
-               DLIST_ADD(paiv->def_entry_list, paie);
+               entry_offset += PAI_V2_ENTRY_LENGTH;
+       }
+       return entry_offset;
+}
+
+/************************************************************************
+ Convert to in-memory format from version 2.
+************************************************************************/
+
+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: num_entries = %u, num_def_entries = %u\n",
+                       paiv->num_entries, paiv->num_def_entries ));
+
+       entry_offset = create_pai_v2_entries(paiv, entry_offset, false);
+       if (entry_offset == NULL) {
+               free_inherited_info(paiv);
+               return NULL;
+       }
+       entry_offset = create_pai_v2_entries(paiv, 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.
 ************************************************************************/
@@ -437,19 +586,22 @@ static struct pai_val *fload_inherited_info(files_struct *fsp)
        struct pai_val *paiv = NULL;
        ssize_t ret;
 
-       if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+       if (!lp_map_acl_inherit(SNUM(fsp->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)
+               if (fsp->fh->fd != -1) {
                        ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
                                        pai_buf, pai_buf_size);
-               else
+               } else {
                        ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
                                        pai_buf, pai_buf_size);
+               }
 
                if (ret == -1) {
                        if (errno != ERANGE) {
@@ -483,8 +635,11 @@ static struct pai_val *fload_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 is 0x%x for file %s\n",
+                       (unsigned int)paiv->sd_type,
+                       fsp->fsp_name));
+       }
 
        SAFE_FREE(pai_buf);
        return paiv;
@@ -547,8 +702,10 @@ static struct pai_val *load_inherited_info(const struct connection_struct *conn,
 
        paiv = create_pai_val(pai_buf, ret);
 
-       if (paiv && paiv->pai_protected) {
-               DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fname));
+       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);
@@ -563,12 +720,12 @@ static struct pai_val *load_inherited_info(const struct connection_struct *conn,
  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;
@@ -578,13 +735,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);
        }
 }
@@ -641,8 +798,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' : '-');
@@ -727,8 +884,8 @@ static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_AC
 
 void create_file_sids(const 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 );
+       uid_to_sid( powner_sid, psbuf->st_ex_uid );
+       gid_to_sid( pgroup_sid, psbuf->st_ex_gid );
 }
 
 /****************************************************************************
@@ -760,7 +917,7 @@ static bool identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
 
 static void merge_aces( canon_ace **pp_list_head )
 {
-       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;
 
@@ -769,7 +926,7 @@ 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;
 
@@ -791,7 +948,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                /* Merge two allow or two deny ACE's. */
 
                                curr_ace_outer->perms |= curr_ace->perms;
-                               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. */
                        }
@@ -804,7 +961,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;
 
@@ -836,7 +993,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. */
 
@@ -852,7 +1009,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;
                                }
@@ -863,7 +1020,7 @@ 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;
 }
 
 /****************************************************************************
@@ -1116,16 +1273,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 (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid)
-               return True;
+               if (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);
 }
 
@@ -1192,7 +1369,7 @@ 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;
 
@@ -1222,7 +1399,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
 
                        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);
@@ -1237,7 +1414,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) {
@@ -1248,7 +1425,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                                pace->perms = 0;
                        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);
@@ -1270,7 +1447,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                        pace->perms = 0;
                        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);
        }
@@ -1448,7 +1625,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
                        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;
 
                        /*
@@ -1461,7 +1638,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                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;
 
                        /*
@@ -1476,7 +1653,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
                        current_ace->owner_type = UID_ACE;
                        /* If it's the owning user, this is a user_obj, not
                         * a user. */
-                       if (current_ace->unix_ug.uid == pst->st_uid) {
+                       if (current_ace->unix_ug.uid == pst->st_ex_uid) {
                                current_ace->type = SMB_ACL_USER_OBJ;
                        } else {
                                current_ace->type = SMB_ACL_USER;
@@ -1485,7 +1662,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
                        current_ace->owner_type = GID_ACE;
                        /* If it's the primary group, this is a group_obj, not
                         * a group. */
-                       if (current_ace->unix_ug.gid == pst->st_gid) {
+                       if (current_ace->unix_ug.gid == pst->st_ex_gid) {
                                current_ace->type = SMB_ACL_GROUP_OBJ;
                        } else {
                                current_ace->type = SMB_ACL_GROUP;
@@ -1519,7 +1696,9 @@ static bool create_canon_ace_lists(files_struct *fsp,
 
                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
@@ -2093,7 +2272,7 @@ static bool unpack_canon_ace(files_struct *fsp,
         * A default 3 element mode entry for a directory should be rwx --- ---.
         */
 
-       pst->st_mode = create_default_mode(fsp, False);
+       pst->st_ex_mode = create_default_mode(fsp, False);
 
        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);
@@ -2109,7 +2288,7 @@ static bool unpack_canon_ace(files_struct *fsp,
         * it's a directory.
         */
 
-       pst->st_mode = create_default_mode(fsp, True);
+       pst->st_ex_mode = create_default_mode(fsp, 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);
@@ -2147,12 +2326,12 @@ static bool unpack_canon_ace(files_struct *fsp,
 
 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) {
@@ -2160,7 +2339,7 @@ static void arrange_posix_perms(const 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 ));
@@ -2173,18 +2352,18 @@ static void arrange_posix_perms(const 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.
 ****************************************************************************/
@@ -2195,7 +2374,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                                   const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
 {
        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;
@@ -2223,7 +2402,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                        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:
@@ -2240,7 +2419,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                                         * 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) {
+                                       if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_ex_uid) {
                                                SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
                                                continue;
                                        }
@@ -2253,7 +2432,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                        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:
@@ -2297,17 +2476,17 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                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, conn->params,
-                                     S_ISDIR(psbuf->st_mode), powner, pgroup,
+       if (!ensure_canon_entry_valid(&l_head, conn->params,
+                                     S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
                                      psbuf, False))
                goto fail;
 
@@ -2318,7 +2497,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
 
        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. */
@@ -2326,7 +2505,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                        ace->perms &= acl_mask;
 
                if (ace->perms == 0) {
-                       DLIST_PROMOTE(list_head, ace);
+                       DLIST_PROMOTE(l_head, ace);
                }
 
                if( DEBUGLVL( 10 ) ) {
@@ -2334,15 +2513,15 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                }
        }
 
-       arrange_posix_perms(fname,&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;
 }
 
@@ -2368,10 +2547,11 @@ static bool current_user_in_group(gid_t gid)
 ****************************************************************************/
 
 static bool acl_group_override(connection_struct *conn,
-                               gid_t prim_gid,
+                               const SMB_STRUCT_STAT *psbuf,
                                const char *fname)
 {
-       SMB_STRUCT_STAT sbuf;
+       struct smb_filename *smb_fname = NULL;
+       NTSTATUS status;
 
        if ((errno != EPERM) && (errno != EACCES)) {
                return false;
@@ -2379,15 +2559,24 @@ static bool acl_group_override(connection_struct *conn,
 
        /* file primary group == user primary or supplementary group */
        if (lp_acl_group_control(SNUM(conn)) &&
-                       current_user_in_group(prim_gid)) {
+                       current_user_in_group(psbuf->st_ex_gid)) {
                return true;
        }
 
+       status = create_synthetic_smb_fname_split(talloc_tos(), fname, psbuf,
+                                                 &smb_fname);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+
        /* user has writeable permission */
        if (lp_dos_filemode(SNUM(conn)) &&
-                       can_write_to_file(conn, fname, &sbuf)) {
+           can_write_to_file(conn, smb_fname)) {
+               TALLOC_FREE(smb_fname);
                return true;
        }
+       TALLOC_FREE(smb_fname);
 
        return false;
 }
@@ -2396,7 +2585,11 @@ static bool acl_group_override(connection_struct *conn,
  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;
@@ -2575,7 +2768,7 @@ 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, fsp->fsp_name)) {
+                       if (acl_group_override(conn, psbuf, fsp->fsp_name)) {
                                int sret;
 
                                DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
@@ -2606,7 +2799,7 @@ 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, fsp->fsp_name)) {
+                       if (acl_group_override(conn, psbuf, fsp->fsp_name)) {
                                int sret;
 
                                DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
@@ -2814,6 +3007,42 @@ 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
@@ -2841,19 +3070,22 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
        canon_ace *dir_ace = NULL;
        SEC_ACE *nt_ace_list = NULL;
        size_t num_profile_acls = 0;
+       DOM_SID orig_owner_sid;
        SEC_DESC *psd = NULL;
+       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)) {
@@ -2879,7 +3111,7 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                        goto done;
                }
 
-               if (S_ISDIR(sbuf->st_mode) && def_acl) {
+               if (S_ISDIR(sbuf->st_ex_mode) && def_acl) {
                        dir_ace = canonicalise_acl(conn, name, def_acl,
                                                   sbuf,
                                                   &global_sid_Creator_Owner,
@@ -2963,46 +3195,48 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                                uint32_t acc = map_canon_ace_perms(SNUM(conn),
                                                &nt_acl_type,
                                                ace->perms,
-                                               S_ISDIR(sbuf->st_mode));
+                                               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))) {
-                               init_sec_ace(&nt_ace_list[num_aces++],
-                                               &global_sid_Builtin_Users,
-                                               SEC_ACE_TYPE_ACCESS_ALLOWED,
-                                               FILE_GENERIC_ALL, 0);
+                               add_or_replace_ace(nt_ace_list, &num_aces,
+                                                  &global_sid_Builtin_Users,
+                                                  SEC_ACE_TYPE_ACCESS_ALLOWED,
+                                                  FILE_GENERIC_ALL, 0);
                        }
 
                        for (ace = dir_ace; ace != NULL; ace = ace->next) {
                                uint32_t acc = map_canon_ace_perms(SNUM(conn),
                                                &nt_acl_type,
                                                ace->perms,
-                                               S_ISDIR(sbuf->st_mode));
+                                               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))) {
-                               init_sec_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|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);
                        }
 
                        /*
@@ -3013,6 +3247,18 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
 
                        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) {
@@ -3045,8 +3291,10 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
         * 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) {
@@ -3115,7 +3363,7 @@ NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
        DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
 
        /* Get the stat struct for the owner info. */
-       if(SMB_VFS_STAT(conn, name, &sbuf) != 0) {
+       if(vfs_stat_smb_fname(conn, name, &sbuf) != 0) {
                return map_nt_error_from_unix(errno);
        }
 
@@ -3123,7 +3371,7 @@ NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
        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(sbuf.st_mode)) {
+       if(S_ISDIR(sbuf.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);
        }
@@ -3187,7 +3435,16 @@ 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 (vfs_stat_smb_fname(conn,fname,&st)) {
                return -1;
        }
 
@@ -3195,12 +3452,6 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
                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;
-
        become_root();
        /* Keep the current file gid the same. */
        ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
@@ -3222,20 +3473,18 @@ 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_tos();
        char *parent_name = NULL;
        SEC_ACE *new_ace = NULL;
        unsigned int num_aces = pcsd->dacl->num_aces;
-       SMB_STRUCT_STAT sbuf;
        NTSTATUS status;
        int info;
        unsigned int i, j;
        SEC_DESC *psd = dup_sec_desc(talloc_tos(), pcsd);
-       bool is_dacl_protected = (pcsd->type & SE_DESC_DACL_PROTECTED);
-
-       ZERO_STRUCT(sbuf);
+       bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
 
        if (psd == NULL) {
                return NT_STATUS_NO_MEMORY;
@@ -3245,12 +3494,17 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                return NT_STATUS_NO_MEMORY;
        }
 
+       status = create_synthetic_smb_fname_split(mem_ctx, parent_name, 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 */
-               parent_name,                            /* fname */
-               0,                                      /* create_file_flags */
+               smb_dname,                              /* fname */
                FILE_READ_ATTRIBUTES,                   /* access_mask */
                FILE_SHARE_NONE,                        /* share_access */
                FILE_OPEN,                              /* create_disposition*/
@@ -3261,8 +3515,9 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                NULL,                                   /* sd */
                NULL,                                   /* ea_list */
                &parent_fsp,                            /* result */
-               &info,                                  /* pinfo */
-               &sbuf);                                 /* psbuf */
+               &info);                                 /* pinfo */
+
+       TALLOC_FREE(smb_fname);
 
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -3426,8 +3681,9 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
        bool acl_perms = False;
        mode_t orig_mode = (mode_t)0;
        NTSTATUS status;
-       uid_t orig_uid;
-       gid_t orig_gid;
+       bool set_acl_as_root = false;
+       bool acl_set_support = false;
+       bool ret = false;
 
        DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
 
@@ -3441,17 +3697,15 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
         */
 
        if(fsp->is_directory || fsp->fh->fd == -1) {
-               if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
+               if(vfs_stat_smb_fname(fsp->conn,fsp->fsp_name, &sbuf) != 0)
                        return map_nt_error_from_unix(errno);
        } else {
                if(SMB_VFS_FSTAT(fsp, &sbuf) != 0)
                        return map_nt_error_from_unix(errno);
        }
 
-       /* Save the original elements we check against. */
-       orig_mode = sbuf.st_mode;
-       orig_uid = sbuf.st_uid;
-       orig_gid = sbuf.st_gid;
+       /* Save the original element we check against. */
+       orig_mode = sbuf.st_ex_mode;
 
        /*
         * Unpack the user/group/world id's.
@@ -3468,7 +3722,7 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
         * Noticed by Simo.
         */
 
-       if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) {
+       if (((user != (uid_t)-1) && (sbuf.st_ex_uid != user)) || (( grp != (gid_t)-1) && (sbuf.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 ));
@@ -3488,179 +3742,198 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                 */
 
                if(fsp->is_directory) {
-                       if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
+                       if(vfs_stat_smb_fname(fsp->conn, fsp->fsp_name,
+                                             &sbuf) != 0) {
                                return map_nt_error_from_unix(errno);
                        }
                } else {
 
-                       int ret;
+                       int sret;
 
                        if(fsp->fh->fd == -1)
-                               ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
+                               sret = vfs_stat_smb_fname(fsp->conn,
+                                                         fsp->fsp_name,
+                                                         &sbuf);
                        else
-                               ret = SMB_VFS_FSTAT(fsp, &sbuf);
+                               sret = SMB_VFS_FSTAT(fsp, &sbuf);
 
-                       if(ret != 0)
+                       if(sret != 0)
                                return map_nt_error_from_unix(errno);
                }
 
-               /* Save the original elements we check against. */
-               orig_mode = sbuf.st_mode;
-               orig_uid = sbuf.st_uid;
-               orig_gid = sbuf.st_gid;
+               /* Save the original element we check against. */
+               orig_mode = sbuf.st_ex_mode;
+
+               /* 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(&sbuf, &file_owner_sid, &file_grp_sid);
 
-#if 0
-       /* Disable this - prevents ACL inheritance from the ACL editor. JRA. */
-
-       /* See here: http://www.codeproject.com/KB/winsdk/accessctrl2.aspx
-        * for details and also the log trace in bug #4308. JRA.
-        */
-
-       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) ) {
-               SEC_DESC *new_sd = NULL;
-               status = append_parent_acl(fsp, psd, &new_sd);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               psd = new_sd;
-       }
-#endif
-
        acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
                                        &file_ace_list, &dir_ace_list, security_info_sent, psd);
 
        /* Ignore W2K traverse DACL set. */
-       if (file_ace_list || dir_ace_list) {
+       if (!file_ace_list && !dir_ace_list) {
+               return NT_STATUS_OK;
+       }
 
-               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 (!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;
+       }
 
-               /*
-                * Only change security if we got a DACL.
-                */
+       /*
+        * Only change security if we got a DACL.
+        */
+
+       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;
+       }
 
-               if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
+       /*
+        * Try using the POSIX ACL set first. Fall back to chmod if
+        * we have no ACL support on this filesystem.
+        */
+
+       if (acl_perms && file_ace_list) {
+               if (set_acl_as_root) {
+                       become_root();
+               }
+               ret = set_canon_ace_list(fsp, file_ace_list, False, &sbuf, &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->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);
+               }
+       }
 
-                       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, &sbuf, &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->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 {
+                       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);
+                       if (set_acl_as_root) {
+                               unbecome_root();
+                       }
+                       if (sret == -1) {
+                               if (acl_group_override(conn, &sbuf, 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->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); 
+                                       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, 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->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->fsp_name ));
+                       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->fsp_name, (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",
+                       if (set_acl_as_root) {
+                               become_root();
+                       }
+                       sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
+                       if (set_acl_as_root) {
+                               unbecome_root();
+                       }
+                       if(sret == -1) {
+                               if (acl_group_override(conn, &sbuf, fsp->fsp_name)) {
+                                       DEBUG(5,("set_nt_acl: acl group control on and "
+                                               "current user in file %s primary group. Override chmod\n",
                                                fsp->fsp_name ));
-                                       return NT_STATUS_ACCESS_DENIED;
+
+                                       become_root();
+                                       sret = SMB_VFS_CHMOD(conn,fsp->fsp_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, fsp->fsp_name)) {
-                                                       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->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);
                                }
                        }
                }
-
-               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);
+
        return NT_STATUS_OK;
 }
 
@@ -4033,10 +4306,14 @@ bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_
 {
        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) {