[newlib-cygwin/topic/posix_acl_funcs] Implement POSIX.1e ACL functions

Corinna Vinschen corinna@sourceware.org
Wed Jan 27 14:56:00 GMT 2016


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

commit 7cdb1269322405fca0d72ba69f64810babc7e03b
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Wed Jan 6 18:41:36 2016 +0100

    Implement POSIX.1e ACL functions
    
    	* Makefile.in (DLL_OFILES): Add sec_posixacl.o.
    	(SUBLIBS): Add libacl.a
    	(libacl.a): New rule to create libacl.a.
    	* common.din: Export POSIX ACL functions as well as most libacl.a
    	extensions.
    	* fhandler.h (fhander_base::acl_get): New prototype.
    	(fhander_base::acl_set): Ditto.
    	(fhandler_disk_file::acl_get): Ditto.
    	(fhandler_disk_file::acl_set): Ditto.
    	* include/acl/libacl.h: New file.
    	* include/cygwin/version.h: Bump API minor version.
    	* include/sys/acl.h: Drop including cygwin/acl.h.  Accommodate
    	throughout Cygwin.  Add POSIX ACL definitions.
    	* sec_acl.cc: Include sec_posixacl.h.  Replace ILLEGAL_UID and
    	ILLEGAL_GID with ACL_UNDEFINED_ID where sensible.
    	(__aclcheck): New internal acl check function to be used for
    	Solaris and POSIX ACLs.
    	(aclcheck32): Call __aclcheck.
    	(__aclcalcmask): New function to compute ACL_MASK value.
    	(__aclsort): New internal acl sort function to be used for Solaris
    	and POSIX ACLs.
    	(aclsort32): Call __aclsort.
    	(permtostr): Work directly on provided buffer.
    	(__acltotext): New internal acltotext function to be used for
    	Solaris and POSIX ACLs.
    	(acltotext32): Call __acltotext.
    	(__aclfromtext): New internal aclfromtext function to be used for
    	Solaris and POSIX ACLs.
    	(aclfromtext32): Call __aclfromtext.
    	* sec_posixacl.cc: New file implemeting POSIX ACL functions.
    	* sec_posixacl.h: New internal header.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/Makefile.in              |    6 +-
 winsup/cygwin/common.din               |   39 ++
 winsup/cygwin/fhandler.cc              |    2 +-
 winsup/cygwin/fhandler.h               |    5 +
 winsup/cygwin/fhandler_disk_file.cc    |    2 +-
 winsup/cygwin/fhandler_socket.cc       |    2 +-
 winsup/cygwin/fhandler_tty.cc          |    2 +-
 winsup/cygwin/fhandler_virtual.cc      |    2 +-
 winsup/cygwin/include/acl/libacl.h     |   55 ++
 winsup/cygwin/include/cygwin/version.h |    3 +-
 winsup/cygwin/include/sys/acl.h        |   85 ++-
 winsup/cygwin/sec_acl.cc               |  768 +++++++++++++++--------
 winsup/cygwin/sec_helper.cc            |    2 +-
 winsup/cygwin/sec_posixacl.cc          | 1052 ++++++++++++++++++++++++++++++++
 winsup/cygwin/sec_posixacl.h           |   68 +++
 winsup/cygwin/security.cc              |    2 +-
 winsup/utils/getfacl.c                 |    3 +-
 winsup/utils/setfacl.c                 |    3 +-
 18 files changed, 1825 insertions(+), 276 deletions(-)

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 271a5be..fac9b3e 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -267,6 +267,7 @@ DLL_OFILES:= \
 	sec_acl.o \
 	sec_auth.o \
 	sec_helper.o \
+	sec_posixacl.o \
 	security.o \
 	select.o \
 	sem.o \
@@ -443,7 +444,7 @@ endif
 API_VER:=$(srcdir)/include/cygwin/version.h
 
 LIB_NAME:=libcygwin.a
-SUBLIBS:=libpthread.a libutil.a ${CURDIR}/libm.a ${CURDIR}/libc.a libdl.a libresolv.a librt.a
+SUBLIBS:=libpthread.a libutil.a ${CURDIR}/libm.a ${CURDIR}/libc.a libdl.a libresolv.a librt.a libacl.a
 EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a libtextreadmode.a
 INSTOBJS:=automode.o binmode.o textmode.o textreadmode.o
 TARGET_LIBS:=$(LIB_NAME) $(CYGWIN_START) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS)
@@ -643,6 +644,9 @@ libresolv.a: ${LIB_NAME} minires.o
 librt.a: ${LIB_NAME} posix_ipc.o
 	${speclib} ${@F}
 
+libacl.a: ${LIB_NAME} sec_posixacl.o
+	${speclib} ${@F}
+
 ${EXTRALIBS}: lib%.a: %.o
 	$(AR) cru $@ $?
 
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index d7f4d24..afb81f6 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -117,6 +117,45 @@ accept = cygwin_accept SIGFE
 accept4 SIGFE
 access SIGFE
 acl SIGFE
+acl_add_perm NOSIGFE
+acl_calc_mask SIGFE
+acl_check NOSIGFE
+acl_clear_perms NOSIGFE
+acl_cmp SIGFE
+acl_copy_entry NOSIGFE
+acl_copy_ext NOSIGFE
+acl_copy_int NOSIGFE
+acl_create_entry SIGFE
+acl_delete_def_file SIGFE
+acl_delete_entry NOSIGFE
+acl_delete_perm NOSIGFE
+acl_dup SIGFE
+acl_entries NOSIGFE
+acl_equiv_mode SIGFE
+acl_error NOSIGFE
+acl_extended_fd SIGFE
+acl_extended_file SIGFE
+acl_extended_file_nofollow SIGFE
+acl_free SIGFE
+acl_from_mode NOSIGFE
+acl_from_text SIGFE
+acl_get_entry NOSIGFE
+acl_get_fd SIGFE
+acl_get_file SIGFE
+acl_get_perm NOSIGFE
+acl_get_permset NOSIGFE
+acl_get_qualifier SIGFE
+acl_get_tag_type NOSIGFE
+acl_init SIGFE
+acl_set_fd SIGFE
+acl_set_file SIGFE
+acl_set_permset NOSIGFE
+acl_set_qualifier NOSIGFE
+acl_set_tag_type NOSIGFE
+acl_size NOSIGFE
+acl_to_any_text SIGFE
+acl_to_text SIGFE
+acl_valid NOSIGFE
 aclcheck NOSIGFE
 aclfrommode SIGFE
 aclfrompbits SIGFE
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 7e4d996..33743d4 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -13,7 +13,7 @@ details. */
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/uio.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <sys/param.h>
 #include "cygerrno.h"
 #include "perprocess.h"
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index d94f38d..134fd71 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -56,6 +56,7 @@ typedef struct __DIR DIR;
 struct dirent;
 struct iovec;
 struct acl;
