s3: include smbd/smbd.h where needed.
[kai/samba-autobuild/.git] / source3 / smbd / posix_acls.c
index 2f84a831c6c30737f15a01058dbcc83857fb490a..1544ca88b57ee7d9ef7549f3a378dde58fd91bb9 100644 (file)
@@ -3,6 +3,7 @@
    SMB NT Security Descriptor / Unix permission conversion.
    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
 */
 
 #include "includes.h"
+#include "smbd/smbd.h"
+#include "system/filesys.h"
+#include "../libcli/security/security.h"
+#include "trans2.h"
+#include "passdb/lookup_sid.h"
 
-extern struct current_user current_user;
 extern const struct generic_mapping file_generic_mapping;
 
 #undef  DBGC_CLASS
@@ -43,7 +48,7 @@ typedef struct canon_ace {
        struct canon_ace *next, *prev;
        SMB_ACL_TAG_T type;
        mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
-       DOM_SID trustee;
+       struct dom_sid trustee;
        enum ace_owner owner_type;
        enum ace_attribute attr;
        posix_id unix_ug;
@@ -180,6 +185,7 @@ static char *create_pai_buf_v2(canon_ace *file_ace_list,
        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) {
                num_entries++;
@@ -206,8 +212,12 @@ static char *create_pai_buf_v2(canon_ace *file_ace_list,
        SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
        SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
 
+       DEBUG(10,("create_pai_buf_v2: sd_type = 0x%x\n",
+                       (unsigned int)sd_type ));
+
        entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
 
+       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);
@@ -215,6 +225,12 @@ static char *create_pai_buf_v2(canon_ace *file_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;
        }
 
@@ -225,6 +241,12 @@ static char *create_pai_buf_v2(canon_ace *file_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;
        }
 
@@ -255,15 +277,16 @@ static void store_inheritance_attributes(files_struct *fsp,
                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, SAMBA_POSIX_INHERITANCE_EA_NAME,
-                               pai_buf, store_size, 0);
+               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: type 0x%x for file %s\n",
                (unsigned int)sd_type,
-               fsp->fsp_name));
+               fsp_str_dbg(fsp)));
 
        if (ret == -1 && !no_acl_syscall_error(errno)) {
                DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
@@ -398,6 +421,8 @@ static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
                        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;
@@ -421,6 +446,7 @@ static const char *create_pai_v1_entries(struct pai_val *paiv,
 
                paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
                if (!get_pai_owner_type(paie, entry_offset)) {
+                       SAFE_FREE(paie);
                        return NULL;
                }
 
@@ -455,7 +481,7 @@ static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
        memset(paiv, '\0', sizeof(struct pai_val));
 
        paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
-                       SE_DESC_DACL_PROTECTED : 0;
+                       SEC_DESC_DACL_PROTECTED : 0;
 
        paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
        paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
@@ -484,12 +510,13 @@ static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
 ************************************************************************/
 
 static const char *create_pai_v2_entries(struct pai_val *paiv,
+                               unsigned int num_entries,
                                const char *entry_offset,
                                bool def_entry)
 {
-       int i;
+       unsigned int i;
 
-       for (i = 0; i < paiv->num_entries; i++) {
+       for (i = 0; i < num_entries; i++) {
                struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
                if (!paie) {
                        return NULL;
@@ -497,9 +524,8 @@ static const char *create_pai_v2_entries(struct pai_val *paiv,
 
                paie->ace_flags = CVAL(entry_offset,0);
 
-               entry_offset++;
-
-               if (!get_pai_owner_type(paie, entry_offset)) {
+               if (!get_pai_owner_type(paie, entry_offset+1)) {
+                       SAFE_FREE(paie);
                        return NULL;
                }
                if (!def_entry) {
@@ -539,15 +565,18 @@ static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
 
        entry_offset = buf + PAI_V2_ENTRIES_BASE;
 
-       DEBUG(10,("create_pai_val_v2: num_entries = %u, num_def_entries = %u\n",
+       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, entry_offset, false);
+       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, entry_offset, true);
+       entry_offset = create_pai_v2_entries(paiv, paiv->num_def_entries,
+                               entry_offset, true);
        if (entry_offset == NULL) {
                free_inherited_info(paiv);
                return NULL;
@@ -598,8 +627,10 @@ static struct pai_val *fload_inherited_info(files_struct *fsp)
                        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,SAMBA_POSIX_INHERITANCE_EA_NAME,
-                                       pai_buf, pai_buf_size);
+                       ret = SMB_VFS_GETXATTR(fsp->conn,
+                                              fsp->fsp_name->base_name,
+                                              SAMBA_POSIX_INHERITANCE_EA_NAME,
+                                              pai_buf, pai_buf_size);
                }
 
                if (ret == -1) {
@@ -617,7 +648,8 @@ static struct pai_val *fload_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, fsp_str_dbg(fsp)));
 
        if (ret == -1) {
                /* No attribute or not supported. */
@@ -636,8 +668,7 @@ static struct pai_val *fload_inherited_info(files_struct *fsp)
 
        if (paiv) {
                DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
-                       (unsigned int)paiv->sd_type,
-                       fsp->fsp_name));
+                         (unsigned int)paiv->sd_type, fsp_str_dbg(fsp)));
        }
 
        SAFE_FREE(pai_buf);
@@ -881,31 +912,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.
 ****************************************************************************/
 
-void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
+void create_file_sids(const SMB_STRUCT_STAT *psbuf, struct dom_sid *powner_sid, struct 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)
-{
-       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 );
 }
 
 /****************************************************************************
@@ -914,7 +924,7 @@ 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 *l_head = *pp_list_head;
        canon_ace *curr_ace_outer;
@@ -932,12 +942,24 @@ static void merge_aces( canon_ace **pp_list_head )
                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 = (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                                               (curr_ace->attr == curr_ace_outer->attr));
+                       } else {
+                               can_merge = (dom_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);
@@ -946,7 +968,13 @@ 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;
+                               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. */
@@ -975,7 +1003,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 (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
                                (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
 
                                if( DEBUGLVL( 10 )) {
@@ -1046,7 +1074,7 @@ bool nt4_compatible_acls(void)
  not get. Deny entries are implicit on get with ace->perms = 0.
 ****************************************************************************/
 
-static uint32_t map_canon_ace_perms(int snum,
+uint32_t map_canon_ace_perms(int snum,
                                enum security_ace_type *pacl_type,
                                mode_t perms,
                                bool directory_ace)
@@ -1087,6 +1115,10 @@ static uint32_t 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 ));
 
@@ -1139,13 +1171,16 @@ static mode_t map_nt_perms( uint32 *mask, int type)
 }
 
 /****************************************************************************
- Unpack a SEC_DESC into a UNIX owner and group.
+ Unpack a struct security_descriptor into a UNIX owner and group.
 ****************************************************************************/
 
-NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS unpack_nt_owners(struct connection_struct *conn,
+                       uid_t *puser, gid_t *pgrp,
+                       uint32 security_info_sent, const struct
+                       security_descriptor *psd)
 {
-       DOM_SID owner_sid;
-       DOM_SID grp_sid;
+       struct dom_sid owner_sid;
+       struct dom_sid grp_sid;
 
        *puser = (uid_t)-1;
        *pgrp = (gid_t)-1;
@@ -1169,13 +1204,13 @@ NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_i
         * This may be a group chown only set.
         */
 
-       if (security_info_sent & OWNER_SECURITY_INFORMATION) {
+       if (security_info_sent & SECINFO_OWNER) {
                sid_copy(&owner_sid, psd->owner_sid);
                if (!sid_to_uid(&owner_sid, puser)) {
-                       if (lp_force_unknown_acl_user(snum)) {
+                       if (lp_force_unknown_acl_user(SNUM(conn))) {
                                /* this allows take ownership to work
                                 * reasonably */
-                               *puser = current_user.ut.uid;
+                               *puser = get_current_uid(conn);
                        } else {
                                DEBUG(3,("unpack_nt_owners: unable to validate"
                                         " owner sid for %s\n",
@@ -1192,13 +1227,13 @@ NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_i
         * This may be an owner chown only set.
         */
 
-       if (security_info_sent & GROUP_SECURITY_INFORMATION) {
+       if (security_info_sent & SECINFO_GROUP) {
                sid_copy(&grp_sid, psd->group_sid);
                if (!sid_to_gid( &grp_sid, pgrp)) {
-                       if (lp_force_unknown_acl_user(snum)) {
+                       if (lp_force_unknown_acl_user(SNUM(conn))) {
                                /* this allows take group ownership to work
                                 * reasonably */
-                               *pgrp = current_user.ut.gid;
+                               *pgrp = get_current_gid(conn);
                        } else {
                                DEBUG(3,("unpack_nt_owners: unable to validate"
                                         " group sid.\n"));
@@ -1263,25 +1298,47 @@ static void apply_default_perms(const struct share_params *params,
  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(connection_struct *conn, canon_ace *uid_ace, canon_ace *group_ace )
 {
        const char *u_name = NULL;
 
        /* "Everyone" always matches every uid. */
 
-       if (sid_equal(&group_ace->trustee, &global_sid_World))
+       if (dom_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 == get_current_uid(conn)) {
+               const struct security_unix_token *curr_utok = NULL;
+               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 == get_current_gid(conn)) {
+                       return True;
+               }
+
+               curr_utok = get_current_utok(conn);
+               for (i=0; i < curr_utok->ngroups; i++) {
+                       if (group_ace->unix_ug.gid == curr_utok->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);
 }
 
@@ -1295,11 +1352,11 @@ 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,
+static bool ensure_canon_entry_valid(connection_struct *conn, 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 struct dom_sid *pfile_owner_sid,
+                                                       const struct dom_sid *pfile_grp_sid,
                                                        const SMB_STRUCT_STAT *pst,
                                                        bool setting_acl)
 {
@@ -1348,7 +1405,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;
 
@@ -1361,7 +1418,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
 
                        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 (uid_entry_in_group(pace, pace_iter)) {
+                                       if (uid_entry_in_group(conn, pace, pace_iter)) {
                                                pace->perms |= pace_iter->perms;
                                                group_matched = True;
                                        }
@@ -1378,7 +1435,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);
@@ -1393,7 +1450,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) {
@@ -1404,7 +1461,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);
@@ -1426,7 +1483,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);
        }
@@ -1440,7 +1497,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
  file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
 ****************************************************************************/
 
-static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
+static void check_owning_objs(canon_ace *ace, struct dom_sid *pfile_owner_sid, struct dom_sid *pfile_grp_sid)
 {
        bool got_user_obj, got_group_obj;
        canon_ace *current_ace;
@@ -1463,12 +1520,12 @@ static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID
 
        for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
                if (!got_user_obj && current_ace->owner_type == UID_ACE &&
-                               sid_equal(&current_ace->trustee, pfile_owner_sid)) {
+                               dom_sid_equal(&current_ace->trustee, pfile_owner_sid)) {
                        current_ace->type = SMB_ACL_USER_OBJ;
                        got_user_obj = True;
                }
                if (!got_group_obj && current_ace->owner_type == GID_ACE &&
-                               sid_equal(&current_ace->trustee, pfile_grp_sid)) {
+                               dom_sid_equal(&current_ace->trustee, pfile_grp_sid)) {
                        current_ace->type = SMB_ACL_GROUP_OBJ;
                        got_group_obj = True;
                }
@@ -1480,16 +1537,60 @@ static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID
 }
 
 /****************************************************************************
- Unpack a SEC_DESC into two canonical ace lists.
+ If an ACE entry is SMB_ACL_USER_OBJ and not CREATOR_OWNER, map to SMB_ACL_USER.
+ If an ACE entry is SMB_ACL_GROUP_OBJ and not CREATOR_GROUP, map to SMB_ACL_GROUP
+****************************************************************************/
+
+static bool dup_owning_ace(canon_ace *dir_ace, canon_ace *ace)
+{
+       /* dir ace must be followings.
+          SMB_ACL_USER_OBJ : trustee(CREATOR_OWNER) -> Posix ACL d:u::perm
+          SMB_ACL_USER     : not trustee    -> Posix ACL u:user:perm
+          SMB_ACL_USER_OBJ : trustee -> convert to SMB_ACL_USER : trustee
+          Posix ACL u:trustee:perm
+
+          SMB_ACL_GROUP_OBJ: trustee(CREATOR_GROUP) -> Posix ACL d:g::perm
+          SMB_ACL_GROUP    : not trustee   -> Posix ACL g:group:perm
+          SMB_ACL_GROUP_OBJ: trustee -> convert to SMB_ACL_GROUP : trustee
+          Posix ACL g:trustee:perm
+       */
+
+       if (ace->type == SMB_ACL_USER_OBJ &&
+                       !(dom_sid_equal(&ace->trustee, &global_sid_Creator_Owner))) {
+               canon_ace *dup_ace = dup_canon_ace(ace);
+
+               if (dup_ace == NULL) {
+                       return false;
+               }
+               dup_ace->type = SMB_ACL_USER;
+               DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
+       }
+
+       if (ace->type == SMB_ACL_GROUP_OBJ &&
+                       !(dom_sid_equal(&ace->trustee, &global_sid_Creator_Group))) {
+               canon_ace *dup_ace = dup_canon_ace(ace);
+
+               if (dup_ace == NULL) {
+                       return false;
+               }
+               dup_ace->type = SMB_ACL_GROUP;
+               DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
+       }
+
+       return true;
+}
+
+/****************************************************************************
+ Unpack a struct security_descriptor 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,
+                                       const SMB_STRUCT_STAT *pst,
+                                       struct dom_sid *pfile_owner_sid,
+                                       struct dom_sid *pfile_grp_sid,
                                        canon_ace **ppfile_ace,
                                        canon_ace **ppdir_ace,
-                                       const SEC_ACL *dacl)
+                                       const struct security_acl *dacl)
 {
        bool all_aces_are_inherit_only = (fsp->is_directory ? True : False);
        canon_ace *file_ace = NULL;
@@ -1507,7 +1608,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
         */
 
        for(i = 0; i < dacl->num_aces; i++) {
-               SEC_ACE *psa = &dacl->aces[i];
+               struct security_ace *psa = &dacl->aces[i];
 
                if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
                        DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
@@ -1544,15 +1645,15 @@ static bool create_canon_ace_lists(files_struct *fsp,
         */
 
        for(i = 0; i < dacl->num_aces; i++) {
-               SEC_ACE *psa1 = &dacl->aces[i];
+               struct security_ace *psa1 = &dacl->aces[i];
 
                for (j = i + 1; j < dacl->num_aces; j++) {
-                       SEC_ACE *psa2 = &dacl->aces[j];
+                       struct security_ace *psa2 = &dacl->aces[j];
 
                        if (psa1->access_mask != psa2->access_mask)
                                continue;
 
-                       if (!sid_equal(&psa1->trustee, &psa2->trustee))
+                       if (!dom_sid_equal(&psa1->trustee, &psa2->trustee))
                                continue;
 
                        /*
@@ -1575,10 +1676,10 @@ static bool create_canon_ace_lists(files_struct *fsp,
        }
 
        for(i = 0; i < dacl->num_aces; i++) {
-               SEC_ACE *psa = &dacl->aces[i];
+               struct security_ace *psa = &dacl->aces[i];
 
                /*
-                * Create a cannon_ace entry representing this NT DACL ACE.
+                * Create a canon_ace entry representing this NT DACL ACE.
                 */
 
                if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
@@ -1598,41 +1699,40 @@ static bool create_canon_ace_lists(files_struct *fsp,
                 * Note what kind of a POSIX ACL this should map to.
                 */
 
-               if( sid_equal(&current_ace->trustee, &global_sid_World)) {
+               if( dom_sid_equal(&current_ace->trustee, &global_sid_World)) {
                        current_ace->owner_type = WORLD_ACE;
                        current_ace->unix_ug.world = -1;
                        current_ace->type = SMB_ACL_OTHER;
-               } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
+               } else if (dom_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;
-               } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
+                       psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
+               } else if (dom_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;
                        /* 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;
@@ -1641,7 +1741,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;
@@ -1659,6 +1759,14 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                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 "
@@ -1706,8 +1814,12 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                        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;
@@ -1718,6 +1830,34 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                                        print_canon_ace( current_ace, 0);
                                }
 
+                               /*
+                                * We have a lossy mapping: directory ACE entries
+                                * CREATOR_OWNER ------\
+                                *     (map to)         +---> SMB_ACL_USER_OBJ
+                                * owning sid    ------/
+                                *
+                                * CREATOR_GROUP ------\
+                                *     (map to)         +---> SMB_ACL_GROUP_OBJ
+                                * primary group sid --/
+                                *
+                                * on set. And on read of a directory ACL
+                                *
+                                * SMB_ACL_USER_OBJ ----> CREATOR_OWNER
+                                * SMB_ACL_GROUP_OBJ ---> CREATOR_GROUP.
+                                *
+                                * Deal with this on set by duplicating
+                                * owning sid and primary group sid ACE
+                                * entries into the directory ACL.
+                                * Fix from Tsukasa Hamano <hamano@osstech.co.jp>.
+                                */
+
+                               if (!dup_owning_ace(dir_ace, current_ace)) {
+                                       DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+                                       free_canon_ace_list(file_ace);
+                                       free_canon_ace_list(dir_ace);
+                                       return false;
+                               }
+
                                /*
                                 * If this is not an inherit only ACE we need to add a duplicate
                                 * to the file acl.
@@ -1738,6 +1878,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
@@ -1764,8 +1911,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;
@@ -1927,7 +2076,7 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
  allow entries.
 ****************************************************************************/
 
-static void process_deny_list( canon_ace **pp_ace_list )
+static void process_deny_list(connection_struct *conn, canon_ace **pp_ace_list )
 {
        canon_ace *ace_list = *pp_ace_list;
        canon_ace *curr_ace = NULL;
@@ -1951,7 +2100,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
                        continue;
                }
 
-               if (!sid_equal(&curr_ace->trustee, &global_sid_World))
+               if (!dom_sid_equal(&curr_ace->trustee, &global_sid_World))
                        continue;
 
                /* JRATEST - assert. */
@@ -1964,11 +2113,11 @@ static void process_deny_list( canon_ace **pp_ace_list )
                         * list at this point including this entry.
                         */
 
-                       canon_ace *prev_entry = curr_ace->prev;
+                       canon_ace *prev_entry = DLIST_PREV(curr_ace);
 
                        free_canon_ace_list( curr_ace );
                        if (prev_entry)
-                               prev_entry->next = NULL;
+                               DLIST_REMOVE(ace_list, prev_entry);
                        else {
                                /* We deleted the entire list. */
                                ace_list = NULL;
@@ -2032,7 +2181,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
                        if (allow_ace_p->owner_type == UID_ACE)
                                continue;
 
-                       if (uid_entry_in_group( curr_ace, allow_ace_p))
+                       if (uid_entry_in_group(conn, curr_ace, allow_ace_p))
                                new_perms |= allow_ace_p->perms;
                }
 
@@ -2076,7 +2225,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
 
                        /* Mask off the deny group perms. */
 
-                       if (uid_entry_in_group( allow_ace_p, curr_ace))
+                       if (uid_entry_in_group(conn, allow_ace_p, curr_ace))
                                allow_ace_p->perms &= ~curr_ace->perms;
                }
 
@@ -2126,7 +2275,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
 
                        /* OR in the group perms. */
 
-                       if (uid_entry_in_group( curr_ace, allow_ace_p))
+                       if (uid_entry_in_group(conn, curr_ace, allow_ace_p))
                                curr_ace->perms |= allow_ace_p->perms;
                }
        }
@@ -2145,10 +2294,14 @@ 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;
+       mode_t mode;
+
+       if (interitable_mode) {
+               mode = unix_mode(fsp->conn, FILE_ATTRIBUTE_ARCHIVE,
+                                fsp->fsp_name, NULL);
+       } else {
+               mode = S_IRUSR;
+       }
 
        if (fsp->is_directory)
                mode |= (S_IWUSR|S_IXUSR);
@@ -2170,19 +2323,20 @@ static mode_t create_default_mode(files_struct *fsp, bool interitable_mode)
 }
 
 /****************************************************************************
- Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
+ Unpack a struct security_descriptor 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,
+                               const SMB_STRUCT_STAT *pst,
+                               struct dom_sid *pfile_owner_sid,
+                               struct dom_sid *pfile_grp_sid,
                                canon_ace **ppfile_ace,
                                canon_ace **ppdir_ace,
                                uint32 security_info_sent,
-                               const SEC_DESC *psd)
+                               const struct security_descriptor *psd)
 {
+       SMB_STRUCT_STAT st;
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
 
@@ -2198,7 +2352,7 @@ static bool unpack_canon_ace(files_struct *fsp,
         * If no DACL then this is a chown only security descriptor.
         */
 
-       if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
+       if(!(security_info_sent & SECINFO_DACL) || !psd->dacl)
                return True;
 
        /*
@@ -2222,10 +2376,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
@@ -2233,10 +2387,10 @@ static bool unpack_canon_ace(files_struct *fsp,
         */
 
        print_canon_ace_list( "file ace - before deny", file_ace);
-       process_deny_list( &file_ace);
+       process_deny_list(fsp->conn, &file_ace);
 
        print_canon_ace_list( "dir ace - before deny", dir_ace);
-       process_deny_list( &dir_ace);
+       process_deny_list(fsp->conn, &dir_ace);
 
        /*
         * A well formed POSIX file or default ACL has at least 3 entries, a 
@@ -2246,14 +2400,17 @@ static bool unpack_canon_ace(files_struct *fsp,
 
        print_canon_ace_list( "file ace - before valid", file_ace);
 
+       st = *pst;
+
        /*
         * 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);
+       st.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)) {
+       if (!ensure_canon_entry_valid(fsp->conn, &file_ace, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2267,9 +2424,10 @@ static bool unpack_canon_ace(files_struct *fsp,
         * it's a directory.
         */
 
-       pst->st_mode = create_default_mode(fsp, True);
+       st.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)) {
+       if (dir_ace && !ensure_canon_entry_valid(fsp->conn, &dir_ace, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2350,7 +2508,7 @@ static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
 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)
+                                  const struct dom_sid *powner, const struct 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 *l_head = NULL;
@@ -2363,7 +2521,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
        while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
                SMB_ACL_TAG_T tagtype;
                SMB_ACL_PERMSET_T permset;
-               DOM_SID sid;
+               struct dom_sid sid;
                posix_id unix_ug;
                enum ace_owner owner_type;
 
@@ -2381,7 +2539,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:
@@ -2391,17 +2549,6 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                                                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;
@@ -2411,7 +2558,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:
@@ -2464,8 +2611,8 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
         * This next call will ensure we have at least a user/group/world set.
         */
 
-       if (!ensure_canon_entry_valid(&l_head, conn->params,
-                                     S_ISDIR(psbuf->st_mode), powner, pgroup,
+       if (!ensure_canon_entry_valid(conn, &l_head, conn->params,
+                                     S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
                                      psbuf, False))
                goto fail;
 
@@ -2508,12 +2655,13 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
  Check if the current user group list contains a given group.
 ****************************************************************************/
 
-static bool current_user_in_group(gid_t gid)
+static bool current_user_in_group(connection_struct *conn, gid_t gid)
 {
        int i;
+       const struct security_unix_token *utok = get_current_utok(conn);
 
-       for (i = 0; i < current_user.ut.ngroups; i++) {
-               if (current_user.ut.groups[i] == gid) {
+       for (i = 0; i < utok->ngroups; i++) {
+               if (utok->groups[i] == gid) {
                        return True;
                }
        }
@@ -2526,24 +2674,21 @@ static bool current_user_in_group(gid_t gid)
 ****************************************************************************/
 
 static bool acl_group_override(connection_struct *conn,
-                               gid_t prim_gid,
-                               const char *fname)
+                              const struct smb_filename *smb_fname)
 {
-       SMB_STRUCT_STAT sbuf;
-
        if ((errno != EPERM) && (errno != EACCES)) {
                return false;
        }
 
        /* 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(conn, smb_fname->st.st_ex_gid)) {
                return true;
        }
 
        /* user has writeable permission */
        if (lp_dos_filemode(SNUM(conn)) &&
-                       can_write_to_file(conn, fname, &sbuf)) {
+           can_write_to_file(conn, smb_fname)) {
                return true;
        }
 
@@ -2554,7 +2699,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;
@@ -2568,6 +2717,11 @@ static bool set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, bool defau
        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;
@@ -2584,7 +2738,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 )) {
@@ -2724,7 +2878,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.
@@ -2733,14 +2888,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, fsp->fsp_name)) {
+                       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;     
@@ -2748,9 +2907,12 @@ 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;
                        }
                }
@@ -2764,11 +2926,13 @@ 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, 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, the_acl);
@@ -2779,8 +2943,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;
                        }
                }
@@ -2848,8 +3014,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;
        }
 
@@ -2863,8 +3030,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;
        }
 
@@ -2898,9 +3065,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;
 }
@@ -2912,7 +3080,7 @@ posix perms.\n", fsp->fsp_name ));
   Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
 ****************************************************************************/
 
-static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
+static size_t merge_default_aces( struct security_ace *nt_ace_list, size_t num_aces)
 {
        size_t i, j;
 
@@ -2927,7 +3095,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
                        if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
                                (nt_ace_list[i].size == nt_ace_list[j].size) &&
                                (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
-                               sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
+                               dom_sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
                                (i_inh == j_inh) &&
                                (i_flags_ni == 0) &&
                                (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
@@ -2944,7 +3112,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
                                                                (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
                                        if (num_aces - i - 1 > 0)
                                                memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
-                                                               sizeof(SEC_ACE));
+                                                               sizeof(struct security_ace));
 
                                        DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
                                                (unsigned int)i, (unsigned int)j ));
@@ -2958,7 +3126,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
                                                                (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
                                        if (num_aces - j - 1 > 0)
                                                memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
-                                                               sizeof(SEC_ACE));
+                                                               sizeof(struct security_ace));
 
                                        DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
                                                (unsigned int)j, (unsigned int)i ));
@@ -2972,6 +3140,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(struct security_ace *nt_ace_list, size_t *num_aces,
+                               const struct 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 (dom_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
@@ -2986,35 +3190,38 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                                      SMB_ACL_T posix_acl,
                                      SMB_ACL_T def_acl,
                                      uint32_t security_info,
-                                     SEC_DESC **ppdesc)
+                                     struct security_descriptor **ppdesc)
 {
-       DOM_SID owner_sid;
-       DOM_SID group_sid;
+       struct dom_sid owner_sid;
+       struct dom_sid group_sid;
        size_t sd_size = 0;
-       SEC_ACL *psa = NULL;
+       struct security_acl *psa = NULL;
        size_t num_acls = 0;
        size_t num_def_acls = 0;
        size_t num_aces = 0;
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
-       SEC_ACE *nt_ace_list = NULL;
+       struct security_ace *nt_ace_list = NULL;
        size_t num_profile_acls = 0;
-       SEC_DESC *psd = NULL;
+       struct dom_sid orig_owner_sid;
+       struct security_descriptor *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)) {
+       if ((security_info & SECINFO_DACL) && !(security_info & SECINFO_PROTECTED_DACL)) {
 
                /*
                 * In the optimum case Creator Owner and Creator Group would be used for
@@ -3037,7 +3244,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,
@@ -3106,12 +3313,12 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                        num_def_acls = count_canon_ace_list(dir_ace);
 
                        /* Allocate the ace list. */
-                       if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
+                       if ((nt_ace_list = SMB_MALLOC_ARRAY(struct security_ace,num_acls + num_profile_acls + num_def_acls)) == NULL) {
                                DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
                                goto done;
                        }
 
-                       memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
+                       memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(struct security_ace) );
 
                        /*
                         * Create the NT ACE list from the canonical ace lists.
@@ -3121,7 +3328,7 @@ 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,
@@ -3132,17 +3339,17 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                        /* 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,
@@ -3156,9 +3363,13 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                        /* 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);
                        }
 
                        /*
@@ -3169,6 +3380,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 (dom_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) {
@@ -3177,11 +3400,11 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
                                goto done;
                        }
                }
-       } /* security_info & DACL_SECURITY_INFORMATION */
+       } /* security_info & SECINFO_DACL */
 
        psd = make_standard_sec_desc( talloc_tos(),
-                       (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
-                       (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
+                       (security_info & SECINFO_OWNER) ? &owner_sid : NULL,
+                       (security_info & SECINFO_GROUP) ? &group_sid : NULL,
                        psa,
                        &sd_size);
 
@@ -3230,7 +3453,7 @@ static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
 }
 
 NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
-                          SEC_DESC **ppdesc)
+                          struct security_descriptor **ppdesc)
 {
        SMB_STRUCT_STAT sbuf;
        SMB_ACL_T posix_acl = NULL;
@@ -3238,11 +3461,12 @@ NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
 
        *ppdesc = NULL;
 
-       DEBUG(10,("posix_fget_nt_acl: called for file %s\n", fsp->fsp_name ));
+       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,
+               return posix_get_nt_acl(fsp->conn, fsp->fsp_name->base_name,
                                        security_info, ppdesc);
        }
 
@@ -3256,24 +3480,35 @@ NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
 
        pal = fload_inherited_info(fsp);
 
-       return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name, &sbuf, pal,
-                                      posix_acl, NULL, security_info, ppdesc);
+       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)
+                         uint32_t security_info, struct security_descriptor **ppdesc)
 {
-       SMB_STRUCT_STAT sbuf;
        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(SMB_VFS_STAT(conn, name, &sbuf) != 0) {
+       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);
        }
 
@@ -3281,95 +3516,89 @@ 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(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, &sbuf, pal, posix_acl,
-                                      def_acl, security_info, ppdesc);
+       return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
+                                      posix_acl, def_acl, security_info,
+                                      ppdesc);
 }
 
 /****************************************************************************
  Try to chown a file. We will be able to chown it under the following conditions.
 
   1) If we have root privileges, then it will just work.
-  2) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
-  3) If we have SeRestorePrivilege we can change the user to any other user. 
+  2) If we have SeRestorePrivilege we can change the user + group to any other user. 
+  3) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
   4) If we have write permission to the file and dos_filemodes is set
      then allow chown to the currently authenticated user.
 ****************************************************************************/
 
-int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
+NTSTATUS try_chown(files_struct *fsp, uid_t uid, gid_t gid)
 {
-       int ret;
-       files_struct *fsp;
-       SMB_STRUCT_STAT st;
+       NTSTATUS status;
 
-       if(!CAN_WRITE(conn)) {
-               return -1;
+       if(!CAN_WRITE(fsp->conn)) {
+               return NT_STATUS_MEDIA_WRITE_PROTECTED;
        }
 
        /* Case (1). */
-       /* try the direct way first */
-       ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
-       if (ret == 0)
-               return 0;
+       status = vfs_chown_fsp(fsp, uid, gid);
+       if (NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
        /* Case (2) / (3) */
        if (lp_enable_privileges()) {
+               bool has_take_ownership_priv = security_token_has_privilege(
+                                               get_current_nttok(fsp->conn),
+                                               SEC_PRIV_TAKE_OWNERSHIP);
+               bool has_restore_priv = security_token_has_privilege(
+                                               get_current_nttok(fsp->conn),
+                                               SEC_PRIV_RESTORE);
+
+               if (has_restore_priv) {
+                       ; /* Case (2) */
+               } else if (has_take_ownership_priv) {
+                       /* Case (3) */
+                       if (uid == get_current_uid(fsp->conn)) {
+                               gid = (gid_t)-1;
+                       } else {
+                               has_take_ownership_priv = false;
+                       }
+               }
 
-               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,
-                                                      &se_restore);
-
-               /* Case (2) */
-               if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) ||
-               /* Case (3) */
-                    ( has_restore_priv ) ) {
-
+               if (has_take_ownership_priv || has_restore_priv) {
                        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);
+                       status = vfs_chown_fsp(fsp, uid, gid);
                        unbecome_root();
-                       return ret;
+                       return status;
                }
        }
 
        /* Case (4). */
-       if (!lp_dos_filemode(SNUM(conn))) {
-               errno = EPERM;
-               return -1;
+       if (!lp_dos_filemode(SNUM(fsp->conn))) {
+               return NT_STATUS_ACCESS_DENIED;
        }
 
        /* 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 (SMB_VFS_STAT(conn,fname,&st)) {
-               return -1;
-       }
-
-       if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname, &st, &fsp))) {
-               return -1;
+       if (uid != get_current_uid(fsp->conn)) {
+               return NT_STATUS_ACCESS_DENIED;
        }
 
        become_root();
        /* Keep the current file gid the same. */
-       ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+       status = vfs_chown_fsp(fsp, uid, (gid_t)-1);
        unbecome_root();
 
-       close_file_fchmod(NULL, fsp);
-
-       return ret;
+       return status;
 }
 
 #if 0
@@ -3380,38 +3609,42 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
 ****************************************************************************/
 
 NTSTATUS append_parent_acl(files_struct *fsp,
-                               const SEC_DESC *pcsd,
-                               SEC_DESC **pp_new_sd)
+                               const struct security_descriptor *pcsd,
+                               struct security_descriptor **pp_new_sd)
 {
-       SEC_DESC *parent_sd = NULL;
+       struct smb_filename *smb_dname = NULL;
+       struct security_descriptor *parent_sd = NULL;
        files_struct *parent_fsp = NULL;
        TALLOC_CTX *mem_ctx = talloc_tos();
        char *parent_name = NULL;
-       SEC_ACE *new_ace = NULL;
+       struct security_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);
+       struct security_descriptor *psd = dup_sec_desc(talloc_tos(), pcsd);
        bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
 
-       ZERO_STRUCT(sbuf);
-
        if (psd == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       if (!parent_dirname(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;
        }
 
+       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 */
-               parent_name,                            /* fname */
-               0,                                      /* create_file_flags */
+               smb_dname,                              /* fname */
                FILE_READ_ATTRIBUTES,                   /* access_mask */
                FILE_SHARE_NONE,                        /* share_access */
                FILE_OPEN,                              /* create_disposition*/
@@ -3422,17 +3655,18 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                NULL,                                   /* sd */
                NULL,                                   /* ea_list */
                &parent_fsp,                            /* result */
-               &info,                                  /* pinfo */
-               &sbuf);                                 /* psbuf */
+               &info);                                 /* pinfo */
 
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb_dname);
                return status;
        }
 
