Tests for module 'log2l-ieee'.
[gnulib.git] / lib / set-mode-acl.c
index 5e99440..25a1eee 100644 (file)
@@ -1,6 +1,6 @@
 /* set-mode-acl.c - set access control list equivalent to a mode
 
-   Copyright (C) 2002-2003, 2005-2011 Free Software Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2012 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -57,7 +57,7 @@ qset_acl (char const *name, int desc, mode_t mode)
 # if HAVE_ACL_GET_FILE
   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
   /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
-#  if MODE_INSIDE_ACL
+#  if !HAVE_ACL_TYPE_EXTENDED
   /* Linux, FreeBSD, IRIX, Tru64 */
 
   /* We must also have acl_from_text and acl_delete_def_file.
@@ -132,21 +132,17 @@ qset_acl (char const *name, int desc, mode_t mode)
   if (S_ISDIR (mode) && acl_delete_def_file (name))
     return -1;
 
-  if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+  if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
     {
-      /* We did not call chmod so far, so the special bits have not yet
-         been set.  */
+      /* 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 (name, desc, mode);
     }
   return 0;
 
-#  else /* !MODE_INSIDE_ACL */
+#  else /* HAVE_ACL_TYPE_EXTENDED */
   /* MacOS X */
 
-#   if !HAVE_ACL_TYPE_EXTENDED
-#    error Must have ACL_TYPE_EXTENDED
-#   endif
-
   /* On MacOS X,  acl_get_file (name, ACL_TYPE_ACCESS)
      and          acl_get_file (name, ACL_TYPE_DEFAULT)
      always return NULL / EINVAL.  You have to use
@@ -201,58 +197,11 @@ qset_acl (char const *name, int desc, mode_t mode)
   return chmod_or_fchmod (name, desc, mode);
 #  endif
 
-# elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
-
-#  if defined ACL_NO_TRIVIAL && 0
-  /* Solaris 10 (newer version), which has additional API declared in
-     <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
-     acl_fromtext, ...).  */
-
-  acl_t *aclp;
-  char acl_text[] = "user::---,group::---,mask:---,other:---";
-  int ret;
-  int saved_errno;
-
-  if (mode & S_IRUSR) acl_text[ 6] = 'r';
-  if (mode & S_IWUSR) acl_text[ 7] = 'w';
-  if (mode & S_IXUSR) acl_text[ 8] = 'x';
-  if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r';
-  if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w';
-  if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x';
-  if (mode & S_IROTH) acl_text[36] = 'r';
-  if (mode & S_IWOTH) acl_text[37] = 'w';
-  if (mode & S_IXOTH) acl_text[38] = 'x';
-
-  if (acl_fromtext (acl_text, &aclp) != 0)
-    {
-      errno = ENOMEM;
-      return -1;
-    }
-
-  ret = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp));
-  saved_errno = errno;
-  acl_free (aclp);
-  if (ret < 0)
-    {
-      if (saved_errno == ENOSYS || saved_errno == EOPNOTSUPP)
-        return chmod_or_fchmod (name, desc, mode);
-      errno = saved_errno;
-      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 /* Solaris, Cygwin, general case */
+# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
 
   int done_setacl = 0;
 
-#   ifdef ACE_GETACL
+#  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).  */
 
@@ -265,47 +214,60 @@ qset_acl (char const *name, int desc, mode_t mode)
   int convention;
 
   {
+    /* Initially, try to read the entries into a stack-allocated buffer.
+       Use malloc if it does not fit.  */
+    enum
+      {
+        alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
+        alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
+      };
+    ace_t buf[alloc_init];
+    size_t alloc = alloc_init;
+    ace_t *entries = buf;
+    ace_t *malloced = NULL;
     int count;
-    ace_t *entries;
 
     for (;;)
       {
-        if (desc != -1)
-          count = facl (desc, ACE_GETACLCNT, 0, NULL);
-        else
-          count = acl (name, ACE_GETACLCNT, 0, NULL);
-        if (count <= 0)
+        count = (desc != -1
+                 ? facl (desc, ACE_GETACL, alloc, entries)
+                 : acl (name, ACE_GETACL, alloc, entries));
+        if (count < 0 && errno == ENOSPC)
           {
-            convention = -1;
-            break;
+            /* Increase the size of the buffer.  */
+            free (malloced);
+            if (alloc > alloc_max / 2)
+              {
+                errno = ENOMEM;
+                return -1;
+              }
+            alloc = 2 * alloc; /* <= alloc_max */
+            entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
+            if (entries == NULL)
+              {
+                errno = ENOMEM;
+                return -1;
+              }
+            continue;
           }
-        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;
+        break;
+      }
 
-            convention = 0;
-            for (i = 0; i < count; i++)
-              if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
-                {
-                  convention = 1;
-                  break;
-                }
-            free (entries);
-            break;
-          }
-        /* Huh? The number of ACL entries changed since the last call.
-           Repeat.  */
-        free (entries);
+    if (count <= 0)
+      convention = -1;
+    else
+      {
+        int i;
+
+        convention = 0;
+        for (i = 0; i < count; i++)
+          if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
+            {
+              convention = 1;
+              break;
+            }
       }
+    free (malloced);
   }
 
   if (convention >= 0)
@@ -335,14 +297,6 @@ qset_acl (char const *name, int desc, mode_t mode)
         {
           /* Running on Solaris 10 (newer version) or Solaris 11.
              The details here were found through "/bin/ls -lvd somefiles".  */
-          struct stat statbuf;
-          int dir;
-
-          if ((desc != -1 ? fstat (desc, &statbuf) : stat (name, &statbuf)) >= 0)
-            dir = S_ISDIR (statbuf.st_mode);
-          else
-            dir = 0;
-
           entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
           entries[0].a_flags = NEW_ACE_OWNER;
           entries[0].a_who = 0; /* irrelevant */
@@ -387,14 +341,14 @@ qset_acl (char const *name, int desc, mode_t mode)
           else
             entries[2].a_access_mask |= NEW_ACE_EXECUTE;
           entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
-          entries[4].a_flags = ACE_EVERYONE;
+          entries[4].a_flags = NEW_ACE_EVERYONE;
           entries[4].a_who = 0;
           entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
                                      | NEW_ACE_WRITE_ATTRIBUTES
                                      | NEW_ACE_WRITE_ACL
                                      | NEW_ACE_WRITE_OWNER;
           entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
-          entries[5].a_flags = ACE_EVERYONE;
+          entries[5].a_flags = NEW_ACE_EVERYONE;
           entries[5].a_who = 0;
           entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
                                      | NEW_ACE_READ_ATTRIBUTES
@@ -427,7 +381,7 @@ qset_acl (char const *name, int desc, mode_t mode)
       if (ret == 0)
         done_setacl = 1;
     }
-#   endif
+#  endif
 
   if (!done_setacl)
     {
@@ -466,8 +420,6 @@ qset_acl (char const *name, int desc, mode_t mode)
     }
   return 0;
 
-#  endif
-
 # elif HAVE_GETACL /* HP-UX */
 
   struct stat statbuf;
@@ -738,8 +690,8 @@ qset_acl (char const *name, int desc, mode_t mode)
 int
 set_acl (char const *name, int desc, mode_t mode)
 {
-  int r = qset_acl (name, desc, mode);
-  if (r != 0)
+  int ret = qset_acl (name, desc, mode);
+  if (ret != 0)
     error (0, errno, _("setting permissions for %s"), quote (name));
-  return r;
+  return ret;
 }