+struct __acl_t;
 
 enum dirent_states
 {
@@ -355,6 +356,8 @@ public:
   virtual int __reg1 fchmod (mode_t mode);
   virtual int __reg2 fchown (uid_t uid, gid_t gid);
   virtual int __reg3 facl (int, int, struct acl *);
+  virtual struct __acl_t * __reg2 acl_get (uint32_t);
+  virtual int __reg3 acl_set (struct __acl_t *, uint32_t);
   virtual ssize_t __reg3 fgetxattr (const char *, void *, size_t);
   virtual int __reg3 fsetxattr (const char *, const void *, size_t, int);
   virtual int __reg3 fadvise (off_t, off_t, int);
@@ -1011,6 +1014,8 @@ class fhandler_disk_file: public fhandler_base
   int __reg1 fchmod (mode_t mode);
   int __reg2 fchown (uid_t uid, gid_t gid);
   int __reg3 facl (int, int, struct acl *);
+  struct __acl_t * __reg2 acl_get (uint32_t);
+  int __reg3 acl_set (struct __acl_t *, uint32_t);
   ssize_t __reg3 fgetxattr (const char *, void *, size_t);
   int __reg3 fsetxattr (const char *, const void *, size_t, int);
   int __reg3 fadvise (off_t, off_t, int);
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index 4ee67e2..faea9be 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -13,7 +13,7 @@ details. */
 #include <winioctl.h>
 #include <lm.h>
 #include <stdlib.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <sys/statvfs.h>
 #include "cygerrno.h"
 #include "security.h"
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
index 7d3efad..990cdc7 100644
--- a/winsup/cygwin/fhandler_socket.cc
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -41,7 +41,7 @@
 #include "wininfo.h"
 #include <unistd.h>
 #include <sys/param.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include "cygtls.h"
 #include <sys/un.h>
 #include "ntdll.h"
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index ee37ed4..a39a566 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -12,7 +12,7 @@ details. */
 #include "winsup.h"
 #include <stdlib.h>
 #include <sys/param.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <cygwin/kd.h>
 #include "cygerrno.h"
 #include "security.h"
diff --git a/winsup/cygwin/fhandler_virtual.cc b/winsup/cygwin/fhandler_virtual.cc
index 2d56d74..8f302a7 100644
--- a/winsup/cygwin/fhandler_virtual.cc
+++ b/winsup/cygwin/fhandler_virtual.cc
@@ -10,7 +10,7 @@ Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
 #include "winsup.h"
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <sys/statvfs.h>
 #include "cygerrno.h"
 #include "path.h"
diff --git a/winsup/cygwin/include/acl/libacl.h b/winsup/cygwin/include/acl/libacl.h
new file mode 100644
index 0000000..b93d686
--- /dev/null
+++ b/winsup/cygwin/include/acl/libacl.h
@@ -0,0 +1,55 @@
+/* acl/libacl.h: Non-POSIX extensions of libacl
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _ACL_LIBACL_H
+#define _ACL_LIBACL_H
+
+#include <sys/acl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Sync'd with cygwin/acl.h values. */
+#define ACL_MULTI_ERROR     (0x4)
+#define ACL_DUPLICATE_ERROR (0x5)
+#define ACL_ENTRY_ERROR     (0x6)
+#define ACL_MISS_ERROR      (0x7)
+
+/* acl_to_any_text options. */
+#define TEXT_ABBREVIATE		(0x01)
+#define TEXT_NUMERIC_IDS	(0x02)
+#define TEXT_SOME_EFFECTIVE	(0x04)
+#define TEXT_ALL_EFFECTIVE	(0x08)
+#define TEXT_SMART_INDENT	(0x10)
+
+extern int acl_check (acl_t __acl, int *__last);
+extern int acl_cmp (acl_t __acl1, acl_t __acl2);
+extern int acl_entries (acl_t __acl);
+extern int acl_equiv_mode (acl_t __acl, mode_t *__mode_p);
+extern const char *acl_error (int __code);
+extern int acl_extended_fd (int __fd);
+extern int acl_extended_file (const char *__path_p);
+extern int acl_extended_file_nofollow (const char *__path_p);
+extern acl_t acl_from_mode (mode_t __mode);
+extern int acl_get_perm (acl_permset_t __permset_d, acl_perm_t __perm);
+extern char *acl_to_any_text (acl_t __acl, const char *__prefix,
+			      char __separator, int __options);
+
+#if 0
+/* TODO */
+struct error_context;
+extern int perm_copy_file (const char *, const char *, struct error_context *);
+extern int perm_copy_fd (const char *, int, const char *, int,
+			 struct error_context *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _ACL_LIBACL_H */
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index 0a82b3f..d254a79 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -475,13 +475,14 @@ details. */
       291: Export aligned_alloc, at_quick_exit, quick_exit.
       292: Export rpmatch.
       293: Convert utmpname/utmpxname to int.
+      294: Export POSIX ACL functions.
      */
 
      /* Note that we forgot to bump the api for ualarm, strtoll, strtoull,
 	sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 293
+#define CYGWIN_VERSION_API_MINOR 294
 
      /* There is also a compatibity version number associated with the
 	shared memory regions.  It is incremented when incompatible
diff --git a/winsup/cygwin/include/sys/acl.h b/winsup/cygwin/include/sys/acl.h
index 89c38bc..c3a9fc1 100644
--- a/winsup/cygwin/include/sys/acl.h
+++ b/winsup/cygwin/include/sys/acl.h
@@ -12,6 +12,89 @@ details. */
 #ifndef _SYS_ACL_H
 #define _SYS_ACL_H
 
-#include <cygwin/acl.h>
+#include <_ansi.h>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POSIX ACL types and functions.  The implementation is based on the
+   internal original Solaris implementation as defined in cygwin/acl.h.
+   However, we don't include cygwin/acl.h from here to avoid poisoning
+   the namespace. */
+
+/* acl_perm_t constants */
+#define ACL_READ		(0x4)
+#define ACL_WRITE		(0x2)
+#define ACL_EXECUTE		(0x1)
+
+/* acl_tag_t constants, in sync with values from cygwin/acl.h */
+#define ACL_UNDEFINED_TAG	(0x0000)
+#define ACL_USER_OBJ		(0x0001)
+#define ACL_USER		(0x0002)
+#define ACL_GROUP_OBJ		(0x0004)
+#define ACL_GROUP		(0x0008)
+#define ACL_MASK		(0x0010)
+#define ACL_OTHER		(0x0020)
+
+/* acl_type_t constants */
+#define ACL_TYPE_ACCESS         (0x0)
+#define ACL_TYPE_DEFAULT        (0x1)
+
+/* qualifier constant */
+#define ACL_UNDEFINED_ID        ((id_t) -1)
+
+/* entry_id constants */
+#define ACL_FIRST_ENTRY         (0x0)
+#define ACL_NEXT_ENTRY          (0x1)
+
+/* types */
+typedef uint32_t acl_perm_t, acl_type_t, acl_tag_t;
+typedef uint64_t acl_permset_t;
+typedef uint64_t acl_entry_t;
+
+struct __acl_t;
+typedef struct __acl_t *acl_t;
+
+extern int	acl_add_perm (acl_permset_t __permset_d, acl_perm_t __perm);
+extern int	acl_calc_mask (acl_t *__acl_p);
+extern int	acl_clear_perms (acl_permset_t __permset_d);
+extern int	acl_copy_entry (acl_entry_t __dest_d, acl_entry_t __src_d);
+extern ssize_t	acl_copy_ext (void *__buf_p, acl_t __acl, ssize_t __size);
+extern acl_t	acl_copy_int (const void *__buf_p);
+extern int	acl_create_entry (acl_t *__acl_p, acl_entry_t *__entry_p);
+extern int	acl_delete_def_file (const char *__path_p);
+extern int	acl_delete_entry (acl_t __acl, acl_entry_t __entry_d);
+extern int	acl_delete_perm (acl_permset_t __permset_d, acl_perm_t __perm);
+extern acl_t	acl_dup (acl_t __acl);
+extern int	acl_free (void *__obj_p);
+extern acl_t	acl_from_text (const char *__buf_p);
+extern int	acl_get_entry (acl_t __acl, int __entry_id,
+			       acl_entry_t *__entry_p);
+extern acl_t	acl_get_fd (int __fd);
+extern acl_t	acl_get_file (const char *__path_p, acl_type_t __type);
+extern int	acl_get_permset (acl_entry_t __entry_d,
+				 acl_permset_t *__permset_p);
+extern void    *acl_get_qualifier (acl_entry_t __entry_d);
+extern int	acl_get_tag_type (acl_entry_t __entry_d,
+				  acl_tag_t *__tag_type_p);
+extern acl_t	acl_init (int __count);
+extern int	acl_set_fd (int __fd, acl_t __acl);
+extern int	acl_set_file (const char *__path_p, acl_type_t __type,
+			      acl_t __acl);
+extern int	acl_set_permset (acl_entry_t __entry_d,
+				 acl_permset_t __permset_d);
+extern int	acl_set_qualifier (acl_entry_t __entry_d,
+				   const void *__tag_qualifier_p);
+extern int	acl_set_tag_type (acl_entry_t __entry_d, acl_tag_t __tag_type);
+extern ssize_t	acl_size (acl_t __acl);
+extern char    *acl_to_text (acl_t __acl, ssize_t *__len_p);
+extern int	acl_valid (acl_t __acl);
+
+#ifdef __cplusplus
+}
+#endif
 #endif /* _SYS_ACL_H */
diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc
index 51003c4..73c0425 100644
--- a/winsup/cygwin/sec_acl.cc
+++ b/winsup/cygwin/sec_acl.cc
@@ -13,7 +13,6 @@ details. */
 
 #include "winsup.h"
 #include <stdlib.h>
-#include <sys/acl.h>
 #include <ctype.h>
 #include "cygerrno.h"
 #include "security.h"
@@ -23,6 +22,7 @@ details. */
 #include "cygheap.h"
 #include "ntdll.h"
 #include "tls_pbuf.h"
+#include "sec_posixacl.h"
 
 /* How does a correctly constructed new-style Windows ACL claiming to be a
    POSIX ACL look like?
@@ -118,7 +118,8 @@ searchace (aclent_t *aclp, int nentries, int type, uid_t id)
   int i;
 
   for (i = 0; i < nentries; ++i)
-    if ((aclp[i].a_type == type && (id == ILLEGAL_UID || aclp[i].a_id == id))
+    if ((aclp[i].a_type == type
+	 && (id == ACL_UNDEFINED_ID || aclp[i].a_id == id))
 	|| !aclp[i].a_type)
       return i;
   return -1;
@@ -186,25 +187,25 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
     {
       aclbufp = (aclent_t *) tp.c_get ();
       aclbufp[0].a_type = USER_OBJ;
-      aclbufp[0].a_id = ILLEGAL_UID;
+      aclbufp[0].a_id = ACL_UNDEFINED_ID;
       aclbufp[0].a_perm = (attr >> 6) & S_IRWXO;
       aclbufp[1].a_type = GROUP_OBJ;
-      aclbufp[1].a_id = ILLEGAL_GID;
+      aclbufp[1].a_id = ACL_UNDEFINED_ID;
       aclbufp[1].a_perm = (attr >> 3) & S_IRWXO;
       aclbufp[2].a_type = OTHER_OBJ;
-      aclbufp[2].a_id = ILLEGAL_GID;
+      aclbufp[2].a_id = ACL_UNDEFINED_ID;
       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_id = ACL_UNDEFINED_ID;
 	  aclbufp[3].a_perm = (attr >> 6) & S_IRWXO;
 	  aclbufp[4].a_type = GROUP_OBJ;
-	  aclbufp[4].a_id = ILLEGAL_GID;
+	  aclbufp[4].a_id = ACL_UNDEFINED_ID;
 	  aclbufp[4].a_perm = (attr >> 3) & S_IRWXO;
 	  aclbufp[5].a_type = OTHER_OBJ;
-	  aclbufp[5].a_id = ILLEGAL_GID;
+	  aclbufp[5].a_id = ACL_UNDEFINED_ID;
 	  aclbufp[5].a_perm = attr & S_IRWXO;
 	  nentries += MIN_ACL_ENTRIES;
 	}
@@ -612,19 +613,19 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
       if (attr_ret)
         *attr_ret &= S_IFMT;
       if (uid_ret)
-        *uid_ret = ILLEGAL_UID;
+        *uid_ret = ACL_UNDEFINED_ID;
       if (gid_ret)
-        *gid_ret = ILLEGAL_GID;
+        *gid_ret = ACL_UNDEFINED_ID;
       if (aclbufp)
 	{
 	  aclbufp[0].a_type = USER_OBJ;
-	  aclbufp[0].a_id = ILLEGAL_UID;
+	  aclbufp[0].a_id = ACL_UNDEFINED_ID;
 	  aclbufp[0].a_perm = 0;
 	  aclbufp[1].a_type = GROUP_OBJ;
-	  aclbufp[1].a_id = ILLEGAL_GID;
+	  aclbufp[1].a_id = ACL_UNDEFINED_ID;
 	  aclbufp[1].a_perm = 0;
 	  aclbufp[2].a_type = OTHER_OBJ;
-	  aclbufp[2].a_id = ILLEGAL_GID;
+	  aclbufp[2].a_id = ACL_UNDEFINED_ID;
 	  aclbufp[2].a_perm = 0;
 	  return MIN_ACL_ENTRIES;
 	}
@@ -668,7 +669,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
   lacl[1].a_type = GROUP_OBJ;
   lacl[1].a_id = gid;
   lacl[2].a_type = OTHER_OBJ;
-  lacl[2].a_id = ILLEGAL_GID;
+  lacl[2].a_id = ACL_UNDEFINED_ID;
   /* Create array to collect SIDs of all entries in lacl. */
   aclsid = (cygpsid *) tp.w_get ();
   aclsid[0] = owner_sid;
@@ -724,7 +725,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
 			  >= 0)
 			{
 			  lacl[pos].a_type = CLASS_OBJ;
-			  lacl[pos].a_id = ILLEGAL_GID;
+			  lacl[pos].a_id = ACL_UNDEFINED_ID;
 			  lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
 			  aclsid[pos] = well_known_null_sid;
 			}