-       status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, parent_fsp->fsp_name,
-                                   DACL_SECURITY_INFORMATION, &parent_sd );
+       status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, smb_dname->base_name,
+                                   SECINFO_DACL, &parent_sd );
 
        close_file(NULL, parent_fsp, NORMAL_CLOSE);
+       TALLOC_FREE(smb_dname);
 
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -3452,7 +3686,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
 
        num_aces += parent_sd->dacl->num_aces;
 
-       if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
+       if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, struct security_ace,
                                        num_aces)) == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -3469,7 +3703,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
 
        /* Finally append any inherited ACEs. */
        for (j = 0; j < parent_sd->dacl->num_aces; j++) {
-               SEC_ACE *se = &parent_sd->dacl->aces[j];
+               struct security_ace *se = &parent_sd->dacl->aces[j];
 
                if (fsp->is_directory) {
                        if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
@@ -3478,7 +3712,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                                        "ignoring non container "
                                        "inherit flags %u on ACE with sid %s "
                                        "from parent %s\n",
-                                       fsp->fsp_name,
+                                       fsp_str_dbg(fsp),
                                        (unsigned int)se->flags,
                                        sid_string_dbg(&se->trustee),
                                        parent_name));
@@ -3491,7 +3725,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                                        "ignoring non object "
                                        "inherit flags %u on ACE with sid %s "
                                        "from parent %s\n",
