From b20b7b0b3ccf501dff204be860597d192d34b5b8 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 27 Apr 2013 17:39:07 -0700 Subject: [PATCH] qacl: new module, broken out from the acl module This is for GNU Emacs, which wants the acl functions but does not want 'error' invoked when they fail. * lib/acl-internal.h: Do not include error.h, quote.h. (ENOSYS, ENOTSUP): Remove; no longer needed. (ACL_NOT_WELL_SUPPORTED): Remove; replaced by acl_errno_valid. * lib/acl.h: Include . (acl_errno_valid): New function. * lib/copy-acl.c, lib/set-acl.c: Include errno,h, not acl-internal.h. * lib/copy-acl.c (qcopy_acl): Move to lib/qcopy-acl.c. * lib/set-acl.c: Rename from lib/set-mode-acl.c. (chmod_or_fchmod, qset_acl): Move to lib/qset-acl.c. (ACL_INTERNAL_INLINE): Remove; no longer needed. * lib/file-has-acl.c (file_has_acl): * lib/qcopy-acl.c (qcopy_acl): * lib/qset-acl.c (qset_acl): Use acl_errno_valid instead of ACL_NOT_WELL_SUPPORTED. * modules/acl (Files): Move lib/acl.h, lib/acl-internal.h, lib/acl_entries.c, lib/set-mode-acl.c (renamed to lib/set-acl.c), lib/file-has-acl.c, m4/acl.m4 to qacl module. Add lib/set-acl.c. (Depends-on): Move extern-inline, fstat, sys_stat to qacl module. Add qacl. (configure.ac): Move gl_FUNC_ACL to qacl module. (lib_SOURCES): Remove file-has-acl.c (moved to qacl module). Rename set-mode-acl.c to set-acl.c. * lib/acl-errno-valid.c: New file. * lib/qcopy-acl.c: New file, moved from the old lib/copy-acl.c; the copy_acl function remains in copy-acl.c. * lib/qcopy-acl.c, lib/qset-acl.c: Do not include gettext.h. (_): Remove; not needed. * lib/qset-acl.c: New file, moved from the old lib/set-mode-acl.c; the set_acl function remains in set-acl.c (renamed from set-mode-acl.c). * modules/qacl: New file, moved from the old modules/acl. (Files, lib_SOURCES): Add acl-errno-valid.c, qcopy-acl.c, qset-acl.c. Remove set-mode-acl.c, copy-acl.c. (Depends-on): Remove error, gettext-h, quote. Add stdbool. --- ChangeLog | 38 ++ lib/acl-errno-valid.c | 48 +++ lib/acl-internal.h | 22 -- lib/acl.h | 2 + lib/copy-acl.c | 562 +-------------------------- lib/file-has-acl.c | 2 +- lib/qcopy-acl.c | 583 +++++++++++++++++++++++++++++ lib/{set-mode-acl.c => qset-acl.c} | 41 +- lib/set-acl.c | 45 +++ modules/acl | 16 +- modules/qacl | 36 ++ 11 files changed, 767 insertions(+), 628 deletions(-) create mode 100644 lib/acl-errno-valid.c create mode 100644 lib/qcopy-acl.c rename lib/{set-mode-acl.c => qset-acl.c} (96%) create mode 100644 lib/set-acl.c create mode 100644 modules/qacl diff --git a/ChangeLog b/ChangeLog index eb12be04c4..bee658abce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,43 @@ 2013-04-27 Paul Eggert + qacl: new module, broken out from the acl module + This is for GNU Emacs, which wants the acl functions but does + not want 'error' invoked when they fail. + * lib/acl-internal.h: Do not include error.h, quote.h. + (ENOSYS, ENOTSUP): Remove; no longer needed. + (ACL_NOT_WELL_SUPPORTED): Remove; replaced by acl_errno_valid. + * lib/acl.h: Include . + (acl_errno_valid): New function. + * lib/copy-acl.c, lib/set-acl.c: Include errno,h, not acl-internal.h. + * lib/copy-acl.c (qcopy_acl): Move to lib/qcopy-acl.c. + * lib/set-acl.c: Rename from lib/set-mode-acl.c. + (chmod_or_fchmod, qset_acl): Move to lib/qset-acl.c. + (ACL_INTERNAL_INLINE): Remove; no longer needed. + * lib/file-has-acl.c (file_has_acl): + * lib/qcopy-acl.c (qcopy_acl): + * lib/qset-acl.c (qset_acl): + Use acl_errno_valid instead of ACL_NOT_WELL_SUPPORTED. + * modules/acl (Files): Move lib/acl.h, lib/acl-internal.h, + lib/acl_entries.c, lib/set-mode-acl.c (renamed to lib/set-acl.c), + lib/file-has-acl.c, m4/acl.m4 to qacl module. + Add lib/set-acl.c. + (Depends-on): Move extern-inline, fstat, sys_stat to qacl module. + Add qacl. + (configure.ac): Move gl_FUNC_ACL to qacl module. + (lib_SOURCES): Remove file-has-acl.c (moved to qacl module). + Rename set-mode-acl.c to set-acl.c. + * lib/acl-errno-valid.c: New file. + * lib/qcopy-acl.c: New file, moved from the old lib/copy-acl.c; the + copy_acl function remains in copy-acl.c. + * lib/qcopy-acl.c, lib/qset-acl.c: Do not include gettext.h. + (_): Remove; not needed. + * lib/qset-acl.c: New file, moved from the old lib/set-mode-acl.c; the + set_acl function remains in set-acl.c (renamed from set-mode-acl.c). + * modules/qacl: New file, moved from the old modules/acl. + (Files, lib_SOURCES): Add acl-errno-valid.c, qcopy-acl.c, qset-acl.c. + Remove set-mode-acl.c, copy-acl.c. + (Depends-on): Remove error, gettext-h, quote. Add stdbool. + alignof, intprops, malloca: port better to IBM's C compiler * lib/alignof.h (alignof_type) [__IBM_ALIGNOF__]: Use __alignof__. * lib/intprops.h (_GL_HAVE___TYPEOF__) [__IBM_TYPEOF__]: Now 1. diff --git a/lib/acl-errno-valid.c b/lib/acl-errno-valid.c new file mode 100644 index 0000000000..e2999c9fce --- /dev/null +++ b/lib/acl-errno-valid.c @@ -0,0 +1,48 @@ +/* Test whether ACLs are well supported on this system. + + Copyright 2013 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert. */ + +#include + +#include + +#include + +/* Return true if errno value ERRNUM indicates that ACLs are well + supported on this system. ERRNUM should be an errno value obtained + after an ACL-related system call fails. */ +bool +acl_errno_valid (int errnum) +{ + /* Recognize some common errors such as from an NFS mount that does + not support ACLs, even when local drives do. */ + switch (errnum) + { + case EBUSY: return false; + case EINVAL: return false; +#if defined __APPLE__ && defined __MACH__ + case ENOENT: return false; +#endif + case ENOSYS: return false; +#if defined ENOTSUP && ENOTSUP != EOPNOTSUPP + case ENOTSUP: return false; +#endif + case EOPNOTSUPP: return false; + default: return true; + } +} diff --git a/lib/acl-internal.h b/lib/acl-internal.h index 37c6ebfda4..7e6d77a5fd 100644 --- a/lib/acl-internal.h +++ b/lib/acl-internal.h @@ -44,16 +44,7 @@ extern int acl (char *, int, int, struct acl *); extern int aclsort (int, int, struct acl *); #endif -#include "error.h" -#include "quote.h" - #include -#ifndef ENOSYS -# define ENOSYS (-1) -#endif -#ifndef ENOTSUP -# define ENOTSUP (-1) -#endif #include #ifndef MIN @@ -69,19 +60,6 @@ extern int aclsort (int, int, struct acl *); # define fchmod(fd, mode) (-1) #endif -/* Recognize some common errors such as from an NFS mount that does - not support ACLs, even when local drives do. */ -#if defined __APPLE__ && defined __MACH__ /* Mac OS X */ -# define ACL_NOT_WELL_SUPPORTED(Err) \ - ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == ENOENT) -#elif defined EOPNOTSUPP /* Tru64 NFS */ -# define ACL_NOT_WELL_SUPPORTED(Err) \ - ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == EOPNOTSUPP) -#else -# define ACL_NOT_WELL_SUPPORTED(Err) \ - ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY) -#endif - _GL_INLINE_HEADER_BEGIN #ifndef ACL_INTERNAL_INLINE # define ACL_INTERNAL_INLINE _GL_INLINE diff --git a/lib/acl.h b/lib/acl.h index 9471f9940d..056bf7f30f 100644 --- a/lib/acl.h +++ b/lib/acl.h @@ -17,9 +17,11 @@ Written by Paul Eggert. */ +#include #include #include +bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST; int file_has_acl (char const *, struct stat const *); int qset_acl (char const *, int, mode_t); int set_acl (char const *, int, mode_t); diff --git a/lib/copy-acl.c b/lib/copy-acl.c index 58ff54aec7..bcc86b684e 100644 --- a/lib/copy-acl.c +++ b/lib/copy-acl.c @@ -21,571 +21,11 @@ #include "acl.h" -#include "acl-internal.h" - +#include "error.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Copy access control lists from one file to another. If SOURCE_DESC is - a valid file descriptor, use file descriptor operations, else use - filename based operations on SRC_NAME. Likewise for DEST_DESC and - DST_NAME. - If access control lists are not available, fchmod the target file to - MODE. Also sets the non-permission bits of the destination file - (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. - Return 0 if successful. - Return -2 and set errno for an error relating to the source file. - Return -1 and set errno for an error relating to the destination file. */ - -int -qcopy_acl (const char *src_name, int source_desc, const char *dst_name, - int dest_desc, mode_t mode) -{ -#if USE_ACL && HAVE_ACL_GET_FILE - /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ - /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ -# if !HAVE_ACL_TYPE_EXTENDED - /* Linux, FreeBSD, IRIX, Tru64 */ - - 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_ACCESS); - 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_ACCESS, acl); - if (ret != 0) - { - int saved_errno = errno; - - if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl)) - { - 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); - - if (!MODE_INSIDE_ACL || (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. */ - - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - return -1; - } - - if (S_ISDIR (mode)) - { - acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); - if (acl == NULL) - return -2; - - if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) - { - int saved_errno = errno; - - acl_free (acl); - errno = saved_errno; - return -1; - } - else - acl_free (acl); - } - return 0; - -# else /* HAVE_ACL_TYPE_EXTENDED */ - /* Mac OS X */ - - /* On Mac OS 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_extended_nontrivial (acl)) - { - 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 GETACL /* Solaris, Cygwin, not HP-UX */ - - /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions - of Unixware. The acl() call returns the access and default ACL both - at once. */ -# ifdef ACE_GETACL - int ace_count; - ace_t *ace_entries; -# endif - int count; - aclent_t *entries; - int did_chmod; - int saved_errno; - int ret; - -# ifdef ACE_GETACL - /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 - file systems (whereas the other ones are used in UFS file systems). - There is an API - pathconf (name, _PC_ACL_ENABLED) - fpathconf (desc, _PC_ACL_ENABLED) - that allows to determine which of the two kinds of ACLs is supported - for the given file. But some file systems may implement this call - incorrectly, so better not use it. - When fetching the source ACL, we simply fetch both ACL types. - When setting the destination ACL, we try either ACL types, assuming - that the kernel will translate the ACL from one form to the other. - (See in - the description of ENOTSUP.) */ - for (;;) - { - ace_count = (source_desc != -1 - ? facl (source_desc, ACE_GETACLCNT, 0, NULL) - : acl (src_name, ACE_GETACLCNT, 0, NULL)); - - if (ace_count < 0) - { - if (errno == ENOSYS || errno == EINVAL) - { - ace_count = 0; - ace_entries = NULL; - break; - } - else - return -2; - } - - if (ace_count == 0) - { - ace_entries = NULL; - break; - } - - ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t)); - if (ace_entries == NULL) - { - errno = ENOMEM; - return -2; - } - - ret = (source_desc != -1 - ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) - : acl (src_name, ACE_GETACL, ace_count, ace_entries)); - if (ret < 0) - { - free (ace_entries); - if (errno == ENOSYS || errno == EINVAL) - { - ace_count = 0; - ace_entries = NULL; - break; - } - else - return -2; - } - if (ret == ace_count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ - } -# endif - - for (;;) - { - count = (source_desc != -1 - ? facl (source_desc, GETACLCNT, 0, NULL) - : acl (src_name, GETACLCNT, 0, NULL)); - - if (count < 0) - { - if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) - { - count = 0; - entries = NULL; - break; - } - else - return -2; - } - - if (count == 0) - { - entries = NULL; - break; - } - - entries = (aclent_t *) malloc (count * sizeof (aclent_t)); - if (entries == NULL) - { - errno = ENOMEM; - return -2; - } - - if ((source_desc != -1 - ? facl (source_desc, GETACL, count, entries) - : acl (src_name, GETACL, count, entries)) - == count) - break; - /* Huh? The number of ACL entries changed since the last call. - Repeat. */ - } - - /* Is there an ACL of either kind? */ -# ifdef ACE_GETACL - if (ace_count == 0) -# endif - if (count == 0) - return qset_acl (dst_name, dest_desc, mode); - - did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ - saved_errno = 0; /* the first non-ignorable error code */ - - if (!MODE_INSIDE_ACL) - { - /* On Cygwin, it is necessary to call chmod before acl, because - chmod can change the contents of the ACL (in ways that don't - change the allowed accesses, but still visible). */ - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - saved_errno = errno; - did_chmod = 1; - } - - /* If both ace_entries and entries are available, try SETACL before - ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL - can. */ - - if (count > 0) - { - ret = (dest_desc != -1 - ? facl (dest_desc, SETACL, count, entries) - : acl (dst_name, SETACL, count, entries)); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - && !acl_nontrivial (count, entries)) - saved_errno = 0; - } - else - did_chmod = 1; - } - free (entries); - -# ifdef ACE_GETACL - if (ace_count > 0) - { - ret = (dest_desc != -1 - ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries) - : acl (dst_name, ACE_SETACL, ace_count, ace_entries)); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) - && !acl_ace_nontrivial (ace_count, ace_entries)) - saved_errno = 0; - } - } - free (ace_entries); -# endif - - if (MODE_INSIDE_ACL - && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) - { - /* 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) - { - if (saved_errno == 0) - saved_errno = errno; - } - } - - if (saved_errno) - { - errno = saved_errno; - return -1; - } - return 0; - -#elif USE_ACL && HAVE_GETACL /* HP-UX */ - - struct acl_entry entries[NACLENTRIES]; - int count; -# if HAVE_ACLV_H - struct acl aclv_entries[NACLVENTRIES]; - int aclv_count; -# endif - int did_chmod; - int saved_errno; - int ret; - - count = (source_desc != -1 - ? fgetacl (source_desc, NACLENTRIES, entries) - : getacl (src_name, NACLENTRIES, entries)); - - if (count < 0) - { - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) - count = 0; - else - return -2; - } - else if (count > 0) - { - if (count > NACLENTRIES) - /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ - abort (); - } - -# if HAVE_ACLV_H - aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries); - - if (aclv_count < 0) - { - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - count = 0; - else - return -2; - } - else if (aclv_count > 0) - { - if (aclv_count > NACLVENTRIES) - /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ - abort (); - } -# endif - - if (count == 0) -# if HAVE_ACLV_H - if (aclv_count == 0) -# endif - return qset_acl (dst_name, dest_desc, mode); - - did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ - saved_errno = 0; /* the first non-ignorable error code */ - - if (count > 0) - { - ret = (dest_desc != -1 - ? fsetacl (dest_desc, count, entries) - : setacl (dst_name, count, entries)); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) - { - struct stat source_statbuf; - - if ((source_desc != -1 - ? fstat (source_desc, &source_statbuf) - : stat (src_name, &source_statbuf)) == 0) - { - if (!acl_nontrivial (count, entries, &source_statbuf)) - saved_errno = 0; - } - else - saved_errno = errno; - } - } - else - did_chmod = 1; - } - -# if HAVE_ACLV_H - if (aclv_count > 0) - { - ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries); - if (ret < 0 && saved_errno == 0) - { - saved_errno = errno; - if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) - { - if (!aclv_nontrivial (aclv_count, aclv_entries)) - saved_errno = 0; - } - } - else - did_chmod = 1; - } -# endif - - if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) - { - /* We did not call chmod so far, and special bits are to be set which - don't fit into ACLs. */ - - if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) - { - if (saved_errno == 0) - saved_errno = errno; - } - } - - if (saved_errno) - { - errno = saved_errno; - return -1; - } - return 0; - -#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */ - - /* TODO */ - -#elif USE_ACL && HAVE_STATACL /* older AIX */ - - union { struct acl a; char room[4096]; } u; - int ret; - - if ((source_desc != -1 - ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u)) - : statacl (src_name, STX_NORMAL, &u.a, sizeof (u))) - < 0) - return -2; - - ret = (dest_desc != -1 - ? fchacl (dest_desc, &u.a, u.a.acl_len) - : chacl (dst_name, &u.a, u.a.acl_len)); - if (ret < 0) - { - int saved_errno = errno; - - chmod_or_fchmod (dst_name, dest_desc, mode); - errno = saved_errno; - return -1; - } - - /* No need to call chmod_or_fchmod at this point, since the mode bits - S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ - - return 0; - -#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ - - struct acl entries[NACLENTRIES]; - int count; - int ret; - - count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries); - - if (count < 0) - { - if (0) - count = 0; - else - return -2; - } - else if (count > 0) - { - if (count > NACLENTRIES) - /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ - abort (); - } - - if (count == 0) - return qset_acl (dst_name, dest_desc, mode); - - ret = acl ((char *) dst_name, ACL_SET, count, entries); - if (ret < 0) - { - int saved_errno = errno; - - if (0) - { - if (!acl_nontrivial (count, entries)) - return chmod_or_fchmod (dst_name, dest_desc, mode); - } - - chmod_or_fchmod (dst_name, dest_desc, mode); - errno = saved_errno; - return -1; - } - - 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. */ - - return chmod_or_fchmod (dst_name, dest_desc, mode); - } - return 0; - -#else - - return qset_acl (dst_name, dest_desc, mode); - -#endif -} - - /* Copy access control lists from one file to another. If SOURCE_DESC is a valid file descriptor, use file descriptor operations, else use filename based operations on SRC_NAME. Likewise for DEST_DESC and diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index 2fedcc6340..bb8bae1286 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -553,7 +553,7 @@ file_has_acl (char const *name, struct stat const *sb) # endif } if (ret < 0) - return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1; + return - acl_errno_valid (errno); return ret; # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c new file mode 100644 index 0000000000..25a2ff154d --- /dev/null +++ b/lib/qcopy-acl.c @@ -0,0 +1,583 @@ +/* copy-acl.c - copy access control list from one file to another file + + Copyright (C) 2002-2003, 2005-2013 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ + +#include + +#include "acl.h" + +#include "acl-internal.h" + + +/* Copy access control lists from one file to another. If SOURCE_DESC is + a valid file descriptor, use file descriptor operations, else use + filename based operations on SRC_NAME. Likewise for DEST_DESC and + DST_NAME. + If access control lists are not available, fchmod the target file to + MODE. Also sets the non-permission bits of the destination file + (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. + Return 0 if successful. + Return -2 and set errno for an error relating to the source file. + Return -1 and set errno for an error relating to the destination file. */ + +int +qcopy_acl (const char *src_name, int source_desc, const char *dst_name, + int dest_desc, mode_t mode) +{ +#if USE_ACL && HAVE_ACL_GET_FILE + /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ + /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ +# if !HAVE_ACL_TYPE_EXTENDED + /* Linux, FreeBSD, IRIX, Tru64 */ + + 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_ACCESS); + if (acl == NULL) + { + if (! acl_errno_valid (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_ACCESS, acl); + if (ret != 0) + { + int saved_errno = errno; + + if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl)) + { + 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); + + if (!MODE_INSIDE_ACL || (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. */ + + if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) + return -1; + } + + if (S_ISDIR (mode)) + { + acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); + if (acl == NULL) + return -2; + + if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) + { + int saved_errno = errno; + + acl_free (acl); + errno = saved_errno; + return -1; + } + else + acl_free (acl); + } + return 0; + +# else /* HAVE_ACL_TYPE_EXTENDED */ + /* Mac OS X */ + + /* On Mac OS 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_errno_valid (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_errno_valid (saved_errno) && !acl_extended_nontrivial (acl)) + { + 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 GETACL /* Solaris, Cygwin, not HP-UX */ + + /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions + of Unixware. The acl() call returns the access and default ACL both + at once. */ +# ifdef ACE_GETACL + int ace_count; + ace_t *ace_entries; +# endif + int count; + aclent_t *entries; + int did_chmod; + int saved_errno; + int ret; + +# ifdef ACE_GETACL + /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 + file systems (whereas the other ones are used in UFS file systems). + There is an API + pathconf (name, _PC_ACL_ENABLED) + fpathconf (desc, _PC_ACL_ENABLED) + that allows to determine which of the two kinds of ACLs is supported + for the given file. But some file systems may implement this call + incorrectly, so better not use it. + When fetching the source ACL, we simply fetch both ACL types. + When setting the destination ACL, we try either ACL types, assuming + that the kernel will translate the ACL from one form to the other. + (See in + the description of ENOTSUP.) */ + for (;;) + { + ace_count = (source_desc != -1 + ? facl (source_desc, ACE_GETACLCNT, 0, NULL) + : acl (src_name, ACE_GETACLCNT, 0, NULL)); + + if (ace_count < 0) + { + if (errno == ENOSYS || errno == EINVAL) + { + ace_count = 0; + ace_entries = NULL; + break; + } + else + return -2; + } + + if (ace_count == 0) + { + ace_entries = NULL; + break; + } + + ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t)); + if (ace_entries == NULL) + { + errno = ENOMEM; + return -2; + } + + ret = (source_desc != -1 + ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) + : acl (src_name, ACE_GETACL, ace_count, ace_entries)); + if (ret < 0) + { + free (ace_entries); + if (errno == ENOSYS || errno == EINVAL) + { + ace_count = 0; + ace_entries = NULL; + break; + } + else + return -2; + } + if (ret == ace_count) + break; + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + } +# endif + + for (;;) + { + count = (source_desc != -1 + ? facl (source_desc, GETACLCNT, 0, NULL) + : acl (src_name, GETACLCNT, 0, NULL)); + + if (count < 0) + { + if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) + { + count = 0; + entries = NULL; + break; + } + else + return -2; + } + + if (count == 0) + { + entries = NULL; + break; + } + + entries = (aclent_t *) malloc (count * sizeof (aclent_t)); + if (entries == NULL) + { + errno = ENOMEM; + return -2; + } + + if ((source_desc != -1 + ? facl (source_desc, GETACL, count, entries) + : acl (src_name, GETACL, count, entries)) + == count) + break; + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + } + + /* Is there an ACL of either kind? */ +# ifdef ACE_GETACL + if (ace_count == 0) +# endif + if (count == 0) + return qset_acl (dst_name, dest_desc, mode); + + did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ + saved_errno = 0; /* the first non-ignorable error code */ + + if (!MODE_INSIDE_ACL) + { + /* On Cygwin, it is necessary to call chmod before acl, because + chmod can change the contents of the ACL (in ways that don't + change the allowed accesses, but still visible). */ + if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) + saved_errno = errno; + did_chmod = 1; + } + + /* If both ace_entries and entries are available, try SETACL before + ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL + can. */ + + if (count > 0) + { + ret = (dest_desc != -1 + ? facl (dest_desc, SETACL, count, entries) + : acl (dst_name, SETACL, count, entries)); + if (ret < 0 && saved_errno == 0) + { + saved_errno = errno; + if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + && !acl_nontrivial (count, entries)) + saved_errno = 0; + } + else + did_chmod = 1; + } + free (entries); + +# ifdef ACE_GETACL + if (ace_count > 0) + { + ret = (dest_desc != -1 + ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries) + : acl (dst_name, ACE_SETACL, ace_count, ace_entries)); + if (ret < 0 && saved_errno == 0) + { + saved_errno = errno; + if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) + && !acl_ace_nontrivial (ace_count, ace_entries)) + saved_errno = 0; + } + } + free (ace_entries); +# endif + + if (MODE_INSIDE_ACL + && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) + { + /* 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) + { + if (saved_errno == 0) + saved_errno = errno; + } + } + + if (saved_errno) + { + errno = saved_errno; + return -1; + } + return 0; + +#elif USE_ACL && HAVE_GETACL /* HP-UX */ + + struct acl_entry entries[NACLENTRIES]; + int count; +# if HAVE_ACLV_H + struct acl aclv_entries[NACLVENTRIES]; + int aclv_count; +# endif + int did_chmod; + int saved_errno; + int ret; + + count = (source_desc != -1 + ? fgetacl (source_desc, NACLENTRIES, entries) + : getacl (src_name, NACLENTRIES, entries)); + + if (count < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) + count = 0; + else + return -2; + } + else if (count > 0) + { + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + } + +# if HAVE_ACLV_H + aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries); + + if (aclv_count < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + count = 0; + else + return -2; + } + else if (aclv_count > 0) + { + if (aclv_count > NACLVENTRIES) + /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + } +# endif + + if (count == 0) +# if HAVE_ACLV_H + if (aclv_count == 0) +# endif + return qset_acl (dst_name, dest_desc, mode); + + did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ + saved_errno = 0; /* the first non-ignorable error code */ + + if (count > 0) + { + ret = (dest_desc != -1 + ? fsetacl (dest_desc, count, entries) + : setacl (dst_name, count, entries)); + if (ret < 0 && saved_errno == 0) + { + saved_errno = errno; + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) + { + struct stat source_statbuf; + + if ((source_desc != -1 + ? fstat (source_desc, &source_statbuf) + : stat (src_name, &source_statbuf)) == 0) + { + if (!acl_nontrivial (count, entries, &source_statbuf)) + saved_errno = 0; + } + else + saved_errno = errno; + } + } + else + did_chmod = 1; + } + +# if HAVE_ACLV_H + if (aclv_count > 0) + { + ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries); + if (ret < 0 && saved_errno == 0) + { + saved_errno = errno; + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + { + if (!aclv_nontrivial (aclv_count, aclv_entries)) + saved_errno = 0; + } + } + else + did_chmod = 1; + } +# endif + + if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) + { + /* We did not call chmod so far, and special bits are to be set which + don't fit into ACLs. */ + + if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) + { + if (saved_errno == 0) + saved_errno = errno; + } + } + + if (saved_errno) + { + errno = saved_errno; + return -1; + } + return 0; + +#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */ + + /* TODO */ + +#elif USE_ACL && HAVE_STATACL /* older AIX */ + + union { struct acl a; char room[4096]; } u; + int ret; + + if ((source_desc != -1 + ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u)) + : statacl (src_name, STX_NORMAL, &u.a, sizeof (u))) + < 0) + return -2; + + ret = (dest_desc != -1 + ? fchacl (dest_desc, &u.a, u.a.acl_len) + : chacl (dst_name, &u.a, u.a.acl_len)); + if (ret < 0) + { + int saved_errno = errno; + + chmod_or_fchmod (dst_name, dest_desc, mode); + errno = saved_errno; + return -1; + } + + /* No need to call chmod_or_fchmod at this point, since the mode bits + S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ + + return 0; + +#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ + + struct acl entries[NACLENTRIES]; + int count; + int ret; + + count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries); + + if (count < 0) + { + if (0) + count = 0; + else + return -2; + } + else if (count > 0) + { + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + } + + if (count == 0) + return qset_acl (dst_name, dest_desc, mode); + + ret = acl ((char *) dst_name, ACL_SET, count, entries); + if (ret < 0) + { + int saved_errno = errno; + + if (0) + { + if (!acl_nontrivial (count, entries)) + return chmod_or_fchmod (dst_name, dest_desc, mode); + } + + chmod_or_fchmod (dst_name, dest_desc, mode); + errno = saved_errno; + return -1; + } + + 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. */ + + return chmod_or_fchmod (dst_name, dest_desc, mode); + } + return 0; + +#else + + return qset_acl (dst_name, dest_desc, mode); + +#endif +} diff --git a/lib/set-mode-acl.c b/lib/qset-acl.c similarity index 96% rename from lib/set-mode-acl.c rename to lib/qset-acl.c index 35fe2a8193..7bde2c15b0 100644 --- a/lib/set-mode-acl.c +++ b/lib/qset-acl.c @@ -1,4 +1,4 @@ -/* set-mode-acl.c - set access control list equivalent to a mode +/* qset-acl.c - set access control list equivalent to a mode Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc. @@ -25,13 +25,10 @@ #include "acl-internal.h" -#include "gettext.h" -#define _(msgid) gettext (msgid) - /* If DESC is a valid file descriptor use fchmod to change the - file's mode to MODE on systems that have fchown. On systems - that don't have fchown and if DESC is invalid, use chown on + file's mode to MODE on systems that have fchmod. On systems + that don't have fchmod and if DESC is invalid, use chmod on NAME instead. Return 0 if successful. Return -1 and set errno upon failure. */ @@ -119,14 +116,10 @@ qset_acl (char const *name, int desc, mode_t mode) { int saved_errno = errno; acl_free (acl); - - if (ACL_NOT_WELL_SUPPORTED (errno)) + if (! acl_errno_valid (errno)) return chmod_or_fchmod (name, desc, mode); - else - { - errno = saved_errno; - return -1; - } + errno = saved_errno; + return -1; } else acl_free (acl); @@ -180,16 +173,11 @@ qset_acl (char const *name, int desc, mode_t mode) if (ret != 0) { int saved_errno = errno; - acl_free (acl); - - if (ACL_NOT_WELL_SUPPORTED (saved_errno)) + if (! acl_errno_valid (saved_errno)) return chmod_or_fchmod (name, desc, mode); - else - { - errno = saved_errno; - return -1; - } + errno = saved_errno; + return -1; } acl_free (acl); } @@ -686,14 +674,3 @@ qset_acl (char const *name, int desc, mode_t mode) return chmod_or_fchmod (name, desc, mode); #endif } - -/* As with qset_acl, but also output a diagnostic on failure. */ - -int -set_acl (char const *name, int desc, mode_t mode) -{ - int ret = qset_acl (name, desc, mode); - if (ret != 0) - error (0, errno, _("setting permissions for %s"), quote (name)); - return ret; -} diff --git a/lib/set-acl.c b/lib/set-acl.c new file mode 100644 index 0000000000..f41ad82429 --- /dev/null +++ b/lib/set-acl.c @@ -0,0 +1,45 @@ +/* set-acl.c - set access control list equivalent to a mode + + Copyright (C) 2002-2003, 2005-2013 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */ + +#include + +#include "acl.h" + +#include "error.h" +#include "gettext.h" +#define _(msgid) gettext (msgid) + + +/* Set the access control lists of a file. If DESC is a valid file + descriptor, use file descriptor operations where available, else use + filename based operations on NAME. If access control lists are not + available, fchmod the target file to MODE. Also sets the + non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX) + to those from MODE if any are set. + Return 0 if successful. On failure, output a diagnostic, set errno and + return -1. */ + +int +set_acl (char const *name, int desc, mode_t mode) +{ + int ret = qset_acl (name, desc, mode); + if (ret != 0) + error (0, errno, _("setting permissions for %s"), quote (name)); + return ret; +} diff --git a/modules/acl b/modules/acl index 73d2f3ed5c..8ed7693c17 100644 --- a/modules/acl +++ b/modules/acl @@ -1,28 +1,20 @@ Description: -Access control lists of files. (Unportable.) +Access control lists of files, with diagnostics. (Unportable.) Files: -lib/acl.h -lib/acl-internal.h -lib/acl_entries.c -lib/set-mode-acl.c lib/copy-acl.c -lib/file-has-acl.c -m4/acl.m4 +lib/set-acl.c Depends-on: error -extern-inline -fstat gettext-h +qacl quote -sys_stat configure.ac: -gl_FUNC_ACL Makefile.am: -lib_SOURCES += set-mode-acl.c copy-acl.c file-has-acl.c +lib_SOURCES += copy-acl.c set-acl.c Include: "acl.h" diff --git a/modules/qacl b/modules/qacl new file mode 100644 index 0000000000..b6bada5569 --- /dev/null +++ b/modules/qacl @@ -0,0 +1,36 @@ +Description: +Access control lists of files. (Unportable.) + +Files: +lib/acl.h +lib/acl-internal.h +lib/acl-errno-valid.c +lib/acl_entries.c +lib/file-has-acl.c +lib/qcopy-acl.c +lib/qset-acl.c +m4/acl.m4 + +Depends-on: +extern-inline +fstat +stdbool +sys_stat + +configure.ac: +gl_FUNC_ACL + +Makefile.am: +lib_SOURCES += acl-errno-valid.c file-has-acl.c qcopy-acl.c qset-acl.c + +Include: +"acl.h" + +Link: +$(LIB_ACL) + +License: +GPL + +Maintainer: +Paul Eggert, Jim Meyering, Bruno Haible -- 2.39.5