@@ -737,7 +738,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
 					    DEF_CLASS_OBJ)) >= 0)
 			{
 			  lacl[pos].a_type = DEF_CLASS_OBJ;
-			  lacl[pos].a_id = ILLEGAL_GID;
+			  lacl[pos].a_id = ACL_UNDEFINED_ID;
 			  lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
 			  aclsid[pos] = well_known_null_sid;
 			}
@@ -761,7 +762,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
       else if (ace_sid == well_known_world_sid)
 	{
 	  type = OTHER_OBJ;
-	  id = ILLEGAL_GID;
+	  id = ACL_UNDEFINED_ID;
 	  if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
 	      && !(ace->Header.AceFlags & INHERIT_ONLY))
 	    saw_other_obj = true;
@@ -770,14 +771,14 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
 	{
 	  type = DEF_USER_OBJ;
 	  types_def |= type;
-	  id = ILLEGAL_GID;
+	  id = ACL_UNDEFINED_ID;
 	  saw_def_user_obj = true;
 	}
       else if (ace_sid == well_known_creator_group_sid)
 	{
 	  type = DEF_GROUP_OBJ;
 	  types_def |= type;
-	  id = ILLEGAL_GID;
+	  id = ACL_UNDEFINED_ID;
 	  saw_def_group_obj = true;
 	}
       else
@@ -878,10 +879,10 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
 		{
 		  if (owner_eq_group && !saw_def_group_obj && attr & S_ISGID)
 		    {
-		      /* This needs post-processing in the following GROUP_OBJ
-		         handling...  Set id to ILLEGAL_GID to play it safe. */
+		      /* Needs post-processing in the following GROUP_OBJ block.
+		         Set id to ACL_UNDEFINED_ID to play it safe. */
 		      type = GROUP_OBJ;
-		      id = ILLEGAL_GID;
+		      id = ACL_UNDEFINED_ID;
 		    }
 		  else
 		    type = USER;
@@ -934,7 +935,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
       && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
     {
       lacl[pos].a_type = CLASS_OBJ;
-      lacl[pos].a_id = ILLEGAL_GID;
+      lacl[pos].a_id = ACL_UNDEFINED_ID;
       class_perm |= lacl[1].a_perm;
       lacl[pos].a_perm = class_perm;
       aclsid[pos] = well_known_null_sid;
@@ -950,7 +951,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
 	   && (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0)
     {
       lacl[pos].a_type = CLASS_OBJ;
-      lacl[pos].a_id = ILLEGAL_GID;
+      lacl[pos].a_id = ACL_UNDEFINED_ID;
       lacl[pos].a_perm = lacl[1].a_perm; /* == group perms */
       aclsid[pos] = well_known_null_sid;
     }
@@ -994,7 +995,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
       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_id = ACL_UNDEFINED_ID;
 	  lacl[pos].a_perm = lacl[2].a_perm;
 	  aclsid[pos] = well_known_world_sid;
 	  pos++;
@@ -1009,7 +1010,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
       && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
     {
       lacl[pos].a_type = DEF_CLASS_OBJ;
-      lacl[pos].a_id = ILLEGAL_GID;
+      lacl[pos].a_id = ACL_UNDEFINED_ID;
       lacl[pos].a_perm = def_class_perm;
       if (def_pgrp_pos >= 0)
 	lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
@@ -1163,130 +1164,151 @@ facl32 (int fd, int cmd, int nentries, aclent_t *aclbufp)
   return res;
 }
 
-extern "C" int
-aclcheck32 (aclent_t *aclbufp, int nentries, int *which)
+int
+__aclcheck (aclent_t *aclbufp, int nentries, int *which, bool posix)
 {
   bool has_user_obj = false;
   bool has_group_obj = false;
   bool has_other_obj = false;
   bool has_class_obj = false;
-  bool has_ug_objs __attribute__ ((unused)) = false;
-  bool has_def_objs __attribute__ ((unused)) = false;
-  bool has_def_user_obj __attribute__ ((unused)) = false;
+  bool has_ug_objs = false;
+  bool has_def_objs = false;
+  bool has_def_user_obj = false;
   bool has_def_group_obj = false;
   bool has_def_other_obj = false;
   bool has_def_class_obj = false;
-  bool has_def_ug_objs __attribute__ ((unused)) = false;
+  bool has_def_ug_objs = false;
   int pos2;
 
   for (int pos = 0; pos < nentries; ++pos)
-    switch (aclbufp[pos].a_type)
-      {
-      case USER_OBJ:
-	if (has_user_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return USER_ERROR;
-	  }
-	has_user_obj = true;
-	break;
-      case GROUP_OBJ:
-	if (has_group_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return GRP_ERROR;
-	  }
-	has_group_obj = true;
-	break;
-      case OTHER_OBJ:
-	if (has_other_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return OTHER_ERROR;
-	  }
-	has_other_obj = true;
-	break;
-      case CLASS_OBJ:
-	if (has_class_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return CLASS_ERROR;
-	  }
-	has_class_obj = true;
-	break;
-      case USER:
-      case GROUP:
-	if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
-			       aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
-	  {
-	    if (which)
-	      *which = pos2;
-	    return DUPLICATE_ERROR;
-	  }
-	has_ug_objs = true;
-	break;
-      case DEF_USER_OBJ:
-	if (has_def_user_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return USER_ERROR;
-	  }
-	has_def_objs = has_def_user_obj = true;
-	break;
-      case DEF_GROUP_OBJ:
-	if (has_def_group_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return GRP_ERROR;
-	  }
-	has_def_objs = has_def_group_obj = true;
-	break;
-      case DEF_OTHER_OBJ:
-	if (has_def_other_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return OTHER_ERROR;
-	  }
-	has_def_objs = has_def_other_obj = true;
-	break;
-      case DEF_CLASS_OBJ:
-	if (has_def_class_obj)
-	  {
-	    if (which)
-	      *which = pos;
-	    return CLASS_ERROR;
-	  }
-	has_def_objs = has_def_class_obj = true;
-	break;
-      case DEF_USER:
-      case DEF_GROUP:
-	if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
-			       aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
-	  {
-	    if (which)
-	      *which = pos2;
-	    return DUPLICATE_ERROR;
-	  }
-	has_def_objs = has_def_ug_objs = true;
-	break;
-      default:
-	return ENTRY_ERROR;
-      }
+    {
+      /* POSIX ACLs may contain deleted entries.  Just ignore them. */
+      if (posix && aclbufp[pos].a_type == ACL_DELETED_TAG)
+	continue;
+      /* POSIX defines two sorts of ACLs, access and default, none of which
+	 is supposed to have the ACL_DEFAULT flag set. */
+      if (posix && (aclbufp[pos].a_type & ACL_DEFAULT))
+	{
+	  if (which)
+	    *which = pos;
+	  return ENTRY_ERROR;
+	}
+      switch (aclbufp[pos].a_type)
+	{
+	case USER_OBJ:
+	  if (has_user_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return USER_ERROR;
+	    }
+	  has_user_obj = true;
+	  break;
+	case GROUP_OBJ:
+	  if (has_group_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return GRP_ERROR;
+	    }
+	  has_group_obj = true;
+	  break;
+	case OTHER_OBJ:
+	  if (has_other_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return OTHER_ERROR;
+	    }
+	  has_other_obj = true;
+	  break;
+	case CLASS_OBJ:
+	  if (has_class_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return CLASS_ERROR;
+	    }
+	  has_class_obj = true;
+	  break;
+	case USER:
+	case GROUP:
+	  if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
+				 aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
+	    {
+	      if (which)
+		*which = pos2;
+	      return DUPLICATE_ERROR;
+	    }
+	  has_ug_objs = true;
+	  break;
+	case DEF_USER_OBJ:
+	  if (has_def_user_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return USER_ERROR;
+	    }
+	  has_def_objs = has_def_user_obj = true;
+	  break;
+	case DEF_GROUP_OBJ:
+	  if (has_def_group_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return GRP_ERROR;
+	    }
+	  has_def_objs = has_def_group_obj = true;
+	  break;
+	case DEF_OTHER_OBJ:
+	  if (has_def_other_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return OTHER_ERROR;
+	    }
+	  has_def_objs = has_def_other_obj = true;
+	  break;
+	case DEF_CLASS_OBJ:
+	  if (has_def_class_obj)
+	    {
+	      if (which)
+		*which = pos;
+	      return CLASS_ERROR;
+	    }
+	  has_def_objs = has_def_class_obj = true;
+	  break;
+	case DEF_USER:
+	case DEF_GROUP:
+	  if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
+				 aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
+	    {
+	      if (which)
+		*which = pos2;
+	      return DUPLICATE_ERROR;
+	    }
+	  has_def_objs = has_def_ug_objs = true;
+	  break;
+	default:
+	  if (which)
+	    *which = pos;
+	  return ENTRY_ERROR;
+	}
+    }
   if (!has_user_obj
       || !has_group_obj
       || !has_other_obj
-      || (has_def_objs
-	  && (!has_def_user_obj || !has_def_group_obj || !has_def_other_obj))
-      || (has_ug_objs && !has_class_obj)
-      || (has_def_ug_objs && !has_def_class_obj)
-     )
+      || (has_ug_objs && !has_class_obj))
+    {
+      if (which)
+	*which = -1;
+      return MISS_ERROR;
+    }
+  /* Check for missing default entries only on Solaris ACLs. */
+  if (!posix &&
+      ((has_def_objs
+	&& !(has_def_user_obj && has_def_group_obj && has_def_other_obj))
+       || (has_def_ug_objs && !has_def_class_obj)))
     {
       if (which)
 	*which = -1;
@@ -1295,22 +1317,44 @@ aclcheck32 (aclent_t *aclbufp, int nentries, int *which)
   return 0;
 }
 
-void
+extern "C" int
+aclcheck32 (aclent_t *aclbufp, int nentries, int *which)
+{
+  return __aclcheck (aclbufp, nentries, which, false);
+}
+
+/* For the sake of acl_calc_mask, return -1 if the ACL doesn't need a mask
+   or if a mask entry already exists (__aclcalcmask sets the mask by itself).
+   Otherwise return the mask value so acl_calc_mask can create a mask entry.
+   This doesn't matter when called from aclsort. */
+mode_t
 __aclcalcmask (aclent_t *aclbufp, int nentries)
 {
   mode_t mask = 0;
+  bool need_mask = false;
   int mask_idx = -1;
 
   for (int idx = 0; idx < nentries; ++idx)
-    {
-      if (aclbufp[idx].a_type == CLASS_OBJ)
-	mask_idx = idx;
-      else if (aclbufp[idx].a_type
-	       & (USER | GROUP_OBJ | GROUP))
+    switch (aclbufp[idx].a_type)
+      {
+      case USER:
+      case GROUP:
+	need_mask = true;
+	/*FALLTHRU*/
+      case GROUP_OBJ:
 	mask |= aclbufp[idx].a_perm;
-    }
+	break;
+      case CLASS_OBJ:
+	mask_idx = idx;
+	break;
+      default:
+	break;
+      }
   if (mask_idx != -1)
     aclbufp[mask_idx].a_perm = mask;
+  if (need_mask && mask_idx == -1)
+    return mask;
+  return (acl_perm_t) -1;
 }
 
 static int
@@ -1324,15 +1368,25 @@ acecmp (const void *a1, const void *a2)
 #undef ace
 }
 
-extern "C" int
-aclsort32 (int nentries, int calclass, aclent_t *aclbufp)
+/* Sorts any acl.  Called from sec_posixacl.cc. */
+int
+__aclsort (int nentries, aclent_t *aclbufp)
 {
-  if (aclcheck32 (aclbufp, nentries, NULL))
+  if (!aclbufp || nentries < 0)
     {
       set_errno (EINVAL);
       return -1;
     }
-  if (!aclbufp || nentries < 1)
+  if (nentries > 0)
+    qsort ((void *) aclbufp, nentries, sizeof (aclent_t), acecmp);
+  return 0;
+}
+
+extern "C" int
+aclsort32 (int nentries, int calclass, aclent_t *aclbufp)
+{
+  if (!aclbufp || nentries < MIN_ACL_ENTRIES
+      || aclcheck32 (aclbufp, nentries, NULL))
     {
       set_errno (EINVAL);
       return -1;
@@ -1432,79 +1486,224 @@ aclfrompbits32 (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
 }
 
 static char *
-permtostr (mode_t perm)
+permtostr (char *bufp, mode_t perm)
 {
-  static char pbuf[4];
-
-  pbuf[0] = (perm & S_IROTH) ? 'r' : '-';
-  pbuf[1] = (perm & S_IWOTH) ? 'w' : '-';
-  pbuf[2] = (perm & S_IXOTH) ? 'x' : '-';
-  pbuf[3] = '\0';
-  return pbuf;
+  *bufp++ = (perm & S_IROTH) ? 'r' : '-';
+  *bufp++ = (perm & S_IWOTH) ? 'w' : '-';
+  *bufp++ = (perm & S_IXOTH) ? 'x' : '-';
+  return bufp;
 }
 
-extern "C" char *
-acltotext32 (aclent_t *aclbufp, int aclcnt)
+#define _OPT(o) (options & (o))
+
+#define _CHK(l) \
+		  if (bufp + (l) >= buf + 2 * NT_MAX_PATH - 1) \
+		    { \
+		      set_errno (ENOMEM); \
+		      return NULL; \
+		    }
+#define _CPY(s)	({ \
+		  const char *_s = (s); \
+		  _CHK (strlen (_s)); \
+		  bufp = stpcpy (bufp, _s); \
+		})
+#define _PTS(p) { \
+		  _CHK (3); \
+		  bufp = permtostr (bufp, p); \
+		}
+
+#define _CMP(s)		(!strncmp (bufp, acl_part[s].str, acl_part[s].len))
+
+struct _acl_part
+{
+  const char *str;
+  size_t len;
+};
+
+static _acl_part acl_part_l[] =
+{
+  { "default:",	8 },
+  { "user:",	5 },
+  { "group:",	6 },
+  { "mask:",	5 },
+  { "other:",	6 }
+};
+
+static _acl_part acl_part_s[] =
+{
+  { "d:",	2 },
+  { "u:",	2 },
+  { "g:",	2 },
+  { "m:",	2 },
+  { "o:",	2 }
+};
+
+enum _acl_type {
+  default_s,
+  user_s,
+  group_s,
+  mask_s,
+  other_s,
+  none_s
+};
+
+char *
+__acltotext (aclent_t *aclbufp, int aclcnt, const char *prefix, char separator,
+	     int options)
 {
   if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES
-      || aclcheck32 (aclbufp, aclcnt, NULL))
+      || aclsort32 (aclcnt, 0, aclbufp))
     {
       set_errno (EINVAL);
       return NULL;
     }
