This is the mail archive of the cygwin-cvs@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[newlib-cygwin/cygwin-acl] Reapply Cygwin POSIX ACL changes from master


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=e9ca3fa538186be53c1448ddf671676172c9e54b

commit e9ca3fa538186be53c1448ddf671676172c9e54b
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Fri May 29 16:19:55 2015 +0200

    Reapply Cygwin POSIX ACL changes from master
    
    	Reapply POSIX ACL changes.
    	* sec_acl.cc (get_posix_access): Check for Cygwin "standard" ACL.
    	Apply umask, if so.  Align comments.
    	* security.cc (set_created_file_access): Fix permission masking by
    	incoming requested file mode.
    
    	* sec_acl.cc (set_posix_access): Apply mask only in terms of execute bit
    	for SYSTEM and Admins group.
    
    	* sec_acl.cc (set_posix_access): Don't create DENY ACEs for USER and
    	GROUP entries if they are the same as USER_OBJ or GROUP_OBJ.
    
    	* fhandler.h (fhandler_pty_slave::facl): Add prototype.
    	* fhandler_tty.cc (fhandler_pty_slave::facl): New method.
    	(fhandler_pty_slave::fchown): Fix uid/gid handling.
    	* sec_acl.cc (set_posix_access): Drop superfluous class_idx variable.
    	Simplify and move around code in a few places.  To improve ACL
    	readability, add r/w permissions to Admins ACE appended to pty ACL.
    	Add comment to explain Windows ACE Mask filtering being in the way of
    	creating a real CLASS_OBJ.
    	(get_posix_access): Fake CLASS_OBJ for ptys.  Explain why.
    	* security.cc (get_object_attribute): Add S_IFCHR flag to attributes
    	when calling get_posix_access.
    
    	* sec_acl.cc (set_posix_access): Move merging group perms into owner
    	perms in case of owner == group after mask has been computed.  Take
    	mask into account when doing so to avoid unnecessary ACCESS_DENIED_ACE.
    
    	* sec_acl.cc (get_posix_access): Only set saw_group_obj flag if we saw
    	the ACCESS_ALLOWED_ACE.
    
    	* fhandler_disk_file.cc (fhandler_disk_file::fchmod): Deliberatly
    	set GROUP_OBJ and CLASS_OBJ perms to new group perms.  Add comment
    	to explain why.
    	* security.cc (set_created_file_access): Ditto.
    
    	* sec_acl.cc (set_posix_access): Replace previous patch.  Return
    	EINVAL if uid and/or guid is invalid and not backed by an actual
    	Windows account.
    
    	* sec_acl.cc (set_posix_access): Workaround owner/group SIDs being NULL.
    
    	* sec_acl.cc (set_posix_access): Handle files with owner == group.
    	Rephrase switch statement checking against unfiltered a_type value.
    	(get_posix_access): Handle files with owner == group.
    
    	* sec_acl.cc (get_posix_access): Don't use GROUP_OBJ access to fix up
    	CLASS_OBJ mask on old-style ACLs.  Fix a comment.
    
    	* sec_acl.cc (set_posix_access): Always make sure Admins have
    	WRITE_DAC and WRITE_OWNER permissions.
    	* security.h (create_object_sd_from_attribute): Drop handle parameter
    	from prototype.
    	* security.cc (create_object_sd_from_attribute): Drop handle parameter.
    	Just create the standard POSIXy security descriptor.
    	(set_object_attribute): Accommodate dropped paramter in call to
    	create_object_sd_from_attribute.
    	* fhandler_tty.cc: Ditto, throughout.
    
    	* fhandler_disk_file.cc (fhandler_disk_file::fchmod): Fix typo in
    	mask computation.
    
    	* fhandler.cc (fhandler_base::open_with_arch): Call open with mode
    	not umasked.
    	(fhandler_base::open): Explicitely umask mode on NFS here.  Call new
    	set_created_file_access rather than set_file_attribute.
    	* fhandler_disk_file.cc (fhandler_disk_file::fchmod): Reimplement
    	setting permissions on filesystems supporting ACLs using the new
    	set_posix_access call.
    	(fhandler_disk_file::fchown): Ditto.
    	(fhandler_disk_file::mkdir): Call new set_created_file_access rather
    	than set_file_attribute.
    	* fhandler_socket.cc (fhandler_socket::bind): Don't umask here.  Add
    	WRITE_OWNER access to allow writing group in case of SGID bit set.
    	Call new set_created_file_access rather than set_file_attribute.
    	* path.cc (symlink_worker): Call new set_created_file_access rather
    	than set_file_attribute.
    	* sec_acl.cc (searchace): Un-staticize.
    	(set_posix_access): New, complementary functionality to
    	get_posix_access.
    	(setacl): Implement in terms of get_posix_access/set_posix_access.
    	(get_posix_access): Add handling for just created files requiring
    	their first Cygwin ACL.  Fix new_style recognition.  Handle SGID
    	bit.  For old-style ACLs, ignore SYSTEM and Administrators when
    	computing the {DEF_}CLASS_OBJ perms.
    	* security.cc (get_file_sd): Revamp comment.  Change and (hopefully)
    	speed up inheritance processing for just created files.
    	(alloc_sd): Remove.
    	(set_security_attribute): Call set_posix_access instead of alloc_sd.
    	(get_object_attribute): Fix return value.
    	(create_object_sd_from_attribute): Call set_posix_access instead of
    	alloc_sd.
    	(set_file_attribute): Remove.
    	(set_created_file_access): New function implemented in terms of
    	get_posix_access/set_posix_access.
    	* security.h (set_file_attribute): Remove prototype.
    	(set_created_file_access): Add prototype.
    	(searchace): Ditto.
    	(set_posix_access): Ditto.
    	* syscalls.cc (open): Call open_with_arch with mode not umasked.
    
    	* sec_acl.cc: Change preceeding comment explaining new-style ACLs.
    	Describe how to generate deny ACEs in more detail.  Accommodate the
    	fact that a NULL deny ACE is used for {DEF_}CLASS_OBJ, rather than
    	a special Cygwin ACE.  Improve further comments.
    	(CYG_ACE_NEW_STYLE): Define.
    	(get_posix_access): Change from Cygwin ACE to NULL deny ACE.  Fix
    	CLASS_OBJ handling to generate CLASS_OBJ and DEF_CLASS_OBJ from a single
    	NULL deny ACE if the inheritance flags say so.
    	* sec_helper.cc (well_known_cygwin_sid): Remove.
    	* security.h (well_known_cygwin_sid): Drop declaration.
    
    	* sec_acl.cc (CYG_ACE_ISBITS_TO_WIN): Fix typo.
    	(get_posix_access): Rename index variable from i to idx.  Define only
    	once at top level.
    
    	* security.cc (add_access_allowed_ace): Drop unused parameter "offset".
    	Accommodate throughout.
    	(add_access_denied_ace): Ditto.
    	* sec_acl.cc: Accommodate above change throughout.
    	* security.h (add_access_allowed_ace): Adjust prototype to above change.
    	(add_access_denied_ace): Ditto.
    
    	* sec_acl.cc (get_posix_access): Handle multiple ACEs for the
    	owner and primary group of the file.  Handle the default primary
    	group ACE as DEF_GROUP_OBJ entry if the directory has the S_ISGID bit
    	set.  Add comments.  Minor code rearrangements.
    
    	Preliminary read side implementation of new permission handling.
    	* acl.h (MAX_ACL_ENTRIES): Raise to 2730.  Add comment to explain.
    	* sec_acl.cc:  Add leading comment to explain new ACL style.
    	Add definitions and macros to use for bits in new Cygwin ACL.
    	(DENY_RWX): New mask value for all temporary deny bits.
    	(getace): Add bool parameter to decide when leaving all bits intact,
    	rather than filtering them per the already set bits.
    	(get_posix_access): New function, taking over functionality to read
    	POSIX ACL from SECURITY_DESCRIPTOR.
    	(getacl): Just call get_posix_access.
    	* sec_helper.cc (well_known_cygwin_sid): Define.
    	* security.cc (get_attribute_from_acl): Remove.
    	(get_info_from_sd): Remove.
    	(get_reg_sd): Call get_posix_access instead of get_info_from_sd.
    	(get_file_attribute): Ditto.
    	(get_object_attribute): Ditto.
    	* security.h (well_known_cygwin_sid): Declare.
    	(get_posix_access): Add prototype.
    
    	* Throughout, use simpler ACE macros from Windows' accctrl.h.
    
    	* getfacl.c (main): Special-case SYSTEM and Admins group.  Add comments.
    
    	* setfacl.c: Align more to Linux tool.
    	(delacl): New function to delete acl entries only.
    	(modacl): Drop delete functionality.  Add handling of recomputing the
    	mask and default mask values.
    	(delallacl): Rename from delacl.
    	(setfacl): Call delacl in Delete case.  Call delallacl in DeleteAll
    	and DeleteDef case.
    	(usage): Accommodate new options.  Rearrange and rephrase slightly.
    	(longopts): Emit 'x' in --delete case.  Add --no-mask and --mask
    	options.
    	(opts): Add -x and -n options.
    	(main): Handle -d and -x the same.  Handle -n and --mask options.
    	Drop handling for -r option.
    
    	* getfacl.c (usage): Align more closely to Linux version.  Add new
    	options -c, -e, -E.  Change formatting to accommodate longer options.
    	(longopts): Rename --noname to --numeric.  Keep --noname for backward
    	compatibility.  Add --omit-header, --all-effective and --no-effective
    	options.
    	(opts): Add -c, -e and -E option.
    	(main): Handle new -c, -e, and -E options.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/fhandler.cc           |    9 +-
 winsup/cygwin/fhandler.h            |    1 +
 winsup/cygwin/fhandler_disk_file.cc |  158 +++--
 winsup/cygwin/fhandler_socket.cc    |   13 +-
 winsup/cygwin/fhandler_tty.cc       |   79 ++-
 winsup/cygwin/include/cygwin/acl.h  |   12 +-
 winsup/cygwin/path.cc               |    7 +-
 winsup/cygwin/sec_acl.cc            | 1195 ++++++++++++++++++++++++-----------
 winsup/cygwin/sec_helper.cc         |    9 +-
 winsup/cygwin/security.cc           |  794 +++++------------------
 winsup/cygwin/security.h            |   16 +-
 winsup/cygwin/syscalls.cc           |    3 +-
 winsup/doc/ChangeLog                |    8 +
 winsup/doc/utils.xml                |  113 ++--
 winsup/utils/getfacl.c              |  197 ++++--
 winsup/utils/setfacl.c              |  375 +++++++----
 16 files changed, 1645 insertions(+), 1344 deletions(-)

diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 6f024da..4c1bdba 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -463,7 +463,7 @@ fhandler_base::open_with_arch (int flags, mode_t mode)
 {
   int res;
   if (!(res = (archetype && archetype->io_handle)
-	|| open (flags, (mode & 07777) & ~cygheap->umask)))
+	|| open (flags, mode & 07777)))
     {
       if (archetype)
 	delete archetype;
@@ -662,9 +662,10 @@ fhandler_base::open (int flags, mode_t mode)
 					     + p->EaNameLength + 1);
 	      memset (nfs_attr, 0, sizeof (fattr3));
 	      nfs_attr->type = NF3REG;
-	      nfs_attr->mode = mode;
+	      nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
 	    }
-	  else if (!has_acls () && !(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+	  else if (!has_acls ()
+		   && !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH)))
 	    /* If mode has no write bits set, and ACLs are not used, we set
 	       the DOS R/O attribute. */
 	    file_attributes |= FILE_ATTRIBUTE_READONLY;
@@ -716,7 +717,7 @@ fhandler_base::open (int flags, mode_t mode)
      This is the result of a discussion on the samba-technical list, starting at
      http://lists.samba.org/archive/samba-technical/2008-July/060247.html */
   if (io.Information == FILE_CREATED && has_acls ())
-    set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID, S_JUSTCREATED | mode);
+    set_created_file_access (fh, pc, mode);
 
   /* If you O_TRUNC a file on Linux, the data is truncated, but the EAs are
      preserved.  If you open a file on Windows with FILE_OVERWRITE{_IF} or
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index e15f946..db3cef5 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1550,6 +1550,7 @@ class fhandler_pty_slave: public fhandler_pty_common
   select_record *select_read (select_stuff *);
   virtual char const *ttyname () { return pc.dev.name; }
   int __reg2 fstat (struct stat *buf);
+  int __reg3 facl (int, int, struct acl *);
   int __reg1 fchmod (mode_t mode);
   int __reg2 fchown (uid_t uid, gid_t gid);
 
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index f5edb03..b283934 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -835,7 +835,7 @@ int __reg1
 fhandler_disk_file::fchmod (mode_t mode)
 {
   extern int chmod_device (path_conv& pc, mode_t mode);
-  int res = -1;
+  int ret = -1;
   int oret = 0;
   NTSTATUS status;
   IO_STATUS_BLOCK io;
@@ -882,17 +882,45 @@ fhandler_disk_file::fchmod (mode_t mode)
       if (!NT_SUCCESS (status))
 	__seterrno_from_nt_status (status);
       else
-	res = 0;
+	ret = 0;
       goto out;
     }
 
   if (pc.has_acls ())
     {
-      if (pc.isdir ())
-	mode |= S_IFDIR;
-      if (!set_file_attribute (get_handle (), pc,
-			       ILLEGAL_UID, ILLEGAL_GID, mode))
-	res = 0;
+      security_descriptor sd, sd_ret;
+      uid_t uid;
+      gid_t gid;
+      tmp_pathbuf tp;
+      aclent_t *aclp;
+      int nentries, idx;
+
+      if (!get_file_sd (get_handle (), pc, sd, false))
+	{
+	  aclp = (aclent_t *) tp.c_get ();
+	  if ((nentries = get_posix_access (sd, NULL, &uid, &gid,
+					    aclp, MAX_ACL_ENTRIES)) >= 0)
+	    {
+	      /* Overwrite ACL permissions as required by POSIX 1003.1e
+		 draft 17. */
+	      aclp[0].a_perm = (mode >> 6) & S_IRWXO;
+	      /* Deliberate deviation from POSIX 1003.1e here.  We're not
+		 writing CLASS_OBJ *or* GROUP_OBJ, but both.  Otherwise we're
+		 going to be in constant trouble with user expectations. */
+	      if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
+		aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
+	      if (nentries > MIN_ACL_ENTRIES
+		  && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
+		aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
+	      if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
+		aclp[idx].a_perm = mode & S_IRWXO;
+	      if (pc.isdir ())
+		mode |= S_IFDIR;
+	      if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
+				    pc.fs_is_samba ()))
+		ret = set_file_sd (get_handle (), pc, sd_ret, false);
+	    }
+	}
     }
 
   /* If the mode has any write bits set, the DOS R/O flag is in the way. */
