changeset 10191:ccfd3047da72

Work around the Solaris 10 ACE ACLs ABI change.
author Bruno Haible <bruno@clisp.org>
date Tue, 10 Jun 2008 02:40:28 +0200
parents e33912e3fd26
children 0553e4d56bcc
files ChangeLog doc/acl-resources.txt lib/acl-internal.h lib/file-has-acl.c lib/set-mode-acl.c
diffstat 5 files changed, 215 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jun 09 16:41:11 2008 -0600
+++ b/ChangeLog	Tue Jun 10 02:40:28 2008 +0200
@@ -1,3 +1,21 @@
+2008-06-09  Bruno Haible  <bruno@clisp.org>
+
+	Work around the Solaris 10 ACE ACLs ABI change.
+	* lib/acl-internal.h (acl_nontrivial, acl_ace_nontrivial): Don't
+	declare if ACL_NO_TRIVIAL is present.
+	(ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_ACCESS_DENIED_ACE_TYPE,
+	NEW_ACE_OWNER, NEW_ACE_GROUP, NEW_ACE_IDENTIFIER_GROUP, ACE_EVERYONE,
+	NEW_ACE_READ_DATA, NEW_ACE_WRITE_DATA, NEW_ACE_EXECUTE): New macros.
+	* lib/file-has-acl.c (acl_nontrivial, acl_ace_nontrivial): Don't
+	define if ACL_NO_TRIVIAL is present.
+	(acl_ace_nontrivial): Detect whether the old or new ABI is in use,
+	and use the current ABI.
+	(file_has_acl): Use same #if condition as elsewhere.
+	* lib/set-mode-acl.c (qset_acl): Detect whether the old or new ABI is
+	in use, and use the current ABI.
+	* doc/acl-resources.txt: More doc about newer Solaris 10 versions.
+	Reported by Jim Meyering.
+
 2008-06-09  Eric Blake  <ebb9@byu.net>
 
 	Work around environments that (stupidly) ignore SIGALRM.
--- a/doc/acl-resources.txt	Mon Jun 09 16:41:11 2008 -0600
+++ b/doc/acl-resources.txt	Tue Jun 10 02:40:28 2008 +0200
@@ -90,6 +90,22 @@
   aclsort
   acltomode
   acltotext
+Additionally in Solaris 10 patch 118833-17 (<sys/acl.h> version 1.15):
+  acl_t type
+  ACL_NO_TRIVIAL macro
+  ACE_OTHER macro replaced with ACE_EVERYONE macro
+  ACE_OWNER, ACE_GROUP changed their values(!)
+  ALLOW, DENY macros removed(!)
+  acl_check
+  acl_free
+  acl_fromtext
+  acl_get
+  acl_set
+  acl_strip
+  acl_totext
+  acl_trivial
+  facl_get
+  facl_set
 Utilities:
   getfacl
   setfacl
--- a/lib/acl-internal.h	Mon Jun 09 16:41:11 2008 -0600
+++ b/lib/acl-internal.h	Tue Jun 10 02:40:28 2008 +0200
@@ -168,15 +168,36 @@
 #   define MODE_INSIDE_ACL 1
 #  endif
 
+#  if !defined ACL_NO_TRIVIAL /* Solaris <= 10, Cygwin */
+
 /* Return 1 if the given ACL is non-trivial.
    Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.  */
 extern int acl_nontrivial (int count, aclent_t *entries);
 
-#  ifdef ACE_GETACL
+#   ifdef ACE_GETACL /* Solaris 10 */
+
 /* Test an ACL retrieved with ACE_GETACL.
    Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
    Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.  */
 extern int acl_ace_nontrivial (int count, ace_t *entries);
+
+/* Definitions for when the built executable is executed on Solaris 10
+   (newer version) or Solaris 11.  */
+/* For a_type.  */
+#    define ACE_ACCESS_ALLOWED_ACE_TYPE 0 /* replaces ALLOW */
+#    define ACE_ACCESS_DENIED_ACE_TYPE  1 /* replaces DENY */
+/* For a_flags.  */
+#    define NEW_ACE_OWNER            0x1000
+#    define NEW_ACE_GROUP            0x2000
+#    define NEW_ACE_IDENTIFIER_GROUP 0x0040
+#    define ACE_EVERYONE             0x4000
+/* For a_access_mask.  */
+#    define NEW_ACE_READ_DATA  0x001 /* corresponds to 'r' */
+#    define NEW_ACE_WRITE_DATA 0x002 /* corresponds to 'w' */
+#    define NEW_ACE_EXECUTE    0x004 /* corresponds to 'x' */
+
+#   endif
+
 #  endif
 
 # elif HAVE_GETACL /* HP-UX */