+  cyg_ldap cldap;
   tmp_pathbuf tp;
-  char *buf = tp.c_get ();
-  buf[0] = '\0';
+  char *buf = tp.t_get ();
+  char *bufp = buf;
+  char *entry_start;
   bool first = true;
+  struct passwd *pw;
+  struct group *gr;
+  mode_t mask = S_IRWXO;
+  mode_t def_mask = S_IRWXO;
+  mode_t effective;
+  int pos;
+  _acl_part *acl_part = _OPT (TEXT_ABBREVIATE) ? acl_part_s : acl_part_l;
 
-  for (int pos = 0; pos < aclcnt; ++pos)
+  *bufp = '\0';
+  /* If effective rights are requested, fetch mask values. */
+  if (_OPT (TEXT_SOME_EFFECTIVE | TEXT_ALL_EFFECTIVE))
+    {
+      if ((pos = searchace (aclbufp, aclcnt, CLASS_OBJ)) >= 0)
+	mask = aclbufp[pos].a_perm;
+      if ((pos = searchace (aclbufp, aclcnt, DEF_CLASS_OBJ)) >= 0)
+	def_mask = aclbufp[pos].a_perm;
+    }
+  for (pos = 0; pos < aclcnt; ++pos)
     {
       if (!first)
-	strcat (buf, ",");
+	{
+	  _CHK (1);
+	  *bufp++ = separator;
+	}
       first = false;
-      if (aclbufp[pos].a_type & ACL_DEFAULT)
-	strcat (buf, "default");
+      /* Rememeber start position of entry to compute TEXT_SMART_INDENT tabs. */
+      entry_start = bufp;
+      /* prefix */
+      if (prefix)
+	_CPY (prefix);
+      /* Solaris default acl? */
+      if (!_OPT (TEXT_IS_POSIX) && aclbufp[pos].a_type & ACL_DEFAULT)
+	_CPY (acl_part[default_s].str);
+      /* acl type */
       switch (aclbufp[pos].a_type & ~ACL_DEFAULT)
 	{
 	case USER_OBJ:
-	  __small_sprintf (buf + strlen (buf), "user::%s",
-		   permtostr (aclbufp[pos].a_perm));
-	  break;
 	case USER:
-	  __small_sprintf (buf + strlen (buf), "user:%d:%s",
-		   aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm));
+	  _CPY (acl_part[user_s].str);
 	  break;
 	case GROUP_OBJ:
-	  __small_sprintf (buf + strlen (buf), "group::%s",
-		   permtostr (aclbufp[pos].a_perm));
-	  break;
 	case GROUP:
-	  __small_sprintf (buf + strlen (buf), "group:%d:%s",
-		   aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm));
+	  _CPY (acl_part[group_s].str);
 	  break;
 	case CLASS_OBJ:
-	  __small_sprintf (buf + strlen (buf), "mask::%s",
-		   permtostr (aclbufp[pos].a_perm));
+	  _CPY (acl_part[mask_s].str);
 	  break;
 	case OTHER_OBJ:
-	  __small_sprintf (buf + strlen (buf), "other::%s",
-		   permtostr (aclbufp[pos].a_perm));
+	  _CPY (acl_part[other_s].str);
+	  break;
+	}
+      /* id, if any  */
+      switch (aclbufp[pos].a_type & ~ACL_DEFAULT)
+	{
+	case USER:
+	  if (_OPT (TEXT_NUMERIC_IDS)
+	      || !(pw = internal_getpwuid (aclbufp[pos].a_id, &cldap)))
+	    {
+	      _CHK (11);
+	      bufp += __small_sprintf (bufp, "%u:", aclbufp[pos].a_id);
+	    }
+	  else
+	    {
+	      _CHK (strlen (pw->pw_name + 1));
+	      bufp += __small_sprintf (bufp, "%s:", pw->pw_name);
+	    }
+	  break;
+	case GROUP:
+	  if (_OPT (TEXT_NUMERIC_IDS)
+	      || !(gr = internal_getgrgid (aclbufp[pos].a_id, &cldap)))
+	    {
+	      _CHK (11);
+	      bufp += __small_sprintf (bufp, "%u:", aclbufp[pos].a_id);
+	    }
+	  else
+	    {
+	      _CHK (strlen (gr->gr_name));
+	      bufp += __small_sprintf (bufp, "%s:", gr->gr_name);
+	    }
 	  break;
 	default:
-	  set_errno (EINVAL);
-	  return NULL;
+	  _CPY (":");
+	  break;
 	}