@@ -929,20 +957,28 @@ fhandler_disk_file::fchmod (mode_t mode)
       if (!NT_SUCCESS (status))
 	__seterrno_from_nt_status (status);
       else
-	res = 0;
+	ret = 0;
     }
 
 out:
   if (oret)
     close_fs ();
 
-  return res;
+  return ret;
 }
 
 int __reg2
 fhandler_disk_file::fchown (uid_t uid, gid_t gid)
 {
   int oret = 0;
+  int ret = -1;
+  security_descriptor sd, sd_ret;
+  mode_t attr = pc.isdir () ? S_IFDIR : 0;
+  uid_t old_uid;
+  gid_t old_gid;
+  tmp_pathbuf tp;
+  aclent_t *aclp;
+  int nentries;
 
   if (!pc.has_acls ())
     {
@@ -959,52 +995,71 @@ fhandler_disk_file::fchown (uid_t uid, gid_t gid)
 	return -1;
     }
 
-  mode_t attrib = 0;
-  if (pc.isdir ())
-    attrib |= S_IFDIR;
-  uid_t old_uid;
-  int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
-  if (!res)
+  if (get_file_sd (get_handle (), pc, sd, false))
+    goto out;
+
+  aclp = (aclent_t *) tp.c_get ();
+  if ((nentries = get_posix_access (sd, &attr, &old_uid, &old_gid,
+				    aclp, MAX_ACL_ENTRIES)) < 0)
+    goto out;
+
+  if (uid == ILLEGAL_UID)
+    uid = old_uid;
+  if (gid == ILLEGAL_GID)
+    gid = old_gid;
+  if (uid == old_uid && gid == old_gid)
     {
-      /* Typical Windows default ACLs can contain permissions for one
-	 group, while being owned by another user/group.  The permission
-	 bits returned above are pretty much useless then.  Creating a
-	 new ACL with these useless permissions results in a potentially
-	 broken symlink.  So what we do here is to set the underlying
-	 permissions of symlinks to a sensible value which allows the
-	 world to read the symlink and only the new owner to change it. */
-      if (pc.issymlink ())
-	attrib = S_IFLNK | STD_RBITS | STD_WBITS;
-      res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
-      /* If you're running a Samba server which has no winbind running, the
-	 uid<->SID mapping is disfunctional.  Even trying to chown to your
-	 own account fails since the account used on the server is the UNIX
-	 account which gets used for the standard user mapping.  This is a
-	 default mechanism which doesn't know your real Windows SID.
-	 There are two possible error codes in different Samba releases for
-	 this situation, one of them is unfortunately the not very significant
-	 STATUS_ACCESS_DENIED.  Instead of relying on the error codes, we're
-	 using the below very simple heuristic.  If set_file_attribute failed,
-	 and the original user account was either already unknown, or one of
-	 the standard UNIX accounts, we're faking success. */
-      if (res == -1 && pc.fs_is_samba ())
-	{
-	  PSID sid;
+      ret = 0;
+      goto out;
+    }
 
-	  if (old_uid == ILLEGAL_UID
-	      || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
-		  && RtlEqualPrefixSid (sid,
-					well_known_samba_unix_user_fake_sid)))
-	    {
-	      debug_printf ("Faking chown worked on standalone Samba");
-	      res = 0;
-	    }
+  /* Windows ACLs can contain permissions for one group, while being owned by
+     another user/group.  The permission bits returned above are pretty much
+     useless then.  Creating a new ACL with these useless permissions results
+     in a potentially broken symlink.  So what we do here is to set the
+     underlying permissions of symlinks to a sensible value which allows the
+     world to read the symlink and only the new owner to change it. */
+  if (pc.issymlink ())
+    for (int idx = 0; idx < nentries; ++idx)
+      {
+	aclp[idx].a_perm |= S_IROTH;
+	if (aclp[idx].a_type & USER_OBJ)
+	  aclp[idx].a_perm |= S_IWOTH;
+      }
+
+  if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
+			pc.fs_is_samba ()))
+    ret = set_file_sd (get_handle (), pc, sd_ret, true);
+
+  /* If you're running a Samba server with no winbind, the uid<->SID mapping
+     is disfunctional.  Even trying to chown to your own account fails since
+     the account used on the server is the UNIX account which gets used for
+     the standard user mapping.  This is a default mechanism which doesn't
+     know your real Windows SID.  There are two possible error codes in
+     different Samba releases for this situation, one of them unfortunately
+     the not very significant STATUS_ACCESS_DENIED.  Instead of relying on
+     the error codes, we're using the below very simple heuristic.
+     If set_file_sd failed, and the original user account was either already
+     unknown, or one of the standard UNIX accounts, we're faking success. */
+  if (ret == -1 && pc.fs_is_samba ())
+    {
+      PSID sid;
+
+      if (uid == old_uid
+	  || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
+	      && RtlEqualPrefixSid (sid,
+				    well_known_samba_unix_user_fake_sid)))
+	{
+	  debug_printf ("Faking chown worked on standalone Samba");
+	  ret = 0;
 	}
     }
+
+out:
   if (oret)
     close_fs ();
 
-  return res;
+  return ret;
 }
 
 int __reg3
@@ -1763,10 +1818,11 @@ fhandler_disk_file::mkdir (mode_t mode)
 			 p, plen);
   if (NT_SUCCESS (status))
     {
+      /* Set the "directory attribute" so that pc.isdir() returns correct
+	 value in subsequent function calls. */
+      pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
       if (has_acls ())
-	set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
-			    S_JUSTCREATED | S_IFDIR
-			    | ((mode & 07777) & ~cygheap->umask));
+	set_created_file_access (dir, pc, mode & 07777);
       NtClose (dir);
       res = 0;
     }
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
index 1b28e52..e441fd1 100644
--- a/winsup/cygwin/fhandler_socket.cc
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -1,7 +1,7 @@
 /* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
 
    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-   2011, 2012, 2013, 2014 Red Hat, Inc.
+   2011, 2012, 2013, 2014, 2015 Red Hat, Inc.
 
    This file is part of Cygwin.
 
@@ -1039,10 +1039,10 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
       sin.sin_port = ntohs (sin.sin_port);
       debug_printf ("AF_LOCAL: socket bound to port %u", sin.sin_port);
 
-      mode_t mode = adjust_socket_file_mode ((S_IRWXU | S_IRWXG | S_IRWXO)
-					     & ~cygheap->umask);
+      mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
       DWORD fattr = FILE_ATTRIBUTE_SYSTEM;
-      if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && !pc.has_acls ())
+      if (!pc.has_acls ()
+	  && !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH)))
 	fattr |= FILE_ATTRIBUTE_READONLY;
       SECURITY_ATTRIBUTES sa = sec_none_nih;
       NTSTATUS status;
@@ -1060,7 +1060,7 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
 	 I don't know what setting that is or how to recognize such a share,
 	 so for now we don't request WRITE_DAC on remote drives. */
       if (pc.has_acls () && !pc.isremote ())
-	access |= READ_CONTROL | WRITE_DAC;
+	access |= READ_CONTROL | WRITE_DAC | WRITE_OWNER;
 
       status = NtCreateFile (&fh, access, pc.get_object_attr (attr, sa), &io,
 			     NULL, fattr, 0, FILE_CREATE,
@@ -1078,8 +1078,7 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
       else
 	{
 	  if (pc.has_acls ())
-	    set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID,
-				S_JUSTCREATED | mode);
+	    set_created_file_access (fh, pc, mode);
 	  char buf[sizeof (SOCKET_COOKIE) + 80];
 	  __small_sprintf (buf, "%s%u %c ", SOCKET_COOKIE, sin.sin_port,
 			   get_socket_type () == SOCK_STREAM ? 's'
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 1b5c18b..b74c397 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -12,6 +12,7 @@ details. */
 #include "winsup.h"
 #include <stdlib.h>
 #include <sys/param.h>
+#include <sys/acl.h>
 #include <cygwin/kd.h>
 #include "cygerrno.h"
 #include "security.h"
@@ -388,9 +389,8 @@ fhandler_pty_slave::open (int flags, mode_t)
     sd.malloc (sizeof (SECURITY_DESCRIPTOR));
     RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
     SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
-    if (!create_object_sd_from_attribute (NULL, myself->uid, myself->gid,
-					  S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP,
-					  sd))
+    if (!create_object_sd_from_attribute (myself->uid, myself->gid,
+					  S_IRUSR | S_IWUSR | S_IWGRP, sd))
       sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) sd;
     acquire_output_mutex (INFINITE);
     inuse = get_ttyp ()->create_inuse (&sa);
@@ -1039,6 +1039,62 @@ fhandler_pty_slave::fstat (struct stat *st)
   return 0;
 }
 
+int __reg3
+fhandler_pty_slave::facl (int cmd, int nentries, aclent_t *aclbufp)
+{
+  int res = -1;
+  bool to_close = false;
+  security_descriptor sd;
+  mode_t attr = S_IFCHR;
+
+  switch (cmd)
+    {
+      case SETACL:
+	if (!aclsort32 (nentries, 0, aclbufp))
+	  set_errno (ENOTSUP);
+	break;
+      case GETACL:
+	if (!aclbufp)
+	  {
+	    set_errno (EFAULT);
+	    break;
+	  }
+	/*FALLTHRU*/
+      case GETACLCNT:
+	if (!input_available_event)
+	  {
+	    char buf[MAX_PATH];
+	    shared_name (buf, INPUT_AVAILABLE_EVENT, get_minor ());
+	    input_available_event = OpenEvent (READ_CONTROL, TRUE, buf);
+	    if (input_available_event)
+	      to_close = true;
+	  }
+	if (!input_available_event
+	    || get_object_sd (input_available_event, sd))
+	  {
+	    res = get_posix_access (NULL, &attr, NULL, NULL, aclbufp, nentries);
+	    if (aclbufp && res == MIN_ACL_ENTRIES)
+	      {
+		aclbufp[0].a_perm = S_IROTH | S_IWOTH;
+		aclbufp[0].a_id = 18;
+		aclbufp[1].a_id = 544;
+	      }
+	    break;
+	  }
+	if (cmd == GETACL)
+	  res = get_posix_access (sd, &attr, NULL, NULL, aclbufp, nentries);
+	else
+	  res = get_posix_access (sd, &attr, NULL, NULL, NULL, 0);
+	break;
+      default:
+	set_errno (EINVAL);
+	break;
+    }
+  if (to_close)
+    CloseHandle (input_available_event);
+  return res;
+}
+
 /* Helper function for fchmod and fchown, which just opens all handles
    and signals success via bool return. */
 bool
@@ -1111,7 +1167,7 @@ fhandler_pty_slave::fchmod (mode_t mode)
   sd.malloc (sizeof (SECURITY_DESCRIPTOR));
   RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
   if (!get_object_attribute (input_available_event, &uid, &gid, NULL)
-      && !create_object_sd_from_attribute (NULL, uid, gid, S_IFCHR | mode, sd))
+      && !create_object_sd_from_attribute (uid, gid, mode, sd))
     ret = fch_set_sd (sd, false);
 errout:
   if (to_close)
@@ -1141,11 +1197,13 @@ fhandler_pty_slave::fchown (uid_t uid, gid_t gid)
   RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
   if (!get_object_attribute (input_available_event, &o_uid, &o_gid, &mode))
     {
-      if ((uid == ILLEGAL_UID || uid == o_uid)
-	  && (gid == ILLEGAL_GID || gid == o_gid))
+      if (uid == ILLEGAL_UID)
+	uid = o_uid;
+      if (gid == ILLEGAL_GID)
+	gid = o_gid;
+      if (uid == o_uid && gid == o_gid)
 	ret = 0;
-      else if (!create_object_sd_from_attribute (input_available_event,
-						 uid, gid, S_IFCHR | mode, sd))
+      else if (!create_object_sd_from_attribute (uid, gid, mode, sd))
 	ret = fch_set_sd (sd, true);
     }
 errout:
@@ -1683,9 +1741,8 @@ fhandler_pty_master::setup ()
   /* Create security attribute.  Default permissions are 0620. */
   sd.malloc (sizeof (SECURITY_DESCRIPTOR));
   RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
-  if (!create_object_sd_from_attribute (NULL, myself->uid, myself->gid,
-					S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP,
-					sd))
+  if (!create_object_sd_from_attribute (myself->uid, myself->gid,
+					S_IRUSR | S_IWUSR | S_IWGRP, sd))
     sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) sd;
 
   /* Carefully check that the input_available_event didn't already exist.
diff --git a/winsup/cygwin/include/cygwin/acl.h b/winsup/cygwin/include/cygwin/acl.h
index 34f1c0c..b7cbf03 100644
--- a/winsup/cygwin/include/cygwin/acl.h
+++ b/winsup/cygwin/include/cygwin/acl.h
@@ -1,6 +1,6 @@
 /* cygwin/acl.h header file for Cygwin.
 
-   Copyright 1999, 2000, 2001, 2002, 2010, 2014 Red Hat, Inc.
+   Copyright 1999, 2000, 2001, 2002, 2010, 2014, 2015 Red Hat, Inc.
    Written by C. Vinschen.
 
 This file is part of Cygwin.
@@ -25,8 +25,16 @@ extern "C" {
 #define GETACL          (0x1)
 #define GETACLCNT       (0x2)
 
+/* Windows ACLs have a maximum size of 64K.  Counting the most pessimistic way,
+   the maximum number of ACEs is 3276.  Technet claims "approximately 1820",
+   which uses the length of normal user and group SIDs for the computation. 
+   We're now going with 2730, the number of aclent_t entries matching a 32K
+   buffer.
+   On one hand, there are only a limited number of SIDs shorter than the normal
+   user/group SIDs, on the other hand there are no deny aclent_t entries, so we
+   should be fine with 32K aclbuf_t buffers provided by the caller. */
 #define	MIN_ACL_ENTRIES (3)    // minimal acl entries from GETACLCNT
-#define	MAX_ACL_ENTRIES	(256)  // max entries of each type
+#define	MAX_ACL_ENTRIES	(2730) // max entries of each type
 
 // Return values of aclcheck(3) in case of error */
 #define GRP_ERROR       (0x1)
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 446d746..b1a02e1 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -2049,10 +2049,9 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice)
 	  __seterrno_from_nt_status (status);
 	  __leave;
 	}
-      if (win32_newpath.has_acls ())
-	set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID,
-			    (io.Information == FILE_CREATED ? S_JUSTCREATED : 0)
-			    | S_IFLNK | STD_RBITS | STD_WBITS);
+      if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
+	set_created_file_access (fh, win32_newpath,
+				 S_IFLNK | STD_RBITS | STD_WBITS);
       status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
 			    NULL, NULL);
       if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc
index 5a2048c..7d97fca 100644
--- a/winsup/cygwin/sec_acl.cc
+++ b/winsup/cygwin/sec_acl.cc
@@ -24,8 +24,75 @@ details. */
 #include "ntdll.h"
 #include "tls_pbuf.h"
 
