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