acl: Add support for ACLs on NonStop Kernel.
authorBruno Haible <bruno@clisp.org>
Sun, 3 Oct 2010 16:03:48 +0000 (18:03 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 3 Oct 2010 16:03:48 +0000 (18:03 +0200)
* m4/acl.m4 (gl_FUNC_ACL): For Solaris, test for facl(), not for acl().
Check whether the function aclsort() exists.
* lib/acl-internal.h: For Solaris, test HAVE_FACL, not HAVE_ACL.
(acl_nontrivial) [HAVE_ACLSORT]: New declaration.
* lib/file-has-acl.c: For Solaris, test HAVE_FACL, not HAVE_ACL.
(acl_nontrivial [HAVE_ACLSORT]: New function.
(file_has_acl): Implement for NonStop Kernel.
* lib/set-mode-acl.c: For Solaris, test HAVE_FACL, not HAVE_ACL.
(qset_acl): Implement for NonStop Kernel.
* lib/copy-acl.c (qcopy_acl): Implement for NonStop Kernel.
* tests/test-sameacls.c: For Solaris, test HAVE_FACL, not HAVE_ACL.
(main): Implement for NonStop Kernel.
* tests/test-file-has-acl.sh (acl_flavor): Set to 'nsk' on NonStop
Kernel. Handle this flavor.
* tests/test-set-mode-acl.sh: Likewise.
* tests/test-copy-acl.sh: Likewise.
* tests/test-copy-file.sh: Likewise.

ChangeLog
lib/acl-internal.h
lib/copy-acl.c
lib/file-has-acl.c
lib/set-mode-acl.c
m4/acl.m4
tests/test-copy-acl.sh
tests/test-copy-file.sh
tests/test-file-has-acl.sh
tests/test-sameacls.c
tests/test-set-mode-acl.sh

index 74db2ae..18d7d4b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,26 @@
 2010-10-03  Bruno Haible  <bruno@clisp.org>
+            Joachim Schmitz  <schmitz@hp.com>  (tiny change)
+
+       acl: Add support for ACLs on NonStop Kernel.
+       * m4/acl.m4 (gl_FUNC_ACL): For Solaris, test for facl(), not for acl().
+       Check whether the function aclsort() exists.
+       * lib/acl-internal.h: For Solaris, test HAVE_FACL, not HAVE_ACL.
+       (acl_nontrivial) [HAVE_ACLSORT]: New declaration.
+       * lib/file-has-acl.c: For Solaris, test HAVE_FACL, not HAVE_ACL.
+       (acl_nontrivial [HAVE_ACLSORT]: New function.
+       (file_has_acl): Implement for NonStop Kernel.
+       * lib/set-mode-acl.c: For Solaris, test HAVE_FACL, not HAVE_ACL.
+       (qset_acl): Implement for NonStop Kernel.
+       * lib/copy-acl.c (qcopy_acl): Implement for NonStop Kernel.
+       * tests/test-sameacls.c: For Solaris, test HAVE_FACL, not HAVE_ACL.
+       (main): Implement for NonStop Kernel.
+       * tests/test-file-has-acl.sh (acl_flavor): Set to 'nsk' on NonStop
+       Kernel. Handle this flavor.
+       * tests/test-set-mode-acl.sh: Likewise.
+       * tests/test-copy-acl.sh: Likewise.
+       * tests/test-copy-file.sh: Likewise.
+
+2010-10-03  Bruno Haible  <bruno@clisp.org>
 
        Info about ACLs on NonStop Kernel.
        * doc/acl-resources.txt: Add info about NonStop Kernel.
index 7f9cde1..33a76e3 100644 (file)
@@ -26,7 +26,7 @@
 #if HAVE_SYS_ACL_H
 # include <sys/acl.h>
 #endif
-#if defined HAVE_ACL && ! defined GETACLCNT && defined ACL_CNT
+#if defined HAVE_FACL && ! defined GETACLCNT && defined ACL_CNT
 # define GETACLCNT ACL_CNT
 #endif
 
@@ -158,7 +158,7 @@ extern int acl_extended_nontrivial (acl_t);
 extern int acl_access_nontrivial (acl_t);
 #  endif
 
-# elif HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
 
 /* Set to 1 if a file's mode is implicit by the ACL.
    Set to 0 if a file's mode is stored independently from the ACL.  */
@@ -216,6 +216,12 @@ extern int acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb
    Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.  */
 extern int acl_nontrivial (struct acl *a);
 
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+/* 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, struct acl *entries);
+
 # endif
 
 #endif
index 421907d..e8a4460 100644 (file)
@@ -516,6 +516,68 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
 
   return 0;
 
+#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
+
+  int count;
+  struct acl entries[NACLENTRIES];
+  int ret;
+
+  for (;;)
+    {
+      count = acl ((char *) src_name, ACL_CNT, NACLENTRIES, NULL);
+
+      if (count < 0)
+        {
+          if (0)
+            {
+              count = 0;
+              break;
+            }
+          else
+            return -2;
+        }
+
+      if (count == 0)
+        break;
+
+      if (count > NACLENTRIES)
+        /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
+        abort ();
+
+      if (acl ((char *) src_name, ACL_GET, count, entries) == count)
+        break;
+      /* Huh? The number of ACL entries changed since the last call.
+         Repeat.  */
+    }
+
+  if (count == 0)
+    return qset_acl (dst_name, dest_desc, mode);
+
+  ret = acl ((char *) dst_name, ACL_SET, count, entries);
+  if (ret < 0)
+    {
+      int saved_errno = errno;
+
+      if (0)
+        {
+          if (!acl_nontrivial (count, entries))
+            return chmod_or_fchmod (dst_name, dest_desc, mode);
+        }
+
+      chmod_or_fchmod (dst_name, dest_desc, mode);
+      errno = saved_errno;
+      return -1;
+    }
+
+  if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+    {
+      /* We did not call chmod so far, and either the mode and the ACL are
+         separate or special bits are to be set which don't fit into ACLs.  */
+
+      return chmod_or_fchmod (dst_name, dest_desc, mode);
+    }
+  return 0;
+
 #else
 
   return qset_acl (dst_name, dest_desc, mode);
index 92aa07e..c11c63a 100644 (file)
@@ -118,7 +118,7 @@ acl_access_nontrivial (acl_t acl)
 # endif
 
 
-#elif USE_ACL && HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
 
 # if !defined ACL_NO_TRIVIAL /* Solaris <= 10, Cygwin */
 
@@ -294,6 +294,32 @@ acl_nfs4_nontrivial (nfs4_acl_int_t *a)
 
 # endif
 
+#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
+
+/* Test an ACL retrieved with ACL_GET.
+   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.  */
+int
+acl_nontrivial (int count, struct acl *entries)
+{
+  int i;
+
+  for (i = 0; i < count; i++)
+    {
+      struct acl *ace = &entries[i];
+
+      /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
+         If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
+         We don't need to check ace->a_id in these cases.  */
+      if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */
+            || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */
+            || ace->a_type == CLASS_OBJ
+            || ace->a_type == OTHER_OBJ))
+        return 1;
+    }
+  return 0;
+}
+
 #endif
 
 