-                                       fsp->fsp_name,
+                                       fsp_str_dbg(fsp),
                                        (unsigned int)se->flags,
                                        sid_string_dbg(&se->trustee),
                                        parent_name));
@@ -3505,7 +3739,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                         * 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,
+                               if (dom_sid_equal(&psd->dacl->aces[k].trustee,
                                                &se->trustee)) {
                                        break;
                                }
@@ -3515,7 +3749,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                                DEBUG(10,("append_parent_acl: path %s "
                                        "ignoring ACE with protected sid %s "
                                        "from parent %s\n",
-                                       fsp->fsp_name,
+                                       fsp_str_dbg(fsp),
                                        sid_string_dbg(&se->trustee),
                                        parent_name));
                                continue;
@@ -3553,15 +3787,15 @@ NTSTATUS append_parent_acl(files_struct *fsp,
                DEBUG(10,("append_parent_acl: path %s "
                        "inheriting ACE with sid %s "
                        "from parent %s\n",
-                       fsp->fsp_name,
+                       fsp_str_dbg(fsp),
                        sid_string_dbg(&se->trustee),
                        parent_name));
        }
 
        psd->dacl->aces = new_ace;
        psd->dacl->num_aces = i;
-       psd->type &= ~(SE_DESC_DACL_AUTO_INHERITED|
-                         SE_DESC_DACL_AUTO_INHERIT_REQ);
+       psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|
+                         SEC_DESC_DACL_AUTO_INHERIT_REQ);
 
        *pp_new_sd = psd;
        return status;