+      /* real permissions */
+      _PTS (aclbufp[pos].a_perm);
+      if (!_OPT (TEXT_SOME_EFFECTIVE | TEXT_ALL_EFFECTIVE))
+	continue;
+      /* effective permissions */
+      switch (aclbufp[pos].a_type)
+	{
+	case USER:
+	case GROUP_OBJ:
+	case GROUP:
+	  effective = aclbufp[pos].a_perm & mask;
+	  break;
+	case DEF_USER:
+	case DEF_GROUP_OBJ:
+	case DEF_GROUP:
+	  effective = aclbufp[pos].a_perm & def_mask;
+	  break;
+	default:
+	  continue;
+	}
+      if (_OPT (TEXT_ALL_EFFECTIVE) || effective != aclbufp[pos].a_perm)
+	{
+	  if (_OPT (TEXT_SMART_INDENT))
+	    {
+	      int tabs = 3 - (bufp - entry_start) / 8;
+	      if (tabs-- > 0)
+		{
+		  _CHK (tabs);
+		  while (tabs-- > 0)
+		    *bufp++ = '\t';
+		}
+	    }
+	  _CPY ("\t#effective:");
+	  _PTS (effective);
+	}
+    }
+  if (_OPT (TEXT_END_SEPARATOR))
+    {
+      _CHK (1);
+      *bufp++ = separator;
+      *bufp++ = '\0';
     }
   return strdup (buf);
 }
 
+extern "C" char *
+acltotext32 (aclent_t *aclbufp, int aclcnt)
+{
+  return __acltotext (aclbufp, aclcnt, NULL, ',', 0);
+}
+
 static mode_t
-permfromstr (char *perm)
+permfromstr (char *perm, bool posix_long)
 {
   mode_t mode = 0;
+  int idx;
 
-  if (strlen (perm) != 3)
-    return 01000;
   if (perm[0] == 'r')
     mode |= S_IROTH;
   else if (perm[0] != '-')
@@ -1517,125 +1716,170 @@ permfromstr (char *perm)
     mode |= S_IXOTH;
   else if (perm[2] != '-')
     return 01000;
-  return mode;
+  idx = 3;
+  /* In posix long mode, only tabs up to a hash sign allowed. */
+  if (posix_long)
+    while (perm[idx] == '\t')
+      ++idx;
+  if (perm[idx] == '\0' || (posix_long && perm[idx] == '#'))
+    return mode;
+  return 01000;
 }
 
-extern "C" aclent_t *
-aclfromtext32 (const char *acltextp, int *aclcnt)
+void *
+__aclfromtext (const char *acltextp, int *aclcnt, bool posix)
 {
-  if (!acltextp || strlen (acltextp) > NT_MAX_PATH)
+  if (!acltextp || strlen (acltextp) >= 2 * NT_MAX_PATH)
     {
       set_errno (EINVAL);
       return NULL;
     }
+  cyg_ldap cldap;
   tmp_pathbuf tp;
-  aclent_t lacl[MAX_ACL_ENTRIES];
-  memset (lacl, 0, sizeof lacl);
+  const char *delim;
+  _acl_part *acl_part;
+  char *bufp, *lasts, *qualifier;
   int pos = 0;
+  int acl_type;
+
+  aclent_t *lacl = (aclent_t *) tp.c_get ();
+  memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *));
   char *buf = tp.t_get ();
   stpcpy (buf, acltextp);
-  char *lasts;
-  cyg_ldap cldap;
-  for (char *c = strtok_r (buf, ",", &lasts);
-       c;
-       c = strtok_r (NULL, ",", &lasts))
+
+  if (posix)
+    {
+      /* Posix long or short form.  Any \n in the string means long form. */
+      if (strchr (buf, '\n'))
+	{
+	  delim = "\n";
+	  acl_part = acl_part_l;
+	}
+      else
+	{
+	  delim = ",";
+	  acl_part = acl_part_s;
+	}
+    }
+  else
     {
-      if (!strncmp (c, "default", 7))
+      /* Solaris aclfromtext format. */
+      delim = ",";
+      acl_part = acl_part_l;
+    }
+
+  for (bufp = strtok_r (buf, delim, &lasts);
+       bufp;
+       bufp = strtok_r (NULL, delim, &lasts))
+    {
+      /* Handle default acl entries only for Solaris ACLs. */
+      if (!posix && _CMP (default_s))
 	{
 	  lacl[pos].a_type |= ACL_DEFAULT;
-	  c += 7;
+	  bufp += acl_part[default_s].len;
 	}
-      if (!strncmp (c, "user:", 5))
+      lacl[pos].a_id = ACL_UNDEFINED_ID;
+      for (acl_type = user_s; acl_type < none_s; ++acl_type)
+	if (_CMP (acl_type))
+	  break;
+      if (acl_type == none_s)
 	{
-	  if (c[5] == ':')
-	    lacl[pos].a_type |= USER_OBJ;
-	  else
-	    {
-	      lacl[pos].a_type |= USER;
-	      c += 5;
-	      if (isalpha (*c))
-		{
-		  struct passwd *pw = internal_getpwnam (c, &cldap);
-		  if (!pw)
-		    {
-		      set_errno (EINVAL);
-		      return NULL;
-		    }
-		  lacl[pos].a_id = pw->pw_uid;
-		  c = strchrnul (c, ':');
-		}
-	      else if (isdigit (*c))
-		lacl[pos].a_id = strtol (c, &c, 10);
-	      if (*c != ':')
-		{
-		  set_errno (EINVAL);
-		  return NULL;
-		}
-	    }
+	  set_errno (EINVAL);
+	  return NULL;
 	}
-      else if (!strncmp (c, "group:", 6))
+      bufp += acl_part[acl_type].len;
+      switch (acl_type)
 	{
-	  if (c[5] == ':')
-	    lacl[pos].a_type |= GROUP_OBJ;
-	  else
+	case user_s:
+	case group_s:
+	  qualifier = bufp;
+	  bufp = strchrnul (bufp, ':');
+	  *bufp++ = '\0';
+	  /* No qualifier?  USER_OBJ or GROUP_OBJ */
+	  if (!*qualifier)
 	    {
-	      lacl[pos].a_type |= GROUP;
-	      c += 5;
-	      if (isalpha (*c))
-		{
-		  struct group *gr = internal_getgrnam (c, &cldap);
-		  if (!gr)
-		    {
-		      set_errno (EINVAL);
-		      return NULL;
-		    }
-		  lacl[pos].a_id = gr->gr_gid;
-		  c = strchrnul (c, ':');
-		}
-	      else if (isdigit (*c))
-		lacl[pos].a_id = strtol (c, &c, 10);
-	      if (*c != ':')
+	      lacl[pos].a_type |= (acl_type == user_s) ? USER_OBJ : GROUP_OBJ;
+	      break;
+	    }
+	  /* Some qualifier, USER or GROUP */
+	  lacl[pos].a_type |= (acl_type == user_s) ? USER : GROUP;
+	  if (isdigit (*qualifier))
+	    {
+	      char *ep;
+
+	      id_t id = strtol (qualifier, &ep, 10);
+	      if (*ep == '\0')
 		{
-		  set_errno (EINVAL);
-		  return NULL;
+		  lacl[pos].a_id = id;
+		  break;
 		}
 	    }
-	}
-      else if (!strncmp (c, "mask:", 5))
-	{
-	  if (c[5] == ':')
-	    lacl[pos].a_type |= CLASS_OBJ;
+	  if (acl_type == user_s)
+	    {
+	      struct passwd *pw = internal_getpwnam (qualifier, &cldap);
+	      if (pw)
+		lacl[pos].a_id = pw->pw_uid;
+	    }
 	  else
 	    {
+	      struct group *gr = internal_getgrnam (qualifier, &cldap);
+	      if (gr)
+		lacl[pos].a_id = gr->gr_gid;
+	    }
+	  if (lacl[pos].a_id == ACL_UNDEFINED_ID)
+	    {
 	      set_errno (EINVAL);
 	      return NULL;
 	    }
-	}
-      else if (!strncmp (c, "other:", 6))
-	{
-	  if (c[5] == ':')
-	    lacl[pos].a_type |= OTHER_OBJ;
-	  else
+	  break;
+	case mask_s:
+	case other_s:
+	  if (*bufp++ != ':')
 	    {
 	      set_errno (EINVAL);
 	      return NULL;
 	    }
+	  lacl[pos].a_type |= (acl_type == mask_s) ? CLASS_OBJ : OTHER_OBJ;
+	  break;
 	}
-      if ((lacl[pos].a_perm = permfromstr (c)) == 01000)
+      /* In posix long mode, the next char after the permissions may be a tab
+	 followed by effective permissions we can ignore here. */
+      if ((lacl[pos].a_perm = permfromstr (bufp, *delim == '\n')) == 01000)
 	{
 	  set_errno (EINVAL);
 	  return NULL;
 	}
       ++pos;
     }
-  aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t));
-  if (aclp)
+  if (posix)
     {
-      memcpy (aclp, lacl, pos * sizeof (aclent_t));
-      if (aclcnt)
-	*aclcnt = pos;
+      acl_t acl = (acl_t) acl_init (pos);
+      if (acl)
+	{
+	  memcpy (acl->entry, lacl, pos * sizeof (aclent_t));
+	  acl->count = pos;
+	  if (aclcnt)
+	    *aclcnt = pos;
+	}
+      return (void *) acl;
     }
-  return aclp;
+  else
+    {
+      aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t));
+      if (aclp)
+	{
+	  memcpy (aclp, lacl, pos * sizeof (aclent_t));
+	  if (aclcnt)
+	    *aclcnt = pos;
+	}
+      return (void *) aclp;
+    }
+}
+
+extern "C" aclent_t *
+aclfromtext32 (char *acltextp, int *aclcnt)
+{
+  return (aclent_t *) __aclfromtext (acltextp, aclcnt, false);
 }
 
 #ifdef __x86_64__
diff --git a/winsup/cygwin/sec_helper.cc b/winsup/cygwin/sec_helper.cc
index af3307e..0c37ad2 100644
--- a/winsup/cygwin/sec_helper.cc
+++ b/winsup/cygwin/sec_helper.cc
@@ -13,7 +13,7 @@ details. */
 
 #include "winsup.h"
 #include <stdlib.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <sys/queue.h>
 #include <authz.h>
 #include <wchar.h>