@@ -377,7 +403,7 @@ file_has_acl (char const *name, struct stat const *sb)
         return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
       return ret;
 
-# elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
+# elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
 
 #  if defined ACL_NO_TRIVIAL
 
@@ -598,6 +624,37 @@ file_has_acl (char const *name, struct stat const *sb)
 
       return acl_nontrivial (&u.a);
 
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+      int count;
+      struct acl entries[NACLENTRIES];
+
+      for (;;)
+        {
+          count = acl ((char *) name, ACL_CNT, NACLENTRIES, NULL);
+
+          if (count < 0)
+            return -1;
+
+          if (count == 0)
+            return 0;
+
+          if (count > NACLENTRIES)
+            /* If NACLENTRIES cannot be trusted, use dynamic memory
+               allocation.  */
+            abort ();
+
+          /* If there are more than 4 entries, there cannot be only the
+             four base ACL entries.  */
+          if (count > 4)
+            return 1;
+
+          if (acl ((char *) name, ACL_GET, count, entries) == count)
+            return acl_nontrivial (count, entries);
+          /* Huh? The number of ACL entries changed since the last call.
+             Repeat.  */
+        }
+
 # endif
     }
 #endif
index 2cd2c75..f24ed0f 100644 (file)
@@ -201,7 +201,7 @@ qset_acl (char const *name, int desc, mode_t mode)
   return chmod_or_fchmod (name, desc, mode);
 #  endif
 
-# elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
+# elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
 
 #  if defined ACL_NO_TRIVIAL
   /* Solaris 10 (newer version), which has additional API declared in
@@ -573,6 +573,51 @@ qset_acl (char const *name, int desc, mode_t mode)
 
   return ret;
 
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+  struct acl entries[4];
+  int ret;
+
+  entries[0].a_type = USER_OBJ;
+  entries[0].a_id = 0; /* irrelevant */
+  entries[0].a_perm = (mode >> 6) & 7;
+  entries[1].a_type = GROUP_OBJ;
+  entries[1].a_id = 0; /* irrelevant */
+  entries[1].a_perm = (mode >> 3) & 7;
+  entries[2].a_type = CLASS_OBJ;
+  entries[2].a_id = 0;
+  entries[2].a_perm = (mode >> 3) & 7;
+  entries[3].a_type = OTHER_OBJ;
+  entries[3].a_id = 0;
+  entries[3].a_perm = mode & 7;
+
+  ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
+  if (ret > 0)
+    abort ();
+  if (ret < 0)
+    {
+      if (0)
+        return chmod_or_fchmod (name, desc, mode);
+      return -1;
+    }
+
+  ret = acl ((char *) name, ACL_SET,
+             sizeof (entries) / sizeof (struct acl), entries);
+  if (ret < 0)
+    {
+      if (0)
+        return chmod_or_fchmod (name, desc, mode);
+      return -1;
+    }
+
+  if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+    {
+      /* We did not call chmod so far, so the special bits have not yet
+         been set.  */
+      return chmod_or_fchmod (name, desc, mode);
+    }
+  return 0;
+
 # else /* Unknown flavor of ACLs */
   return chmod_or_fchmod (name, desc, mode);
 # endif
index 6033d06..dd047a3 100644 (file)
--- a/m4/acl.m4
+++ b/m4/acl.m4
@@ -1,5 +1,5 @@
 # acl.m4 - check for access control list (ACL) primitives
-# serial 10
+# serial 11
 
 # Copyright (C) 2002, 2004-2010 Free Software Foundation, Inc.
 # This file is free software; the Free Software Foundation
@@ -76,8 +76,8 @@ int type = ACL_TYPE_EXTENDED;]])],
 
       dnl Test for Solaris API (Solaris, Cygwin).
       if test $use_acl = 0; then
-        AC_CHECK_FUNCS([acl])
-        if test $ac_cv_func_acl = yes; then
+        AC_CHECK_FUNCS([facl])
+        if test $ac_cv_func_facl = yes; then
           AC_SEARCH_LIBS([acl_trivial], [sec],
             [if test "$ac_cv_search_acl_trivial" != "none required"; then
                LIB_ACL=$ac_cv_search_acl_trivial
@@ -89,7 +89,7 @@ int type = ACL_TYPE_EXTENDED;]])],
       fi
 
       dnl Test for HP-UX API.
-      if test $use_acl = 0 || test "$ac_cv_func_acl" = yes; then
+      if test $use_acl = 0; then
         AC_CHECK_FUNCS([getacl])
         if test $ac_cv_func_getacl = yes; then
           use_acl=1
@@ -112,6 +112,14 @@ int type = ACL_TYPE_EXTENDED;]])],
         fi
       fi
 
+      dnl Test for NonStop Kernel API.
+      if test $use_acl = 0; then
+        AC_CHECK_FUNCS([aclsort])
+        if test $ac_cv_func_aclsort = yes; then
+          use_acl=1
+        fi
+      fi
+
       LIBS=$ac_save_LIBS
     fi
     if test "x$enable_acl$use_acl" = "xyes0"; then
index a16990b..1452c80 100755 (executable)
@@ -91,8 +91,14 @@ cd "$builddir" ||
       acl_flavor=hpux
     else
       if (getacl tmpfile0 >/dev/null) 2>/dev/null; then
-        # Tru64.
-        acl_flavor=osf1
+        # Tru64, NonStop Kernel.
+        if (getacl -m tmpfile0 >/dev/null) 2>/dev/null; then
+          # Tru64.
+          acl_flavor=osf1
+        else
+          # NonStop Kernel.
+          acl_flavor=nsk
+        fi
       else
         if (aclget tmpfile0 >/dev/null) 2>/dev/null; then
           # AIX.
@@ -132,7 +138,7 @@ cd "$builddir" ||
         cmp tmpaclout1 tmpaclout2 > /dev/null
       }
       ;;