-static int
-searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID)
+/* How does a correctly constructed new-style Windows ACL claiming to be a
+   POSIX ACL look like?
+
+   - NULL ACE (special bits, CLASS_OBJ).
+
+   - USER_OBJ deny.  If the user has less permissions than the sum of CLASS_OBJ
+     (or GROUP_OBJ if CLASS_OBJ doesn't exist) and OTHER_OBJ, deny the excess
+     permissions so that group and other perms don't spill into the owner perms.
+
+       USER_OBJ deny ACE   == ~USER_OBJ & (CLASS_OBJ | OTHER_OBJ)
+     or
+       USER_OBJ deny ACE   == ~USER_OBJ & (GROUP_OBJ | OTHER_OBJ)
+
+   - USER deny.  If a user has different permissions from CLASS_OBJ, or if the
+     user has less permissions than OTHER_OBJ, deny the excess permissions.
+
+       USER deny ACE       == (USER ^ CLASS_OBJ) | (~USER & OTHER_OBJ)
+
+   - USER_OBJ  allow ACE
+   - USER      allow ACEs
+
+     The POSIX permissions returned for a USER entry are the allow bits alone!
+
+   - GROUP{_OBJ} deny.  If a group has more permissions than CLASS_OBJ,
+     or less permissions than OTHER_OBJ, deny the excess permissions.
+
+       GROUP{_OBJ} deny ACEs  == (GROUP & ~CLASS_OBJ) | (~GROUP & OTHER_OBJ)
+
+   - GROUP_OBJ	allow ACE
+   - GROUP	allow ACEs
+
+     The POSIX permissions returned for a GROUP entry are the allow bits alone!
+
+   - OTHER_OBJ	allow ACE
+
+   Rinse and repeat for default ACEs with INHERIT flags set.
+
+   - Default NULL ACE (S_ISGID, CLASS_OBJ). */
+
+						/* POSIX <-> Win32 */
+
+/* Historically, these bits are stored in a NULL SID ACE.  To distinguish the
+   new ACL style from the old one, we're using an access denied ACE, plus
+   setting an as yet unused bit in the access mask.  The new ACEs can exist
+   twice in an ACL, the "normal one" containing CLASS_OBJ and special bits
+   and the one with INHERIT bit set to pass the DEF_CLASS_OBJ bits and the
+   S_ISGID bit on. */
+#define CYG_ACE_ISVTX		0x001		/* 0x200 <-> 0x001 */
+#define CYG_ACE_ISGID		0x002		/* 0x400 <-> 0x002 */
+#define CYG_ACE_ISUID		0x004		/* 0x800 <-> 0x004 */
+#define CYG_ACE_ISBITS_TO_POSIX(val)	\
+				(((val) & 0x007) << 9)
+#define CYG_ACE_ISBITS_TO_WIN(val) \
+				(((val) & (S_ISVTX | S_ISUID | S_ISGID)) >> 9)
+
+#define CYG_ACE_MASK_X		0x008		/* 0x001 <-> 0x008 */
+#define CYG_ACE_MASK_W		0x010		/* 0x002 <-> 0x010 */
+#define CYG_ACE_MASK_R		0x020		/* 0x004 <-> 0x020 */
+#define CYG_ACE_MASK_RWX	0x038
+#define CYG_ACE_MASK_VALID	0x040		/* has mask if set */
+#define CYG_ACE_MASK_TO_POSIX(val)	\
+				(((val) & CYG_ACE_MASK_RWX) >> 3)
+#define CYG_ACE_MASK_TO_WIN(val)	\
+				((((val) & S_IRWXO) << 3) \
+				 | CYG_ACE_MASK_VALID)
+#define CYG_ACE_NEW_STYLE	READ_CONTROL	/* New style if set. */
+
+int
+searchace (aclent_t *aclp, int nentries, int type, uid_t id)
 {
   int i;
 
@@ -36,270 +103,325 @@ searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID)
   return -1;
 }
 
