changeset 10172:50e666f281ef

Add support for MacOS X ACLs.
author Bruno Haible <bruno@clisp.org>
date Sun, 08 Jun 2008 05:06:19 +0200
parents f988df7e273d
children 33543159ba5b
files ChangeLog lib/copy-acl.c lib/file-has-acl.c lib/set-mode-acl.c
diffstat 4 files changed, 117 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- 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.
--- a/lib/copy-acl.c
+++ b/lib/copy-acl.c
@@ -42,6 +42,8 @@
 #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 @@
   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 @@
     }
   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.  */
 
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -127,9 +127,29 @@
       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 @@
 	    }
 	  else
 	    ret = -1;
+#  endif
 	}
       if (ret < 0)
 	return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
@@ -179,7 +200,7 @@
     }
 #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;
--- a/lib/set-mode-acl.c
+++ b/lib/set-mode-acl.c
@@ -143,6 +143,23 @@
 #  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 @@
   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 @@
 	  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;