--- a/lib/file-has-acl.c	Mon Jun 09 16:41:11 2008 -0600
+++ b/lib/file-has-acl.c	Tue Jun 10 02:40:28 2008 +0200
@@ -120,6 +120,8 @@
 
 #elif USE_ACL && HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
 
+# if !defined ACL_NO_TRIVIAL /* Solaris <= 10, Cygwin */
+
 /* Test an ACL retrieved with GETACL.
    Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
    Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.  */
@@ -146,7 +148,7 @@
   return 0;
 }
 
-# if defined ACE_GETACL && defined ALLOW && defined ACE_OWNER
+#  ifdef ACE_GETACL
 
 /* Test an ACL retrieved with ACE_GETACL.
    Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
@@ -156,22 +158,59 @@
 {
   int i;
 
+  /* The flags in the ace_t structure changed in a binary incompatible way
+     when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
+     How to distinguish the two conventions at runtime?
+     In the old convention, usually three ACEs have a_flags = ACE_OWNER /
+     ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.  In the new
+     convention, these values are not used.  */
+  int old_convention = 0;
+
   for (i = 0; i < count; i++)
-    {
-      ace_t *ace = &entries[i];
+    if (entries[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_OTHER))
+      {
+	old_convention = 1;
+	break;
+      }
+
+  if (old_convention)
+    /* Running on Solaris 10.  */
+    for (i = 0; i < count; i++)
+      {
+	ace_t *ace = &entries[i];
 
-      /* Note: If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from
-	 stat().  If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from
-	 stat().  We don't need to check ace->a_who in these cases.  */
-      if (!(ace->a_type == ALLOW
-	    && (ace->a_flags == ACE_OWNER
-		|| ace->a_flags == ACE_GROUP
-		|| ace->a_flags == ACE_OTHER)))
-	return 1;
-    }
+	/* Note:
+	   If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from stat().
+	   If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from stat().
+	   We don't need to check ace->a_who in these cases.  */
+	if (!(ace->a_type == ALLOW
+	      && (ace->a_flags == ACE_OWNER
+		  || ace->a_flags == ACE_GROUP
+		  || ace->a_flags == ACE_OTHER)))
+	  return 1;
+      }
+  else
+    /* Running on Solaris 10 (newer version) or Solaris 11.  */
+    for (i = 0; i < count; i++)
+      {
+	ace_t *ace = &entries[i];
+
+	if (!(ace->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE
+	      && (ace->a_flags == NEW_ACE_OWNER
+		  || ace->a_flags
+		     == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP)
+		  || ace->a_flags == ACE_EVERYONE)
+	      && (ace->a_access_mask
+		  & ~(NEW_ACE_READ_DATA | NEW_ACE_WRITE_DATA | NEW_ACE_EXECUTE))
+		 == 0))
+	  return 1;
+      }
+
   return 0;
 }
 
+#  endif
+
 # endif
 
 #elif USE_ACL && HAVE_GETACL /* HP-UX */
@@ -310,7 +349,7 @@
 
 # elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
 
-#  if HAVE_ACL_TRIVIAL
+#  if defined ACL_NO_TRIVIAL
 
       /* Solaris 10 (newer version), which has additional API declared in
 	 <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
--- a/lib/set-mode-acl.c	Mon Jun 09 16:41:11 2008 -0600
+++ b/lib/set-mode-acl.c	Tue Jun 10 02:40:28 2008 +0200
@@ -249,34 +249,118 @@
 #   ifdef ACE_GETACL
   /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
      file systems (whereas the other ones are used in UFS file systems).  */