-/* This function *requires* an acl list sorted with aclsort{32}. */
-int
-setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
-	bool &writable)
+/* Define own bit masks rather than using the GENERIC masks.  The latter
+   also contain standard rights, which we don't need here. */
+#define FILE_ALLOW_READ		(FILE_READ_DATA | FILE_READ_ATTRIBUTES | \
+				 FILE_READ_EA)
+#define FILE_DENY_READ		(FILE_READ_DATA | FILE_READ_EA)
+#define FILE_ALLOW_WRITE	(FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | \
+				 FILE_WRITE_EA | FILE_APPEND_DATA)
+#define FILE_DENY_WRITE		FILE_ALLOW_WRITE | FILE_DELETE_CHILD
+#define FILE_DENY_WRITE_OWNER	(FILE_WRITE_DATA | FILE_WRITE_EA | \
+				 FILE_APPEND_DATA | FILE_DELETE_CHILD)
+#define FILE_ALLOW_EXEC		(FILE_EXECUTE)
+#define FILE_DENY_EXEC		FILE_ALLOW_EXEC
+
+#define STD_RIGHTS_OTHER	(STANDARD_RIGHTS_READ | SYNCHRONIZE)
+#define STD_RIGHTS_OWNER	(STANDARD_RIGHTS_ALL | SYNCHRONIZE)
+
+/* From the attributes and the POSIX ACL list, compute a new-style Cygwin
+   security descriptor.  The function returns a pointer to the
+   SECURITY_DESCRIPTOR in sd_ret, or NULL if the function fails.
+
+   This function *requires* a verified and sorted acl list! */
+PSECURITY_DESCRIPTOR
+set_posix_access (mode_t attr, uid_t uid, gid_t gid,
+		  aclent_t *aclbufp, int nentries,
+		  security_descriptor &sd_ret,
+		  bool is_samba)
 {
-  security_descriptor sd_ret;
-  tmp_pathbuf tp;
-
-  if (get_file_sd (handle, pc, sd_ret, false))
-    return -1;
-
+  SECURITY_DESCRIPTOR sd;
+  cyg_ldap cldap;
+  PSID owner, group;
   NTSTATUS status;
+  tmp_pathbuf tp;
+  cygpsid *aclsid;
   PACL acl;
-  BOOLEAN acl_exists, dummy;
-
-  /* Get owner SID. */
-  PSID owner_sid;
-  status = RtlGetOwnerSecurityDescriptor (sd_ret, &owner_sid, &dummy);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      return -1;
-    }
-  cygsid owner (owner_sid);
-
-  /* Get group SID. */
-  PSID group_sid;
-  status = RtlGetGroupSecurityDescriptor (sd_ret, &group_sid, &dummy);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      return -1;
-    }
-  cygsid group (group_sid);
-
-  /* Search for NULL ACE and store state of SUID, SGID and VTX bits. */
-  DWORD null_mask = 0;
-  if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &acl,
-						&dummy)))
-    for (USHORT i = 0; i < acl->AceCount; ++i)
-      {
-	ACCESS_ALLOWED_ACE *ace;
-	if (NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
-	  {
-	    cygpsid ace_sid ((PSID) &ace->SidStart);
-	    if (ace_sid == well_known_null_sid)
-	      {
-		null_mask = ace->Mask;
-		break;
-	      }
-	  }
-      }
+  size_t acl_len = sizeof (ACL);
+  mode_t class_obj = 0, other_obj, group_obj, deny;
+  DWORD access;
+  int idx, start_idx, tmp_idx;
+  bool owner_eq_group = false;
+  bool dev_has_admins = false;
 
   /* Initialize local security descriptor. */
-  SECURITY_DESCRIPTOR sd;
   RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
 
   /* As in alloc_sd, set SE_DACL_PROTECTED to prevent the DACL from being
      modified by inheritable ACEs. */
   RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
 
+  /* Fetch owner and group and set in security descriptor. */
+  owner = sidfromuid (uid, &cldap);
+  group = sidfromgid (gid, &cldap);
+  if (!owner || !group)
+    {
+      set_errno (EINVAL);
+      return NULL;
+    }
   status = RtlSetOwnerSecurityDescriptor (&sd, owner, FALSE);
   if (!NT_SUCCESS (status))
     {
       __seterrno_from_nt_status (status);
-      return -1;
+      return NULL;
     }
   status = RtlSetGroupSecurityDescriptor (&sd, group, FALSE);
   if (!NT_SUCCESS (status))
     {
       __seterrno_from_nt_status (status);
-      return -1;
+      return NULL;
     }
+  owner_eq_group = RtlEqualSid (owner, group);
+  if (S_ISCHR (attr))
+    dev_has_admins = well_known_admins_sid == owner
+		     || well_known_admins_sid == group;
 
-  /* Fill access control list. */
-  acl = (PACL) tp.w_get ();
-  size_t acl_len = sizeof (ACL);
-  int ace_off = 0;
+  /* No POSIX ACL?  Use attr to generate one from scratch. */
+  if (!aclbufp)
+    {
+      aclbufp = (aclent_t *) tp.c_get ();
+      aclbufp[0].a_type = USER_OBJ;
+      aclbufp[0].a_id = ILLEGAL_UID;
+      aclbufp[0].a_perm = (attr >> 6) & S_IRWXO;
+      aclbufp[1].a_type = GROUP_OBJ;
+      aclbufp[1].a_id = ILLEGAL_GID;
+      aclbufp[1].a_perm = (attr >> 3) & S_IRWXO;
+      aclbufp[2].a_type = OTHER_OBJ;
+      aclbufp[2].a_id = ILLEGAL_GID;
+      aclbufp[2].a_perm = attr & S_IRWXO;
+      nentries = MIN_ACL_ENTRIES;
+      if (S_ISDIR (attr))
+	{
+	  aclbufp[3].a_type = DEF_USER_OBJ;
+	  aclbufp[3].a_id = ILLEGAL_UID;
+	  aclbufp[3].a_perm = (attr >> 6) & S_IRWXO;
+	  aclbufp[4].a_type = GROUP_OBJ;
+	  aclbufp[4].a_id = ILLEGAL_GID;
+	  aclbufp[4].a_perm = (attr >> 3) & S_IRWXO;
+	  aclbufp[5].a_type = OTHER_OBJ;
+	  aclbufp[5].a_id = ILLEGAL_GID;
+	  aclbufp[5].a_perm = attr & S_IRWXO;
+	  nentries += MIN_ACL_ENTRIES;
+	}
+    }
 
-  cygsid sid;
-  struct passwd *pw;
-  struct group *gr;
-  int pos;
-  cyg_ldap cldap;
+  /* Collect SIDs of all entries in aclbufp. */
+  aclsid = (cygpsid *) tp.w_get ();
+  for (idx = 0; idx < nentries; ++idx)
+    switch (aclbufp[idx].a_type)
+      {
+      case USER_OBJ:
+	aclsid[idx] = owner;
+	break;
+      case DEF_USER_OBJ:
+	aclsid[idx] = well_known_creator_owner_sid;
+	break;
+      case USER:
+      case DEF_USER:
+	aclsid[idx] = sidfromuid (aclbufp[idx].a_id, &cldap);
+	break;
+      case GROUP_OBJ:
+	aclsid[idx] = group;
+	break;
+      case DEF_GROUP_OBJ:
+	aclsid[idx] = !(attr & S_ISGID) ? (PSID) well_known_creator_group_sid
+					: group;
+	break;
+      case GROUP:
+      case DEF_GROUP:
+	aclsid[idx] = sidfromgid (aclbufp[idx].a_id, &cldap);
+	break;
+      case CLASS_OBJ:
+      case DEF_CLASS_OBJ:
+	aclsid[idx] = well_known_null_sid;
+	break;
+      case OTHER_OBJ:
+      case DEF_OTHER_OBJ:
+	aclsid[idx] = well_known_world_sid;
+	break;
+      }
 
+  /* Initialize ACL. */
+  acl = (PACL) tp.w_get ();
   RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
 
-  writable = false;
-
-  bool *invalid = (bool *) tp.c_get ();
-  memset (invalid, 0, nentries * sizeof *invalid);
-
-  /* Pre-compute owner, group, and other permissions to allow creating
-     matching deny ACEs as in alloc_sd. */
-  DWORD owner_allow = 0, group_allow = 0, other_allow = 0;
-  PDWORD allow;
-  for (int i = 0; i < nentries; ++i)
+  /* This loop has two runs, the first handling the actual permission,
+     the second handling the default permissions. */
+  idx = 0;
+  for (int def = 0; def <= ACL_DEFAULT; def += ACL_DEFAULT)
     {
-      switch (aclbufp[i].a_type)
+      DWORD inherit = def ? SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY
+			  : NO_INHERITANCE;
+
+      /* No default ACEs on files. */
+      if (def && !S_ISDIR (attr))
 	{
-	case USER_OBJ:
-	  allow = &owner_allow;
-	  *allow = STANDARD_RIGHTS_ALL
-		   | (pc.fs_is_samba () ? 0 : FILE_WRITE_ATTRIBUTES);
-	  break;
-	case GROUP_OBJ:
-	  allow = &group_allow;
-	  break;
-	case OTHER_OBJ:
-	  allow = &other_allow;
+	  /* Trying to set default ACEs on a non-directory is an error.
+	     The underlying functions on Linux return EACCES. */
+	  if (idx < nentries && aclbufp[idx].a_type & ACL_DEFAULT)
+	    {
+	      set_errno (EACCES);
+	      return NULL;
+	    }
 	  break;
-	default:
-	  continue;
-	}
-      *allow |= STANDARD_RIGHTS_READ | SYNCHRONIZE
-		| (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
-      if (aclbufp[i].a_perm & S_IROTH)
-	*allow |= FILE_GENERIC_READ;
-      if (aclbufp[i].a_perm & S_IWOTH)
-	{
-	  *allow |= FILE_GENERIC_WRITE;
-	  writable = true;
 	}
-      if (aclbufp[i].a_perm & S_IXOTH)
-	*allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
-      /* Keep S_ISVTX rule in sync with alloc_sd. */
-      if (pc.isdir ()
-	  && (aclbufp[i].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
-	  && (aclbufp[i].a_type == USER_OBJ
-	      || !(null_mask & FILE_READ_DATA)))
-	*allow |= FILE_DELETE_CHILD;
-      invalid[i] = true;
-    }
-  bool isownergroup = (owner == group);
-  DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
-  owner_deny &= ~(STANDARD_RIGHTS_READ
-		  | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
-  DWORD group_deny = ~group_allow & other_allow;
-  group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
-
-  /* Set deny ACE for owner. */
-  if (owner_deny
-      && !add_access_denied_ace (acl, ace_off++, owner_deny,
-				 owner, acl_len, NO_INHERITANCE))
-    return -1;
-  /* Set deny ACE for group here to respect the canonical order,
-     if this does not impact owner */
-  if (group_deny && !(group_deny & owner_allow) && !isownergroup
-      && !add_access_denied_ace (acl, ace_off++, group_deny,
-				 group, acl_len, NO_INHERITANCE))
-    return -1;
-  /* Set allow ACE for owner. */
-  if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
-			       owner, acl_len, NO_INHERITANCE))
-    return -1;
-  /* Set deny ACE for group, if still needed. */
-  if (group_deny & owner_allow && !isownergroup
-      && !add_access_denied_ace (acl, ace_off++, group_deny,
-				 group, acl_len, NO_INHERITANCE))
-    return -1;
-  /* Set allow ACE for group. */
-  if (!isownergroup
-      && !add_access_allowed_ace (acl, ace_off++, group_allow,
-                                  group, acl_len, NO_INHERITANCE))
-    return -1;
-  /* Set allow ACE for everyone. */
-  if (!add_access_allowed_ace (acl, ace_off++, other_allow,
-			       well_known_world_sid, acl_len, NO_INHERITANCE))
-    return -1;
-  /* If a NULL ACE exists, copy it verbatim. */
-  if (null_mask)
-    if (!add_access_allowed_ace (acl, ace_off++, null_mask, well_known_null_sid,
-				 acl_len, NO_INHERITANCE))
-      return -1;
-  for (int i = 0; i < nentries; ++i)
-    {
-      DWORD allow;
-      /* Skip invalidated entries. */
-      if (invalid[i])
-	continue;
 
-      allow = STANDARD_RIGHTS_READ
-	      | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
-      if (aclbufp[i].a_perm & S_IROTH)
-	allow |= FILE_GENERIC_READ;
-      if (aclbufp[i].a_perm & S_IWOTH)
+      /* To compute deny access masks, we need group_obj, other_obj and... */
+      tmp_idx = searchace (aclbufp, nentries, def | GROUP_OBJ);
+      /* No default entries present? */
+      if (tmp_idx < 0)
+	break;
+      group_obj = aclbufp[tmp_idx].a_perm;
+      tmp_idx = searchace (aclbufp, nentries, def | OTHER_OBJ);
+      other_obj = aclbufp[tmp_idx].a_perm;
+
+      /* ... class_obj.  Create Cygwin ACE.  Only the S_ISGID attribute gets
+	 inherited. */
+      access = CYG_ACE_ISBITS_TO_WIN (def ? attr & S_ISGID : attr)
+	       | CYG_ACE_NEW_STYLE;
+      tmp_idx = searchace (aclbufp, nentries, def | CLASS_OBJ);
+      if (tmp_idx >= 0)
 	{
-	  allow |= FILE_GENERIC_WRITE;
-	  writable = true;
+	  class_obj = aclbufp[tmp_idx].a_perm;
+	  access |= CYG_ACE_MASK_TO_WIN (class_obj);
 	}
-      if (aclbufp[i].a_perm & S_IXOTH)
-	allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
-      /* Keep S_ISVTX rule in sync with alloc_sd. */
-      if (pc.isdir ()
-	  && (aclbufp[i].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
-	  && !(null_mask & FILE_READ_DATA))
-	allow |= FILE_DELETE_CHILD;
-      /* Set inherit property. */
-      DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT)
-			  ? (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
-			     | INHERIT_ONLY_ACE)
-			  : NO_INHERITANCE;
-      /*
-       * If a specific acl contains a corresponding default entry with
-       * identical permissions, only one Windows ACE with proper
-       * inheritance bits is created.
-       */
-      if (!(aclbufp[i].a_type & ACL_DEFAULT)
-	  && aclbufp[i].a_type & (USER|GROUP)
-	  && (pos = searchace (aclbufp + i + 1, nentries - i - 1,
-			       aclbufp[i].a_type | ACL_DEFAULT,
-			       (aclbufp[i].a_type & (USER|GROUP))
-			       ? aclbufp[i].a_id : ILLEGAL_UID)) >= 0
-	  && aclbufp[i].a_perm == aclbufp[i + 1 + pos].a_perm)
+      else
 	{
-	  inheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
-	  /* invalidate the corresponding default entry. */
-	  invalid[i + 1 + pos] = true;
+	  /* Setting class_obj to group_obj allows to write below code without
+	     additional checks for existence of a CLASS_OBJ. */
+	  class_obj = group_obj;
 	}
-      switch (aclbufp[i].a_type)
+      /* Note that Windows filters the ACE Mask value so it only reflects
+	 the bit values supported by the object type.  The result is that
+	 we can't set a CLASS_OBJ value for ptys.  The get_posix_access
+	 function has to workaround that. */
+      if (!add_access_denied_ace (acl, access, well_known_null_sid, acl_len,
+				  inherit))
+	return NULL;
+
+      /* Do we potentially chmod a file with owner SID == group SID?  If so,
+	 make sure the owner perms are always >= group perms. */
+      if (!def && owner_eq_group)
+	  aclbufp[0].a_perm |= group_obj & class_obj;
+
+      /* This loop has two runs, the first w/ check_types == (USER_OBJ | USER),
+	 the second w/ check_types == (GROUP_OBJ | GROUP).  Each run creates
+	 first the deny, then the allow ACEs for the current types. */
+      for (int check_types = USER_OBJ | USER;
+	   check_types < CLASS_OBJ;
+	   check_types <<= 2)
 	{
-	case DEF_USER_OBJ:
-	  allow |= STANDARD_RIGHTS_ALL
-		   | (pc.fs_is_samba () ? 0 : FILE_WRITE_ATTRIBUTES);
-	  if (!add_access_allowed_ace (acl, ace_off++, allow,
-				       well_known_creator_owner_sid, acl_len, inheritance))
-	    return -1;
-	  break;
-	case USER:
-	case DEF_USER:
-	  if (!(pw = internal_getpwuid (aclbufp[i].a_id, &cldap))
-	      || !sid.getfrompw (pw))
+	  /* Create deny ACEs for users, then groups. */
+	  for (start_idx = idx;
+	       idx < nentries && aclbufp[idx].a_type & check_types;
+	       ++idx)
 	    {
-	      set_errno (EINVAL);
-	      return -1;
+	      /* Avoid creating DENY ACEs for the second occurrence of
+		 accounts which show up twice, as USER_OBJ and USER, or
+		 GROUP_OBJ and GROUP. */
+	      if ((aclbufp[idx].a_type & USER && aclsid[idx] == owner)
+		  || (aclbufp[idx].a_type & GROUP && aclsid[idx] == group))
+		continue;
+	      /* For the rules how to construct the deny access mask, see the
+		 comment right at the start of this file. */
+	      if (aclbufp[idx].a_type & USER_OBJ)
+		deny = ~aclbufp[idx].a_perm & (class_obj | other_obj);
+	      else if (aclbufp[idx].a_type & USER)
+		deny = (aclbufp[idx].a_perm ^ class_obj)
+		       | (~aclbufp[idx].a_perm & other_obj);
+	      /* Accommodate Windows: Only generate deny masks for SYSTEM
+		 and the Administrators group in terms of the execute bit,
+		 if they are not the primary group. */
+	      else if (aclbufp[idx].a_type & GROUP
+		       && (aclsid[idx] == well_known_system_sid
+			   || aclsid[idx] == well_known_admins_sid))
+		deny = aclbufp[idx].a_perm & ~(class_obj | S_IROTH | S_IWOTH);
+	      else
+		deny = (aclbufp[idx].a_perm & ~class_obj)
+		       | (~aclbufp[idx].a_perm & other_obj);
+	      if (!deny)
+		continue;
+	      access = 0;
+	      if (deny & S_IROTH)
+		access |= FILE_DENY_READ;
+	      if (deny & S_IWOTH)
+		access |= (aclbufp[idx].a_type & USER_OBJ)
+			  ? FILE_DENY_WRITE_OWNER : FILE_DENY_WRITE;
+	      if (deny & S_IXOTH)
+		access |= FILE_DENY_EXEC;
+	      if (!add_access_denied_ace (acl, access, aclsid[idx], acl_len,
+					  inherit))
+		return NULL;
 	    }
-	  if (!add_access_allowed_ace (acl, ace_off++, allow,
-				       sid, acl_len, inheritance))
-	    return -1;
-	  break;
-	case DEF_GROUP_OBJ:
-	  if (!add_access_allowed_ace (acl, ace_off++, allow,
-				       well_known_creator_group_sid, acl_len, inheritance))
-	    return -1;
-	  break;
-	case GROUP:
-	case DEF_GROUP:
-	  if (!(gr = internal_getgrgid (aclbufp[i].a_id, &cldap))
-	      || !sid.getfromgr (gr))
+	  /* Create allow ACEs for users, then groups. */
+	  for (idx = start_idx;
+	       idx < nentries && aclbufp[idx].a_type & check_types;
+	       ++idx)
 	    {
-	      set_errno (EINVAL);
-	      return -1;
+	      /* Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba,
+		 otherwise it enforces read permissions. */
+	      access = STD_RIGHTS_OTHER | (is_samba ? 0 : FILE_READ_ATTRIBUTES);
+	      if (aclbufp[idx].a_type & USER_OBJ)
+		{
+		  access |= STD_RIGHTS_OWNER;
+		  if (!is_samba)
+		    access |= FILE_WRITE_ATTRIBUTES;
+		  /* Set FILE_DELETE_CHILD on files with "rwx" perms for the
+		     owner so that the owner gets "full control" (Duh). */
+		  if (aclbufp[idx].a_perm == S_IRWXO)
+		    access |= FILE_DELETE_CHILD;
+		}
+	      if (aclbufp[idx].a_perm & S_IROTH)
+		access |= FILE_ALLOW_READ;
+	      if (aclbufp[idx].a_perm & S_IWOTH)
+		access |= FILE_ALLOW_WRITE;
+	      if (aclbufp[idx].a_perm & S_IXOTH)
+		access |= FILE_ALLOW_EXEC;
+	      /* Handle S_ISVTX. */
+	      if (S_ISDIR (attr)
+		  && (aclbufp[idx].a_perm & (S_IWOTH | S_IXOTH))
+		     == (S_IWOTH | S_IXOTH)
+		  && (!(attr & S_ISVTX) || aclbufp[idx].a_type & USER_OBJ))
+		access |= FILE_DELETE_CHILD;
+	      /* For ptys, make sure the Administrators group has WRITE_DAC
+		 and WRITE_OWNER perms. */
+	      if (dev_has_admins && aclsid[idx] == well_known_admins_sid)
+		access |= STD_RIGHTS_OWNER;
+	      if (!add_access_allowed_ace (acl, access, aclsid[idx], acl_len,
+					   inherit))
+		return NULL;
 	    }
-	  if (!add_access_allowed_ace (acl, ace_off++, allow,
-				       sid, acl_len, inheritance))
-	    return -1;
-	  break;
-	case DEF_OTHER_OBJ:
-	  if (!add_access_allowed_ace (acl, ace_off++, allow,
-				       well_known_world_sid,
-				       acl_len, inheritance))
-	    return -1;
 	}
+      /* For ptys if the admins group isn't in the ACL, add an ACE to make
+	 sure the group has WRITE_DAC and WRITE_OWNER perms. */
+      if (S_ISCHR (attr) && !dev_has_admins
+	  && !add_access_allowed_ace (acl,
+				      STD_RIGHTS_OWNER | FILE_ALLOW_READ
+				      | FILE_ALLOW_WRITE,
+				      well_known_admins_sid, acl_len,
+				      NO_INHERITANCE))
+	return NULL;
+      /* Create allow ACE for other.  It's preceeded by class_obj if it exists.
+	 If so, skip it. */
+      if (aclbufp[idx].a_type & CLASS_OBJ)
+	++idx;
+      access = STD_RIGHTS_OTHER | (is_samba ? 0 : FILE_READ_ATTRIBUTES);
+      if (aclbufp[idx].a_perm & S_IROTH)
+	access |= FILE_ALLOW_READ;
+      if (aclbufp[idx].a_perm & S_IWOTH)
+	access |= FILE_ALLOW_WRITE;
+      if (aclbufp[idx].a_perm & S_IXOTH)
+	access |= FILE_ALLOW_EXEC;
+      /* Handle S_ISVTX. */
+      if (S_ISDIR (attr)
+	  && (aclbufp[idx].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
+	  && !(attr & S_ISVTX))
+	access |= FILE_DELETE_CHILD;
+      if (!add_access_allowed_ace (acl, access, aclsid[idx++], acl_len,
+				   inherit))
+	return NULL;
     }
+
   /* Set AclSize to computed value. */
   acl->AclSize = acl_len;
   debug_printf ("ACL-Size: %u", acl_len);
@@ -308,7 +430,7 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
   if (!NT_SUCCESS (status))
     {
       __seterrno_from_nt_status (status);
-      return -1;
+      return NULL;
     }
   /* Make self relative security descriptor in sd_ret. */
   DWORD sd_size = 0;
@@ -316,36 +438,65 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
   if (sd_size <= 0)
     {
       __seterrno ();
-      return -1;
+      return NULL;
     }
   if (!sd_ret.realloc (sd_size))
     {
       set_errno (ENOMEM);
-      return -1;
+      return NULL;
     }
   status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
   if (!NT_SUCCESS (status))
     {
       __seterrno_from_nt_status (status);
-      return -1;
+      return NULL;
     }
   debug_printf ("Created SD-Size: %u", sd_ret.size ());
+  return sd_ret;
+}
+
+/* This function *requires* a verified and sorted acl list! */
+int
+setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
+	bool &writable)
+{
+  security_descriptor sd, sd_ret;
+  mode_t attr = pc.isdir () ? S_IFDIR : 0;
+  uid_t uid;
+  gid_t gid;
+
+  if (get_file_sd (handle, pc, sd, false))
+    return -1;
+  if (get_posix_access (sd, &attr, &uid, &gid, NULL, 0) < 0)
+    return -1;
+  if (!set_posix_access (attr, uid, gid, aclbufp, nentries,
+			 sd_ret, pc.fs_is_samba ()))
+    return -1;
+  /* FIXME?  Caller needs to know if any write perms are set to allow removing
+     the DOS R/O bit. */
+  writable = true;
   return set_file_sd (handle, pc, sd_ret, false);
 }
 
-/* Temporary access denied bits */
+/* Temporary access denied bits used by getace and get_posix_access during
+   Windows ACL processing.  These bits get removed before the created POSIX
+   ACL gets published. */
 #define DENY_R 040000
 #define DENY_W 020000
 #define DENY_X 010000
+#define DENY_RWX (DENY_R | DENY_W | DENY_X)
 
+/* New style ACL means, just read the bits and store them away.  Don't
+   create masked values on your own. */
 static void
 getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
-	DWORD win_ace_type)
+	DWORD win_ace_type, bool new_style)
 {
   acl.a_type = type;
   acl.a_id = id;
 
-  if ((win_ace_mask & FILE_READ_BITS) && !(acl.a_perm & (S_IROTH | DENY_R)))
+  if ((win_ace_mask & FILE_READ_BITS)
+      && (new_style || !(acl.a_perm & (S_IROTH | DENY_R))))
     {
       if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 	acl.a_perm |= S_IROTH;
@@ -353,7 +504,8 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
 	acl.a_perm |= DENY_R;
     }
 
-  if ((win_ace_mask & FILE_WRITE_BITS) && !(acl.a_perm & (S_IWOTH | DENY_W)))
+  if ((win_ace_mask & FILE_WRITE_BITS)
+      && (new_style || !(acl.a_perm & (S_IWOTH | DENY_W))))
     {
       if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 	acl.a_perm |= S_IWOTH;
@@ -361,7 +513,8 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
 	acl.a_perm |= DENY_W;
     }
 
-  if ((win_ace_mask & FILE_EXEC_BITS) && !(acl.a_perm & (S_IXOTH | DENY_X)))
+  if ((win_ace_mask & FILE_EXEC_BITS)
+      && (new_style || !(acl.a_perm & (S_IXOTH | DENY_X))))
     {
       if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 	acl.a_perm |= S_IXOTH;
@@ -370,40 +523,110 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
     }
 }
 
