Add support for MacOS X ACLs.
authorBruno Haible <bruno@clisp.org>
Sun, 8 Jun 2008 03:06:19 +0000 (05:06 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 8 Jun 2008 03:06:19 +0000 (05:06 +0200)
ChangeLog
lib/copy-acl.c
lib/file-has-acl.c
lib/set-mode-acl.c

index edce9825dc50be0965bee7d6ba97005468e57dbd..e9e48fa5dbd51242945aaec571efe994468653ba 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-06-07  Bruno Haible  <bruno@clisp.org>
+
+       Add support for MacOS X ACLs.
+       * lib/file-has-acl.c (file_has_acl): Use ACL_TYPE_EXTENDED instead of
+       ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT.
+       * lib/set-mode-acl.c (qset_acl): Likewise.
+       * lib/copy-acl.c (qcopy_acl): Likewise.
+
 2008-06-07  Bruno Haible  <bruno@clisp.org>
 
        Fix memory leak introduced on 2008-05-22.
index f0c8dc2ce38e2b2561ce9b173a996c9c8b9619e6..87952f4086eaa3a0f2d0efff8b5eaf5b5f53d090 100644 (file)
@@ -42,6 +42,8 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
 #if USE_ACL && HAVE_ACL_GET_FILE
   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
   /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
+# if MODE_INSIDE_ACL
+  /* Linux, FreeBSD, IRIX, Tru64 */
 
   acl_t acl;
   int ret;
@@ -82,7 +84,7 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
   else
     acl_free (acl);
 
-  if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
+  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.  */
@@ -110,6 +112,70 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
     }
   return 0;
 
+# else /* !MODE_INSIDE_ACL */
+  /* 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
+                 acl_get_file (name, ACL_TYPE_EXTENDED)
+     or           acl_get_fd (open (name, ...))
+     to retrieve an ACL.
+     On the other hand,
+                 acl_set_file (name, ACL_TYPE_ACCESS, acl)
+     and          acl_set_file (name, ACL_TYPE_DEFAULT, acl)
+     have the same effect as
+                 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
+     Each of these calls sets the file's ACL.  */
+
+  acl_t acl;
+  int ret;
+
+  if (HAVE_ACL_GET_FD && source_desc != -1)
+    acl = acl_get_fd (source_desc);
+  else
+    acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
+  if (acl == NULL)
+    {
+      if (ACL_NOT_WELL_SUPPORTED (errno))
+       return qset_acl (dst_name, dest_desc, mode);
+      else
+        return -2;
+    }
+
+  if (HAVE_ACL_SET_FD && dest_desc != -1)
+    ret = acl_set_fd (dest_desc, acl);
+  else
+    ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
+  if (ret != 0)
+    {
+      int saved_errno = errno;
+
+      if (ACL_NOT_WELL_SUPPORTED (errno) && !(acl_entries (acl) > 0))
+        {
+         acl_free (acl);
+         return chmod_or_fchmod (dst_name, dest_desc, mode);
+       }
+      else
+       {
+         acl_free (acl);
+         chmod_or_fchmod (dst_name, dest_desc, mode);
+         errno = saved_errno;
+         return -1;
+       }
+    }
+  else
+    acl_free (acl);
+
+  /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
+  return chmod_or_fchmod (dst_name, dest_desc, mode);
+
+# endif
+
 #elif USE_ACL && defined ACL_NO_TRIVIAL
   /* Solaris 10 NFSv4 ACLs.  */
 
index 5b1e8ff2328608a317c4c7152f74a8eb408171a5..f3d8bfdac2da5441c10e587dfb5621be8e1c1721 100644 (file)
@@ -127,9 +127,29 @@ file_has_acl (char const *name, struct stat const *sb)
       int ret;
 
       if (HAVE_ACL_EXTENDED_FILE)
-       ret = acl_extended_file (name);
+       {
+         /* On Linux, acl_extended_file is an optimized function: It only
+            makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
+            ACL_TYPE_DEFAULT.  */
+         ret = acl_extended_file (name);
+       }
       else
        {
+#  if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
+         /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
+            and acl_get_file (name, ACL_TYPE_DEFAULT)
+            always return NULL / EINVAL.  There is no point in making
+            these two useless calls.  The real ACL is retrieved through
+            acl_get_file (name, ACL_TYPE_EXTENDED).  */
+         acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED);
+         if (acl)
+           {
+             ret = (0 < acl_entries (acl));
+             acl_free (acl);
+           }
+         else
+           ret = -1;
+#  else /* FreeBSD, IRIX, Tru64 */
          acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
          if (acl)
            {
@@ -153,6 +173,7 @@ file_has_acl (char const *name, struct stat const *sb)
            }
          else
            ret = -1;
+#  endif
        }
       if (ret < 0)
        return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
@@ -179,7 +200,7 @@ file_has_acl (char const *name, struct stat const *sb)
     }
 #endif
 
-  /* FIXME: Add support for AIX, Irix, and Tru64.  Please see Samba's
+  /* FIXME: Add support for AIX.  Please see Samba's
      source/lib/sysacls.c file for fix-related ideas.  */
 
   return 0;
index 6780d2393929dcc03889b59979683804b735bd9f..b56ce03a94de29a4df2aa33b74039f65ca77df85 100644 (file)
@@ -143,6 +143,23 @@ qset_acl (char const *name, int desc, mode_t mode)
 #  else /* !MODE_INSIDE_ACL */
   /* 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
+                 acl_get_file (name, ACL_TYPE_EXTENDED)
+     or           acl_get_fd (open (name, ...))
+     to retrieve an ACL.
+     On the other hand,
+                 acl_set_file (name, ACL_TYPE_ACCESS, acl)
+     and          acl_set_file (name, ACL_TYPE_DEFAULT, acl)
+     have the same effect as
+                 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
+     Each of these calls sets the file's ACL.  */
+
   acl_t acl;
   int ret;
 
@@ -150,7 +167,7 @@ qset_acl (char const *name, int desc, mode_t mode)
   if (HAVE_ACL_GET_FD && desc != -1)
     acl = acl_get_fd (desc);
   else
-    acl = acl_get_file (name, ACL_TYPE_ACCESS);
+    acl = acl_get_file (name, ACL_TYPE_EXTENDED);
   if (acl)
     {
       acl_free (acl);
@@ -161,7 +178,7 @@ qset_acl (char const *name, int desc, mode_t mode)
          if (HAVE_ACL_SET_FD && desc != -1)
            ret = acl_set_fd (desc, acl);
          else
-           ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
+           ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
          if (ret != 0)
            {
              int saved_errno = errno;