From 465b7e91c204e559dff926acbe9bbef0e4f81fbb Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 4 Jun 2008 22:48:48 +0200 Subject: [PATCH] New function for testing triviality of ACL with POSIX-draft like API. --- ChangeLog | 7 ++++ lib/acl-internal.h | 6 ++++ lib/copy-acl.c | 10 ++---- lib/file-has-acl.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 108 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 19792e695..9260f7588 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2008-06-04 Bruno Haible + + * lib/acl-internal.h (acl_access_nontrivial): New declaration. + * lib/file-has-acl.c (acl_access_nontrivial): New function. + (file_has_acl): Use it. Save errno afterwards. + * lib/copy-acl.c (qcopy_acl): Use acl_access_nontrivial. + 2008-06-03 Bruno Haible * lib/file-has-acl.c (file_has_acl): Put Solaris 10 code after POSIX- diff --git a/lib/acl-internal.h b/lib/acl-internal.h index 478fafcb5..664cd5bc9 100644 --- a/lib/acl-internal.h +++ b/lib/acl-internal.h @@ -145,6 +145,12 @@ rpl_acl_set_fd (int fd, acl_t acl) extern int acl_entries (acl_t); # endif +/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS. + Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. + Return -1 and set errno upon failure to determine it. */ +extern int acl_access_nontrivial (acl_t); + # endif #endif diff --git a/lib/copy-acl.c b/lib/copy-acl.c index eefbff768..743f6ec7c 100644 --- a/lib/copy-acl.c +++ b/lib/copy-acl.c @@ -68,15 +68,11 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, if (ACL_NOT_WELL_SUPPORTED (errno)) { - int n = acl_entries (acl); + int nontrivial = acl_access_nontrivial (acl); acl_free (acl); - /* On most hosts with MODE_INSIDE_ACL an ACL is trivial if n == 3, - and it cannot be less than 3. - 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 * MODE_INSIDE_ACL) + + if (!nontrivial) { if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) saved_errno = errno; diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index 977708963..5b1e8ff23 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -23,6 +23,93 @@ #include "acl-internal.h" + +#if USE_ACL && HAVE_ACL_GET_FILE + +/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS. + Return 1 if the given ACL is non-trivial. + Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. + Return -1 and set errno upon failure to determine it. */ +int +acl_access_nontrivial (acl_t acl) +{ +# if MODE_INSIDE_ACL /* Linux, FreeBSD, IRIX, Tru64 */ + /* acl is non-trivial if it has some entries other than for "user::", + "group::", and "other::". Normally these three should be present + at least, allowing us to write + return (3 < acl_entries (acl)); + but the following code is more robust. */ +# if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD */ + + acl_entry_t ace; + int at_end; + + for (at_end = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace); + !at_end; + at_end = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace)) + { + acl_tag_t tag; + if (acl_get_tag_type (ace, &tag) < 0) + return -1; + if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) + return 1; + } + return 0; + +# else /* IRIX, Tru64 */ +# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */ + /* Don't use acl_get_entry: it is undocumented. */ + + int count = acl->acl_cnt; + int i; + + for (i = 0; i < count; i++) + { + acl_entry_t ace = &acl->acl_entry[i]; + acl_tag_t tag = ace->ae_tag; + + if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ + || tag == ACL_OTHER_OBJ)) + return 1; + } + return 0; + +# endif +# if HAVE_ACL_FREE_TEXT /* Tru64 */ + /* Don't use acl_get_entry: it takes only one argument and does not work. */ + + int count = acl->acl_num; + acl_entry_t ace; + + for (ace = acl->acl_first; count > 0; ace = ace->next, count--) + { + acl_tag_t tag; + acl_perm_t perm; + + tag = ace->entry->acl_type; + if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) + return 1; + + perm = ace->entry->acl_perm; + /* On Tru64, perm can also contain non-standard bits such as + PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ... */ + if ((perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)) != 0) + return 1; + } + return 0; + +# endif +# endif +# else /* MacOS X */ + + /* acl is non-trivial if it is non-empty. */ + return (acl_entries (acl) > 0); +# endif +} + +#endif + + /* Return 1 if NAME has a nontrivial access control list, 0 if NAME only has no or a base access control list, and -1 (setting errno) on error. SB must be set to the stat buffer of FILE. */ @@ -46,8 +133,12 @@ file_has_acl (char const *name, struct stat const *sb) acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); if (acl) { - ret = (3 * MODE_INSIDE_ACL < acl_entries (acl)); + int saved_errno; + + ret = acl_access_nontrivial (acl); + saved_errno = errno; acl_free (acl); + errno = saved_errno; if (ret == 0 && S_ISDIR (sb->st_mode)) { acl = acl_get_file (name, ACL_TYPE_DEFAULT); -- 2.11.0