+/* From the SECURITY_DESCRIPTOR given in psd, compute user, owner, posix
+   attributes, as well as the POSIX acl.  The function returns the number
+   of entries returned in aclbufp, or -1 in case of error. */
 int
-getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
+get_posix_access (PSECURITY_DESCRIPTOR psd,
+		  mode_t *attr_ret, uid_t *uid_ret, gid_t *gid_ret,
+		  aclent_t *aclbufp, int nentries)
 {
-  security_descriptor sd;
-
-  if (get_file_sd (handle, pc, sd, false))
-    return -1;
-
-  cygpsid owner_sid;
-  cygpsid group_sid;
+  tmp_pathbuf tp;
   NTSTATUS status;
-  BOOLEAN dummy;
+  BOOLEAN dummy, acl_exists;
+  SECURITY_DESCRIPTOR_CONTROL ctrl;
+  ULONG rev;
+  PACL acl;
+  PACCESS_ALLOWED_ACE ace;
+  cygpsid owner_sid, group_sid;
+  cyg_ldap cldap;
   uid_t uid;
   gid_t gid;
-  cyg_ldap cldap;
-
-  status = RtlGetOwnerSecurityDescriptor (sd, (PSID *) &owner_sid, &dummy);
+  mode_t attr = 0;
+  aclent_t *lacl = NULL;
+  cygpsid ace_sid;
+  int pos, type, id, idx;
+
+  bool owner_eq_group;
+  bool just_created = false;
+  bool standard_ACEs_only = true;
+  bool new_style = false;
+  bool saw_user_obj = false;
+  bool saw_group_obj = false;
+  bool saw_other_obj = false;
+  bool saw_def_user_obj = false;
+  bool saw_def_group_obj = false;
+  bool has_class_perm = false;
+  bool has_def_class_perm = false;
+
+  mode_t class_perm = 0;
+  mode_t def_class_perm = 0;
+  int types_def = 0;
+  int def_pgrp_pos = -1;
+
+  if (aclbufp && nentries < MIN_ACL_ENTRIES)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  /* If reading the security descriptor failed, treat the object as
+     unreadable. */
+  if (!psd)
+    {
+      if (attr_ret)
+        *attr_ret &= S_IFMT;
+      if (uid_ret)
+        *uid_ret = ILLEGAL_UID;
+      if (gid_ret)
+        *gid_ret = ILLEGAL_GID;
+      if (aclbufp)
+	{
+	  aclbufp[0].a_type = USER_OBJ;
+	  aclbufp[0].a_id = ILLEGAL_UID;
+	  aclbufp[0].a_perm = 0;
+	  aclbufp[1].a_type = GROUP_OBJ;
+	  aclbufp[1].a_id = ILLEGAL_GID;
+	  aclbufp[1].a_perm = 0;
+	  aclbufp[2].a_type = OTHER_OBJ;
+	  aclbufp[2].a_id = ILLEGAL_GID;
+	  aclbufp[2].a_perm = 0;
+	  return MIN_ACL_ENTRIES;
+	}
+      return 0;
+    }
+  /* Fetch owner, group, and ACL from security descriptor. */
+  status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
   if (!NT_SUCCESS (status))
     {
       __seterrno_from_nt_status (status);
       return -1;
     }
-  uid = owner_sid.get_uid (&cldap);
-
-  status = RtlGetGroupSecurityDescriptor (sd, (PSID *) &group_sid, &dummy);
+  status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
   if (!NT_SUCCESS (status))
     {
       __seterrno_from_nt_status (status);
       return -1;
     }
+  status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
+  if (!NT_SUCCESS (status))
+    {
+      __seterrno_from_nt_status (status);
+      return -1;
+    }
+  /* Set uidret, gidret, and initalize attributes. */
+  uid = owner_sid.get_uid (&cldap);
   gid = group_sid.get_gid (&cldap);
+  if (attr_ret)
+    {
+      attr = *attr_ret & S_IFMT;
+      just_created = *attr_ret & S_JUSTCREATED;
+    }
+  /* Remember the fact that owner and group are the same account. */
+  owner_eq_group = owner_sid == group_sid;
 
-  aclent_t lacl[MAX_ACL_ENTRIES];
-  memset (&lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t));
+  /* Create and initialize local aclent_t array. */
+  lacl = (aclent_t *) tp.c_get ();
+  memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *));
   lacl[0].a_type = USER_OBJ;
   lacl[0].a_id = uid;
   lacl[1].a_type = GROUP_OBJ;
@@ -411,186 +634,404 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
   lacl[2].a_type = OTHER_OBJ;
   lacl[2].a_id = ILLEGAL_GID;
 
-  PACL acl;
-  BOOLEAN acl_exists;
-
-  status = RtlGetDaclSecurityDescriptor (sd, &acl_exists, &acl, &dummy);
-  if (!NT_SUCCESS (status))
+  /* No ACEs?  Everybody has full access. */
+  if (!acl_exists || !acl || acl->AceCount == 0)
     {
-      __seterrno_from_nt_status (status);
-      return -1;
+      for (pos = 0; pos < MIN_ACL_ENTRIES; ++pos)
+	lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
+      goto out;
     }
 
-  int pos, i, types_def = 0;
-  int pgrp_pos = 1, def_pgrp_pos = -1;
-  bool has_class_perm = false, has_def_class_perm = false;
-  mode_t class_perm = 0, def_class_perm = 0;
-
-  if (!acl_exists || !acl)
-    for (pos = 0; pos < 3; ++pos)
-      lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
-  else
+  /* Files and dirs are created with a NULL descriptor, so inheritence
+     rules kick in.  If no inheritable entries exist in the parent object,
+     Windows will create entries according to the user token's default DACL.
+     These entries are not desired and we ignore them at creation time.
+     We're just checking the SE_DACL_AUTO_INHERITED flag here, since that's
+     what we set in get_file_sd.  Read the longish comment there before
+     changing this test! */
+  if (just_created
+      && NT_SUCCESS (RtlGetControlSecurityDescriptor (psd, &ctrl, &rev))
+      && !(ctrl & SE_DACL_AUTO_INHERITED))
+    ;
+  else for (idx = 0; idx < acl->AceCount; ++idx)
     {
-      for (i = 0; i < acl->AceCount; ++i)
-	{
-	  ACCESS_ALLOWED_ACE *ace;
-
-	  if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
-	    continue;
+      if (!NT_SUCCESS (RtlGetAce (acl, idx, (PVOID *) &ace)))
+	continue;
 
-	  cygpsid ace_sid ((PSID) &ace->SidStart);
-	  int id;
-	  int type = 0;
+      ace_sid = (PSID) &ace->SidStart;
 
-	  if (ace_sid == well_known_null_sid)
-	    {
-	      /* Simply ignore. */
-	      continue;
-	    }
-	  if (ace_sid == well_known_world_sid)
-	    {
-	      type = OTHER_OBJ;
-	      id = ILLEGAL_GID;
-	    }
-	  else if (ace_sid == owner_sid)
-	    {
-	      type = USER_OBJ;
-	      id = uid;
-	    }
-	  else if (ace_sid == group_sid)
+      if (ace_sid == well_known_null_sid)
+	{
+	  /* Fetch special bits. */
+	  attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
+	  if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
+	      && ace->Mask & CYG_ACE_NEW_STYLE)
 	    {
-	      type = GROUP_OBJ;
-	      id = gid;
+	      /* New-style ACL.  Note the fact that a mask value is present
+		 since that changes how getace fetches the information.  That's
+		 fine, because the Cygwin SID ACE is supposed to precede all
+		 USER, GROUP and GROUP_OBJ entries.  Any ACL not created that
+		 way has been rearranged by the Windows functionality to create
+		 the brain-dead "canonical" ACL order and is broken anyway. */
+	      new_style = true;
+	      attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
+	      if (ace->Mask & CYG_ACE_MASK_VALID)
+		{
+		  if (!(ace->Header.AceFlags & INHERIT_ONLY))
+		    {
+		      if ((pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ))
+			  >= 0)
+			{
+			  lacl[pos].a_type = CLASS_OBJ;
+			  lacl[pos].a_id = ILLEGAL_GID;
+			  lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
+			}
+		      has_class_perm = true;
+		      class_perm = lacl[pos].a_perm;
+		    }
+		  if (ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT)
+		    {
+		      if ((pos = searchace (lacl, MAX_ACL_ENTRIES,
+					    DEF_CLASS_OBJ)) >= 0)
+			{
+			  lacl[pos].a_type = DEF_CLASS_OBJ;
+			  lacl[pos].a_id = ILLEGAL_GID;
+			  lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
+			}
+		      has_def_class_perm = true;
+		      def_class_perm = lacl[pos].a_perm;
+		    }
+		}
 	    }
-	  else if (ace_sid == well_known_creator_group_sid)
+	  continue;
+	}
+      if (ace_sid == owner_sid)
+	{
+	  type = USER_OBJ;
+	  id = uid;
+	}
+      else if (ace_sid == group_sid)
+	{
+	  type = GROUP_OBJ;
+	  id = gid;
+	}
+      else if (ace_sid == well_known_world_sid)
+	{
+	  type = OTHER_OBJ;
+	  id = ILLEGAL_GID;
+	  if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
+	      && !(ace->Header.AceFlags & INHERIT_ONLY))
+	    saw_other_obj = true;
+	}
+      else if (ace_sid == well_known_creator_owner_sid)
+	{
+	  type = DEF_USER_OBJ;
+	  types_def |= type;
+	  id = ILLEGAL_GID;
+	  saw_def_user_obj = true;
+	}
+      else if (ace_sid == well_known_creator_group_sid)
+	{
+	  type = DEF_GROUP_OBJ;
+	  types_def |= type;
+	  id = ILLEGAL_GID;
+	  saw_def_group_obj = true;
+	}
+      else
+	{
+	  id = ace_sid.get_id (TRUE, &type, &cldap);
+	  if (!type)
+	    continue;
+	}
+      /* If the SGID attribute is set on a just created file or dir, the
+         first group in the ACL is the desired primary group of the new
+	 object.  Alternatively, the first repetition of the owner SID is
+	 the desired primary group, and we mark the object as owner_eq_group
+	 object. */
+      if (just_created && attr & S_ISGID && !saw_group_obj
+	  && (type == GROUP || (type == USER_OBJ && saw_user_obj)))
+	{
+	  type = GROUP_OBJ;
+	  lacl[1].a_id = gid = id;
+	  owner_eq_group = true;
+	}
+      if (!(ace->Header.AceFlags & INHERIT_ONLY || type & ACL_DEFAULT))
+	{
+	  if (type == USER_OBJ)
 	    {
-	      type = DEF_GROUP_OBJ;
-	      types_def |= type;
-	      id = ILLEGAL_GID;
+	      /* If we get a second entry for the owner SID, it's either a
+		 GROUP_OBJ entry for the same SID, if owner SID == group SID,
+		 or it's an additional USER entry.  The latter can happen
+		 when chown'ing a file. */
+	      if (saw_user_obj)
+		{
+		  if (owner_eq_group && !saw_group_obj)
+		    {
+		      type = GROUP_OBJ;
+		      if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
+			saw_group_obj = true;
+		    }
+		  else
+		    type = USER;
+		}
+	      else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
+		saw_user_obj = true;
 	    }
-	  else if (ace_sid == well_known_creator_owner_sid)
+	  else if (type == GROUP_OBJ)
 	    {
-	      type = DEF_USER_OBJ;
-	      types_def |= type;
-	      id = ILLEGAL_GID;
+	      /* Same for the primary group. */
+	      if (saw_group_obj)
+		type = GROUP;
+	      if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
+		saw_group_obj = true;
 	    }
-	  else
-	    id = ace_sid.get_id (TRUE, &type, &cldap);
-
-	  if (!type)
-	    continue;
-	  if (!(ace->Header.AceFlags & INHERIT_ONLY_ACE || type & ACL_DEFAULT))
+	  if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
 	    {
-	      if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
+	      getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
+		      new_style && type & (USER | GROUP_OBJ | GROUP));
+	      if (!new_style)
 		{
-		  getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
 		  /* Fix up CLASS_OBJ value. */
-		  if (type == USER || type == GROUP)
+		  if (type & (USER | GROUP))
 		    {
 		      has_class_perm = true;
-		      class_perm |= lacl[pos].a_perm;
+		      /* Accommodate Windows: Never add SYSTEM and Admins to
+			 CLASS_OBJ.  Unless (implicitly) if they are the
+			 GROUP_OBJ entry. */
+		      if (ace_sid != well_known_system_sid
+			  && ace_sid != well_known_admins_sid)
+			class_perm |= lacl[pos].a_perm;
 		    }
 		}
+	      /* For a newly created file, we'd like to know if we're running
+		 with a standard ACL, one only consisting of POSIX perms, plus
+		 SYSTEM and Admins as maximum non-POSIX perms entries.  If it's
+		 a standard ACL, we apply umask.  That's not entirely correct,
+		 but it's probably the best we can do. */
+	      else if (type & (USER | GROUP)
+		       && just_created
+		       && standard_ACEs_only
+		       && ace_sid != well_known_system_sid
+		       && ace_sid != well_known_admins_sid)
+		standard_ACEs_only = false;
 	    }
