From da9fd677c05325560fa533cbf7c3e67eb9844fbe Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Fri, 23 May 2008 01:13:51 +0200
Subject: [PATCH] Make copy_acl work on MacOS X 10.5.
---
ChangeLog | 14 ++++++++
lib/acl-internal.h | 17 ++++++++-
lib/acl.c | 95 ++++++++++++++++++++++++++++++++++++++++++++--------
lib/copy-acl.c | 14 ++++---
lib/file-has-acl.c | 5 ++-
m4/acl.m4 | 6 ++-
6 files changed, 125 insertions(+), 26 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 9bbd1e9..a2aef6a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
2008-05-22 Bruno Haible
+ Make copy_acl work on MacOS X 10.5.
+ * lib/acl-internal.h (MODE_INSIDE_ACL): New macro.
+ (ACL_NOT_WELL_SUPPORTED): On MacOS X, also handle ENOENT.
+ * lib/acl.c (qset_acl): Add different code branch for !MODE_INSIDE_ACL.
+ If MODE_INSIDE_ACL, don't assume that every system has the same text
+ representation for ACLs as FreeBSD.
+ * lib/copy-acl.c (copy_acl): Add support for platforms with
+ !MODE_INSIDE_ACL.
+ * lib/file-has-acl.c (file_has_acl): Likewise.
+ * m4/acl.m4 (gl_FUNC_ACL): Test for some functions that are witness
+ of FreeBSD or MacOS X, respectively.
+
+2008-05-22 Bruno Haible
+
* lib/acl.h: Don't include .
(GETACLCNT): Move fallback to lib/acl-internal.h.
* lib/acl-internal.h: Include here.
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
index c23d6f4..75bca17 100644
--- a/lib/acl-internal.h
+++ b/lib/acl-internal.h
@@ -84,8 +84,21 @@
# define acl_from_mode(mode) (NULL)
#endif
-#define ACL_NOT_WELL_SUPPORTED(Err) \
- ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY)
+/* Set to 1 if a file's mode is implicit by the ACL.
+ Set to 0 if a file's mode is stored independently from the ACL. */
+#if HAVE_ACL_COPY_EXT_NATIVE && HAVE_ACL_CREATE_ENTRY_NP /* MacOS X */
+# define MODE_INSIDE_ACL 0
+#else
+# define MODE_INSIDE_ACL 1
+#endif
+
+#if defined __APPLE__ && defined __MACH__ /* MacOS X */
+# define ACL_NOT_WELL_SUPPORTED(Err) \
+ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == ENOENT)
+#else
+# define ACL_NOT_WELL_SUPPORTED(Err) \
+ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY)
+#endif
/* Define a replacement for acl_entries if needed. */
#if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE && !HAVE_ACL_ENTRIES
diff --git a/lib/acl.c b/lib/acl.c
index 10499a0..7655d1b 100644
--- a/lib/acl.c
+++ b/lib/acl.c
@@ -48,32 +48,42 @@ chmod_or_fchmod (const char *name, int desc, mode_t mode)
int
qset_acl (char const *name, int desc, mode_t mode)
{
-#if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
+#if USE_ACL
+# if MODE_INSIDE_ACL
+# if HAVE_ACL_SET_FILE && HAVE_ACL_FREE
/* POSIX 1003.1e draft 17 (abandoned) specific version. */
+ /* Linux, FreeBSD, IRIX, Tru64 */
- /* We must also have have_acl_from_text and acl_delete_def_file.
+ /* We must also have acl_from_text and acl_delete_def_file.
(acl_delete_def_file could be emulated with acl_init followed
by acl_set_file, but acl_set_file with an empty acl is
unspecified.) */
-# ifndef HAVE_ACL_FROM_TEXT
-# error Must have acl_from_text (see POSIX 1003.1e draft 17).
-# endif
-# ifndef HAVE_ACL_DELETE_DEF_FILE
-# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
-# endif
+# ifndef HAVE_ACL_FROM_TEXT
+# error Must have acl_from_text (see POSIX 1003.1e draft 17).
+# endif
+# ifndef HAVE_ACL_DELETE_DEF_FILE
+# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
+# endif
acl_t acl;
int ret;
- if (HAVE_ACL_FROM_MODE)
+ if (HAVE_ACL_FROM_MODE) /* Linux */
{
acl = acl_from_mode (mode);
if (!acl)
return -1;
}
- else
+ else /* FreeBSD, IRIX, Tru64 */
{
+ /* If we were to create the ACL using the functions acl_init(),
+ acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
+ acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
+ would need to create a qualifier. I don't know how to do this.
+ So create it using acl_from_text(). */
+
+# if HAVE_ACL_DELETE_FD_NP && HAVE_ACL_DELETE_FILE_NP /* FreeBSD */
char acl_text[] = "u::---,g::---,o::---";
if (mode & S_IRUSR) acl_text[ 3] = 'r';
@@ -89,6 +99,9 @@ qset_acl (char const *name, int desc, mode_t mode)
acl = acl_from_text (acl_text);
if (!acl)
return -1;
+# else /* Unknown flavor of POSIX-like ACLs */
+ return chmod_or_fchmod (name, desc, mode);
+# endif
}
if (HAVE_ACL_SET_FD && desc != -1)
ret = acl_set_fd (desc, acl);
@@ -124,10 +137,8 @@ qset_acl (char const *name, int desc, mode_t mode)
return -1;
}
return 0;
-#else
-
-# if USE_ACL && defined ACL_NO_TRIVIAL
+# elif defined ACL_NO_TRIVIAL
/* Solaris 10, with NFSv4 ACLs. */
acl_t *aclp;
char acl_text[] = "user::---,group::---,mask:---,other:---";
@@ -158,10 +169,66 @@ qset_acl (char const *name, int desc, mode_t mode)
return acl_result;
}
}
-# endif
return chmod_or_fchmod (name, desc, mode);
+# else /* Unknown flavor of ACLs */
+ return chmod_or_fchmod (name, desc, mode);
+# endif
+# else /* !MODE_INSIDE_ACL */
+# if HAVE_ACL_SET_FILE && HAVE_ACL_FREE
+ /* POSIX 1003.1e draft 17 (abandoned) specific version. */
+ /* MacOS X */
+
+ acl_t acl;
+ int ret;
+
+ /* Remove the ACL if the file has ACLs. */
+ if (HAVE_ACL_GET_FD && desc != -1)
+ acl = acl_get_fd (desc);
+ else
+ acl = acl_get_file (name, ACL_TYPE_ACCESS);
+ if (acl)
+ {
+# if HAVE_ACL_COPY_EXT_NATIVE && HAVE_ACL_CREATE_ENTRY_NP /* MacOS X */
+ static const char empty_acl_text[] = "!#acl 1\n";
+# else /* Unknown flavor of POSIX-like ACLs */
+# error Unknown flavor of POSIX-like ACLs - add support for your platform.
+# endif
+
+ acl = acl_from_text (empty_acl_text);
+ if (acl)
+ {
+ if (HAVE_ACL_SET_FD && desc != -1)
+ ret = acl_set_fd (desc, acl);
+ else
+ ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
+ if (ret != 0)
+ {
+ int saved_errno = errno;
+
+ acl_free (acl);
+
+ if (ACL_NOT_WELL_SUPPORTED (saved_errno))
+ {
+ if (chmod_or_fchmod (name, desc, mode) != 0)
+ saved_errno = errno;
+ else
+ return 0;
+ }
+ errno = saved_errno;
+ return -1;
+ }
+ }
+ }
+
+ return chmod_or_fchmod (name, desc, mode);
+# else /* Unknown flavor of ACLs */
+ return chmod_or_fchmod (name, desc, mode);
+# endif
+# endif
+#else /* !USE_ACL */
+ return chmod_or_fchmod (name, desc, mode);
#endif
}
diff --git a/lib/copy-acl.c b/lib/copy-acl.c
index f5a879d..97d2087 100644
--- a/lib/copy-acl.c
+++ b/lib/copy-acl.c
@@ -40,6 +40,7 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name,
#if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
+ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
acl_t acl;
if (HAVE_ACL_GET_FD && source_desc != -1)
@@ -70,12 +71,13 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name,
int n = acl_entries (acl);
acl_free (acl);
- /* On most hosts an ACL is trivial if n == 3, and it cannot be
- less than 3. On IRIX 6.5 it is also trivial if n == -1.
+ /* On most hosts with MODE_INSIDE_ACL an ACL is trivial if n == 3,
+ and it cannot be less than 3. On IRIX 6.5 it is also trivial if
+ n == -1.
For simplicity and safety, assume the ACL is trivial if n <= 3.
Also see file-has-acl.c for some of the other possibilities;
it's not clear whether that complexity is needed here. */
- if (n <= 3)
+ if (n <= 3 * MODE_INSIDE_ACL)
{
if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
saved_errno = errno;
@@ -97,10 +99,10 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name,
else
acl_free (acl);
- 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. */
if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
{
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index c9786e6..c69d012 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -1,6 +1,6 @@
/* Test whether a file has a nontrivial access control list.
- Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2005-2008 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
@@ -47,6 +47,7 @@ file_has_acl (char const *name, struct stat const *sb)
#elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
+ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
int ret;
if (HAVE_ACL_EXTENDED_FILE)
@@ -56,7 +57,7 @@ file_has_acl (char const *name, struct stat const *sb)
acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
if (acl)
{
- ret = (3 < acl_entries (acl));
+ ret = (3 * MODE_INSIDE_ACL < acl_entries (acl));
acl_free (acl);
if (ret == 0 && S_ISDIR (sb->st_mode))
{
diff --git a/m4/acl.m4 b/m4/acl.m4
index 2916a00..72d4b4a 100644
--- a/m4/acl.m4
+++ b/m4/acl.m4
@@ -1,5 +1,5 @@
# acl.m4 - check for access control list (ACL) primitives
-# serial 3
+# serial 4
# Copyright (C) 2002, 2004-2008 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
@@ -35,7 +35,9 @@ AC_DEFUN([gl_FUNC_ACL],
AC_CHECK_FUNCS(
[acl_get_file acl_get_fd acl_set_file acl_set_fd \
acl_free acl_from_mode acl_from_text \
- acl_delete_def_file acl_extended_file])
+ acl_delete_def_file acl_extended_file \
+ acl_delete_fd_np acl_delete_file_np \
+ acl_copy_ext_native acl_create_entry_np])
if test $ac_cv_func_acl_get_file = yes; then
# If the acl_get_file bug is detected, disable all ACL support.
gl_ACL_GET_FILE( , [use_acl=0])
--
1.5.4.1