+
+  /* The flags in the ace_t structure changed in a binary incompatible way
+     when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
+     How to distinguish the two conventions at runtime?
+     We fetch the existing ACL.  In the old convention, usually three ACEs have
+     a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
+     In the new convention, these values are not used.  */
+  int convention;
+
   {
-    ace_t entries[3];
-    int ret;
+    int count;
+    ace_t *entries;
 
-    entries[0].a_type = ALLOW;
-    entries[0].a_flags = ACE_OWNER;
-    entries[0].a_who = 0; /* irrelevant */
-    entries[0].a_access_mask = (mode >> 6) & 7;
-    entries[1].a_type = ALLOW;
-    entries[1].a_flags = ACE_GROUP;
-    entries[1].a_who = 0; /* irrelevant */
-    entries[1].a_access_mask = (mode >> 3) & 7;
-    entries[2].a_type = ALLOW;
-    entries[2].a_flags = ACE_OTHER;
-    entries[2].a_who = 0;
-    entries[2].a_access_mask = mode & 7;
+    for (;;)
+      {
+	if (desc != -1)
+	  count = facl (desc, ACE_GETACLCNT, 0, NULL);
+	else
+	  count = acl (name, ACE_GETACLCNT, 0, NULL);
+	if (count <= 0)
+	  {
+	    convention = -1;
+	    break;
+	  }
+	entries = (ace_t *) malloc (count * sizeof (ace_t));
+	if (entries == NULL)
+	  {
+	    errno = ENOMEM;
+	    return -1;
+	  }
+	if ((desc != -1
+	     ? facl (desc, ACE_GETACL, count, entries)
+	     : acl (name, ACE_GETACL, count, entries))
+	    == count)
+	  {
+	    int i;
 
-    if (desc != -1)
-      ret = facl (desc, ACE_SETACL, sizeof (entries) / sizeof (aclent_t), entries);
-    else
-      ret = acl (name, ACE_SETACL, sizeof (entries) / sizeof (aclent_t), entries);
-    if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
-      {
-	if (errno == ENOSYS)
-	  return chmod_or_fchmod (name, desc, mode);
-	return -1;
+	    convention = 0;
+	    for (i = 0; i < count; i++)
+	      if (entries[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_OTHER))
+		{
+		  convention = 1;
+		  break;
+		}
+	    free (entries);
+	    break;
+	  }
+	/* Huh? The number of ACL entries changed since the last call.
+	   Repeat.  */
+	free (entries);
       }
   }
+
+  if (convention >= 0)
+    {
+      ace_t entries[3];
+      int ret;
+
+      if (convention)
+	{
+	  /* Running on Solaris 10.  */
+	  entries[0].a_type = ALLOW;
+	  entries[0].a_flags = ACE_OWNER;
+	  entries[0].a_who = 0; /* irrelevant */
+	  entries[0].a_access_mask = (mode >> 6) & 7;
+	  entries[1].a_type = ALLOW;
+	  entries[1].a_flags = ACE_GROUP;
+	  entries[1].a_who = 0; /* irrelevant */
+	  entries[1].a_access_mask = (mode >> 3) & 7;
+	  entries[2].a_type = ALLOW;
+	  entries[2].a_flags = ACE_OTHER;
+	  entries[2].a_who = 0;
+	  entries[2].a_access_mask = mode & 7;
+	}
+      else
+	{
+	  /* Running on Solaris 10 (newer version) or Solaris 11.  */
+	  entries[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	  entries[0].a_flags = NEW_ACE_OWNER;
+	  entries[0].a_who = 0; /* irrelevant */
+	  entries[0].a_access_mask =
+	    (mode & 0400 ? NEW_ACE_READ_DATA : 0)
+	    | (mode & 0200 ? NEW_ACE_WRITE_DATA : 0)
+	    | (mode & 0100 ? NEW_ACE_EXECUTE : 0);
+	  entries[1].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	  entries[1].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
+	  entries[1].a_who = 0; /* irrelevant */
+	  entries[1].a_access_mask =
+	    (mode & 0040 ? NEW_ACE_READ_DATA : 0)
+	    | (mode & 0020 ? NEW_ACE_WRITE_DATA : 0)
+	    | (mode & 0010 ? NEW_ACE_EXECUTE : 0);
+	  entries[2].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	  entries[2].a_flags = ACE_EVERYONE;
+	  entries[2].a_who = 0;
+	  entries[2].a_access_mask =
+	    (mode & 0004 ? NEW_ACE_READ_DATA : 0)
+	    | (mode & 0002 ? NEW_ACE_WRITE_DATA : 0)
+	    | (mode & 0001 ? NEW_ACE_EXECUTE : 0);
+	}
+      if (desc != -1)
+	ret = facl (desc, ACE_SETACL,
+		    sizeof (entries) / sizeof (aclent_t), entries);
+      else
+	ret = acl (name, ACE_SETACL,
+		   sizeof (entries) / sizeof (aclent_t), entries);
+      if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
+	{
+	  if (errno == ENOSYS)
+	    return chmod_or_fchmod (name, desc, mode);
+	  return -1;
+	}
+    }
 #   endif
 
   {