-	  if ((ace->Header.AceFlags
-	      & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))
-	      && pc.isdir ())
+	}
+      if ((ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT))
+	{
+	  if (type == USER_OBJ)
+	    {
+	      /* As above: If we get a second entry for the owner SID, it's
+		 a GROUP_OBJ entry for the same SID if owner SID == group SID,
+		 but this time only if the S_ISGID bit is set. Otherwise it's
+		 an additional USER entry. */
+	      if (saw_def_user_obj)
+		{
+		  if (owner_eq_group && !saw_def_group_obj && attr & S_ISGID)
+		    type = GROUP_OBJ;	/* This needs post-processing in the
+					   following GROUP_OBJ handling... */
+		  else
+		    type = USER;
+		}
+	      else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
+		saw_def_user_obj = true;
+	    }
+	  if (type == GROUP_OBJ)
 	    {
-	      if (type == USER_OBJ)
-		type = USER;
-	      else if (type == GROUP_OBJ)
+	      /* If the SGID bit is set, the inheritable entry for the
+		 primary group is, in fact, the DEF_GROUP_OBJ entry,
+		 so don't change the type to GROUP in this case. */
+	      if (!new_style || saw_def_group_obj || !(attr & S_ISGID))
 		type = GROUP;
-	      type |= ACL_DEFAULT;
-	      types_def |= type;
-	      if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
+	      else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
+		saw_def_group_obj = true;
+	    }
+	  type |= ACL_DEFAULT;
+	  types_def |= type;
+	  if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
+	    {
+	      getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
+		      new_style && type & (USER | GROUP_OBJ | GROUP));
+	      if (!new_style)
 		{
-		  getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
 		  /* Fix up DEF_CLASS_OBJ value. */
-		  if (type == DEF_USER || type == DEF_GROUP)
+		  if (type & (USER | GROUP))
 		    {
 		      has_def_class_perm = true;
+		      /* Accommodate Windows: Never add SYSTEM and Admins to
+			 CLASS_OBJ.  Unless (implicitly) if they are the
+			 GROUP_OBJ entry. */
+		      if (ace_sid != well_known_system_sid
+			  && ace_sid != well_known_admins_sid)
 		      def_class_perm |= lacl[pos].a_perm;
 		    }
 		  /* And note the position of the DEF_GROUP_OBJ entry. */
-		  else if (type == DEF_GROUP_OBJ)
+		  if (type == DEF_GROUP_OBJ)
 		    def_pgrp_pos = pos;
 		}
 	    }
 	}
-      /* If secondary user and group entries exist in the ACL, fake a matching
-	 CLASS_OBJ entry. The CLASS_OBJ permissions are the or'ed permissions
-	 of the primary group permissions and all secondary user and group
-	 permissions. */
-      if (has_class_perm && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
+    }
+  /* If this is an old-style or non-Cygwin ACL, and secondary user and group
+     entries exist in the ACL, fake a matching CLASS_OBJ entry. The CLASS_OBJ
+     permissions are the or'ed permissions of the primary group permissions
+     and all secondary user and group permissions. */
+  if (!new_style && has_class_perm
+      && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
+    {
+      lacl[pos].a_type = CLASS_OBJ;
+      lacl[pos].a_id = ILLEGAL_GID;
+      lacl[pos].a_perm = class_perm | lacl[1].a_perm;
+    }
+  /* For ptys, fake a mask if the admins group is neither owner nor group.
+     In that case we have an extra ACE for the admins group, and we need a
+     CLASS_OBJ to get a valid POSIX ACL.  However, Windows filters the ACE
+     Mask value so it only reflects the bit values supported by the object
+     type.  The result is that we can't set an explicit CLASS_OBJ value for
+     ptys in the NULL SID ACE. */
+  else if (S_ISCHR (attr) && owner_sid != well_known_admins_sid
+	   && group_sid != well_known_admins_sid
+	   && (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0)
+    {
+      lacl[pos].a_type = CLASS_OBJ;
+      lacl[pos].a_id = ILLEGAL_GID;
+      lacl[pos].a_perm = lacl[1].a_perm; /* == group perms */
+    }
+  /* If this is a just created file, and this is an ACL with only standard
+     entries, or if standard POSIX permissions are missing (probably no
+     inherited ACEs so created from a default DACL), assign the permissions
+     specified by the file creation mask.  The values get masked by the
+     actually requested permissions by the caller per POSIX 1003.1e draft 17. */
+  if (just_created)
+    {
+      mode_t perms = (S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask;
+      if (standard_ACEs_only || !saw_user_obj)
+	lacl[0].a_perm = (perms >> 6) & S_IRWXO;
+      if (standard_ACEs_only || !saw_group_obj)
+	lacl[1].a_perm = (perms >> 3) & S_IRWXO;
+      if (standard_ACEs_only || !saw_other_obj)
+	lacl[2].a_perm = perms & S_IRWXO;
+    }
+  /* Ensure that the default acl contains at least
+     DEF_(USER|GROUP|OTHER)_OBJ entries.  */
+  if (types_def && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
+    {
+      if (!(types_def & USER_OBJ))
 	{
-	  lacl[pos].a_type = CLASS_OBJ;
-	  lacl[pos].a_id = ILLEGAL_GID;
-	  lacl[pos].a_perm = class_perm | lacl[pgrp_pos].a_perm;
+	  lacl[pos].a_type = DEF_USER_OBJ;
+	  lacl[pos].a_id = uid;
+	  lacl[pos].a_perm = lacl[0].a_perm;
+	  pos++;
 	}
-      /* Ensure that the default acl contains at least
-      	 DEF_(USER|GROUP|OTHER)_OBJ entries.  */
-      if (types_def && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
+      if (!(types_def & GROUP_OBJ) && pos < MAX_ACL_ENTRIES)
 	{
-	  if (!(types_def & USER_OBJ))
-	    {
-	      lacl[pos].a_type = DEF_USER_OBJ;
-	      lacl[pos].a_id = uid;
-	      lacl[pos].a_perm = lacl[0].a_perm;
-	      pos++;
-	    }
-	  if (!(types_def & GROUP_OBJ) && pos < MAX_ACL_ENTRIES)
-	    {
-	      lacl[pos].a_type = DEF_GROUP_OBJ;
-	      lacl[pos].a_id = gid;
-	      lacl[pos].a_perm = lacl[1].a_perm;
-	      /* Note the position of the DEF_GROUP_OBJ entry. */
-	      def_pgrp_pos = pos;
-	      pos++;
-	    }
-	  if (!(types_def & OTHER_OBJ) && pos < MAX_ACL_ENTRIES)
-	    {
-	      lacl[pos].a_type = DEF_OTHER_OBJ;
-	      lacl[pos].a_id = ILLEGAL_GID;
-	      lacl[pos].a_perm = lacl[2].a_perm;
-	      pos++;
-	    }
+	  lacl[pos].a_type = DEF_GROUP_OBJ;
+	  lacl[pos].a_id = gid;
+	  lacl[pos].a_perm = lacl[1].a_perm;
+	  /* Note the position of the DEF_GROUP_OBJ entry. */
+	  def_pgrp_pos = pos;
+	  pos++;
 	}
-      /* If secondary user default and group default entries exist in the ACL,
-	 fake a matching DEF_CLASS_OBJ entry. The DEF_CLASS_OBJ permissions are
-	 the or'ed permissions of the primary group default permissions and all
-	 secondary user and group default permissions. */
-      if (has_def_class_perm
-	  && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
+      if (!(types_def & OTHER_OBJ) && pos < MAX_ACL_ENTRIES)
 	{
-	  lacl[pos].a_type = DEF_CLASS_OBJ;
+	  lacl[pos].a_type = DEF_OTHER_OBJ;
 	  lacl[pos].a_id = ILLEGAL_GID;
-	  lacl[pos].a_perm = def_class_perm;
-	  if (def_pgrp_pos >= 0)
-	    lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
+	  lacl[pos].a_perm = lacl[2].a_perm;
+	  pos++;
 	}
     }
+  /* If this is an old-style or non-Cygwin ACL, and secondary user default
+     and group default entries exist in the ACL, fake a matching DEF_CLASS_OBJ
+     entry. The DEF_CLASS_OBJ permissions are the or'ed permissions of the
+     primary group default permissions and all secondary user and group def.
+     permissions. */
+  if (!new_style && has_def_class_perm
+      && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
+    {
+      lacl[pos].a_type = DEF_CLASS_OBJ;
+      lacl[pos].a_id = ILLEGAL_GID;
+      lacl[pos].a_perm = def_class_perm;
+      if (def_pgrp_pos >= 0)
+	lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
+    }
+
+  /* Make sure `pos' contains the number of used entries in lacl. */
   if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
     pos = MAX_ACL_ENTRIES;
+
+  /* For old-style or non-Cygwin ACLs, check for merging permissions. */
+  if (!new_style)
+    for (idx = 0; idx < pos; ++idx)
+      {
+	/* Current user?  If the user entry has a deny ACE, don't check. */
+	if (lacl[idx].a_id == myself->uid
+	    && lacl[idx].a_type & (USER_OBJ | USER)
+	    && !(lacl[idx].a_type & ACL_DEFAULT)
+	    && !(lacl[idx].a_perm & DENY_RWX))
+	  {
+	    int gpos;
+	    gid_t grps[NGROUPS_MAX];
+	    cyg_ldap cldap;
+
+	    /* Sum up all permissions of groups the user is member of, plus
+	       everyone perms, and merge them to user perms.  */
+	    mode_t grp_perm = lacl[2].a_perm & S_IRWXO;
+	    int gnum = internal_getgroups (NGROUPS_MAX, grps, &cldap);
+	    for (int g = 0; g < gnum && grp_perm != S_IRWXO; ++g)
+	      if ((gpos = 1, grps[g] == lacl[gpos].a_id)
+		  || (gpos = searchace (lacl, MAX_ACL_ENTRIES, GROUP, grps[g]))
+		     >= 0)
+		grp_perm |= lacl[gpos].a_perm & S_IRWXO;
+	    lacl[idx].a_perm |= grp_perm;
+	  }
+	/* For all groups, if everyone has more permissions, add everyone
+	   perms to group perms.  Skip groups with deny ACE. */
+	else if (lacl[idx].a_id & (GROUP_OBJ | GROUP)
+		 && !(lacl[idx].a_type & ACL_DEFAULT)
+		 && !(lacl[idx].a_perm & DENY_RWX))
+	  lacl[idx].a_perm |= lacl[2].a_perm & S_IRWXO;
+      }
+  /* If owner SID == group SID (Microsoft Accounts) merge group perms into
+     user perms but leave group perms intact.  That's a fake, but it allows
+     to keep track of the POSIX group perms without much effort. */
+  if (owner_eq_group)
+    lacl[0].a_perm |= lacl[1].a_perm;
+  /* Construct POSIX permission bits.  Fortunately we know exactly where
+     to fetch the affecting bits from, at least as long as the array
+     hasn't been sorted. */
+  attr |= (lacl[0].a_perm & S_IRWXO) << 6;
+  attr |= (has_class_perm ? class_perm : (lacl[1].a_perm & S_IRWXO)) << 3;
+  attr |= (lacl[2].a_perm & S_IRWXO);
+
+out:
+  if (uid_ret)
+    *uid_ret = uid;
+  if (gid_ret)
+    *gid_ret = gid;
+  if (attr_ret)
+    *attr_ret = attr;
   if (aclbufp)
     {
-#if 0
-      /* Disable owner/group permissions equivalence if owner SID == group SID.
-	 It's technically not quite correct, but it helps in case a security
-	 conscious application checks if a file has too open permissions.  In
-	 fact, since owner == group, there's no security issue here. */
-      if (owner_sid == group_sid)
-	lacl[1].a_perm = lacl[0].a_perm;
-#endif
       if (pos > nentries)
 	{
 	  set_errno (ENOSPC);
 	  return -1;
 	}
       memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
-      for (i = 0; i < pos; ++i)
-	aclbufp[i].a_perm &= ~(DENY_R | DENY_W | DENY_X);
+      for (idx = 0; idx < pos; ++idx)
+	aclbufp[idx].a_perm &= S_IRWXO;
       aclsort32 (pos, 0, aclbufp);
     }
+  return pos;
+}
+
+int
+getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
+{
+  security_descriptor sd;
+
+  if (get_file_sd (handle, pc, sd, false))
+    return -1;
+  int pos = get_posix_access (sd, NULL, NULL, NULL, aclbufp, nentries);
   syscall_printf ("%R = getacl(%S)", pos, pc.get_nt_native_path ());
   return pos;
 }
diff --git a/winsup/cygwin/sec_helper.cc b/winsup/cygwin/sec_helper.cc
index 679f3a8..753f156 100644
--- a/winsup/cygwin/sec_helper.cc
+++ b/winsup/cygwin/sec_helper.cc
@@ -616,22 +616,21 @@ _recycler_sd (void *buf, bool users, bool dir)
      pre-Vista permissions the same way as on Vista and later. */
   RtlCreateAcl (dacl, MAX_DACL_LEN (3), ACL_REVISION);
   RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
-			    dir ? CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
+			    dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 				: NO_INHERITANCE,
 			    FILE_ALL_ACCESS, well_known_admins_sid);
   RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
-			    dir ? CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
+			    dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 				: NO_INHERITANCE,
 			    FILE_ALL_ACCESS, well_known_system_sid);
   if (users)
-    RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, NO_PROPAGATE_INHERIT_ACE,
+    RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, INHERIT_NO_PROPAGATE,
 			      FILE_GENERIC_READ | FILE_GENERIC_EXECUTE
 			      | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES,
 			      well_known_users_sid);
   else
     RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
-			      dir ? CONTAINER_INHERIT_ACE
-				    | OBJECT_INHERIT_ACE
+			      dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 				  : NO_INHERITANCE,
 			      FILE_ALL_ACCESS, cygheap->user.sid ());
   LPVOID ace;
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc
index 86ebe2c..bee9b06 100644
--- a/winsup/cygwin/security.cc
+++ b/winsup/cygwin/security.cc
@@ -15,6 +15,7 @@ details. */
 #include "winsup.h"
 #include <unistd.h>
 #include <stdlib.h>
+#include <sys/acl.h>
 #include "cygerrno.h"
 #include "security.h"
 #include "path.h"
@@ -34,7 +35,6 @@ static GENERIC_MAPPING NO_COPY_RO file_mapping = { FILE_GENERIC_READ,
 						   FILE_GENERIC_WRITE,
 						   FILE_GENERIC_EXECUTE,
 						   FILE_ALL_ACCESS };
-
 LONG
 get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
 	     bool justcreated)
@@ -85,62 +85,46 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
 	  return -1;
 	}
     }