-    osf1)
+    osf1 | nsk)
       func_test_same_acls ()
       {
         getacl "$1" | sed -e "s/$1/FILENAME/g" > tmpaclout1
@@ -411,6 +417,50 @@ cd "$builddir" ||
 
         ;;
 
+      nsk)
+
+        # Set an ACL for a user.
+        setacl -m user:$auid:1 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile2
+
+        # Set an ACL for a group.
+        setacl -m group:$agid:4 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile3
+
+        # Set an ACL for other.
+        setacl -m other:4 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile4
+
+        # Remove the ACL for the user.
+        setacl -d user:$auid tmpfile0
+
+        func_test_copy tmpfile0 tmpfile5
+
+        # Remove the ACL for the group.
+        setacl -d group:$agid tmpfile0
+
+        func_test_copy tmpfile0 tmpfile6
+
+        # Delete all optional ACLs.
+        setacl -m user:$auid:1 tmpfile0
+        setacl -s user::6,group::0,class:7,other:0 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile8
+
+        # Copy ACLs from a file that has no ACLs.
+        echo > tmpfile9
+        chmod a+x tmpfile9
+        getacl tmpfile9 > tmpaclout0
+        setacl -f tmpaclout0 tmpfile0
+        rm -f tmpfile9
+
+        func_test_copy tmpfile0 tmpfile9
+
+        ;;
+
       aix)
 
         # Set an ACL for a user.
index bf71b3b..c63f380 100755 (executable)
@@ -85,8 +85,14 @@ cd "$builddir" ||
       acl_flavor=hpux
     else
       if (getacl tmpfile0 >/dev/null) 2>/dev/null; then
-        # Tru64.
-        acl_flavor=osf1
+        # Tru64, NonStop Kernel.
+        if (getacl -m tmpfile0 >/dev/null) 2>/dev/null; then
+          # Tru64.
+          acl_flavor=osf1
+        else
+          # NonStop Kernel.
+          acl_flavor=nsk
+        fi
       else
         if (aclget tmpfile0 >/dev/null) 2>/dev/null; then
           # AIX.
@@ -126,7 +132,7 @@ cd "$builddir" ||
         cmp tmpaclout1 tmpaclout2 > /dev/null
       }
       ;;
-    osf1)
+    osf1 | nsk)
       func_test_same_acls ()
       {
         getacl "$1" | sed -e "s/$1/FILENAME/g" > tmpaclout1
@@ -405,6 +411,50 @@ cd "$builddir" ||
 
         ;;
 
+      nsk)
+
+        # Set an ACL for a user.
+        setacl -m user:$auid:1 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile2
+
+        # Set an ACL for a group.
+        setacl -m group:$agid:4 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile3
+
+        # Set an ACL for other.
+        setacl -m other:4 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile4
+
+        # Remove the ACL for the user.
+        setacl -d user:$auid tmpfile0
+
+        func_test_copy tmpfile0 tmpfile5
+
+        # Remove the ACL for the group.
+        setacl -d group:$agid tmpfile0
+
+        func_test_copy tmpfile0 tmpfile6
+
+        # Delete all optional ACLs.
+        setacl -m user:$auid:1 tmpfile0
+        setacl -s user::6,group::0,class:7,other:0 tmpfile0
+
+        func_test_copy tmpfile0 tmpfile8
+
+        # Copy ACLs from a file that has no ACLs.
+        echo > tmpfile9
+        chmod a+x tmpfile9
+        getacl tmpfile9 > tmpaclout0
+        setacl -f tmpaclout0 tmpfile0
+        rm -f tmpfile9
+
+        func_test_copy tmpfile0 tmpfile9
+
+        ;;
+
       aix)
 
         # Set an ACL for a user.
index db67388..3f49e8d 100755 (executable)
@@ -91,8 +91,14 @@ cd "$builddir" ||
       acl_flavor=hpux
     else
       if (getacl tmpfile0 >/dev/null) 2>/dev/null; then
-        # Tru64.
-        acl_flavor=osf1
+        # Tru64, NonStop Kernel.
+        if (getacl -m tmpfile0 >/dev/null) 2>/dev/null; then
+          # Tru64.
+          acl_flavor=osf1
+        else
+          # NonStop Kernel.
+          acl_flavor=nsk
+        fi
       else
         if (aclget tmpfile0 >/dev/null) 2>/dev/null; then
           # AIX.
@@ -286,6 +292,20 @@ cd "$builddir" ||
         fi
         ;;
 
+      nsk)
+
+        # Set an ACL for a user.
+        setacl -m user:$auid:1 tmpfile0
+
+        func_test_has_acl tmpfile0 yes
+
+        # Remove the ACL for the user.
+        setacl -d user:$auid tmpfile0
+
+        func_test_has_acl tmpfile0 no
+
+        ;;
+
       aix)
 
         # Set an ACL for a user.
