X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Facl.c;h=b40e43a174c7f3c6b046a5b0feb2db8f4eca2303;hb=b7285ef59198ab3a562df0c91c6b1c781620e45e;hp=7f73103b51b2ab922aff5b1283a003e0035c1519;hpb=3cc42b9d18f48ffaa0320a63e06e295ed079e7d5;p=gnulib.git diff --git a/lib/acl.c b/lib/acl.c index 7f73103b5..b40e43a17 100644 --- a/lib/acl.c +++ b/lib/acl.c @@ -2,10 +2,10 @@ Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + 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 2, or (at your option) - any later version. + 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 @@ -13,8 +13,7 @@ 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + along with this program. If not, see . Written by Paul Eggert and Andreas Gruenbacher. */ @@ -22,94 +21,7 @@ #include "acl.h" -#include -#include -#include -#ifndef S_ISLNK -# define S_ISLNK(Mode) 0 -#endif - -#ifdef HAVE_ACL_LIBACL_H -# include -#endif - -#include "error.h" -#include "quote.h" - -#include -#ifndef ENOSYS -# define ENOSYS (-1) -#endif -#ifndef ENOTSUP -# define ENOTSUP (-1) -#endif - -#if ENABLE_NLS -# include -# define _(Text) gettext (Text) -#else -# define _(Text) Text -#endif - -#ifndef HAVE_FCHMOD -# define HAVE_FCHMOD false -# define fchmod(fd, mode) (-1) -#endif - -/* POSIX 1003.1e (draft 17) */ -#ifndef HAVE_ACL_GET_FD -# define HAVE_ACL_GET_FD false -# define acl_get_fd(fd) (NULL) -#endif - -/* POSIX 1003.1e (draft 17) */ -#ifndef HAVE_ACL_SET_FD -# define HAVE_ACL_SET_FD false -# define acl_set_fd(fd, acl) (-1) -#endif - -/* Linux-specific */ -#ifndef HAVE_ACL_EXTENDED_FILE -# define HAVE_ACL_EXTENDED_FILE false -# define acl_extended_file(name) (-1) -#endif - -/* Linux-specific */ -#ifndef HAVE_ACL_FROM_MODE -# define HAVE_ACL_FROM_MODE false -# define acl_from_mode(mode) (NULL) -#endif - -/* We detect the presence of POSIX 1003.1e (draft 17 -- abandoned) support - by checking for HAVE_ACL_GET_FILE, HAVE_ACL_SET_FILE, and HAVE_ACL_FREE. - Systems that have acl_get_file, acl_set_file, and acl_free must also - have acl_to_text, acl_from_text, and acl_delete_def_file (all defined - in the draft); systems that don't would hit #error statements here. */ - -#if USE_ACL && HAVE_ACL_GET_FILE && !HAVE_ACL_ENTRIES -# ifndef HAVE_ACL_TO_TEXT -# error Must have acl_to_text (see POSIX 1003.1e draft 17). -# endif - -/* Return the number of entries in ACL. Linux implements acl_entries - as a more efficient extension than using this workaround. */ - -static int -acl_entries (acl_t acl) -{ - char *text = acl_to_text (acl, NULL), *t; - int entries; - if (text == NULL) - return -1; - for (entries = 0, t = text; ; t++, entries++) { - t = strchr (t, '\n'); - if (t == NULL) - break; - } - acl_free (text); - return entries; -} -#endif +#include "acl-internal.h" /* If DESC is a valid file descriptor use fchmod to change the file's mode to MODE on systems that have fchown. On systems @@ -125,77 +37,14 @@ chmod_or_fchmod (const char *name, int desc, mode_t mode) return chmod (name, mode); } -/* Return 1 if NAME has a nontrivial access control list, 0 if - NAME only has no or a base access control list, and -1 on - error. SB must be set to the stat buffer of FILE. */ - -int -file_has_acl (char const *name, struct stat const *sb) -{ -#if USE_ACL && HAVE_ACL && defined GETACLCNT - /* This implementation should work on recent-enough versions of HP-UX, - Solaris, and Unixware. */ - -# ifndef MIN_ACL_ENTRIES -# define MIN_ACL_ENTRIES 4 -# endif - - if (! S_ISLNK (sb->st_mode)) - { - int n = acl (name, GETACLCNT, 0, NULL); - return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n); - } -#elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE - /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ - - if (! S_ISLNK (sb->st_mode)) - { - int ret; - - if (HAVE_ACL_EXTENDED_FILE) - ret = acl_extended_file (name); - else - { - acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); - if (acl) - { - ret = (3 < acl_entries (acl)); - acl_free (acl); - if (ret == 0 && S_ISDIR (sb->st_mode)) - { - acl = acl_get_file (name, ACL_TYPE_DEFAULT); - if (acl) - { - ret = (0 < acl_entries (acl)); - acl_free (acl); - } - else - ret = -1; - } - } - else - ret = -1; - } - if (ret < 0) - return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1; - return ret; - } -#endif - - /* FIXME: Add support for AIX, Irix, and Tru64. Please see Samba's - source/lib/sysacls.c file for fix-related ideas. */ - - return 0; -} - /* 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 - DEST_NAME. + 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. - System call return value semantics. */ + Return 0 if successful, otherwise output a diagnostic and return -1. */ int copy_acl (const char *src_name, int source_desc, const char *dst_name, @@ -213,7 +62,7 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name, acl = acl_get_file (src_name, ACL_TYPE_ACCESS); if (acl == NULL) { - if (errno == ENOSYS || errno == ENOTSUP) + if (ACL_NOT_WELL_SUPPORTED (errno)) return set_acl (dst_name, dest_desc, mode); else { @@ -230,12 +79,17 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name, { int saved_errno = errno; - if (errno == ENOSYS || errno == ENOTSUP) + if (ACL_NOT_WELL_SUPPORTED (errno)) { int n = acl_entries (acl); acl_free (acl); - if (n == 3) + /* 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. + 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 (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) saved_errno = errno; @@ -290,10 +144,38 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name, acl_free (acl); } return 0; + #else - ret = chmod_or_fchmod (dst_name, dest_desc, mode); + +# if USE_ACL && defined ACL_NO_TRIVIAL + /* Solaris 10 NFSv4 ACLs. */ + acl_t *aclp = NULL; + ret = (source_desc < 0 + ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp) + : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp)); + if (ret != 0 && errno != ENOSYS) + { + error (0, errno, "%s", quote (src_name)); + return ret; + } +# endif + + ret = qset_acl (dst_name, dest_desc, mode); if (ret != 0) error (0, errno, _("preserving permissions for %s"), quote (dst_name)); + +# if USE_ACL && defined ACL_NO_TRIVIAL + if (ret == 0 && aclp) + { + ret = (dest_desc < 0 + ? acl_set (dst_name, aclp) + : facl_set (dest_desc, aclp)); + if (ret != 0) + error (0, errno, _("preserving permissions for %s"), quote (dst_name)); + acl_free (aclp); + } +# endif + return ret; #endif } @@ -307,7 +189,7 @@ copy_acl (const char *src_name, int source_desc, const char *dst_name, semantics. */ int -set_acl (char const *name, int desc, mode_t mode) +qset_acl (char const *name, int desc, mode_t mode) { #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE /* POSIX 1003.1e draft 17 (abandoned) specific version. */ @@ -331,10 +213,7 @@ set_acl (char const *name, int desc, mode_t mode) { acl = acl_from_mode (mode); if (!acl) - { - error (0, errno, "%s", quote (name)); - return -1; - } + return -1; } else { @@ -352,10 +231,7 @@ set_acl (char const *name, int desc, mode_t mode) acl = acl_from_text (acl_text); if (!acl) - { - error (0, errno, "%s", quote (name)); - return -1; - } + return -1; } if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); @@ -366,24 +242,21 @@ set_acl (char const *name, int desc, mode_t mode) int saved_errno = errno; acl_free (acl); - if (errno == ENOTSUP || errno == ENOSYS || errno == EINVAL) + if (ACL_NOT_WELL_SUPPORTED (errno)) { if (chmod_or_fchmod (name, desc, mode) != 0) saved_errno = errno; else return 0; } - error (0, saved_errno, _("setting permissions for %s"), quote (name)); + errno = saved_errno; return -1; } else acl_free (acl); if (S_ISDIR (mode) && acl_delete_def_file (name)) - { - error (0, errno, _("setting permissions for %s"), quote (name)); - return -1; - } + return -1; if (mode & (S_ISUID | S_ISGID | S_ISVTX)) { @@ -391,16 +264,57 @@ set_acl (char const *name, int desc, mode_t mode) been set. */ if (chmod_or_fchmod (name, desc, mode)) - { - error (0, errno, _("preserving permissions for %s"), quote (name)); - return -1; - } + return -1; } return 0; #else - int ret = chmod_or_fchmod (name, desc, mode); - if (ret) - error (0, errno, _("setting permissions for %s"), quote (name)); - return ret; + +# if USE_ACL && defined ACL_NO_TRIVIAL + + /* Solaris 10, with NFSv4 ACLs. */ + acl_t *aclp; + char acl_text[] = "user::---,group::---,mask:---,other:---"; + + if (mode & S_IRUSR) acl_text[ 6] = 'r'; + if (mode & S_IWUSR) acl_text[ 7] = 'w'; + if (mode & S_IXUSR) acl_text[ 8] = 'x'; + if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r'; + if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w'; + if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x'; + if (mode & S_IROTH) acl_text[36] = 'r'; + if (mode & S_IWOTH) acl_text[37] = 'w'; + if (mode & S_IXOTH) acl_text[38] = 'x'; + + if (acl_fromtext (acl_text, &aclp) != 0) + { + errno = ENOMEM; + return -1; + } + else + { + int acl_result = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp)); + int acl_errno = errno; + acl_free (aclp); + if (acl_result == 0 || acl_errno != ENOSYS) + { + errno = acl_errno; + return acl_result; + } + } +# endif + + 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 r = qset_acl (name, desc, mode); + if (r != 0) + error (0, errno, _("setting permissions for %s"), quote (name)); + return r; +}