-  /* Ok, so we have a security descriptor now.  Unfortunately, if you want
-     to know if an ACE is inherited from the parent object, you can't just
-     call NtQuerySecurityObject once.  The problem is this:
+  /* We have a security descriptor now.  Unfortunately, if you want to know
+     if an ACE is inherited from the parent object, this isn't sufficient.
 
      In the simple case, the SDs control word contains one of the
      SE_DACL_AUTO_INHERITED or SE_DACL_PROTECTED flags, or at least one of
-     the ACEs has the INHERITED_ACE flag set.  In all of these cases the
-     GetSecurityInfo function calls NtQuerySecurityObject only once, too,
-     apparently because it figures that the DACL is self-sufficient, which
-     it usually is.  Windows Explorer, for instance, takes great care to
-     set these flags in a security descriptor if you change the ACL in the
-     GUI property dialog.
-
-     The tricky case is if none of these flags is set in the SD.  That means
-     the information whether or not an ACE has been inherited is not available
-     in the DACL of the object.  In this case GetSecurityInfo also fetches the
-     SD from the parent directory and tests if the object's SD contains
-     inherited ACEs from the parent.  The below code is closly emulating the
-     behaviour of GetSecurityInfo so we can get rid of this advapi32 dependency.
-
-     However, this functionality is slow, and the extra information is only
-     required when the file has been created and the permissions are about
-     to be set to POSIX permissions.  Therefore we only use it in case the
-     file just got created.
-
-     Note that GetSecurityInfo has a problem on 5.1 and 5.2 kernels.  Sometimes
-     it returns ERROR_INVALID_ADDRESS if a former request for the parent
-     directories' SD used NtQuerySecurityObject, rather than GetSecurityInfo
-     as well.  See http://cygwin.com/ml/cygwin-developers/2011-03/msg00027.html
-     for the solution.  This problem does not occur with the below code, so
-     the workaround has been removed. */
+     the ACEs has the INHERITED_ACE flag set.  In all of these cases we
+     know the DACL has been inherited.
+
+     If none of these flags is set in the SD, the information whether
+     or not an ACE has been inherited is not available in the DACL of the
+     object.  In this case GetSecurityInfo fetches the SD from the parent
+     directory and tests if the object's SD contains inherited ACEs from the
+     parent.
+
+     Note that we're not testing the SE_DACL_AUTO_INHERITED and
+     SE_DACL_PROTECTED flags here because we know the state the file's SD
+     is in.  Since we're creating all files with a NULL descriptor, the DACL
+     is either inherited from the parent, or it's the default DACL.  In
+     neither case, one of these flags is set.
+
+     For speed, we're not calling RtlConvertToAutoInheritSecurityObject
+     anymore (but keep the code here for reference).  Rather we just test
+     if one of the parent's ACEs is inheritable.  If so, we know we inherited
+     it and set the SE_DACL_AUTO_INHERITED flag.  If not, we may assume our
+     object's DACL is the default DACL.
+
+     This functionality is slow and the extra information is only required
+     when the file has been created and the permissions are about to be set
+     to POSIX permissions.  Therefore we only use it in case the file just
+     got created. */
   if (justcreated)
     {
-      SECURITY_DESCRIPTOR_CONTROL ctrl;
-      ULONG dummy;
       PACL dacl;
       BOOLEAN exists, def;
       ACCESS_ALLOWED_ACE *ace;
       UNICODE_STRING dirname;
-      PSECURITY_DESCRIPTOR psd, nsd;
+      PSECURITY_DESCRIPTOR psd;
       tmp_pathbuf tp;
 
-      /* Check SDs control flags.  If SE_DACL_AUTO_INHERITED or
-	 SE_DACL_PROTECTED is set we're done. */
-      RtlGetControlSecurityDescriptor (sd, &ctrl, &dummy);
-      if (ctrl & (SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED))
-	return 0;
-      /* Otherwise iterate over the ACEs and see if any one of them has the
-	 INHERITED_ACE flag set.  If so, we're done. */
-      if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl, &def))
-	  && exists && dacl)
-	for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
-	  if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
-	      && (ace->Header.AceFlags & INHERITED_ACE))
-	    return 0;
-      /* Otherwise, open the parent directory with READ_CONTROL... */
+      /* Open the parent directory with READ_CONTROL... */
       RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
       InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
 				  NULL, NULL);
@@ -164,12 +148,14 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
 			&dirname, status);
 	  return 0;
 	}
+#if 0
       /* ... and create a new security descriptor in which all inherited ACEs
 	 are marked with the INHERITED_ACE flag.  For a description of the
 	 undocumented RtlConvertToAutoInheritSecurityObject function from
 	 ntdll.dll see the MSDN man page for the advapi32 function
 	 ConvertToAutoInheritPrivateObjectSecurity.  Fortunately the latter
 	 is just a shim. */
+      PSECURITY_DESCRIPTOR nsd;
       status = RtlConvertToAutoInheritSecurityObject (psd, sd, &nsd, NULL,
 						      pc.isdir (),
 						      &file_mapping);
@@ -185,6 +171,36 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
       len = RtlLengthSecurityDescriptor (nsd);
       memcpy ((PSECURITY_DESCRIPTOR) sd, nsd, len);
       RtlDeleteSecurityObject (&nsd);
+#else
+      /* ... and check the parent descriptor for inheritable ACEs matching
+	 our current object type (file/dir).  The simple truth in our case
+	 is, either the parent dir had inheritable ACEs and all our ACEs are
+	 inherited, or the parent dir didn't have inheritable ACEs and all
+	 our ACEs are taken from the default DACL. */
+      bool inherited = false;
+      BYTE search_flags = pc.isdir () ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
+				      : SUB_OBJECTS_ONLY_INHERIT;
+      if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (psd, &exists, &dacl, &def))
+	  && exists && dacl)
+	for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
+	  if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
+	      && (ace->Header.AceFlags & search_flags))
+	    {
+	      inherited = true;
+	      break;
+	    }
+      /* Then, if the parent descriptor contained inheritable ACEs, we mark
+	 the SD as SE_DACL_AUTO_INHERITED.  Note that this requires the
+	 matching check in get_posix_access.  If we ever revert to
+	 RtlConvertToAutoInheritSecurityObject, the check in get_posix_access
+	 has to test every single ACE for the INHERITED_ACE flag again. */
+      if (inherited
+	  && NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl,
+						       &def))
+	  && exists && dacl)
+	RtlSetControlSecurityDescriptor (sd, SE_DACL_AUTO_INHERITED,
+					     SE_DACL_AUTO_INHERITED);
+#endif
     }
   return 0;
 }
@@ -234,194 +250,6 @@ set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
   return res;
 }
 
