X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fcopy-acl.c;h=4f2ea06a6fffb7f14a786d8974cc0ccdbcbe3a14;hb=1a6fbdd7d28dff1868c5eb0baf0029b27e42526a;hp=87952f4086eaa3a0f2d0efff8b5eaf5b5f53d090;hpb=c9c845c2a4a217b2b1a5c41c3a665e75032f92c9;p=gnulib.git diff --git a/lib/copy-acl.c b/lib/copy-acl.c index 87952f408..4f2ea06a6 100644 --- a/lib/copy-acl.c +++ b/lib/copy-acl.c @@ -1,6 +1,6 @@ /* copy-acl.c - copy access control list from one file to another file - Copyright (C) 2002-2003, 2005-2008 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2005-2011 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 @@ -15,7 +15,7 @@ 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. */ + Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ #include @@ -23,6 +23,9 @@ #include "acl-internal.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 @@ -37,7 +40,7 @@ static int qcopy_acl (const char *src_name, int source_desc, const char *dst_name, - int dest_desc, mode_t mode) + int dest_desc, mode_t mode) { #if USE_ACL && HAVE_ACL_GET_FILE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ @@ -55,7 +58,7 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, if (acl == NULL) { if (ACL_NOT_WELL_SUPPORTED (errno)) - return qset_acl (dst_name, dest_desc, mode); + return qset_acl (dst_name, dest_desc, mode); else return -2; } @@ -70,16 +73,16 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl)) { - acl_free (acl); - return chmod_or_fchmod (dst_name, dest_desc, mode); - } + 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; - } + { + acl_free (acl); + chmod_or_fchmod (dst_name, dest_desc, mode); + errno = saved_errno; + return -1; + } } else acl_free (acl); @@ -87,26 +90,26 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, 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. */ + 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; + return -1; } if (S_ISDIR (mode)) { acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); if (acl == NULL) - return -2; + return -2; if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) - { - int saved_errno = errno; + { + int saved_errno = errno; - acl_free (acl); - errno = saved_errno; - return -1; - } + acl_free (acl); + errno = saved_errno; + return -1; + } else acl_free (acl); } @@ -122,14 +125,14 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, /* 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) + 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) + 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): + acl_set_file (name, ACL_TYPE_EXTENDED, acl): Each of these calls sets the file's ACL. */ acl_t acl; @@ -142,7 +145,7 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, if (acl == NULL) { if (ACL_NOT_WELL_SUPPORTED (errno)) - return qset_acl (dst_name, dest_desc, mode); + return qset_acl (dst_name, dest_desc, mode); else return -2; } @@ -155,18 +158,18 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, { int saved_errno = errno; - if (ACL_NOT_WELL_SUPPORTED (errno) && !(acl_entries (acl) > 0)) + if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl)) { - acl_free (acl); - return chmod_or_fchmod (dst_name, dest_desc, mode); - } + 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; - } + { + acl_free (acl); + chmod_or_fchmod (dst_name, dest_desc, mode); + errno = saved_errno; + return -1; + } } else acl_free (acl); @@ -176,14 +179,18 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, # endif -#elif USE_ACL && defined ACL_NO_TRIVIAL - /* Solaris 10 NFSv4 ACLs. */ +#elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ + +# if defined ACL_NO_TRIVIAL + /* Solaris 10 (newer version), which has additional API declared in + (acl_t) and implemented in libsec (acl_set, acl_trivial, + acl_fromtext, ...). */ int ret; acl_t *aclp = NULL; ret = (source_desc < 0 - ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp) - : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp)); + ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp) + : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp)); if (ret != 0 && errno != ENOSYS) return -2; @@ -194,21 +201,454 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, if (aclp) { ret = (dest_desc < 0 - ? acl_set (dst_name, aclp) - : facl_set (dest_desc, aclp)); + ? acl_set (dst_name, aclp) + : facl_set (dest_desc, aclp)); if (ret != 0) - { - int saved_errno = errno; + { + int saved_errno = errno; - acl_free (aclp); - errno = saved_errno; - return -1; - } + acl_free (aclp); + errno = saved_errno; + return -1; + } acl_free (aclp); } return 0; +# else /* Solaris, Cygwin, general case */ + + /* 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; + } + + if ((source_desc != -1 + ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) + : acl (src_name, ACE_GETACL, ace_count, ace_entries)) + == 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; + +# endif + +#elif USE_ACL && HAVE_GETACL /* HP-UX */ + + int count; + struct acl_entry entries[NACLENTRIES]; +# if HAVE_ACLV_H + int aclv_count; + struct acl aclv_entries[NACLVENTRIES]; +# endif + int did_chmod; + int saved_errno; + int ret; + + for (;;) + { + count = (source_desc != -1 + ? fgetacl (source_desc, 0, NULL) + : getacl (src_name, 0, NULL)); + + if (count < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) + { + count = 0; + break; + } + else + return -2; + } + + if (count == 0) + break; + + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + + if ((source_desc != -1 + ? fgetacl (source_desc, count, entries) + : getacl (src_name, count, entries)) + == count) + break; + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + } + +# if HAVE_ACLV_H + for (;;) + { + aclv_count = acl ((char *) src_name, ACL_CNT, NACLVENTRIES, aclv_entries); + + if (aclv_count < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) + { + count = 0; + break; + } + else + return -2; + } + + if (aclv_count == 0) + break; + + if (aclv_count > NACLVENTRIES) + /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + + if (acl ((char *) src_name, ACL_GET, aclv_count, aclv_entries) + == aclv_count) + break; + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + } +# 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 */ + + int count; + struct acl entries[NACLENTRIES]; + int ret; + + for (;;) + { + count = acl ((char *) src_name, ACL_CNT, NACLENTRIES, NULL); + + if (count < 0) + { + if (0) + { + count = 0; + break; + } + else + return -2; + } + + if (count == 0) + break; + + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ + abort (); + + if (acl ((char *) src_name, ACL_GET, count, entries) == count) + break; + /* Huh? The number of ACL entries changed since the last call. + Repeat. */ + } + + 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); @@ -228,7 +668,7 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name, int copy_acl (const char *src_name, int source_desc, const char *dst_name, - int dest_desc, mode_t mode) + int dest_desc, mode_t mode) { int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode); switch (ret)