diff --git a/winsup/cygwin/sec_posixacl.cc b/winsup/cygwin/sec_posixacl.cc
new file mode 100644
index 0000000..54bac8f
--- /dev/null
+++ b/winsup/cygwin/sec_posixacl.cc
@@ -0,0 +1,1052 @@
+/* sec_posixacl.cc: POSIX ACL functions based on Solaris ACLs.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <unistd.h>
+#include "cygerrno.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "tls_pbuf.h"
+#include "sec_posixacl.h"
+#include <acl/libacl.h>
+
+#define _ENTRY_SIZE(_cnt) ((_cnt) * sizeof (aclent_t))
+#define _ACL_SIZE(_cnt)	  (sizeof (__acl_ext_t) + _ENTRY_SIZE (_cnt))
+#define ACL_SIZE(_acl)    ({ acl_t __acl = _acl; \
+			     _ACL_SIZE((__acl)->count - (__acl)->deleted); \
+			  })
+#define ACL_PERM_MASK		(ACL_READ | ACL_WRITE | ACL_EXECUTE)
+
+extern "C" acl_t
+acl_init (int count)
+{
+  acl_t acl;
+
+  if (count < 0 || count > UINT16_MAX)
+    {
+      set_errno (EINVAL);
+      return NULL;
+    }
+  acl = (acl_t) calloc (1, sizeof (__acl_t));
+  if (!acl)
+    return NULL;
+  acl->entry = (aclent_t *) calloc (count, sizeof (aclent_t));
+  if (!acl->entry)
+    {
+      free (acl);
+      return NULL;
+    }
+  acl->magic = ACL_MAGIC;
+  acl->max_count = count;
+  return acl;
+}
+
+static acl_t
+__acl_dup (acl_t acl, int max)
+{
+  __try
+    {
+      acl_t new_acl = acl_init (max);
+      if (new_acl)
+	{
+	  int new_idx = 0;
+
+	  for (uint16_t idx = 0; idx < acl->count; ++idx)
+	    if (acl->entry[idx].a_type != ACL_DELETED_TAG)
+	      new_acl->entry[new_idx++] = acl->entry[idx];
+	  new_acl->magic = ACL_MAGIC;
+	  new_acl->count = new_idx;
+	  new_acl->max_count = max;
+	  return new_acl;
+	}
+    }
+  __except (EINVAL) {}
+  __endtry
+  return NULL;
+}
+
+extern "C" acl_t
+acl_dup (acl_t acl)
+{
+  return __acl_dup (acl, acl->max_count);
+}
+
+extern "C" int
+acl_free (void *obj_p)
+{
+  __try
+    {
+      acl_t acl;
+
+      if (obj_p)
+	{
+	  if (malloc_usable_size (obj_p) >= sizeof (__acl_t))
+	    {
+	      acl = (acl_t) obj_p;
+	      if (acl->magic == ACL_MAGIC)
+		free (acl->entry);
+	    }
+	  free (obj_p);
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_valid (acl_t acl)
+{
+  __try
+    {
+      if (!(__aclcheck (acl->entry, acl->count, NULL, true)))
+	return 0;
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_copy_entry (acl_entry_t dest_d, acl_entry_t src_d)
+{
+  __try
+    {
+      uint16_t d_idx, s_idx;
+      acl_t d_acl, s_acl;
+
+      d_acl = __from_entry (dest_d, d_idx);
+      s_acl = __from_entry (src_d, s_idx);
+      if (d_acl && s_acl)
+	{
+	  d_acl->entry[d_idx] = s_acl->entry[s_idx];
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_create_entry (acl_t *acl_p, acl_entry_t *entry_p)
+{
+  __try
+    {
+      acl_t acl = *acl_p;
+      uint16_t idx;
+
+      if (acl->deleted > 0)
+	{
+	  for (idx = 0; idx < acl->count; ++idx)
+	    if (acl->entry[idx].a_type == ACL_DELETED_TAG)
+	      {
+		*entry_p = __to_entry (acl, idx);
+		--acl->deleted;
+		goto fill_entry;
+	      }
+	}
+      if (acl->count >= acl->max_count)
+	{
+	  acl_t new_acl = __acl_dup (acl, acl->count + 1);
+	  if (!new_acl)
+	    __leave;
+	  *acl_p = new_acl;
+	  acl_free (acl);
+	  acl = *acl_p;
+	}
+      idx = acl->count++;
+      *entry_p = __to_entry (acl, idx);
+    fill_entry:
+      acl->entry[idx].a_type = ACL_UNDEFINED_TAG;
+      acl->entry[idx].a_id = ACL_UNDEFINED_ID;
+      acl->entry[idx].a_perm = 0;
+      return 0;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_delete_entry (acl_t acl, acl_entry_t entry_d)
+{
+  __try
+    {
+      acl_t acl_p;
+      uint16_t idx;
+
+      acl_p = __from_entry (entry_d, idx);
+
+      if (acl_p == acl)
+	{
+	  acl_p->entry[idx].a_type = ACL_DELETED_TAG;
+	  acl_p->entry[idx].a_id = ACL_UNDEFINED_ID;
+	  acl_p->entry[idx].a_perm = 0;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_get_entry (acl_t acl, int entry_id, acl_entry_t *entry_p)
+{
+  __try
+    {
+      uint16_t idx;
+
+      if (entry_id == ACL_FIRST_ENTRY)
+	acl->next = 0;
+      else if (entry_id != ACL_NEXT_ENTRY)
+	{
+	  set_errno (EINVAL);
+	  __leave;
+	}
+      do
+	{
+	  if (acl->next >= acl->count)
+	    return 0;
+	  idx = acl->next++;
+	}
+      while (acl->entry[idx].a_type == ACL_DELETED_TAG);
+      *entry_p = __to_entry (acl, idx);
+      return 1;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_calc_mask (acl_t *acl_p)
+{
+  __try
+    {
+      acl_t acl = *acl_p;
+      mode_t mask = 0;
+
+      mask = __aclcalcmask (acl->entry, acl->count);
+      /* If __aclcalcmask returns -1 we're done.  Otherwise create a
+         mask entry here. */
+      if (mask != (acl_perm_t) -1)
+	{
+	  acl_entry_t entry_d;
+	  uint16_t mask_idx;
+
+	  if (acl_create_entry (&acl, &entry_d) < 0)
+	    __leave;
+	  if (!__from_entry (entry_d, mask_idx))
+	    {
+	      set_errno (EINVAL);
+	      __leave;
+	    }
+	  acl->entry[mask_idx].a_type = ACL_MASK;
+	  acl->entry[mask_idx].a_id = ACL_UNDEFINED_ID;
+	  acl->entry[mask_idx].a_perm = mask;
+	  *acl_p = acl;
+	}
+      return 0;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_clear_perms (acl_permset_t permset_d)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_permset (permset_d, idx);
+      if (acl)
+	{
+	  acl->entry[idx].a_perm = 0;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_add_perm (acl_permset_t permset_d, acl_perm_t perm)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_permset (permset_d, idx);
+      if (acl && !(perm & ~ACL_PERM_MASK))
+	{
+	  acl->entry[idx].a_perm |= perm;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_delete_perm (acl_permset_t permset_d, acl_perm_t perm)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_permset (permset_d, idx);
+      if (acl && !(perm & ~ACL_PERM_MASK))
+	{
+	  acl->entry[idx].a_perm &= ~perm;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+      return -1;
+}
+
+extern "C" int
+acl_get_permset (acl_entry_t entry_d, acl_permset_t *permset_p)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_entry (entry_d, idx);
+      if (acl)
+	{
+	  *permset_p = (acl_permset_t) entry_d;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_set_permset (acl_entry_t entry_d, acl_permset_t permset_d)
+{
+  __try
+    {
+      acl_t acl_e, acl_p;
+      uint16_t idx_e, idx_p;
+
+      acl_e = __from_entry (entry_d, idx_e);
+      acl_p = __from_permset (permset_d, idx_p);
+      if (acl_e && acl_p && !(acl_p->entry[idx_p].a_perm & ~ACL_PERM_MASK))
+	{
+	  acl_e->entry[idx_e].a_perm = acl_p->entry[idx_p].a_perm;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" void *
+acl_get_qualifier (acl_entry_t entry_d)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_entry (entry_d, idx);
+      if (acl && (acl->entry[idx].a_type & (ACL_USER | ACL_GROUP)))
+	{
+	  id_t *id = (id_t *) malloc (sizeof (id_t));
+	  if (id)
+	    {
+	      *id = acl->entry[idx].a_id;
+	      return (void *) id;
+	    }
+	}
+      else
+	set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return NULL;
+}
+
+extern "C" int
+acl_set_qualifier (acl_entry_t entry_d, const void *qualifier_p)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_entry (entry_d, idx);
+      if (acl && (acl->entry[idx].a_type & (ACL_USER | ACL_GROUP)))
+	{
+	  acl->entry[idx].a_id = *(id_t *) qualifier_p;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_get_tag_type (acl_entry_t entry_d, acl_tag_t *tag_type_p)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_entry (entry_d, idx);
+      if (acl)
+	{
+	  *tag_type_p = acl->entry[idx].a_type;
+	  return 0;
+	}
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_set_tag_type (acl_entry_t entry_d, acl_tag_t tag_type)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_entry (entry_d, idx);
+      if (acl)
+	switch (tag_type)
+	  {
+	  case ACL_USER_OBJ:
+	  case ACL_GROUP_OBJ:
+	  case ACL_MASK:
+	  case ACL_OTHER:
+	    acl->entry[idx].a_id = ACL_UNDEFINED_ID;
+	    /*FALLTHRU*/
+	  case ACL_USER:
+	  case ACL_GROUP:
+	    acl->entry[idx].a_type = tag_type;
+	    return 0;
+	  default:
+	    break;
+	  }
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" ssize_t
+acl_size (acl_t acl)
+{
+  __try
+    {
+      return (ssize_t) ACL_SIZE (acl);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" ssize_t
+acl_copy_ext (void *buf_p, acl_t acl, ssize_t size)
+{
+  __try
+    {
+      ssize_t ext_size = (ssize_t) ACL_SIZE (acl);
+
+      if (size <= 0)
+	set_errno (EINVAL);
+      else if (ext_size > size)
+	set_errno (ERANGE);
+      else
+	{
+	  uint16_t ext_idx = 0;
+	  __acl_ext_t *acl_ext = (__acl_ext_t *) buf_p;
+
+	  acl_ext->count = acl->count - acl->deleted;
+	  for (uint16_t idx = 0; idx < acl->count; ++idx)
+	    if (acl->entry[idx].a_type != ACL_DELETED_TAG)
+	      acl_ext->entry[ext_idx++] = acl->entry[idx];
+	  return ext_size;
+	}
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" acl_t
+acl_copy_int (const void *buf_p)
+{
+  __try
+    {
+      acl_t acl;
+      __acl_ext_t *acl_ext = (__acl_ext_t *) buf_p;
+
+      acl = acl_init (acl_ext->count);
+      if (acl)
+	{
+	  memcpy (acl->entry, acl_ext->entry, _ENTRY_SIZE (acl_ext->count));
+	  acl->count = acl_ext->count;
+	  return acl;
+	}
+    }
+  __except (EINVAL) {}
+  __endtry
+  return NULL;
+}
+
+extern "C" acl_t
+acl_from_text (const char *buf_p)
+{
+  __try
+    {
+      return (acl_t) __aclfromtext (buf_p, NULL, true);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return NULL;
+}
+
+extern "C" char *
+acl_to_text (acl_t acl, ssize_t *len_p)
+{
+  __try
+    {
+      char *ret = __acltotext (acl->entry, acl->count, NULL, '\n',
+			       TEXT_IS_POSIX
+			       | TEXT_SOME_EFFECTIVE
+			       | TEXT_END_SEPARATOR);
+      if (ret && len_p)
+	*len_p = strlen (ret);
+      return ret;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return NULL;
+}
+
+acl_t __reg2
+fhandler_base::acl_get (acl_type_t type)
+{
+  set_errno (ENOTSUP);
+  return NULL;
+}
+
+acl_t __reg2
+fhandler_disk_file::acl_get (acl_type_t type)
+{
+  acl_t acl = NULL;
+  int oret = 0;
+
+  __try
+    {
+      tmp_pathbuf tp;
+      aclent_t *aclbufp;
+      uint16_t cnt, access_cnt;
+
+      if (!pc.has_acls ())
+	{
+	  set_errno (ENOTSUP);
+	  __leave;
+	}
+      if (type == ACL_TYPE_DEFAULT && !pc.isdir ())
+	{
+	  set_errno (ENOTDIR);
+	  __leave;
+	}
+      aclbufp = (aclent_t *) tp.c_get ();
+      if (!get_handle ())
+	{
+	  query_open (query_read_control);
+	  if (!(oret = open (O_BINARY, 0)))
+	    __leave;
+	}
+      cnt = facl (GETACL, MAX_ACL_ENTRIES, aclbufp);
+      if (cnt < 0)
+	__leave;
+      /* Set access_cnt to number of non-default entries from file ACL. */
+      if (!pc.isdir ())
+	access_cnt = cnt;
+      else
+	for (access_cnt = 0; access_cnt < cnt; ++access_cnt)
+	  if (aclbufp[access_cnt].a_type & ACL_DEFAULT)
+	    break;
+      if (type == ACL_TYPE_ACCESS)
+	{
+	  acl = acl_init (access_cnt);
+	  if (!acl)
+	    __leave;
+	  memcpy (acl->entry, aclbufp, _ENTRY_SIZE (access_cnt));
+	  acl->count = access_cnt;
+	}
+      else
+	{
+	  cnt -= access_cnt;
+	  acl = acl_init (cnt);
+	  if (acl && cnt)
+	    {
+	      memcpy (acl->entry, aclbufp + access_cnt, _ENTRY_SIZE (cnt));
+	      acl->count = cnt;
+	      for (cnt = 0; cnt < acl->count; ++cnt)
+		acl->entry[cnt].a_type &= ~ACL_DEFAULT;
+	    }
+	}
+    }
+  __except (EINVAL) {}
+  __endtry
+  if (oret)
+    close_fs ();
+  return acl;
+}
+
+extern "C" acl_t
+acl_get_fd (int fd)
+{
+  cygheap_fdget cfd (fd);
+  if (cfd < 0)
+    return NULL;
+  return cfd->acl_get (ACL_TYPE_ACCESS);
+}
+
+extern "C" acl_t
+acl_get_file (const char *path_p, acl_type_t type)
+{
+  if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_DEFAULT)
+    {
+      set_errno (EINVAL);
+      return NULL;
+    }
+  fhandler_base *fh;
+  if (!(fh = build_fh_name (path_p, PC_SYM_FOLLOW, stat_suffixes)))
+    return NULL;
+  if (fh->error ())
+    {
+      set_errno (fh->error ());
+      return NULL;
+    }
+  acl_t acl = fh->acl_get (type);
+  delete fh;
+  return acl;
+}
+
+int __reg3
+fhandler_base::acl_set (acl_t acl, acl_type_t type)
+{
+  set_errno (ENOTSUP);
+  return -1;
+}
+
+int __reg3
+fhandler_disk_file::acl_set (acl_t acl, acl_type_t type)
+{
+  int ret = -1;
+  int oret = 0;
+
+  __try
+    {
+      tmp_pathbuf tp;
+      aclent_t *aclbufp, *aclbuf_from_file;
+      uint16_t cnt, cnt_from_file, access_cnt;
+
+      if (!pc.has_acls ())
+	{
+	  set_errno (ENOTSUP);
+	  __leave;
+	}
+      if (type == ACL_TYPE_DEFAULT && !pc.isdir ())
+	{
+	  set_errno (ENOTDIR);
+	  __leave;
+	}
+      if (acl->count > MAX_ACL_ENTRIES)
+	{
+	  set_errno (EINVAL);
+	  __leave;
+	}
+      aclbuf_from_file = (aclent_t *) tp.c_get ();
+      if (!get_handle ())
+	{
+	  query_open (query_write_dac);
+	  if (!(oret = open (O_BINARY, 0)))
+	    __leave;
+	}
+      cnt_from_file = facl (GETACL, MAX_ACL_ENTRIES, aclbuf_from_file);
+      if (cnt_from_file < 0)
+	__leave;
+      aclbufp = (aclent_t *) tp.c_get ();
+      /* Set access_cnt to number of non-default entries from file ACL. */
+      if (!pc.isdir ())
+	access_cnt = cnt_from_file;
+      else
+	for (access_cnt = 0; access_cnt < cnt_from_file; ++access_cnt)
+	  if (aclbuf_from_file[access_cnt].a_type & ACL_DEFAULT)
+	    break;
+      if (type == ACL_TYPE_ACCESS)
+	{
+	  /* Check if the number of ACEs fits into the buffer. */
+	  if (acl->count - acl->deleted + cnt_from_file - access_cnt
+	      > MAX_ACL_ENTRIES)
+	    {
+	      set_errno (EINVAL);
+	      __leave;
+	    }
+	  /* Copy the new ACL entries. */
+	  cnt = 0;
+	  for (uint16_t idx = 0; idx < acl->count; ++idx)
+	    if (acl->entry[idx].a_type != ACL_DELETED_TAG)
+	      aclbufp[cnt++] = acl->entry[idx];
+	  /* Append default ACL from file, if any. */
+	  if (access_cnt < cnt_from_file)
+	    {
+	      memcpy (aclbufp + cnt, aclbuf_from_file + access_cnt,
+		      _ENTRY_SIZE (cnt_from_file - access_cnt));
+	      cnt += cnt_from_file - access_cnt;
+	    }
+	}
+      else
+	{
+	  /* Check if the number of ACEs fits into the buffer. */
+	  if (acl->count - acl->deleted + access_cnt > MAX_ACL_ENTRIES)
+	    {
+	      set_errno (EINVAL);
+	      __leave;
+	    }
+	  /* Copy non-default entries from file. */
+	  memcpy (aclbufp, aclbuf_from_file, _ENTRY_SIZE (access_cnt));
+	  cnt = access_cnt;
+	  /* Append new default ACL entries (and add ACL_DEFAULT flag). */
+	  for (uint16_t idx = 0; idx < acl->count; ++idx)
+	    if (acl->entry[idx].a_type != ACL_DELETED_TAG)
+	      {
+		aclbufp[cnt] = acl->entry[idx];
+		aclbufp[cnt++].a_type |= ACL_DEFAULT;
+	      }
+	}
+      ret = facl (SETACL, cnt, aclbufp);
+    }
+  __except (EINVAL) {}
+  __endtry
+  if (oret)
+    close_fs ();
+  return ret;
+}
+
+extern "C" int
+acl_set_fd (int fd, acl_t acl)
+{
+  cygheap_fdget cfd (fd);
+  if (cfd < 0)
+    return -1;
+  return cfd->acl_set (acl, ACL_TYPE_ACCESS);
+}
+
+extern "C" int
+acl_set_file(const char *path_p, acl_type_t type, acl_t acl)
+{
+  if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_DEFAULT)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  fhandler_base *fh;
+  if (!(fh = build_fh_name (path_p, PC_SYM_FOLLOW, stat_suffixes)))
+    return -1;
+  if (fh->error ())
+    {
+      set_errno (fh->error ());
+      return -1;
+    }
+  int ret = fh->acl_set (acl, type);
+  delete fh;
+  return ret;
+}
+
+extern "C" int
+acl_delete_def_file (const char *path_p)
+{
+  acl_t acl = (acl_t) alloca (sizeof (struct __acl_t));
+  acl->count = acl->max_count = acl->next = 0;
+  if (!acl)
+    return -1;
+  return acl_set_file(path_p, ACL_TYPE_DEFAULT, acl);
+}
+
+/* libacl extensions */
+
+extern "C" int
+acl_check (acl_t acl, int *last)
+{
+
+  __try
+    {
+      int ret = 0;
+
+      if (acl->count != 0)
+	{
+	  ret = __aclcheck (acl->entry, acl->count, last, true);
+	  switch (ret)
+	    {
+	    case GRP_ERROR:
+	    case USER_ERROR:
+	    case CLASS_ERROR:
+	    case OTHER_ERROR:
+	      ret = ACL_MULTI_ERROR;
+	      break;
+	    default:
+	      break;
+	    }
+	}
+      return ret;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_cmp (acl_t acl1, acl_t acl2)
+{
+  int ret = -1;
+
+  __try
+    {
+      tmp_pathbuf tp;
+
+      __acl_ext_t *acl1d = (__acl_ext_t *) tp.c_get ();
+      __acl_ext_t *acl2d = (__acl_ext_t *) tp.c_get ();
+      if (acl_copy_ext (acl1d, acl1, NT_MAX_PATH) < 0)
+	__leave;
+      if (acl_copy_ext (acl2d, acl2, NT_MAX_PATH) < 0)
+	__leave;
+      if (acl1d->count != acl2d->count)
+	return 1;
+      if (__aclsort (acl1d->count, acl1d->entry))
+	__leave;
+      if (__aclsort (acl2d->count, acl2d->entry))
+	__leave;
+      for (int idx = 0; idx < acl1d->count; ++idx)
+	{
+	  if (acl1d->entry[idx].a_type != acl2d->entry[idx].a_type)
+	    {
+	      ret = 1;
+	      __leave;
+	    }
+	  if ((acl1d->entry[idx].a_perm & ACL_PERM_MASK)
+	      != (acl2d->entry[idx].a_perm & ACL_PERM_MASK))
+	    {
+	      ret = 1;
+	      __leave;
+	    }
+	  if ((acl1d->entry[idx].a_type & (ACL_USER | ACL_GROUP))
+	      && acl1d->entry[idx].a_id != acl2d->entry[idx].a_id)
+	    {
+	      ret = 1;
+	      __leave;
+	    }
+	}
+      ret = 0;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return ret;
+}
+
+extern "C" int
+acl_entries (acl_t acl)
+{
+  __try
+    {
+      return acl->count - acl->deleted;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" int
+acl_equiv_mode (acl_t acl, mode_t *mode_p)
+{
+  __try
+    {
+      if (acl->count != 3)
+	{
+	  set_errno (EINVAL);
+	  __leave;
+	}
+      int u_idx = -1, g_idx = -1, o_idx = -1;
+      for (int idx = 0; idx < 3; ++idx)
+	switch (acl->entry[idx].a_type)
+	  {
+	  case ACL_USER_OBJ:
+	    u_idx = idx;
+	    break;
+	  case ACL_GROUP_OBJ:
+	    g_idx = idx;
+	    break;
+	  case ACL_OTHER:
+	    o_idx = idx;
+	    break;
+	  }
+      if (u_idx == -1 || g_idx == -1 || o_idx == -1)
+	{
+	  set_errno (EINVAL);
+	  __leave;
+	}
+      if (mode_p)
+	*mode_p = ((acl->entry[u_idx].a_perm & ACL_PERM_MASK) << 6)
+		  | ((acl->entry[g_idx].a_perm & ACL_PERM_MASK) << 3)
+		  | (acl->entry[o_idx].a_perm & ACL_PERM_MASK);
+      return 0;
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+static const char *acl_err_txt[] =
+{
+  "Multiple entries",
+  "Duplicate entries",
+  "Invalid entry type",
+  "Missing or wrong entry"
+};
+
+extern "C" const char *
+acl_error (int code)
+{
+  if (code < ACL_MULTI_ERROR || code > ACL_MISS_ERROR)
+    return NULL;
+  return acl_err_txt[code - ACL_MULTI_ERROR];
+}
+
+extern "C" int
+acl_extended_fd (int fd)
+{
+  __try
+    {
+      cygheap_fdget cfd (fd);
+      if (cfd < 0)
+	__leave;
+      if (!cfd->pc.has_acls ())
+	{
+	  set_errno (ENOTSUP);
+	  __leave;
+	}
+      return cfd->facl (GETACLCNT, 0, NULL);
+    }
+  __except (EBADF) {}
+  __endtry
+  return -1;
+}
+
+static int
+__acl_extended_file (const char *path_p, mode_t follow)
+{
+  int fd = open (path_p, O_RDONLY | O_CLOEXEC | follow);
+  if (fd < 0)
+    return -1;
+  int ret = acl_extended_fd (fd);
+  close (fd);
+  return ret;
+}
+
+extern "C" int
+acl_extended_file (const char *path_p)
+{
+  return __acl_extended_file (path_p, 0);
+}
+
+extern "C" int
+acl_extended_file_nofollow (const char *path_p)
+{
+  return __acl_extended_file (path_p, O_NOFOLLOW);
+}
+
+extern "C" acl_t
+acl_from_mode (mode_t mode)
+{
+  acl_t acl = acl_init (MIN_ACL_ENTRIES);
+  if (!acl)
+    return NULL;
+  acl->count = 3;
+  acl->entry[0].a_type = USER_OBJ;
+  acl->entry[0].a_id = ACL_UNDEFINED_ID;
+  acl->entry[0].a_perm = (mode >> 6) & ACL_PERM_MASK;
+  acl->entry[1].a_type = GROUP_OBJ;
+  acl->entry[1].a_id = ACL_UNDEFINED_ID;
+  acl->entry[1].a_perm = (mode >> 3) & ACL_PERM_MASK;
+  acl->entry[2].a_type = OTHER_OBJ;
+  acl->entry[2].a_id = ACL_UNDEFINED_ID;
+  acl->entry[2].a_perm = mode & ACL_PERM_MASK;
+  return acl;
+}
+
+extern "C" int
+acl_get_perm (acl_permset_t permset_d, acl_perm_t perm)
+{
+  __try
+    {
+      acl_t acl;
+      uint16_t idx;
+
+      acl = __from_permset (permset_d, idx);
+      if (acl && !(perm & ~ACL_PERM_MASK))
+	return (~acl->entry[idx].a_perm & perm) ? 0 : 1;
+      set_errno (EINVAL);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return -1;
+}
+
+extern "C" char *
+acl_to_any_text (acl_t acl, const char *prefix, char separator, int options)
+{
+  __try
+    {
+      return __acltotext (acl->entry, acl->count, prefix, separator,
+			  TEXT_IS_POSIX | options);
+    }
+  __except (EINVAL) {}
+  __endtry
+  return NULL;
+}
diff --git a/winsup/cygwin/sec_posixacl.h b/winsup/cygwin/sec_posixacl.h
new file mode 100644
index 0000000..a3790a5
--- /dev/null
+++ b/winsup/cygwin/sec_posixacl.h
@@ -0,0 +1,68 @@
+/* sec_posixacl.h: Internal definitions for POSIX ACLs.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include <cygwin/acl.h>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+
+/* Magic marker for acl_t. */
+#define ACL_MAGIC		(0xacdccdcadcaccacdULL)
+
+/* Only used internally as a_type for deleted entries. */
+#define ACL_DELETED_TAG		(0xffff)
+
+/* Only used internally from acl_to_text/acl_to_any_text. */
+#define TEXT_END_SEPARATOR	(0x1000)
+#define TEXT_IS_POSIX		(0x2000)
+
+/* Internal ACL representation. */
+struct __acl_t
+{
+  uint64_t magic;	/* Must be ACL_MAGIC.				*/
+  uint16_t max_count;	/* Max. number of entries.			*/
+  uint16_t count;	/* Number of used entries.			*/
+  uint16_t deleted;	/* Number of used but deleted entries.		*/
+  uint16_t next;	/* Next entry to be returned by acl_get_entry.	*/
+  aclent_t *entry;	/* Pointer to variable array of ACL entries.	*/
+};
+
+inline acl_entry_t
+__to_entry (acl_t acl, uint16_t idx)
+{
+  return ((uint64_t) idx << 48) | (uint64_t) acl;
+}
+#define __to_permset(a,i)	((acl_permset_t)__to_entry((a),(i)))
+
+inline acl_t
+__from_entry (acl_entry_t entry_d, uint16_t &idx)
+{
+  idx = entry_d >> 48;
+  acl_t acl = (acl_t) (entry_d & ~((uint64_t) 0xffff << 48));
+  if (acl->magic != ACL_MAGIC)
+    return NULL;
+  if (idx >= acl->count)
+    return NULL;
+  if (acl->entry[idx].a_type == ACL_DELETED_TAG)
+    return NULL;
+  return acl;
+}
+#define __from_permset(p,i)	__from_entry((acl_permset_t)(p),(i))
+
+/* External (but opaque) ACL representation. */
+struct __acl_ext_t
+{
+  uint16_t count;	/* Number of used entries.			*/
+  aclent_t entry[0];	/* Variable array of ACL entries.		*/
+};
+
+/* Shared functions defined in sec_acl.cc. */
+mode_t __aclcalcmask (aclent_t *, int);
+int __aclsort (int, aclent_t *);
+int __aclcheck (aclent_t *, int, int *, bool);
+char *__acltotext (aclent_t *, int, const char *, char, int);
+void *__aclfromtext (const char *, int *, bool);
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc
index 4e02bca..b733071 100644
--- a/winsup/cygwin/security.cc
+++ b/winsup/cygwin/security.cc
@@ -15,7 +15,7 @@ details. */
 #include "winsup.h"
 #include <unistd.h>
 #include <stdlib.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include "cygerrno.h"
 #include "security.h"
 #include "path.h"
diff --git a/winsup/utils/getfacl.c b/winsup/utils/getfacl.c
index 45e5e20..b3a3b9a 100644
--- a/winsup/utils/getfacl.c
+++ b/winsup/utils/getfacl.c
@@ -15,8 +15,7 @@ details. */
 #include <stdio.h>
 #include <unistd.h>
 #include <getopt.h>
-#include <sys/types.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <sys/stat.h>
 #include <cygwin/version.h>
 #include <string.h>
diff --git a/winsup/utils/setfacl.c b/winsup/utils/setfacl.c
index cd0edc5..ea9447c 100644
--- a/winsup/utils/setfacl.c
+++ b/winsup/utils/setfacl.c
@@ -20,8 +20,7 @@ details. */
 #include <getopt.h>
 #include <pwd.h>
 #include <grp.h>
-#include <sys/types.h>
-#include <sys/acl.h>
+#include <cygwin/acl.h>
 #include <cygwin/version.h>
 
 #ifndef BOOL



More information about the Cygwin-cvs mailing list