Add support for HP-UX ACLs.
authorBruno Haible <bruno@clisp.org>
Sun, 8 Jun 2008 17:08:22 +0000 (19:08 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 8 Jun 2008 17:08:22 +0000 (19:08 +0200)
ChangeLog
lib/acl-internal.h
lib/copy-acl.c
lib/file-has-acl.c
lib/set-mode-acl.c

index 9ff15ce..90eca94 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2008-06-08  Bruno Haible  <bruno@clisp.org>
 
+       Add support for HP-UX ACLs.
+       * lib/acl-internal.h (acl_nontrivial): New declaration.
+       * lib/file-has-acl.c (acl_nontrivial): New function.
+       (file_has_acl): Add implementation using HP-UX 11 ACL API.
+       * lib/set-mode-acl.c (qset_acl): Likewise.
+       * lib/copy-acl.c (qcopy_acl): Likewise.
+
+2008-06-08  Bruno Haible  <bruno@clisp.org>
+
        Add support for Cygwin ACLs.
        * lib/acl-internal.h (MODE_INSIDE_ACL): New macro for Solaris-like API.
        * lib/set-mode-acl.c (qset_acl) [!MODE_INSIDE_ACL]: Don't optimize away
index f629eeb..af7396e 100644 (file)
@@ -179,6 +179,12 @@ extern int acl_nontrivial (int count, aclent_t *entries);
 extern int acl_ace_nontrivial (int count, ace_t *entries);
 #  endif
 
+# elif HAVE_GETACL /* HP-UX */
+
+/* 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_entry *entries, struct stat *sb);
+
 # endif
 
 #endif
index d20fa04..73f370c 100644 (file)
@@ -392,6 +392,84 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
 
 # endif
 
+#elif USE_ACL && HAVE_GETACL /* HP-UX */
+
+  int count;
+  struct acl_entry entries[NACLENTRIES];
+  int ret;
+
+  for (;;)
+    {
+      count = (source_desc != -1
+              ? fgetacl (source_desc, 0, NULL)
+              : getacl (src_name, 0, NULL));
+
+      if (count < 0)
+       {
+         if (errno == ENOSYS || errno == EOPNOTSUPP)
+           {
+             count = 0;
+             break;
+           }
+         else
+           return -2;
+       }
+
+      if (count == 0)
+       break;
+
+      if (count > NACLENTRIES)
+       /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
+       abort ();
+
+      if ((source_desc != -1
+          ? fgetacl (source_desc, count, entries)
+          : getacl (src_name, 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 = (dest_desc != -1
+        ? fsetacl (dest_desc, count, entries)
+        : setacl (dst_name, count, entries));
+  if (ret < 0)
+    {
+      int saved_errno = errno;
+
+      if (errno == ENOSYS || errno == EOPNOTSUPP)
+       {
+         struct stat source_statbuf;
+
+         if ((source_desc != -1
+              ? fstat (source_desc, &source_statbuf)
+              : stat (src_name, &source_statbuf)) == 0)
+           {
+             if (!acl_nontrivial (count, entries, &source_statbuf))
+               return chmod_or_fchmod (dst_name, dest_desc, mode);
+           }
+         else
+           saved_errno = errno;
+       }
+
+      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 cbcc86d..50e60d5 100644 (file)
@@ -174,6 +174,27 @@ acl_ace_nontrivial (int count, ace_t *entries)
 
 # endif
 
+#elif USE_ACL && HAVE_GETACL /* HP-UX */
+
+/* Return 1 if the given ACL is non-trivial.
+   Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.  */
+int
+acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb)
+{
+  int i;
+
+  for (i = 0; i < count; i++)
+    {
+      struct acl_entry *ace = &entries[i];
+
+      if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP)
+           || (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid)
+           || (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP)))
+       return 1;
+    }
+  return 0;
+}
+
 #endif
 
 
@@ -371,6 +392,44 @@ file_has_acl (char const *name, struct stat const *sb)
       return 0;
 #  endif
 
+# elif HAVE_GETACL /* HP-UX */
+
+      int count;
+      struct acl_entry entries[NACLENTRIES];
+
+      for (;;)
+       {
+         count = getacl (name, 0, NULL);
+
+         if (count < 0)
+           return (errno == ENOSYS || errno == EOPNOTSUPP ? 0 : -1);
+
+         if (count == 0)
+           return 0;
+
+         if (count > NACLENTRIES)
+           /* If NACLENTRIES cannot be trusted, use dynamic memory
+              allocation.  */
+           abort ();
+
+         /* If there are more than 3 entries, there cannot be only the
+            (uid,%), (%,gid), (%,%) entries.  */
+         if (count > 3)
+           return 1;
+
+         if (getacl (name, count, entries) == count)
+           {
+             struct stat statbuf;
+
+             if (stat (name, &statbuf) < 0)
+               return -1;
+
+             return acl_nontrivial (count, entries, &statbuf);
+           }
+         /* Huh? The number of ACL entries changed since the last call.
+            Repeat.  */
+       }
+
 # endif
     }
 #endif
index b5b14ad..c25c5fe 100644 (file)
@@ -315,6 +315,48 @@ qset_acl (char const *name, int desc, mode_t mode)
 
 #  endif
 
+# elif HAVE_GETACL /* HP-UX */
+
+  struct stat statbuf;
+  struct acl_entry entries[3];
+  int ret;
+
+  if (desc != -1)
+    ret = fstat (desc, &statbuf);
+  else
+    ret = stat (name, &statbuf);
+  if (ret < 0)
+    return -1;
+
+  entries[0].uid = statbuf.st_uid;
+  entries[0].gid = ACL_NSGROUP;
+  entries[0].mode = (mode >> 6) & 7;
+  entries[1].uid = ACL_NSUSER;
+  entries[1].gid = statbuf.st_gid;
+  entries[1].mode = (mode >> 3) & 7;
+  entries[2].uid = ACL_NSUSER;
+  entries[2].gid = ACL_NSGROUP;
+  entries[2].mode = mode & 7;
+
+  if (desc != -1)
+    ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
+  else
+    ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
+  if (ret < 0)
+    {
+      if (errno == ENOSYS || errno == EOPNOTSUPP)
+       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