-static void
-get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid,
-			PSID group_sid, bool grp_member)
-{
-  ACCESS_ALLOWED_ACE *ace;
-  mode_t allow = 0;
-  mode_t deny = 0;
-  mode_t *flags, *anti;
-  bool isownergroup = RtlEqualSid (owner_sid, group_sid);
-
-  for (DWORD i = 0; i < acl->AceCount; ++i)
-    {
-      if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
-	continue;
-      if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
-	continue;
-      switch (ace->Header.AceType)
-	{
-	case ACCESS_ALLOWED_ACE_TYPE:
-	  flags = &allow;
-	  anti = &deny;
-	  break;
-	case ACCESS_DENIED_ACE_TYPE:
-	  flags = &deny;
-	  anti = &allow;
-	  break;
-	default:
-	  continue;
-	}
-
-      cygpsid ace_sid ((PSID) &ace->SidStart);
-      if (ace_sid == well_known_world_sid)
-	{
-	  if (ace->Mask & FILE_READ_BITS)
-	    *flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0)
-		      | ((!isownergroup && !(*anti & S_IRGRP)) ? S_IRGRP : 0)
-		      | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
-	  if (ace->Mask & FILE_WRITE_BITS)
-	    *flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0)
-		      | ((!isownergroup && !(*anti & S_IWGRP)) ? S_IWGRP : 0)
-		      | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
-	  if (ace->Mask & FILE_EXEC_BITS)
-	    *flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0)
-		      | ((!isownergroup && !(*anti & S_IXGRP)) ? S_IXGRP : 0)
-		      | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
-	  if ((S_ISDIR (*attribute)) &&
-	      (ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD))
-	      == (FILE_WRITE_DATA | FILE_EXECUTE))
-	    *flags |= S_ISVTX;
-	}
-      else if (ace_sid == well_known_null_sid)
-	{
-	  /* Read SUID, SGID and VTX bits from NULL ACE. */
-	  if (ace->Mask & FILE_READ_DATA)
-	    *flags |= S_ISVTX;
-	  if (ace->Mask & FILE_WRITE_DATA)
-	    *flags |= S_ISGID;
-	  if (ace->Mask & FILE_APPEND_DATA)
-	    *flags |= S_ISUID;
-	}
-      else if (ace_sid == owner_sid)
-	{
-	  if (ace->Mask & FILE_READ_BITS)
-	    *flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
-	  if (ace->Mask & FILE_WRITE_BITS)
-	    *flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
-	  if (ace->Mask & FILE_EXEC_BITS)
-	    *flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
-	  /* Apply deny mask to group if group SID == owner SID. */
-	  if (group_sid && isownergroup
-	      && ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
-	    {
-	      if (ace->Mask & FILE_READ_BITS)
-		*flags |= ((!(*anti & S_IRUSR)) ? S_IRGRP : 0);
-	      if (ace->Mask & FILE_WRITE_BITS)
-		*flags |= ((!(*anti & S_IWUSR)) ? S_IWGRP : 0);
-	      if (ace->Mask & FILE_EXEC_BITS)
-		*flags |= ((!(*anti & S_IXUSR)) ? S_IXGRP : 0);
-	    }
-	}
-      else if (ace_sid == group_sid)
-	{
-	  if (ace->Mask & FILE_READ_BITS)
-	    *flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
-		      | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
-	  if (ace->Mask & FILE_WRITE_BITS)
-	    *flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
-		      | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
-	  if (ace->Mask & FILE_EXEC_BITS)
-	    *flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
-		      | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
-	}
-      else if (flags == &allow)
-	{
-	  /* Simplified computation of additional group permissions based on
-	     the CLASS_OBJ value.  CLASS_OBJ represents the or'ed value of
-	     the primary group permissions and all secondary user and group
-	     permissions.  FIXME: This only takes ACCESS_ALLOWED_ACEs into
-	     account.  The computation with additional ACCESS_DENIED_ACE
-	     handling is much more complicated. */
-	  if (ace->Mask & FILE_READ_BITS)
-	    *flags |= S_IRGRP;
-	  if (ace->Mask & FILE_WRITE_BITS)
-	    *flags |= S_IWGRP;
-	  if (ace->Mask & FILE_EXEC_BITS)
-	    *flags |= S_IXGRP;
-	}
-    }
-  *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID);
-#if 0
-  /* Disable owner/group permissions equivalence if owner SID == group SID.
-     It's technically not quite correct, but it helps in case a security
-     conscious application checks if a file has too open permissions.  In
-     fact, since owner == group, there's no security issue here. */
-  if (owner_sid && group_sid && RtlEqualSid (owner_sid, group_sid)
-      /* FIXME: temporary exception for /var/empty */
-      && well_known_system_sid != group_sid)
-    {
-      allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
-      allow |= (((allow & S_IRUSR) ? S_IRGRP : 0)
-		| ((allow & S_IWUSR) ? S_IWGRP : 0)
-		| ((allow & S_IXUSR) ? S_IXGRP : 0));
-    }
-#endif
-  *attribute |= allow;
-}
-
-static void
-get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute,
-		  uid_t *uidret, gid_t *gidret)
-{
-  if (!psd)
-    {
-      /* If reading the security descriptor failed, treat the object
-	 as unreadable. */
-      if (attribute)
-	*attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
-      if (uidret)
-	*uidret = ILLEGAL_UID;
-      if (gidret)
-	*gidret = ILLEGAL_GID;
-      return;
-    }
-
-  cygpsid owner_sid;
-  cygpsid group_sid;
-  NTSTATUS status;
-  BOOLEAN dummy;
-
-  status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
-  if (!NT_SUCCESS (status))
-    debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status);
-  status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
-  if (!NT_SUCCESS (status))
-    debug_printf ("RtlGetGroupSecurityDescriptor: %y", status);
-
-  uid_t uid;
-  gid_t gid;
-  bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid);
-  if (uidret)
-    *uidret = uid;
-  if (gidret)
-    *gidret = gid;
-
-  if (!attribute)
-    {
-      syscall_printf ("uid %u, gid %u", uid, gid);
-      return;
-    }
-
-  PACL acl;
-  BOOLEAN acl_exists;
-
-  status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
-    }
-  else if (!acl_exists || !acl)
-    *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
-  else
-    get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member);
-
-  syscall_printf ("%sACL %y, uid %u, gid %u",
-		  (!acl_exists || !acl)?"NO ":"", *attribute, uid, gid);
-}
-
 static int
 get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
 {
@@ -454,7 +282,7 @@ get_reg_attribute (HKEY hkey, mode_t *attribute, uid_t *uidret,
 
   if (!get_reg_sd (hkey, sd))
     {
-      get_info_from_sd (sd, attribute, uidret, gidret);
+      get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
       return 0;
     }
   /* The entries are already set to default values */
@@ -471,7 +299,7 @@ get_file_attribute (HANDLE handle, path_conv &pc,
 
       if (!get_file_sd (handle, pc, sd, false))
 	{
-	  get_info_from_sd (sd, attribute, uidret, gidret);
+	  get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
 	  return 0;
 	}
       /* ENOSYS is returned by get_file_sd if fetching the DACL from a remote
@@ -499,8 +327,8 @@ get_file_attribute (HANDLE handle, path_conv &pc,
 }
 
 bool
-add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
-			PSID sid, size_t &len_add, DWORD inherit)
+add_access_allowed_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
+			DWORD inherit)
 {
   NTSTATUS status = RtlAddAccessAllowedAceEx (acl, ACL_REVISION, inherit,
 					      attributes, sid);
@@ -514,8 +342,8 @@ add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
 }
 
 bool
-add_access_denied_ace (PACL acl, int offset, DWORD attributes,
-		       PSID sid, size_t &len_add, DWORD inherit)
+add_access_denied_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
+		       DWORD inherit)
 {
   NTSTATUS status = RtlAddAccessDeniedAceEx (acl, ACL_REVISION, inherit,
 					     attributes, sid);
@@ -528,367 +356,6 @@ add_access_denied_ace (PACL acl, int offset, DWORD attributes,
   return true;
 }
 
-static PSECURITY_DESCRIPTOR
-alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute,
-	  security_descriptor &sd_ret)
-{
-  NTSTATUS status;
-  BOOLEAN dummy;
-  tmp_pathbuf tp;
-
-  /* NOTE: If the high bit of attribute is set, we have just created
-     a file or directory.  See below for an explanation. */
-
-  debug_printf("uid %u, gid %u, attribute 0%o", uid, gid, attribute);
-
-  /* Get owner and group from current security descriptor. */
-  PSID cur_owner_sid = NULL;
-  PSID cur_group_sid = NULL;
-  status = RtlGetOwnerSecurityDescriptor (sd_ret, &cur_owner_sid, &dummy);
-  if (!NT_SUCCESS (status))
-    debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status);
-  status = RtlGetGroupSecurityDescriptor (sd_ret, &cur_group_sid, &dummy);
-  if (!NT_SUCCESS (status))
-    debug_printf ("RtlGetGroupSecurityDescriptor: %y", status);
-
-  /* Get SID of owner. */
-  cygsid owner_sid;
-  /* Check for current user first */
-  if (uid == myself->uid)
-    owner_sid = cygheap->user.sid ();
-  else if (uid == ILLEGAL_UID)
-    owner_sid = cur_owner_sid;
-  else if (!owner_sid.getfrompw (internal_getpwuid (uid)))
-    {
-      set_errno (EINVAL);
-      return NULL;
-    }
-  owner_sid.debug_print ("alloc_sd: owner SID =");
-
-  /* Get SID of new group. */
-  cygsid group_sid;
-  /* Check for current user first */
-  if (gid == myself->gid)
-    group_sid = cygheap->user.groups.pgsid;
-  else if (gid == ILLEGAL_GID)
-    group_sid = cur_group_sid;
-  else if (!group_sid.getfromgr (internal_getgrgid (gid)))
-    {
-      set_errno (EINVAL);
-      return NULL;
-    }
-  group_sid.debug_print ("alloc_sd: group SID =");
-
-  /* Initialize local security descriptor. */
-  SECURITY_DESCRIPTOR sd;
-  RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
-
-  /* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being
-     modified by inheritable ACEs. */
-  RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
-
-  /* Create owner for local security descriptor. */
-  status = RtlSetOwnerSecurityDescriptor (&sd, owner_sid, FALSE);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      return NULL;
-    }
-
-  /* Create group for local security descriptor. */
-  status = RtlSetGroupSecurityDescriptor (&sd, group_sid, FALSE);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      return NULL;
-    }
-
-  /* Initialize local access control list. */
-  PACL acl = (PACL) tp.w_get ();
-  RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
-
-  /* From here fill ACL. */
-  size_t acl_len = sizeof (ACL);
-  int ace_off = 0;
-  /* Only used for sync objects (for ttys).  The admins group should
-     always have the right to manipulate the ACL, so we have to make sure
-     that the ACL gives the admins group STANDARD_RIGHTS_ALL access. */
-  bool saw_admins = false;
-
-  /* Construct allow attribute for owner.
-     Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise
-     it enforces read permissions.  Same for other's below. */
-  DWORD owner_allow = STANDARD_RIGHTS_ALL
-		      | (pc.fs_is_samba ()
-			 ? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES));
-  if (attribute & S_IRUSR)
-    owner_allow |= FILE_GENERIC_READ;
-  if (attribute & S_IWUSR)
-    owner_allow |= FILE_GENERIC_WRITE;
-  if (attribute & S_IXUSR)
-    owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
-  if (S_ISDIR (attribute)
-      && (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR))
-    owner_allow |= FILE_DELETE_CHILD;
-  /* For sync objects note that the owner is admin. */
-  if (S_ISCHR (attribute) && owner_sid == well_known_admins_sid)
-    saw_admins = true;
-
-  /* Construct allow attribute for group. */
-  DWORD group_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
-		      | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
-  if (attribute & S_IRGRP)
-    group_allow |= FILE_GENERIC_READ;
-  if (attribute & S_IWGRP)
-    group_allow |= FILE_GENERIC_WRITE;
-  if (attribute & S_IXGRP)
-    group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
-  if (S_ISDIR (attribute)
-      && (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP)
-      && !(attribute & S_ISVTX))
-    group_allow |= FILE_DELETE_CHILD;
-  /* For sync objects, add STANDARD_RIGHTS_ALL for admins group. */
-  if (S_ISCHR (attribute) && group_sid == well_known_admins_sid)
-    {
-      group_allow |= STANDARD_RIGHTS_ALL;
-      saw_admins = true;
-    }
-
-  /* Construct allow attribute for everyone. */
-  DWORD other_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
-		      | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
-  if (attribute & S_IROTH)
-    other_allow |= FILE_GENERIC_READ;
-  if (attribute & S_IWOTH)
-    other_allow |= FILE_GENERIC_WRITE;
-  if (attribute & S_IXOTH)
-    other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
-  if (S_ISDIR (attribute)
-      && (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
-      && !(attribute & S_ISVTX))
-    other_allow |= FILE_DELETE_CHILD;
-
-  /* Construct SUID, SGID and VTX bits in NULL ACE. */
-  DWORD null_allow = 0L;
-  if (attribute & (S_ISUID | S_ISGID | S_ISVTX))
-    {
-      if (attribute & S_ISUID)
-	null_allow |= FILE_APPEND_DATA;
-      if (attribute & S_ISGID)
-	null_allow |= FILE_WRITE_DATA;
-      if (attribute & S_ISVTX)
-	null_allow |= FILE_READ_DATA;
-    }
-
-  /* Add owner and group permissions if SIDs are equal
-     and construct deny attributes for group and owner. */
-  bool isownergroup;
-  if ((isownergroup = (owner_sid == group_sid)))
-    owner_allow |= group_allow;
-
-  DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
-  owner_deny &= ~(STANDARD_RIGHTS_READ
-		  | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
-
-  DWORD group_deny = ~group_allow & other_allow;
-  group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
-
-  /* Set deny ACE for owner. */
-  if (owner_deny
-      && !add_access_denied_ace (acl, ace_off++, owner_deny,
-				 owner_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-  /* Set deny ACE for group here to respect the canonical order,
-     if this does not impact owner */
-  if (group_deny && !(group_deny & owner_allow) && !isownergroup
-      && !add_access_denied_ace (acl, ace_off++, group_deny,
-				 group_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-  /* Set allow ACE for owner. */
-  if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
-			       owner_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-  /* Set deny ACE for group, if still needed. */
-  if ((group_deny & owner_allow) && !isownergroup
-      && !add_access_denied_ace (acl, ace_off++, group_deny,
-				 group_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-  /* Set allow ACE for group. */
-  if (!isownergroup
-      && !add_access_allowed_ace (acl, ace_off++, group_allow,
-				  group_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-
-  /* For sync objects, if we didn't see the admins group so far, add entry
-     with STANDARD_RIGHTS_ALL access. */
-  if (S_ISCHR (attribute) && !saw_admins)
-    {
-      if (!add_access_allowed_ace (acl, ace_off++, STANDARD_RIGHTS_ALL,
-				   well_known_admins_sid, acl_len,
-				   NO_INHERITANCE))
-	return NULL;
-      saw_admins = true;
-    }
-
-  /* Set allow ACE for everyone. */
-  if (!add_access_allowed_ace (acl, ace_off++, other_allow,
-			       well_known_world_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-  /* Set null ACE for special bits. */
-  if (null_allow
-      && !add_access_allowed_ace (acl, ace_off++, null_allow,
-				  well_known_null_sid, acl_len, NO_INHERITANCE))
-    return NULL;
-
-  /* Fill ACL with unrelated ACEs from current security descriptor. */
-  PACL oacl;
-  BOOLEAN acl_exists = FALSE;
-  ACCESS_ALLOWED_ACE *ace;
-
-  status = RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &oacl, &dummy);
-  if (NT_SUCCESS (status) && acl_exists && oacl)
-    for (DWORD i = 0; i < oacl->AceCount; ++i)
-      if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace)))
-	{
-	  cygpsid ace_sid ((PSID) &ace->SidStart);
-
-	  /* Always skip NULL SID as well as admins SID on virtual device files
-	     in /proc/sys. */
-	  if (ace_sid == well_known_null_sid
-	      || (S_ISCHR (attribute) && ace_sid == well_known_admins_sid))
-	    continue;
-	  /* Check for ACEs which are always created in the preceding code
-	     and check for the default inheritence ACEs which will be created
-	     for just created directories.  Skip them for just created
-	     directories or if they are not inherited.  If they are inherited,
-	     make sure they are *only* inherited, so they don't collide with
-	     the permissions set in this function. */
-	  if ((ace_sid == cur_owner_sid)
-	      || (ace_sid == owner_sid)
-	      || (ace_sid == cur_group_sid)
-	      || (ace_sid == group_sid)
-	      || (ace_sid == well_known_creator_owner_sid)
-	      || (ace_sid == well_known_creator_group_sid)
-	      || (ace_sid == well_known_world_sid))
-	    {
-	      if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
-		  || (ace->Header.AceFlags
-		      & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) == 0)
-		continue;
-	      else
-		ace->Header.AceFlags |= INHERIT_ONLY_ACE;
-	    }
-	  if (attribute & S_JUSTCREATED)
-	    {
-	      /* Since files and dirs are created with a NULL descriptor,
-		 inheritence rules kick in.  If no inheritable entries exist
-		 in the parent object, Windows will create entries from the
-		 user token's default DACL in the file DACL.  These entries
-		 are not desired and we drop them silently. */
-	      if (!(ace->Header.AceFlags & INHERITED_ACE))
-		continue;
-	      /* Remove the INHERITED_ACE flag since on POSIX systems
-		 inheritance is settled when the file has been created.
-		 This also avoids error messages in Windows Explorer when
-		 opening a file's security tab.  Explorer complains if
-		 inheritable ACEs are preceding non-inheritable ACEs. */
-	      ace->Header.AceFlags &= ~INHERITED_ACE;
-	      /* However, if the newly created object is a directory,
-	         it inherits the default ACL from its parent, so mark
-		 all unrelated, inherited ACEs inheritable. */
-	      if (S_ISDIR (attribute))
-		ace->Header.AceFlags |= CONTAINER_INHERIT_ACE
-					| OBJECT_INHERIT_ACE;
-	    }
-	  else if (uid == ILLEGAL_UID && gid == ILLEGAL_UID
-		   && ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
-		   && ace_sid != well_known_creator_group_sid
-		   && ace_sid != well_known_creator_owner_sid
-		   && ace_sid != well_known_world_sid)
-	    {
-	      /* FIXME: Temporary workaround for the problem that chmod does
-		 not affect the group permissions if other users and groups
-		 in the ACL have more permissions than the primary group due
-		 to the CLASS_OBJ emulation.  The temporary workaround is to
-		 disallow any secondary ACE in the ACL more permissions than
-		 the primary group when writing a new ACL via chmod. */
-	      ace->Mask &= group_allow;
-	    }
-	  /* Add unrelated ACCESS_DENIED_ACE to the beginning but behind
-	     the owner_deny, ACCESS_ALLOWED_ACE to the end.  FIXME: this
-	     would break the order of the inherit-only ACEs. */
-	  status = RtlAddAce (acl, ACL_REVISION,
-			      ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
-			      ?  (owner_deny ? 1 : 0) : MAXDWORD,
-			      (LPVOID) ace, ace->Header.AceSize);
-	  if (!NT_SUCCESS (status))
-	    {
-	      __seterrno_from_nt_status (status);
-	      return NULL;
-	    }
-	  ace_off++;
-	  acl_len += ace->Header.AceSize;
-	}
-
-  /* Construct appropriate inherit attribute for new directories.  Keep in
-     mind that we do this only for the sake of non-Cygwin applications.
-     Cygwin applications don't need this. */
-  if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
-    {
-      const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
-			    | INHERIT_ONLY_ACE;
-      /* Set allow ACE for owner. */
-      if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
-				   well_known_creator_owner_sid, acl_len,
-				   inherit))
-	return NULL;
-      /* Set allow ACE for group. */
-      if (!add_access_allowed_ace (acl, ace_off++, group_allow,
-				   well_known_creator_group_sid, acl_len,
-				   inherit))
-	return NULL;
-      /* Set allow ACE for everyone. */
-      if (!add_access_allowed_ace (acl, ace_off++, other_allow,
-				   well_known_world_sid, acl_len, inherit))
-	return NULL;
-    }
-
-  /* Set AclSize to computed value. */
-  acl->AclSize = acl_len;
-  debug_printf ("ACL-Size: %d", acl_len);
-
-  /* Create DACL for local security descriptor. */
-  status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      return NULL;
-    }
-
-  /* Make self relative security descriptor. */
-  DWORD sd_size = 0;
-  RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
-  if (sd_size <= 0)
-    {
-      __seterrno ();
-      return NULL;
-    }
-  if (!sd_ret.malloc (sd_size))
-    {
-      set_errno (ENOMEM);
-      return NULL;
-    }
-  status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
-  if (!NT_SUCCESS (status))
-    {
-      __seterrno_from_nt_status (status);
-      return NULL;
-    }
-  debug_printf ("Created SD-Size: %u", sd_ret.size ());
-
-  return sd_ret;
-}
-
 void
 set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
 			security_descriptor &sd)
@@ -896,8 +363,9 @@ set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
   psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
   RtlCreateSecurityDescriptor ((PSECURITY_DESCRIPTOR) psa->lpSecurityDescriptor,
 				SECURITY_DESCRIPTOR_REVISION);
-  psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
-					attribute, sd);
+  psa->lpSecurityDescriptor = set_posix_access (attribute, geteuid32 (),
+						getegid32 (), NULL, 0,
+						sd, false);
 }
 
 int
@@ -933,22 +401,24 @@ get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret,
 		      mode_t *attribute)
 {
   security_descriptor sd;
+  mode_t attr = S_IFCHR;
 
   if (get_object_sd (handle, sd))
     return -1;
-  get_info_from_sd (sd, attribute, uidret, gidret);
-  return 0;
+  if (attribute)
+    *attribute |= S_IFCHR;
+  else
+    attribute = &attr;
+  return get_posix_access (sd, attribute, uidret, gidret, NULL, 0)
+	 >= 0 ? 0 : -1;
 }
 
 int
-create_object_sd_from_attribute (HANDLE handle, uid_t uid, gid_t gid,
-				 mode_t attribute, security_descriptor &sd)
+create_object_sd_from_attribute (uid_t uid, gid_t gid, mode_t attribute,
+				 security_descriptor &sd)
 {
-  path_conv pc;
-  if ((handle && get_object_sd (handle, sd))
-      || !alloc_sd (pc, uid, gid, attribute, sd))
-    return -1;
-  return 0;
+  return set_posix_access (S_IFCHR | attribute, uid, gid, NULL, 0, sd, false)
+  	 ? 0 : -1;
 }
 
 int
@@ -966,36 +436,98 @@ set_object_sd (HANDLE handle, security_descriptor &sd, bool chown)
 }
 
 int
-set_object_attribute (HANDLE handle, uid_t uid, gid_t gid,
-		      mode_t attribute)
+set_object_attribute (HANDLE handle, uid_t uid, gid_t gid, mode_t attribute)
 {
   security_descriptor sd;
 
-  if (create_object_sd_from_attribute (handle, uid, gid, attribute, sd)
+  if (create_object_sd_from_attribute (uid, gid, attribute, sd)
       || set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID))
     return -1;
   return 0;
 }
 
 int
-set_file_attribute (HANDLE handle, path_conv &pc,
-		    uid_t uid, gid_t gid, mode_t attribute)
+set_created_file_access (HANDLE handle, path_conv &pc, mode_t attr)
 {
   int ret = -1;
-
-  if (pc.has_acls ())
-    {
-      security_descriptor sd;
-
-      if (!get_file_sd (handle, pc, sd, (bool)(attribute & S_JUSTCREATED))
-	  && alloc_sd (pc, uid, gid, attribut[...]

[diff truncated at 100000 bytes]


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]