@@ -3572,16 +3806,17 @@ NTSTATUS append_parent_acl(files_struct *fsp,
  Reply to set a security descriptor on an fsp. security_info_sent is the
  description of the following NT ACL.
  This should be the only external function needed for the UNIX style set ACL.
+ We make a copy of psd_orig as internal functions modify the elements inside
+ it, even though it's a const pointer.
 ****************************************************************************/
 
-NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *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;
+       struct dom_sid file_owner_sid;
+       struct dom_sid file_grp_sid;
        canon_ace *file_ace_list = NULL;
        canon_ace *dir_ace_list = NULL;
        bool acl_perms = False;
@@ -3590,34 +3825,50 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
        bool set_acl_as_root = false;
        bool acl_set_support = false;
        bool ret = false;
+       struct security_descriptor *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, &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 element we check against. */
-       orig_mode = sbuf.st_mode;
+       orig_mode = fsp->fsp_name->st.st_ex_mode;
 
        /*
         * Unpack the user/group/world id's.
         */
 
-       status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
+       /* 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( conn, &user, &grp, security_info_sent, psd);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -3628,18 +3879,21 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
         * 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) ));
-                       if (errno == EPERM) {
-                               return NT_STATUS_INVALID_OWNER;
-                       }
-                       return map_nt_error_from_unix(errno);
+                        fsp_str_dbg(fsp), (unsigned int)user,
+                        (unsigned int)grp));
+
+               status = try_chown(fsp, user, grp);
+               if(!NT_STATUS_IS_OK(status)) {
+                       DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error "
+                               "= %s.\n", fsp_str_dbg(fsp),
+                               (unsigned int)user,
+                               (unsigned int)grp,
+                               nt_errstr(status)));
+                       return status;
                }
 
                /*
@@ -3647,25 +3901,13 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                 * (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 sret;
-
-                       if(fsp->fh->fd == -1)
-                               sret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
-                       else
-                               sret = SMB_VFS_FSTAT(fsp, &sbuf);
-
-                       if(sret != 0)
-                               return map_nt_error_from_unix(errno);
+               status = vfs_stat_fsp(fsp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
                }
 
                /* Save the original element we check against. */
-               orig_mode = sbuf.st_mode;
+               orig_mode = fsp->fsp_name->st.st_ex_mode;
 
                /* If we successfully chowned, we know we must
                 * be able to set the acl, so do it as root.
@@ -3673,10 +3915,44 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                set_acl_as_root = true;
        }
 
-       create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
+       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)) {
+               struct security_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;
+               }
+               security_acl_map_generic(psd->dacl, &file_generic_mapping);
+       }
 
-       acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
-                                       &file_ace_list, &dir_ace_list, security_info_sent, psd);
+       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);
 
        /* Ignore W2K traverse DACL set. */
        if (!file_ace_list && !dir_ace_list) {
@@ -3694,7 +3970,7 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
         * Only change security if we got a DACL.
         */
 
-       if(!(security_info_sent & DACL_SECURITY_INFORMATION) || (psd->dacl == NULL)) {
+       if(!(security_info_sent & SECINFO_DACL) || (psd->dacl == NULL)) {
                free_canon_ace_list(file_ace_list);
                free_canon_ace_list(dir_ace_list);
                return NT_STATUS_OK;
@@ -3709,12 +3985,15 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                if (set_acl_as_root) {
                        become_root();
                }
-               ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
+               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->fsp_name, strerror(errno) ));
+                       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);
@@ -3726,12 +4005,16 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                        if (set_acl_as_root) {
                                become_root();
                        }
-                       ret = set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support);
+                       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->fsp_name, strerror(errno) ));
+                               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);
@@ -3746,18 +4029,24 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                        if (set_acl_as_root) {
                                become_root();
                        }