index 7dcec2a..6bd4f5e 100644 (file)
@@ -24,7 +24,7 @@
 #include <string.h>
 #include <sys/stat.h>
 
-#if HAVE_ACL_GET_FILE || HAVE_ACL || HAVE_ACLX_GET || HAVE_STATACL
+#if HAVE_ACL_GET_FILE || HAVE_FACL || HAVE_ACLX_GET || HAVE_STATACL || HAVE_ACLSORT
 # include <sys/types.h>
 # include <sys/acl.h>
 #endif
@@ -218,7 +218,7 @@ main (int argc, char *argv[])
               }
           }
       }
-#elif HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+#elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
   int count1;
   int count2;
 
@@ -520,6 +520,71 @@ main (int argc, char *argv[])
                file1, file2);
       return 1;
     }
+#elif HAVE_ACLSORT /* NonStop Kernel */
+  int count1;
+  int count2;
+
+  count1 = acl ((char *) file1, ACL_CNT, NACLENTRIES, NULL);
+  count2 = acl ((char *) file2, ACL_CNT, NACLENTRIES, NULL);
+
+  if (count1 < 0)
+    {
+      fprintf (stderr, "error accessing the ACLs of file %s\n", file1);
+      fflush (stderr);
+      abort ();
+    }
+  if (count2 < 0)
+    {
+      fprintf (stderr, "error accessing the ACLs of file %s\n", file2);
+      fflush (stderr);
+      abort ();
+    }
+  if (count1 != count2)
+    {
+      fprintf (stderr, "files %s and %s have different number of ACLs: %d and %d\n",
+               file1, file2, count1, count2);
+      return 1;
+    }
+  else if (count1 > 0)
+    {
+      struct acl *entries1 = XNMALLOC (count1, struct acl);
+      struct acl *entries2 = XNMALLOC (count2, struct acl);
+      int i;
+
+      if (acl ((char *) file1, ACL_GET, count1, entries1) < count1)
+        {
+          fprintf (stderr, "error retrieving the ACLs of file %s\n", file1);
+          fflush (stderr);
+          abort ();
+        }
+      if (acl ((char *) file2, ACL_GET, count2, entries2) < count1)
+        {
+          fprintf (stderr, "error retrieving the ACLs of file %s\n", file2);
+          fflush (stderr);
+          abort ();
+        }
+      for (i = 0; i < count1; i++)
+        {
+          if (entries1[i].a_type != entries2[i].a_type)
+            {
+              fprintf (stderr, "files %s and %s: different ACL entry #%d: different types %d and %d\n",
+                       file1, file2, i, entries1[i].a_type, entries2[i].a_type);
+              return 1;
+            }
+          if (entries1[i].a_id != entries2[i].a_id)
+            {
+              fprintf (stderr, "files %s and %s: different ACL entry #%d: different ids %d and %d\n",
+                       file1, file2, i, (int)entries1[i].a_id, (int)entries2[i].a_id);
+              return 1;
+            }
+          if (entries1[i].a_perm != entries2[i].a_perm)
+            {
+              fprintf (stderr, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
+                       file1, file2, i, (unsigned int) entries1[i].a_perm, (unsigned int) entries2[i].a_perm);
+              return 1;
+            }
+        }
+    }
 #endif
   }
 
index ef2b716..56fde29 100755 (executable)
@@ -91,8 +91,14 @@ cd "$builddir" ||
       acl_flavor=hpux
     else
       if (getacl tmpfile0 >/dev/null) 2>/dev/null; then
-        # Tru64.
-        acl_flavor=osf1
+        # Tru64, NonStop Kernel.
+        if (getacl -m tmpfile0 >/dev/null) 2>/dev/null; then
+          # Tru64.
+          acl_flavor=osf1
+        else
+          # NonStop Kernel.
+          acl_flavor=nsk
+        fi
       else
         if (aclget tmpfile0 >/dev/null) 2>/dev/null; then
           # AIX.
@@ -175,6 +181,9 @@ cd "$builddir" ||
           osf1)
             setacl -u user:$auid:1 tmpfile0
             ;;
+          nsk)
+            setacl -m user:$auid:1 tmpfile0
+            ;;
           aix)
             { aclget tmpfile0 | sed -e 's/disabled$/enabled/'; echo "        permit --x u:$auid"; } | aclput tmpfile0
             ;;