canonicalize: add support for not resolving symlinks
[gnulib.git] / lib / set-mode-acl.c
index 2cd2c75..d0fc6f8 100644 (file)
@@ -1,6 +1,6 @@
 /* set-mode-acl.c - set access control list equivalent to a mode
 
-   Copyright (C) 2002-2003, 2005-2010 Free Software Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2011 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,56 +197,11 @@ 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 */
-
-#  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,
-     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;
+# elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
 
-#  else /* Solaris, Cygwin, general case */
+  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).  */
 
@@ -292,7 +243,7 @@ qset_acl (char const *name, int desc, mode_t mode)
 
             convention = 0;
             for (i = 0; i < count; i++)
-              if (entries[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_OTHER))
+              if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
                 {
                   convention = 1;
                   break;
@@ -308,90 +259,145 @@ qset_acl (char const *name, int desc, mode_t mode)
 
   if (convention >= 0)
     {
-      ace_t entries[3];
+      ace_t entries[6];
+      int count;
       int ret;
 
       if (convention)
         {
           /* Running on Solaris 10.  */
-          entries[0].a_type = ALLOW;
-          entries[0].a_flags = ACE_OWNER;
+          entries[0].a_type = OLD_ALLOW;
+          entries[0].a_flags = OLD_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_type = OLD_ALLOW;
+          entries[1].a_flags = OLD_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_type = OLD_ALLOW;
+          entries[2].a_flags = OLD_ACE_OTHER;
           entries[2].a_who = 0;
           entries[2].a_access_mask = mode & 7;
+          count = 3;
         }
       else
         {
-          /* Running on Solaris 10 (newer version) or Solaris 11.  */
-          entries[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+          /* Running on Solaris 10 (newer version) or Solaris 11.
+             The details here were found through "/bin/ls -lvd somefiles".  */
+          entries[0].a_type = NEW_ACE_ACCESS_DENIED_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[0].a_access_mask = 0;
+          entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
+          entries[1].a_flags = NEW_ACE_OWNER;
           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);
+          entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
+                                     | NEW_ACE_WRITE_ATTRIBUTES
+                                     | NEW_ACE_WRITE_ACL
+                                     | NEW_ACE_WRITE_OWNER;
+          if (mode & 0400)
+            entries[1].a_access_mask |= NEW_ACE_READ_DATA;
+          else
+            entries[0].a_access_mask |= NEW_ACE_READ_DATA;
+          if (mode & 0200)
+            entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+          else
+            entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+          if (mode & 0100)
+            entries[1].a_access_mask |= NEW_ACE_EXECUTE;
+          else
+            entries[0].a_access_mask |= NEW_ACE_EXECUTE;
+          entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
+          entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
+          entries[2].a_who = 0; /* irrelevant */
+          entries[2].a_access_mask = 0;
+          entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
+          entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
+          entries[3].a_who = 0; /* irrelevant */
+          entries[3].a_access_mask = 0;
+          if (mode & 0040)
+            entries[3].a_access_mask |= NEW_ACE_READ_DATA;
+          else
+            entries[2].a_access_mask |= NEW_ACE_READ_DATA;
+          if (mode & 0020)
+            entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+          else
+            entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+          if (mode & 0010)
+            entries[3].a_access_mask |= NEW_ACE_EXECUTE;
+          else
+            entries[2].a_access_mask |= NEW_ACE_EXECUTE;
+          entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
+          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 = NEW_ACE_EVERYONE;
+          entries[5].a_who = 0;
+          entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
+                                     | NEW_ACE_READ_ATTRIBUTES
+                                     | NEW_ACE_READ_ACL
+                                     | NEW_ACE_SYNCHRONIZE;
+          if (mode & 0004)
+            entries[5].a_access_mask |= NEW_ACE_READ_DATA;
+          else
+            entries[4].a_access_mask |= NEW_ACE_READ_DATA;
+          if (mode & 0002)
+            entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+          else
+            entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+          if (mode & 0001)
+            entries[5].a_access_mask |= NEW_ACE_EXECUTE;
+          else
+            entries[4].a_access_mask |= NEW_ACE_EXECUTE;
+          count = 6;
         }
       if (desc != -1)
-        ret = facl (desc, ACE_SETACL,
-                    sizeof (entries) / sizeof (ace_t), entries);
+        ret = facl (desc, ACE_SETACL, count, entries);
       else
-        ret = acl (name, ACE_SETACL,
-                   sizeof (entries) / sizeof (ace_t), entries);
+        ret = acl (name, ACE_SETACL, count, entries);
       if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
         {
           if (errno == ENOSYS)
             return chmod_or_fchmod (name, desc, mode);
           return -1;
         }
+      if (ret == 0)
+        done_setacl = 1;
     }
-#   endif
+#  endif
 
-  {
-    aclent_t entries[3];
-    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 = OTHER_OBJ;
-    entries[2].a_id = 0;
-    entries[2].a_perm = mode & 7;
+  if (!done_setacl)
+    {
+      aclent_t entries[3];
+      int ret;
 
-    if (desc != -1)
-      ret = facl (desc, SETACL, sizeof (entries) / sizeof (aclent_t), entries);
-    else
-      ret = acl (name, SETACL, sizeof (entries) / sizeof (aclent_t), entries);
-    if (ret < 0)
-      {
-        if (errno == ENOSYS || errno == EOPNOTSUPP)
-          return chmod_or_fchmod (name, desc, mode);
-        return -1;
-      }
-  }
+      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 = OTHER_OBJ;
+      entries[2].a_id = 0;
+      entries[2].a_perm = mode & 7;
+
+      if (desc != -1)
+        ret = facl (desc, SETACL,
+                    sizeof (entries) / sizeof (aclent_t), entries);
+      else
+        ret = acl (name, SETACL,
+                   sizeof (entries) / sizeof (aclent_t), entries);
+      if (ret < 0)
+        {
+          if (errno == ENOSYS || errno == EOPNOTSUPP)
+            return chmod_or_fchmod (name, desc, mode);
+          return -1;
+        }
+    }
 
   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
     {
@@ -401,12 +407,9 @@ qset_acl (char const *name, int desc, mode_t mode)
     }
   return 0;
 
-#  endif
-
 # elif HAVE_GETACL /* HP-UX */
 
   struct stat statbuf;
-  struct acl_entry entries[3];
   int ret;
 
   if (desc != -1)
@@ -416,25 +419,68 @@ qset_acl (char const *name, int desc, mode_t mode)
   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;
+  {
+    struct acl_entry entries[3];
+
+    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 (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 (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
+        return -1;
+
+#  if HAVE_ACLV_H /* HP-UX >= 11.11 */
+      {
+        struct acl entries[4];
+
+        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 (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+              return chmod_or_fchmod (name, desc, mode);
+            return -1;
+          }
+      }
+#  else
+      return chmod_or_fchmod (name, desc, mode);
+#  endif
     }
 
   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
@@ -573,6 +619,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