-                       sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
+                       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, 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 ));
+                               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);
+                                       sret =
+                                           SMB_VFS_SYS_ACL_DELETE_DEF_FILE(
+                                                   conn,
+                                                   fsp->fsp_name->base_name);
                                        unbecome_root();
                                }
 
@@ -3794,8 +4083,9 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                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 ));
+                       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;
                }
 
@@ -3803,29 +4093,37 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                        int sret = -1;
 
                        DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
-                               fsp->fsp_name, (unsigned int)posix_perms ));
+                                fsp_str_dbg(fsp), (unsigned int)posix_perms));
 
                        if (set_acl_as_root) {
                                become_root();
                        }
-                       sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
+                       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, 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 ));
+                               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, posix_perms);
+                                       sret = SMB_VFS_CHMOD(conn,
+                                           fsp->fsp_name->base_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) ));
+                                       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);
@@ -3837,6 +4135,9 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
        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;
 }
 
@@ -4204,12 +4505,12 @@ 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 (!S_ISDIR(psbuf->st_mode)) {
+       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;
@@ -4434,12 +4735,13 @@ bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *
  Assume we are dealing with files (for now)
 ********************************************************************/
 
-SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
+struct security_descriptor *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
 {
-       SEC_DESC *psd, *ret_sd;
+       struct security_descriptor *psd, *ret_sd;
        connection_struct *conn;
        files_struct finfo;
        struct fd_handle fh;
+       NTSTATUS status;
 
        conn = TALLOC_ZERO_P(ctx, connection_struct);
        if (conn == NULL) {
@@ -4459,7 +4761,7 @@ SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
 
        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;
         }
 
@@ -4470,17 +4772,135 @@ SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
        finfo.conn = conn;
        finfo.fh = &fh;
        finfo.fh->fd = -1;
-       finfo.fsp_name = CONST_DISCARD(char *,fname);
 
-       if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, DACL_SECURITY_INFORMATION, &psd))) {
+       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, SECINFO_DACL, &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,
+                                       struct security_descriptor **ppdesc)
+{
+       struct dom_sid owner_sid, group_sid;
+       size_t size = 0;
+       struct security_ace aces[4];
+       uint32_t access_mask = 0;
+       mode_t mode = psbuf->st_ex_mode;
+       struct security_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;
+}