Use Gnulib ACL implementation, for benefit of Solaris etc.

* configure.ac: Remove -with-acl option, since Gnulib does that for
us now.
(LIBACL_LIBS): Remove; no longer needed.
* lib/Makefile.am (CLEANFILES, SUFFIXES): New (empty) macros,
for the benefit of the new ACL implementation.
* lib/makefile.w32-in (GNULIBOBJS): Add $(BLD)/acl-errno-valid.$(O).
($(BLD)/acl-errno-valid.$(O)): New rule.
* lib/acl-errno-valid.c, lib/acl-internal.h, lib/acl.h:
* lib/acl_entries.c, lib/errno.in.h, lib/file-has-acl.c:
* lib/qcopy-acl.c, lib/qset-acl.c, m4/acl.m4, m4/errno_h.m4:
New files, taken from gnulib.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* admin/merge-gnulib (GNULIB_MODULES): Add qacl.
(GNULIB_TOOL_FLAGS): Do not avoid errno.
* etc/NEWS: Emacs is no longer limited to POSIX ACLs.  --disable-acl,
not --without-acl, since we're now using Gnulib's implementation.
* nt/config.nt (HAVE_ACL_SET_FILE): Rename from HAVE_POSIX_ACL.
* nt/inc/ms-w32.h (EOPNOTSUPP): New macro.
* src/Makefile.in (LIB_ACL): New macro.
(LIBACL_LIBS): Remove.
(LIBES): Use LIB_ACL, not LIBACL_LIBS.
* src/fileio.c: Include <acl.h>.
Use HAVE_ACL_SET_FILE rather than HAVE_POSIX_ACL.
(ACL_NOT_WELL_SUPPORTED): Remove.  All uses replaced by
!acl_errno_valid.
(Fcopy_file) [!WINDOWSNT]: Use qcopy_acl instead of rolling
it ourselves.

Fixes: debbugs:14295
This commit is contained in:
Paul Eggert 2013-05-07 14:34:03 -07:00
parent ad64371062
commit ffdc270a76
26 changed files with 3313 additions and 81 deletions

View file

@ -1,3 +1,19 @@
2013-05-07 Paul Eggert <eggert@cs.ucla.edu>
Use Gnulib ACL implementation, for benefit of Solaris etc. (Bug#14295)
* configure.ac: Remove -with-acl option, since Gnulib does that for
us now.
(LIBACL_LIBS): Remove; no longer needed.
* lib/Makefile.am (CLEANFILES, SUFFIXES): New (empty) macros,
for the benefit of the new ACL implementation.
* lib/makefile.w32-in (GNULIBOBJS): Add $(BLD)/acl-errno-valid.$(O).
($(BLD)/acl-errno-valid.$(O)): New rule.
* lib/acl-errno-valid.c, lib/acl-internal.h, lib/acl.h:
* lib/acl_entries.c, lib/errno.in.h, lib/file-has-acl.c:
* lib/qcopy-acl.c, lib/qset-acl.c, m4/acl.m4, m4/errno_h.m4:
New files, taken from gnulib.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
2013-05-07 Jan Djärv <jan.h.d@swipnet.se>
* configure.ac (HAVE_XRANDR, HAVE_XINERAMA): Define if available.

View file

@ -1,3 +1,9 @@
2013-05-07 Paul Eggert <eggert@cs.ucla.edu>
Use Gnulib ACL implementation, for benefit of Solaris etc. (Bug#14295)
* merge-gnulib (GNULIB_MODULES): Add qacl.
(GNULIB_TOOL_FLAGS): Do not avoid errno.
2013-04-01 Paul Eggert <eggert@cs.ucla.edu>
Use UTF-8 for most files with non-ASCII characters (Bug#13936).

View file

@ -33,7 +33,7 @@ GNULIB_MODULES='
getloadavg getopt-gnu gettime gettimeofday
ignore-value intprops largefile lstat
manywarnings memrchr mktime
pselect pthread_sigmask putenv readlink readlinkat
pselect pthread_sigmask putenv qacl readlink readlinkat
sig2str socklen stat-time stdalign stdarg stdbool stdio
strftime strtoimax strtoumax symlink sys_stat
sys_time time timer-time timespec-add timespec-sub unsetenv utimens
@ -42,7 +42,7 @@ GNULIB_MODULES='
GNULIB_TOOL_FLAGS='
--avoid=dup
--avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
--avoid=fchdir --avoid=fcntl --avoid=fstat
--avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
--avoid=open --avoid=openat-die --avoid=opendir
--avoid=raise

View file

@ -199,7 +199,6 @@ OPTION_DEFAULT_ON([dbus],[don't compile with D-Bus support])
OPTION_DEFAULT_ON([gconf],[don't compile with GConf support])
OPTION_DEFAULT_ON([gsettings],[don't compile with GSettings support])
OPTION_DEFAULT_ON([selinux],[don't compile with SELinux support])
OPTION_DEFAULT_ON([acl],[don't compile with ACL support])
OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support])
OPTION_DEFAULT_ON([inotify],[don't compile with inotify (file-watch) support])
@ -2185,23 +2184,6 @@ if test "$ac_cv_func_inotify_init1" = yes; then
AC_DEFINE(HAVE_INOTIFY, 1, [Define to 1 to use inotify.])
fi
dnl POSIX ACL support: provided by libacl on GNU/Linux, by libc on FreeBSD.
HAVE_POSIX_ACL=no
LIBACL_LIBS=
if test "${with_acl}" = "yes"; then
AC_CHECK_LIB([acl], [acl_set_file], HAVE_POSIX_ACL=yes, HAVE_POSIX_ACL=no)
if test "$HAVE_POSIX_ACL" = yes; then
AC_DEFINE(HAVE_POSIX_ACL, 1, [Define to 1 if using POSIX ACL support.])
LIBACL_LIBS=-lacl
else
AC_CHECK_FUNC(acl_set_file, HAVE_POSIX_ACL=yes, HAVE_POSIX_ACL=no)
if test "$HAVE_POSIX_ACL" = yes; then
AC_DEFINE(HAVE_POSIX_ACL, 1, [Define to 1 if using POSIX ACL support.])
fi
fi
fi
AC_SUBST(LIBACL_LIBS)
dnl Do not put whitespace before the #include statements below.
dnl Older compilers (eg sunos4 cc) choke on it.
HAVE_XAW3D=no

View file

@ -1,3 +1,9 @@
2013-05-07 Paul Eggert <eggert@cs.ucla.edu>
Use Gnulib ACL implementation, for benefit of Solaris etc. (Bug#14295)
* NEWS: Emacs is no longer limited to POSIX ACLs. --disable-acl,
not --without-acl, since we're now using Gnulib's implementation.
2013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
* NEWS: Mention multi-monitor support.

View file

@ -23,10 +23,10 @@ so we will look at it and add it to the manual.
* Installation Changes in Emacs 24.4
** Emacs can be compiled with POSIX ACL support.
** Emacs can be compiled with ACL support.
This happens by default if a suitable support library is found at
build time, like libacl on GNU/Linux. To prevent this, use the
configure option `--without-acl'.
configure option `--disable-acl'.
** The configure option --with-crt-dir has been removed.
It is no longer needed, as the crt*.o files are no longer linked specially.

View file

@ -1,8 +1,10 @@
BUILT_SOURCES =
CLEANFILES =
EXTRA_DIST =
MOSTLYCLEANDIRS =
MOSTLYCLEANFILES =
noinst_LIBRARIES =
SUFFIXES =
AM_CFLAGS = $(PROFILING_CFLAGS) $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src

52
lib/acl-errno-valid.c Normal file
View file

@ -0,0 +1,52 @@
/* 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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert. */
#include <config.h>
#include <acl.h>
#include <errno.h>
/* 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
# if ENOTSUP != ENOSYS /* Needed for the MS-Windows port of GNU Emacs. */
case ENOTSUP: return false;
# endif
#endif
case EOPNOTSUPP: return false;
default: return true;
}
}

250
lib/acl-internal.h Normal file
View file

@ -0,0 +1,250 @@
/* Internal implementation of access control lists.
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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
#include "acl.h"
#include <stdbool.h>
#include <stdlib.h>
/* All systems define the ACL related API in <sys/acl.h>. */
#if HAVE_SYS_ACL_H
# include <sys/acl.h>
#endif
#if defined HAVE_FACL && ! defined GETACLCNT && defined ACL_CNT
# define GETACLCNT ACL_CNT
#endif
/* On Linux, additional ACL related API is available in <acl/libacl.h>. */
#ifdef HAVE_ACL_LIBACL_H
# include <acl/libacl.h>
#endif
/* On HP-UX >= 11.11, additional ACL API is available in <aclv.h>. */
#if HAVE_ACLV_H
# include <sys/types.h>
# include <aclv.h>
/* HP-UX 11.11 lacks these declarations. */
extern int acl (char *, int, int, struct acl *);
extern int aclsort (int, int, struct acl *);
#endif
#include <errno.h>
#include <limits.h>
#ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif
#ifndef HAVE_FCHMOD
# define HAVE_FCHMOD false
# define fchmod(fd, mode) (-1)
#endif
_GL_INLINE_HEADER_BEGIN
#ifndef ACL_INTERNAL_INLINE
# define ACL_INTERNAL_INLINE _GL_INLINE
#endif
#if USE_ACL
# if HAVE_ACL_GET_FILE
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
/* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
# ifndef MIN_ACL_ENTRIES
# define MIN_ACL_ENTRIES 4
# endif
/* POSIX 1003.1e (draft 17) */
# ifdef HAVE_ACL_GET_FD
/* Most platforms have a 1-argument acl_get_fd, only OSF/1 has a 2-argument
macro(!). */
# if HAVE_ACL_FREE_TEXT /* OSF/1 */
ACL_INTERNAL_INLINE acl_t
rpl_acl_get_fd (int fd)
{
return acl_get_fd (fd, ACL_TYPE_ACCESS);
}
# undef acl_get_fd
# define acl_get_fd rpl_acl_get_fd
# endif
# else
# define HAVE_ACL_GET_FD false
# undef acl_get_fd
# define acl_get_fd(fd) (NULL)
# endif
/* POSIX 1003.1e (draft 17) */
# ifdef HAVE_ACL_SET_FD
/* Most platforms have a 2-argument acl_set_fd, only OSF/1 has a 3-argument
macro(!). */
# if HAVE_ACL_FREE_TEXT /* OSF/1 */
ACL_INTERNAL_INLINE int
rpl_acl_set_fd (int fd, acl_t acl)
{
return acl_set_fd (fd, ACL_TYPE_ACCESS, acl);
}
# undef acl_set_fd
# define acl_set_fd rpl_acl_set_fd
# endif
# else
# define HAVE_ACL_SET_FD false
# undef acl_set_fd
# define acl_set_fd(fd, acl) (-1)
# endif
/* POSIX 1003.1e (draft 13) */
# if ! HAVE_ACL_FREE_TEXT
# define acl_free_text(buf) acl_free (buf)
# 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
/* 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) || defined __sgi /* Mac OS X, IRIX */
# define MODE_INSIDE_ACL 0
# else
# define MODE_INSIDE_ACL 1
# endif
/* Return the number of entries in ACL.
Return -1 and set errno upon failure to determine it. */
/* Define a replacement for acl_entries if needed. (Only Linux has it.) */
# if !HAVE_ACL_ENTRIES
# define acl_entries rpl_acl_entries
extern int acl_entries (acl_t);
# endif
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial. */
extern int acl_extended_nontrivial (acl_t);
# else
/* 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
# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
/* 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 defined __CYGWIN__ /* Cygwin */
# define MODE_INSIDE_ACL 0
# else /* Solaris */
# define MODE_INSIDE_ACL 1
# endif
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
extern int acl_nontrivial (int count, aclent_t *entries);
# ifdef ACE_GETACL /* Solaris 10 */
/* Test an ACL retrieved with ACE_GETACL.
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
extern int acl_ace_nontrivial (int count, ace_t *entries);
/* Definitions for when the built executable is executed on Solaris 10
(newer version) or Solaris 11. */
/* For a_type. */
# define OLD_ALLOW 0
# define OLD_DENY 1
# define NEW_ACE_ACCESS_ALLOWED_ACE_TYPE 0 /* replaces ALLOW */
# define NEW_ACE_ACCESS_DENIED_ACE_TYPE 1 /* replaces DENY */
/* For a_flags. */
# define OLD_ACE_OWNER 0x0100
# define OLD_ACE_GROUP 0x0200
# define OLD_ACE_OTHER 0x0400
# define NEW_ACE_OWNER 0x1000
# define NEW_ACE_GROUP 0x2000
# define NEW_ACE_IDENTIFIER_GROUP 0x0040
# define NEW_ACE_EVERYONE 0x4000
/* For a_access_mask. */
# define NEW_ACE_READ_DATA 0x001 /* corresponds to 'r' */
# define NEW_ACE_WRITE_DATA 0x002 /* corresponds to 'w' */
# define NEW_ACE_APPEND_DATA 0x004
# define NEW_ACE_READ_NAMED_ATTRS 0x008
# define NEW_ACE_WRITE_NAMED_ATTRS 0x010
# define NEW_ACE_EXECUTE 0x020
# define NEW_ACE_DELETE_CHILD 0x040
# define NEW_ACE_READ_ATTRIBUTES 0x080
# define NEW_ACE_WRITE_ATTRIBUTES 0x100
# define NEW_ACE_DELETE 0x10000
# define NEW_ACE_READ_ACL 0x20000
# define NEW_ACE_WRITE_ACL 0x40000
# define NEW_ACE_WRITE_OWNER 0x80000
# define NEW_ACE_SYNCHRONIZE 0x100000
# endif
# elif HAVE_GETACL /* HP-UX */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
extern int acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb);
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
extern int aclv_nontrivial (int count, struct acl *entries);
# endif
# elif HAVE_ACLX_GET && 0 /* AIX */
/* TODO */
# elif HAVE_STATACL /* older AIX */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
extern int acl_nontrivial (struct acl *a);
# elif HAVE_ACLSORT /* NonStop Kernel */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
extern int acl_nontrivial (int count, struct acl *entries);
# endif
#endif
_GL_INLINE_HEADER_END

30
lib/acl.h Normal file
View file

@ -0,0 +1,30 @@
/* acl.c - access control lists
Copyright (C) 2002, 2008-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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert. */
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
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);
int qcopy_acl (char const *, int, char const *, int, mode_t);
int copy_acl (char const *, int, char const *, int, mode_t);
int chmod_or_fchmod (char const *, int, mode_t);

75
lib/acl_entries.c Normal file
View file

@ -0,0 +1,75 @@
/* Return the number of entries in an ACL.
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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert and Andreas Gruenbacher. */
#include <config.h>
#include "acl-internal.h"
/* This file assumes POSIX-draft like ACLs
(Linux, FreeBSD, Mac OS X, IRIX, Tru64). */
/* Return the number of entries in ACL.
Return -1 and set errno upon failure to determine it. */
int
acl_entries (acl_t acl)
{
int count = 0;
if (acl != NULL)
{
#if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD, Mac OS X */
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
/* acl_get_entry returns 0 when it successfully fetches an entry,
and -1/EINVAL at the end. */
acl_entry_t ace;
int got_one;
for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
got_one >= 0;
got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
count++;
# else /* Linux, FreeBSD */
/* acl_get_entry returns 1 when it successfully fetches an entry,
and 0 at the end. */
acl_entry_t ace;
int got_one;
for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
got_one > 0;
got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
count++;
if (got_one < 0)
return -1;
# endif
#else /* IRIX, Tru64 */
# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */
/* Don't use acl_get_entry: it is undocumented. */
count = acl->acl_cnt;
# endif
# if HAVE_ACL_FREE_TEXT /* Tru64 */
/* Don't use acl_get_entry: it takes only one argument and does not
work. */
count = acl->acl_num;
# endif
#endif
}
return count;
}

279
lib/errno.in.h Normal file
View file

@ -0,0 +1,279 @@
/* A POSIX-like <errno.h>.
Copyright (C) 2008-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, 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 <http://www.gnu.org/licenses/>. */
#ifndef _@GUARD_PREFIX@_ERRNO_H
#if __GNUC__ >= 3
@PRAGMA_SYSTEM_HEADER@
#endif
@PRAGMA_COLUMNS@
/* The include_next requires a split double-inclusion guard. */
#@INCLUDE_NEXT@ @NEXT_ERRNO_H@
#ifndef _@GUARD_PREFIX@_ERRNO_H
#define _@GUARD_PREFIX@_ERRNO_H
/* On native Windows platforms, many macros are not defined. */
# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
/* These are the same values as defined by MSVC 10, for interoperability. */
# ifndef ENOMSG
# define ENOMSG 122
# define GNULIB_defined_ENOMSG 1
# endif
# ifndef EIDRM
# define EIDRM 111
# define GNULIB_defined_EIDRM 1
# endif
# ifndef ENOLINK
# define ENOLINK 121
# define GNULIB_defined_ENOLINK 1
# endif
# ifndef EPROTO
# define EPROTO 134
# define GNULIB_defined_EPROTO 1
# endif
# ifndef EBADMSG
# define EBADMSG 104
# define GNULIB_defined_EBADMSG 1
# endif
# ifndef EOVERFLOW
# define EOVERFLOW 132
# define GNULIB_defined_EOVERFLOW 1
# endif
# ifndef ENOTSUP
# define ENOTSUP 129
# define GNULIB_defined_ENOTSUP 1
# endif
# ifndef ENETRESET
# define ENETRESET 117
# define GNULIB_defined_ENETRESET 1
# endif
# ifndef ECONNABORTED
# define ECONNABORTED 106
# define GNULIB_defined_ECONNABORTED 1
# endif
# ifndef ECANCELED
# define ECANCELED 105
# define GNULIB_defined_ECANCELED 1
# endif
# ifndef EOWNERDEAD
# define EOWNERDEAD 133
# define GNULIB_defined_EOWNERDEAD 1
# endif
# ifndef ENOTRECOVERABLE
# define ENOTRECOVERABLE 127
# define GNULIB_defined_ENOTRECOVERABLE 1
# endif
# ifndef EINPROGRESS
# define EINPROGRESS 112
# define EALREADY 103
# define ENOTSOCK 128
# define EDESTADDRREQ 109
# define EMSGSIZE 115
# define EPROTOTYPE 136
# define ENOPROTOOPT 123
# define EPROTONOSUPPORT 135
# define EOPNOTSUPP 130
# define EAFNOSUPPORT 102
# define EADDRINUSE 100
# define EADDRNOTAVAIL 101
# define ENETDOWN 116
# define ENETUNREACH 118
# define ECONNRESET 108
# define ENOBUFS 119
# define EISCONN 113
# define ENOTCONN 126
# define ETIMEDOUT 138
# define ECONNREFUSED 107
# define ELOOP 114
# define EHOSTUNREACH 110
# define EWOULDBLOCK 140
# define GNULIB_defined_ESOCK 1
# endif
# ifndef ETXTBSY
# define ETXTBSY 139
# define ENODATA 120 /* not required by POSIX */
# define ENOSR 124 /* not required by POSIX */
# define ENOSTR 125 /* not required by POSIX */
# define ETIME 137 /* not required by POSIX */
# define EOTHER 131 /* not required by POSIX */
# define GNULIB_defined_ESTREAMS 1
# endif
/* These are intentionally the same values as the WSA* error numbers, defined
in <winsock2.h>. */
# define ESOCKTNOSUPPORT 10044 /* not required by POSIX */
# define EPFNOSUPPORT 10046 /* not required by POSIX */
# define ESHUTDOWN 10058 /* not required by POSIX */
# define ETOOMANYREFS 10059 /* not required by POSIX */
# define EHOSTDOWN 10064 /* not required by POSIX */
# define EPROCLIM 10067 /* not required by POSIX */
# define EUSERS 10068 /* not required by POSIX */
# define EDQUOT 10069
# define ESTALE 10070
# define EREMOTE 10071 /* not required by POSIX */
# define GNULIB_defined_EWINSOCK 1
# endif
/* On OSF/1 5.1, when _XOPEN_SOURCE_EXTENDED is not defined, the macros
EMULTIHOP, ENOLINK, EOVERFLOW are not defined. */
# if @EMULTIHOP_HIDDEN@
# define EMULTIHOP @EMULTIHOP_VALUE@
# define GNULIB_defined_EMULTIHOP 1
# endif
# if @ENOLINK_HIDDEN@
# define ENOLINK @ENOLINK_VALUE@
# define GNULIB_defined_ENOLINK 1
# endif
# if @EOVERFLOW_HIDDEN@
# define EOVERFLOW @EOVERFLOW_VALUE@
# define GNULIB_defined_EOVERFLOW 1
# endif
/* On OpenBSD 4.0 and on native Windows, the macros ENOMSG, EIDRM, ENOLINK,
EPROTO, EMULTIHOP, EBADMSG, EOVERFLOW, ENOTSUP, ECANCELED are not defined.
Likewise, on NonStop Kernel, EDQUOT is not defined.
Define them here. Values >= 2000 seem safe to use: Solaris ESTALE = 151,
HP-UX EWOULDBLOCK = 246, IRIX EDQUOT = 1133.
Note: When one of these systems defines some of these macros some day,
binaries will have to be recompiled so that they recognizes the new
errno values from the system. */
# ifndef ENOMSG
# define ENOMSG 2000
# define GNULIB_defined_ENOMSG 1
# endif
# ifndef EIDRM
# define EIDRM 2001
# define GNULIB_defined_EIDRM 1
# endif
# ifndef ENOLINK
# define ENOLINK 2002
# define GNULIB_defined_ENOLINK 1
# endif
# ifndef EPROTO
# define EPROTO 2003
# define GNULIB_defined_EPROTO 1
# endif
# ifndef EMULTIHOP
# define EMULTIHOP 2004
# define GNULIB_defined_EMULTIHOP 1
# endif
# ifndef EBADMSG
# define EBADMSG 2005
# define GNULIB_defined_EBADMSG 1
# endif
# ifndef EOVERFLOW
# define EOVERFLOW 2006
# define GNULIB_defined_EOVERFLOW 1
# endif
# ifndef ENOTSUP
# define ENOTSUP 2007
# define GNULIB_defined_ENOTSUP 1
# endif
# ifndef ENETRESET
# define ENETRESET 2011
# define GNULIB_defined_ENETRESET 1
# endif
# ifndef ECONNABORTED
# define ECONNABORTED 2012
# define GNULIB_defined_ECONNABORTED 1
# endif
# ifndef ESTALE
# define ESTALE 2009
# define GNULIB_defined_ESTALE 1
# endif
# ifndef EDQUOT
# define EDQUOT 2010
# define GNULIB_defined_EDQUOT 1
# endif
# ifndef ECANCELED
# define ECANCELED 2008
# define GNULIB_defined_ECANCELED 1
# endif
/* On many platforms, the macros EOWNERDEAD and ENOTRECOVERABLE are not
defined. */
# ifndef EOWNERDEAD
# if defined __sun
/* Use the same values as defined for Solaris >= 8, for
interoperability. */
# define EOWNERDEAD 58
# define ENOTRECOVERABLE 59
# elif (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
/* We have a conflict here: pthreads-win32 defines these values
differently than MSVC 10. It's hairy to decide which one to use. */
# if defined __MINGW32__ && !defined USE_WINDOWS_THREADS
/* Use the same values as defined by pthreads-win32, for
interoperability. */
# define EOWNERDEAD 43
# define ENOTRECOVERABLE 44
# else
/* Use the same values as defined by MSVC 10, for
interoperability. */
# define EOWNERDEAD 133
# define ENOTRECOVERABLE 127
# endif
# else
# define EOWNERDEAD 2013
# define ENOTRECOVERABLE 2014
# endif
# define GNULIB_defined_EOWNERDEAD 1
# define GNULIB_defined_ENOTRECOVERABLE 1
# endif
# ifndef EILSEQ
# define EILSEQ 2015
# define GNULIB_defined_EILSEQ 1
# endif
#endif /* _@GUARD_PREFIX@_ERRNO_H */
#endif /* _@GUARD_PREFIX@_ERRNO_H */

918
lib/file-has-acl.c Normal file
View file

@ -0,0 +1,918 @@
/* Test whether a file has a nontrivial access control list.
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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
/* Without this pragma, gcc 4.7.0 20120126 may suggest that the
file_has_acl function might be candidate for attribute 'const' */
#if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
# pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
#endif
#include <config.h>
#include "acl.h"
#include "acl-internal.h"
#if USE_ACL && HAVE_ACL_GET_FILE
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial. */
int
acl_extended_nontrivial (acl_t acl)
{
/* acl is non-trivial if it is non-empty. */
return (acl_entries (acl) > 0);
}
# else /* Linux, FreeBSD, IRIX, Tru64 */
/* 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)
{
/* 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 got_one;
for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
got_one > 0;
got_one = 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 got_one;
# 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
}
# endif
#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
/* Test an ACL retrieved with GETACL.
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
acl_nontrivial (int count, aclent_t *entries)
{
int i;
for (i = 0; i < count; i++)
{
aclent_t *ace = &entries[i];
/* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
We don't need to check ace->a_id in these cases. */
if (!(ace->a_type == USER_OBJ
|| ace->a_type == GROUP_OBJ
|| ace->a_type == OTHER_OBJ
/* Note: Cygwin does not return a CLASS_OBJ ("mask:") entry
sometimes. */
|| ace->a_type == CLASS_OBJ))
return 1;
}
return 0;
}
# ifdef ACE_GETACL
/* A shortcut for a bitmask. */
# define NEW_ACE_WRITEA_DATA (NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA)
/* Test an ACL retrieved with ACE_GETACL.
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
acl_ace_nontrivial (int count, ace_t *entries)
{
int i;
/* The flags in the ace_t structure changed in a binary incompatible way
when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
How to distinguish the two conventions at runtime?
In the old convention, usually three ACEs have a_flags = ACE_OWNER /
ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new
convention, these values are not used. */
int old_convention = 0;
for (i = 0; i < count; i++)
if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
{
old_convention = 1;
break;
}
if (old_convention)
/* Running on Solaris 10. */
for (i = 0; i < count; i++)
{
ace_t *ace = &entries[i];
/* Note:
If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from stat().
If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from stat().
We don't need to check ace->a_who in these cases. */
if (!(ace->a_type == OLD_ALLOW
&& (ace->a_flags == OLD_ACE_OWNER
|| ace->a_flags == OLD_ACE_GROUP
|| ace->a_flags == OLD_ACE_OTHER)))
return 1;
}
else
{
/* Running on Solaris 10 (newer version) or Solaris 11. */
unsigned int access_masks[6] =
{
0, /* owner@ deny */
0, /* owner@ allow */
0, /* group@ deny */
0, /* group@ allow */
0, /* everyone@ deny */
0 /* everyone@ allow */
};
for (i = 0; i < count; i++)
{
ace_t *ace = &entries[i];
unsigned int index1;
unsigned int index2;
if (ace->a_type == NEW_ACE_ACCESS_ALLOWED_ACE_TYPE)
index1 = 1;
else if (ace->a_type == NEW_ACE_ACCESS_DENIED_ACE_TYPE)
index1 = 0;
else
return 1;
if (ace->a_flags == NEW_ACE_OWNER)
index2 = 0;
else if (ace->a_flags == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP))
index2 = 2;
else if (ace->a_flags == NEW_ACE_EVERYONE)
index2 = 4;
else
return 1;
access_masks[index1 + index2] |= ace->a_access_mask;
}
/* The same bit shouldn't be both allowed and denied. */
if (access_masks[0] & access_masks[1])
return 1;
if (access_masks[2] & access_masks[3])
return 1;
if (access_masks[4] & access_masks[5])
return 1;
/* Check minimum masks. */
if ((NEW_ACE_WRITE_NAMED_ATTRS
| NEW_ACE_WRITE_ATTRIBUTES
| NEW_ACE_WRITE_ACL
| NEW_ACE_WRITE_OWNER)
& ~ access_masks[1])
return 1;
access_masks[1] &= ~(NEW_ACE_WRITE_NAMED_ATTRS
| NEW_ACE_WRITE_ATTRIBUTES
| NEW_ACE_WRITE_ACL
| NEW_ACE_WRITE_OWNER);
if ((NEW_ACE_READ_NAMED_ATTRS
| NEW_ACE_READ_ATTRIBUTES
| NEW_ACE_READ_ACL
| NEW_ACE_SYNCHRONIZE)
& ~ access_masks[5])
return 1;
access_masks[5] &= ~(NEW_ACE_READ_NAMED_ATTRS
| NEW_ACE_READ_ATTRIBUTES
| NEW_ACE_READ_ACL
| NEW_ACE_SYNCHRONIZE);
/* Check the allowed or denied bits. */
switch ((access_masks[0] | access_masks[1])
& ~(NEW_ACE_READ_NAMED_ATTRS
| NEW_ACE_READ_ATTRIBUTES
| NEW_ACE_READ_ACL
| NEW_ACE_SYNCHRONIZE))
{
case 0:
case NEW_ACE_READ_DATA:
case NEW_ACE_WRITEA_DATA:
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA:
case NEW_ACE_EXECUTE:
case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE:
case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
break;
default:
return 1;
}
switch ((access_masks[2] | access_masks[3])
& ~(NEW_ACE_READ_NAMED_ATTRS
| NEW_ACE_READ_ATTRIBUTES
| NEW_ACE_READ_ACL
| NEW_ACE_SYNCHRONIZE))
{
case 0:
case NEW_ACE_READ_DATA:
case NEW_ACE_WRITEA_DATA:
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA:
case NEW_ACE_EXECUTE:
case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE:
case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
break;
default:
return 1;
}
switch ((access_masks[4] | access_masks[5])
& ~(NEW_ACE_WRITE_NAMED_ATTRS
| NEW_ACE_WRITE_ATTRIBUTES
| NEW_ACE_WRITE_ACL
| NEW_ACE_WRITE_OWNER))
{
case 0:
case NEW_ACE_READ_DATA:
case NEW_ACE_WRITEA_DATA:
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA:
case NEW_ACE_EXECUTE:
case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE:
case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE:
break;
default:
return 1;
}
/* Check that the NEW_ACE_WRITE_DATA and NEW_ACE_APPEND_DATA bits are
either both allowed or both denied. */
if (((access_masks[0] & NEW_ACE_WRITE_DATA) != 0)
!= ((access_masks[0] & NEW_ACE_APPEND_DATA) != 0))
return 1;
if (((access_masks[2] & NEW_ACE_WRITE_DATA) != 0)
!= ((access_masks[2] & NEW_ACE_APPEND_DATA) != 0))
return 1;
if (((access_masks[4] & NEW_ACE_WRITE_DATA) != 0)
!= ((access_masks[4] & NEW_ACE_APPEND_DATA) != 0))
return 1;
}
return 0;
}
# endif
#elif USE_ACL && HAVE_GETACL /* HP-UX */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb)
{
int i;
for (i = 0; i < count; i++)
{
struct acl_entry *ace = &entries[i];
if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP)
|| (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid)
|| (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP)))
return 1;
}
return 0;
}
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
aclv_nontrivial (int count, struct acl *entries)
{
int i;
for (i = 0; i < count; i++)
{
struct acl *ace = &entries[i];
/* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
We don't need to check ace->a_id in these cases. */
if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */
|| ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */
|| ace->a_type == CLASS_OBJ
|| ace->a_type == OTHER_OBJ))
return 1;
}
return 0;
}
# endif
#elif USE_ACL && (HAVE_ACLX_GET || HAVE_STATACL) /* AIX */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
acl_nontrivial (struct acl *a)
{
/* The normal way to iterate through an ACL is like this:
struct acl_entry *ace;
for (ace = a->acl_ext; ace != acl_last (a); ace = acl_nxt (ace))
{
struct ace_id *aei;
switch (ace->ace_type)
{
case ACC_PERMIT:
case ACC_DENY:
case ACC_SPECIFY:
...;
}
for (aei = ace->ace_id; aei != id_last (ace); aei = id_nxt (aei))
...
}
*/
return (acl_last (a) != a->acl_ext ? 1 : 0);
}
# if HAVE_ACLX_GET && defined ACL_AIX_WIP /* newer AIX */
/* Return 1 if the given ACL is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
acl_nfs4_nontrivial (nfs4_acl_int_t *a)
{
# if 1 /* let's try this first */
return (a->aclEntryN > 0 ? 1 : 0);
# else
int count = a->aclEntryN;
int i;
for (i = 0; i < count; i++)
{
nfs4_ace_int_t *ace = &a->aclEntry[i];
if (!((ace->flags & ACE4_ID_SPECIAL) != 0
&& (ace->aceWho.special_whoid == ACE4_WHO_OWNER
|| ace->aceWho.special_whoid == ACE4_WHO_GROUP
|| ace->aceWho.special_whoid == ACE4_WHO_EVERYONE)
&& ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE
&& ace->aceFlags == 0
&& (ace->aceMask & ~(ACE4_READ_DATA | ACE4_LIST_DIRECTORY
| ACE4_WRITE_DATA | ACE4_ADD_FILE
| ACE4_EXECUTE)) == 0))
return 1;
}
return 0;
# endif
}
# endif
#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
/* Test an ACL retrieved with ACL_GET.
Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
int
acl_nontrivial (int count, struct acl *entries)
{
int i;
for (i = 0; i < count; i++)
{
struct acl *ace = &entries[i];
/* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat().
If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat().
We don't need to check ace->a_id in these cases. */
if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */
|| ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */
|| ace->a_type == CLASS_OBJ
|| ace->a_type == OTHER_OBJ))
return 1;
}
return 0;
}
#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 NAME, obtained
through stat() or lstat(). */
int
file_has_acl (char const *name, struct stat const *sb)
{
#if USE_ACL
if (! S_ISLNK (sb->st_mode))
{
# if HAVE_ACL_GET_FILE
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
/* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
int ret;
if (HAVE_ACL_EXTENDED_FILE) /* Linux */
{
/* On Linux, acl_extended_file is an optimized function: It only
makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
ACL_TYPE_DEFAULT. */
ret = acl_extended_file (name);
}
else /* FreeBSD, Mac OS X, IRIX, Tru64 */
{
# if 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. There is no point in making
these two useless calls. The real ACL is retrieved through
acl_get_file (name, ACL_TYPE_EXTENDED). */
acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED);
if (acl)
{
ret = acl_extended_nontrivial (acl);
acl_free (acl);
}
else
ret = -1;
# else /* FreeBSD, IRIX, Tru64 */
acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
if (acl)
{
int saved_errno;
ret = acl_access_nontrivial (acl);
saved_errno = errno;
acl_free (acl);
errno = saved_errno;
# if HAVE_ACL_FREE_TEXT /* Tru64 */
/* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
returns NULL with errno not set. There is no point in
making this call. */
# else /* FreeBSD, IRIX */
/* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
either both succeed or both fail; it depends on the
file system. Therefore there is no point in making the second
call if the first one already failed. */
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;
}
# endif
}
else
ret = -1;
# endif
}
if (ret < 0)
return - acl_errno_valid (errno);
return ret;
# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
# if defined ACL_NO_TRIVIAL
/* Solaris 10 (newer version), which has additional API declared in
<sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
acl_fromtext, ...). */
return acl_trivial (name);
# 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. */
{
/* Initially, try to read the entries into a stack-allocated buffer.
Use malloc if it does not fit. */
enum
{
alloc_init = 4000 / sizeof (aclent_t), /* >= 3 */
alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t))
};
aclent_t buf[alloc_init];
size_t alloc = alloc_init;
aclent_t *entries = buf;
aclent_t *malloced = NULL;
int count;
for (;;)
{
count = acl (name, GETACL, alloc, entries);
if (count < 0 && errno == ENOSPC)
{
/* Increase the size of the buffer. */
free (malloced);
if (alloc > alloc_max / 2)
{
errno = ENOMEM;
return -1;
}
alloc = 2 * alloc; /* <= alloc_max */
entries = malloced =
(aclent_t *) malloc (alloc * sizeof (aclent_t));
if (entries == NULL)
{
errno = ENOMEM;
return -1;
}
continue;
}
break;
}
if (count < 0)
{
if (errno == ENOSYS || errno == ENOTSUP)
;
else
{
int saved_errno = errno;
free (malloced);
errno = saved_errno;
return -1;
}
}
else if (count == 0)
;
else
{
/* Don't use MIN_ACL_ENTRIES: It's set to 4 on Cygwin, but Cygwin
returns only 3 entries for files with no ACL. But this is safe:
If there are more than 4 entries, there cannot be only the
"user::", "group::", "other:", and "mask:" entries. */
if (count > 4)
{
free (malloced);
return 1;
}
if (acl_nontrivial (count, entries))
{
free (malloced);
return 1;
}
}
free (malloced);
}
# 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). */
{
/* Initially, try to read the entries into a stack-allocated buffer.
Use malloc if it does not fit. */
enum
{
alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
};
ace_t buf[alloc_init];
size_t alloc = alloc_init;
ace_t *entries = buf;
ace_t *malloced = NULL;
int count;
for (;;)
{
count = acl (name, ACE_GETACL, alloc, entries);
if (count < 0 && errno == ENOSPC)
{
/* Increase the size of the buffer. */
free (malloced);
if (alloc > alloc_max / 2)
{
errno = ENOMEM;
return -1;
}
alloc = 2 * alloc; /* <= alloc_max */
entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
if (entries == NULL)
{
errno = ENOMEM;
return -1;
}
continue;
}
break;
}
if (count < 0)
{
if (errno == ENOSYS || errno == EINVAL)
;
else
{
int saved_errno = errno;
free (malloced);
errno = saved_errno;
return -1;
}
}
else if (count == 0)
;
else
{
/* In the old (original Solaris 10) convention:
If there are more than 3 entries, there cannot be only the
ACE_OWNER, ACE_GROUP, ACE_OTHER entries.
In the newer Solaris 10 and Solaris 11 convention:
If there are more than 6 entries, there cannot be only the
ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with
NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with
NEW_ACE_ACCESS_DENIED_ACE_TYPE. */
if (count > 6)
{
free (malloced);
return 1;
}
if (acl_ace_nontrivial (count, entries))
{
free (malloced);
return 1;
}
}
free (malloced);
}
# endif
return 0;
# endif
# elif HAVE_GETACL /* HP-UX */
{
struct acl_entry entries[NACLENTRIES];
int count;
count = getacl (name, NACLENTRIES, entries);
if (count < 0)
{
/* ENOSYS is seen on newer HP-UX versions.
EOPNOTSUPP is typically seen on NFS mounts.
ENOTSUP was seen on Quantum StorNext file systems (cvfs). */
if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
;
else
return -1;
}
else if (count == 0)
return 0;
else /* count > 0 */
{
if (count > NACLENTRIES)
/* If NACLENTRIES cannot be trusted, use dynamic memory
allocation. */
abort ();
/* If there are more than 3 entries, there cannot be only the
(uid,%), (%,gid), (%,%) entries. */
if (count > 3)
return 1;
{
struct stat statbuf;
if (stat (name, &statbuf) < 0)
return -1;
return acl_nontrivial (count, entries, &statbuf);
}
}
}
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
{
struct acl entries[NACLVENTRIES];
int count;
count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
if (count < 0)
{
/* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
EINVAL is seen on NFS in HP-UX 11.31. */
if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
;
else
return -1;
}
else if (count == 0)
return 0;
else /* count > 0 */
{
if (count > NACLVENTRIES)
/* If NACLVENTRIES cannot be trusted, use dynamic memory
allocation. */
abort ();
/* If there are more than 4 entries, there cannot be only the
four base ACL entries. */
if (count > 4)
return 1;
return aclv_nontrivial (count, entries);
}
}
# endif
# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
acl_type_t type;
char aclbuf[1024];
void *acl = aclbuf;
size_t aclsize = sizeof (aclbuf);
mode_t mode;
for (;;)
{
/* The docs say that type being 0 is equivalent to ACL_ANY, but it
is not true, in AIX 5.3. */
type.u64 = ACL_ANY;
if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
break;
if (errno == ENOSYS)
return 0;
if (errno != ENOSPC)
{
if (acl != aclbuf)
{
int saved_errno = errno;
free (acl);
errno = saved_errno;
}
return -1;
}
aclsize = 2 * aclsize;
if (acl != aclbuf)
free (acl);
acl = malloc (aclsize);
if (acl == NULL)
{
errno = ENOMEM;
return -1;
}
}
if (type.u64 == ACL_AIXC)
{
int result = acl_nontrivial ((struct acl *) acl);
if (acl != aclbuf)
free (acl);
return result;
}
else if (type.u64 == ACL_NFS4)
{
int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl);
if (acl != aclbuf)
free (acl);
return result;
}
else
{
/* A newer type of ACL has been introduced in the system.
We should better support it. */
if (acl != aclbuf)
free (acl);
errno = EINVAL;
return -1;
}
# elif HAVE_STATACL /* older AIX */
union { struct acl a; char room[4096]; } u;
if (statacl (name, STX_NORMAL, &u.a, sizeof (u)) < 0)
return -1;
return acl_nontrivial (&u.a);
# elif HAVE_ACLSORT /* NonStop Kernel */
{
struct acl entries[NACLENTRIES];
int count;
count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
if (count < 0)
{
if (errno == ENOSYS || errno == ENOTSUP)
;
else
return -1;
}
else if (count == 0)
return 0;
else /* count > 0 */
{
if (count > NACLENTRIES)
/* If NACLENTRIES cannot be trusted, use dynamic memory
allocation. */
abort ();
/* If there are more than 4 entries, there cannot be only the
four base ACL entries. */
if (count > 4)
return 1;
return acl_nontrivial (count, entries);
}
}
# endif
}
#endif
return 0;
}

View file

@ -21,7 +21,7 @@
# the same distribution terms as the rest of that program.
#
# Generated by gnulib-tool.
# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
MOSTLYCLEANFILES += core *.stackdump
@ -217,6 +217,40 @@ EXTRA_libgnu_a_SOURCES += dup2.c
## end gnulib module dup2
## begin gnulib module errno
BUILT_SOURCES += $(ERRNO_H)
# We need the following in order to create <errno.h> when the system
# doesn't have one that is POSIX compliant.
if GL_GENERATE_ERRNO_H
errno.h: errno.in.h $(top_builddir)/config.status
$(AM_V_GEN)rm -f $@-t $@ && \
{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \
sed -e 's|@''GUARD_PREFIX''@|GL|g' \
-e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
-e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
-e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
-e 's|@''NEXT_ERRNO_H''@|$(NEXT_ERRNO_H)|g' \
-e 's|@''EMULTIHOP_HIDDEN''@|$(EMULTIHOP_HIDDEN)|g' \
-e 's|@''EMULTIHOP_VALUE''@|$(EMULTIHOP_VALUE)|g' \
-e 's|@''ENOLINK_HIDDEN''@|$(ENOLINK_HIDDEN)|g' \
-e 's|@''ENOLINK_VALUE''@|$(ENOLINK_VALUE)|g' \
-e 's|@''EOVERFLOW_HIDDEN''@|$(EOVERFLOW_HIDDEN)|g' \
-e 's|@''EOVERFLOW_VALUE''@|$(EOVERFLOW_VALUE)|g' \
< $(srcdir)/errno.in.h; \
} > $@-t && \
mv $@-t $@
else
errno.h: $(top_builddir)/config.status
rm -f $@
endif
MOSTLYCLEANFILES += errno.h errno.h-t
EXTRA_DIST += errno.in.h
## end gnulib module errno
## begin gnulib module euidaccess
if gl_GNULIB_ENABLED_euidaccess
@ -561,6 +595,16 @@ EXTRA_libgnu_a_SOURCES += putenv.c
## end gnulib module putenv
## begin gnulib module qacl
libgnu_a_SOURCES += acl-errno-valid.c file-has-acl.c qcopy-acl.c qset-acl.c
EXTRA_DIST += acl-internal.h acl.h acl_entries.c
EXTRA_libgnu_a_SOURCES += acl_entries.c
## end gnulib module qacl
## begin gnulib module readlink

View file

@ -23,7 +23,8 @@ ALL = gnulib
LOCAL_FLAGS = -I. -I../nt/inc -I../src
LIBS =
GNULIBOBJS = $(BLD)/c-ctype.$(O) \
GNULIBOBJS = $(BLD)/acl-errno-valid.$(O) \
$(BLD)/c-ctype.$(O) \
$(BLD)/c-strcasecmp.$(O) \
$(BLD)/c-strncasecmp.$(O) \
$(BLD)/close-stream.$(O) \
@ -109,6 +110,12 @@ SIG2STR_H = $(GNU_LIB)/sig2str.h \
STAT_TIME_H = $(GNU_LIB)/stat-time.h \
$(NT_INC)/sys/stat.h
$(BLD)/acl-errno-valid.$(O) : \
$(GNU_LIB)/acl-errno-valid.c \
$(CONFIG_H) \
$(GNU_LIB)/acl.h \
$(NT_INC)/stdbool.h
$(BLD)/c-ctype.$(O) : \
$(GNU_LIB)/c-ctype.c \
$(CONFIG_H) \

583
lib/qcopy-acl.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
#include <config.h>
#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 <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
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
}

676
lib/qset-acl.c Normal file
View file

@ -0,0 +1,676 @@
/* qset-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 <http://www.gnu.org/licenses/>.
Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */
#include <config.h>
#define ACL_INTERNAL_INLINE _GL_EXTERN_INLINE
#include "acl.h"
#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 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. */
int
chmod_or_fchmod (const char *name, int desc, mode_t mode)
{
if (HAVE_FCHMOD && desc != -1)
return fchmod (desc, mode);
else
return chmod (name, mode);
}
/* 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. Return -1 and set errno upon failure. */
int
qset_acl (char const *name, int desc, mode_t mode)
{
#if USE_ACL
# if 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 */
/* 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
acl_t acl;
int ret;
if (HAVE_ACL_FROM_MODE) /* Linux */
{
acl = acl_from_mode (mode);
if (!acl)
return -1;
}
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_FREE_TEXT /* Tru64 */
char acl_text[] = "u::---,g::---,o::---,";
# else /* FreeBSD, IRIX */
char acl_text[] = "u::---,g::---,o::---";
# endif
if (mode & S_IRUSR) acl_text[ 3] = 'r';
if (mode & S_IWUSR) acl_text[ 4] = 'w';
if (mode & S_IXUSR) acl_text[ 5] = 'x';
if (mode & S_IRGRP) acl_text[10] = 'r';
if (mode & S_IWGRP) acl_text[11] = 'w';
if (mode & S_IXGRP) acl_text[12] = 'x';
if (mode & S_IROTH) acl_text[17] = 'r';
if (mode & S_IWOTH) acl_text[18] = 'w';
if (mode & S_IXOTH) acl_text[19] = 'x';
acl = acl_from_text (acl_text);
if (!acl)
return -1;
}
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_errno_valid (errno))
return chmod_or_fchmod (name, desc, mode);
errno = saved_errno;
return -1;
}
else
acl_free (acl);
if (S_ISDIR (mode) && acl_delete_def_file (name))
return -1;
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. */
return chmod_or_fchmod (name, desc, mode);
}
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;
/* 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_EXTENDED);
if (acl)
{
acl_free (acl);
acl = acl_init (0);
if (acl)
{
if (HAVE_ACL_SET_FD && desc != -1)
ret = acl_set_fd (desc, acl);
else
ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
if (ret != 0)
{
int saved_errno = errno;
acl_free (acl);
if (! acl_errno_valid (saved_errno))
return chmod_or_fchmod (name, desc, mode);
errno = saved_errno;
return -1;
}
acl_free (acl);
}
}
/* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
return chmod_or_fchmod (name, desc, mode);
# endif
# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
int done_setacl = 0;
# 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). */
/* The flags in the ace_t structure changed in a binary incompatible way
when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
How to distinguish the two conventions at runtime?
We fetch the existing ACL. In the old convention, usually three ACEs have
a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
In the new convention, these values are not used. */
int convention;
{
/* Initially, try to read the entries into a stack-allocated buffer.
Use malloc if it does not fit. */
enum
{
alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
};
ace_t buf[alloc_init];
size_t alloc = alloc_init;
ace_t *entries = buf;
ace_t *malloced = NULL;
int count;
for (;;)
{
count = (desc != -1
? facl (desc, ACE_GETACL, alloc, entries)
: acl (name, ACE_GETACL, alloc, entries));
if (count < 0 && errno == ENOSPC)
{
/* Increase the size of the buffer. */
free (malloced);
if (alloc > alloc_max / 2)
{
errno = ENOMEM;
return -1;
}
alloc = 2 * alloc; /* <= alloc_max */
entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
if (entries == NULL)
{
errno = ENOMEM;
return -1;
}
continue;
}
break;
}
if (count <= 0)
convention = -1;
else
{
int i;
convention = 0;
for (i = 0; i < count; i++)
if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
{
convention = 1;
break;
}
}
free (malloced);
}
if (convention >= 0)
{
ace_t entries[6];
int count;
int ret;
if (convention)
{
/* Running on Solaris 10. */
entries[0].a_type = OLD_ALLOW;
entries[0].a_flags = OLD_ACE_OWNER;
entries[0].a_who = 0; /* irrelevant */
entries[0].a_access_mask = (mode >> 6) & 7;
entries[1].a_type = OLD_ALLOW;
entries[1].a_flags = OLD_ACE_GROUP;
entries[1].a_who = 0; /* irrelevant */
entries[1].a_access_mask = (mode >> 3) & 7;
entries[2].a_type = OLD_ALLOW;
entries[2].a_flags = OLD_ACE_OTHER;
entries[2].a_who = 0;
entries[2].a_access_mask = mode & 7;
count = 3;
}
else
{
/* Running on Solaris 10 (newer version) or Solaris 11.
The details here were found through "/bin/ls -lvd somefiles". */
entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
entries[0].a_flags = NEW_ACE_OWNER;
entries[0].a_who = 0; /* irrelevant */
entries[0].a_access_mask = 0;
entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
entries[1].a_flags = NEW_ACE_OWNER;
entries[1].a_who = 0; /* irrelevant */
entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
| NEW_ACE_WRITE_ATTRIBUTES
| NEW_ACE_WRITE_ACL
| NEW_ACE_WRITE_OWNER;
if (mode & 0400)
entries[1].a_access_mask |= NEW_ACE_READ_DATA;
else
entries[0].a_access_mask |= NEW_ACE_READ_DATA;
if (mode & 0200)
entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
else
entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
if (mode & 0100)
entries[1].a_access_mask |= NEW_ACE_EXECUTE;
else
entries[0].a_access_mask |= NEW_ACE_EXECUTE;
entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
entries[2].a_who = 0; /* irrelevant */
entries[2].a_access_mask = 0;
entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
entries[3].a_who = 0; /* irrelevant */
entries[3].a_access_mask = 0;
if (mode & 0040)
entries[3].a_access_mask |= NEW_ACE_READ_DATA;
else
entries[2].a_access_mask |= NEW_ACE_READ_DATA;
if (mode & 0020)
entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
else
entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
if (mode & 0010)
entries[3].a_access_mask |= NEW_ACE_EXECUTE;
else
entries[2].a_access_mask |= NEW_ACE_EXECUTE;
entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
entries[4].a_flags = NEW_ACE_EVERYONE;
entries[4].a_who = 0;
entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
| NEW_ACE_WRITE_ATTRIBUTES
| NEW_ACE_WRITE_ACL
| NEW_ACE_WRITE_OWNER;
entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
entries[5].a_flags = NEW_ACE_EVERYONE;
entries[5].a_who = 0;
entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
| NEW_ACE_READ_ATTRIBUTES
| NEW_ACE_READ_ACL
| NEW_ACE_SYNCHRONIZE;
if (mode & 0004)
entries[5].a_access_mask |= NEW_ACE_READ_DATA;
else
entries[4].a_access_mask |= NEW_ACE_READ_DATA;
if (mode & 0002)
entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
else
entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
if (mode & 0001)
entries[5].a_access_mask |= NEW_ACE_EXECUTE;
else
entries[4].a_access_mask |= NEW_ACE_EXECUTE;
count = 6;
}
if (desc != -1)
ret = facl (desc, ACE_SETACL, count, entries);
else
ret = acl (name, ACE_SETACL, count, entries);
if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
{
if (errno == ENOSYS)
return chmod_or_fchmod (name, desc, mode);
return -1;
}
if (ret == 0)
done_setacl = 1;
}
# endif
if (!done_setacl)
{
aclent_t entries[3];
int ret;
entries[0].a_type = USER_OBJ;
entries[0].a_id = 0; /* irrelevant */
entries[0].a_perm = (mode >> 6) & 7;
entries[1].a_type = GROUP_OBJ;
entries[1].a_id = 0; /* irrelevant */
entries[1].a_perm = (mode >> 3) & 7;
entries[2].a_type = OTHER_OBJ;
entries[2].a_id = 0;
entries[2].a_perm = mode & 7;
if (desc != -1)
ret = facl (desc, SETACL,
sizeof (entries) / sizeof (aclent_t), entries);
else
ret = acl (name, SETACL,
sizeof (entries) / sizeof (aclent_t), entries);
if (ret < 0)
{
if (errno == ENOSYS || errno == EOPNOTSUPP)
return chmod_or_fchmod (name, desc, mode);
return -1;
}
}
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. */
return chmod_or_fchmod (name, desc, mode);
}
return 0;
# elif HAVE_GETACL /* HP-UX */
struct stat statbuf;
int ret;
if (desc != -1)
ret = fstat (desc, &statbuf);
else
ret = stat (name, &statbuf);
if (ret < 0)
return -1;
{
struct acl_entry entries[3];
entries[0].uid = statbuf.st_uid;
entries[0].gid = ACL_NSGROUP;
entries[0].mode = (mode >> 6) & 7;
entries[1].uid = ACL_NSUSER;
entries[1].gid = statbuf.st_gid;
entries[1].mode = (mode >> 3) & 7;
entries[2].uid = ACL_NSUSER;
entries[2].gid = ACL_NSGROUP;
entries[2].mode = mode & 7;
if (desc != -1)
ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
else
ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
}
if (ret < 0)
{
if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
return -1;
# if HAVE_ACLV_H /* HP-UX >= 11.11 */
{
struct acl entries[4];
entries[0].a_type = USER_OBJ;
entries[0].a_id = 0; /* irrelevant */
entries[0].a_perm = (mode >> 6) & 7;
entries[1].a_type = GROUP_OBJ;
entries[1].a_id = 0; /* irrelevant */
entries[1].a_perm = (mode >> 3) & 7;
entries[2].a_type = CLASS_OBJ;
entries[2].a_id = 0;
entries[2].a_perm = (mode >> 3) & 7;
entries[3].a_type = OTHER_OBJ;
entries[3].a_id = 0;
entries[3].a_perm = mode & 7;
ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
if (ret > 0)
abort ();
if (ret < 0)
{
if (0)
return chmod_or_fchmod (name, desc, mode);
return -1;
}
ret = acl ((char *) name, ACL_SET,
sizeof (entries) / sizeof (struct acl), entries);
if (ret < 0)
{
if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
return chmod_or_fchmod (name, desc, mode);
return -1;
}
}
# else
return chmod_or_fchmod (name, desc, mode);
# endif
}
if (mode & (S_ISUID | S_ISGID | S_ISVTX))
{
/* We did not call chmod so far, so the special bits have not yet
been set. */
return chmod_or_fchmod (name, desc, mode);
}
return 0;
# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
acl_type_list_t types;
size_t types_size = sizeof (types);
acl_type_t type;
if (aclx_gettypes (name, &types, &types_size) < 0
|| types.num_entries == 0)
return chmod_or_fchmod (name, desc, mode);
/* XXX Do we need to clear all types of ACLs for the given file, or is it
sufficient to clear the first one? */
type = types.entries[0];
if (type.u64 == ACL_AIXC)
{
union { struct acl a; char room[128]; } u;
int ret;
u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
u.a.acl_mode = mode & ~(S_IXACL | 0777);
u.a.u_access = (mode >> 6) & 7;
u.a.g_access = (mode >> 3) & 7;
u.a.o_access = mode & 7;
if (desc != -1)
ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
type, &u.a, u.a.acl_len, mode);
else
ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
type, &u.a, u.a.acl_len, mode);
if (!(ret < 0 && errno == ENOSYS))
return ret;
}
else if (type.u64 == ACL_NFS4)
{
union { nfs4_acl_int_t a; char room[128]; } u;
nfs4_ace_int_t *ace;
int ret;
u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
u.a.aclEntryN = 0;
ace = &u.a.aclEntry[0];
{
ace->flags = ACE4_ID_SPECIAL;
ace->aceWho.special_whoid = ACE4_WHO_OWNER;
ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->aceFlags = 0;
ace->aceMask =
(mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
| (mode & 0200
? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
| ACE4_ADD_SUBDIRECTORY
: 0)
| (mode & 0100 ? ACE4_EXECUTE : 0);
ace->aceWhoString[0] = '\0';
ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
u.a.aclEntryN++;
}
{
ace->flags = ACE4_ID_SPECIAL;
ace->aceWho.special_whoid = ACE4_WHO_GROUP;
ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->aceFlags = 0;
ace->aceMask =
(mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
| (mode & 0020
? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
| ACE4_ADD_SUBDIRECTORY
: 0)
| (mode & 0010 ? ACE4_EXECUTE : 0);
ace->aceWhoString[0] = '\0';
ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
u.a.aclEntryN++;
}
{
ace->flags = ACE4_ID_SPECIAL;
ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
ace->aceFlags = 0;
ace->aceMask =
(mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
| (mode & 0002
? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
| ACE4_ADD_SUBDIRECTORY
: 0)
| (mode & 0001 ? ACE4_EXECUTE : 0);
ace->aceWhoString[0] = '\0';
ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
u.a.aclEntryN++;
}
u.a.aclLength = (char *) ace - (char *) &u.a;
if (desc != -1)
ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
type, &u.a, u.a.aclLength, mode);
else
ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
type, &u.a, u.a.aclLength, mode);
if (!(ret < 0 && errno == ENOSYS))
return ret;
}
return chmod_or_fchmod (name, desc, mode);
# elif HAVE_STATACL /* older AIX */
union { struct acl a; char room[128]; } u;
int ret;
u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
u.a.acl_mode = mode & ~(S_IXACL | 0777);
u.a.u_access = (mode >> 6) & 7;
u.a.g_access = (mode >> 3) & 7;
u.a.o_access = mode & 7;
if (desc != -1)
ret = fchacl (desc, &u.a, u.a.acl_len);
else
ret = chacl (name, &u.a, u.a.acl_len);
if (ret < 0 && errno == ENOSYS)
return chmod_or_fchmod (name, desc, mode);
return ret;
# elif HAVE_ACLSORT /* NonStop Kernel */
struct acl entries[4];
int ret;
entries[0].a_type = USER_OBJ;
entries[0].a_id = 0; /* irrelevant */
entries[0].a_perm = (mode >> 6) & 7;
entries[1].a_type = GROUP_OBJ;
entries[1].a_id = 0; /* irrelevant */
entries[1].a_perm = (mode >> 3) & 7;
entries[2].a_type = CLASS_OBJ;
entries[2].a_id = 0;
entries[2].a_perm = (mode >> 3) & 7;
entries[3].a_type = OTHER_OBJ;
entries[3].a_id = 0;
entries[3].a_perm = mode & 7;
ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
if (ret > 0)
abort ();
if (ret < 0)
{
if (0)
return chmod_or_fchmod (name, desc, mode);
return -1;
}
ret = acl ((char *) name, ACL_SET,
sizeof (entries) / sizeof (struct acl), entries);
if (ret < 0)
{
if (0)
return chmod_or_fchmod (name, desc, mode);
return -1;
}
if (mode & (S_ISUID | S_ISGID | S_ISVTX))
{
/* We did not call chmod so far, so the special bits have not yet
been set. */
return chmod_or_fchmod (name, desc, mode);
}
return 0;
# else /* Unknown flavor of ACLs */
return chmod_or_fchmod (name, desc, mode);
# endif
#else /* !USE_ACL */
return chmod_or_fchmod (name, desc, mode);
#endif
}

164
m4/acl.m4 Normal file
View file

@ -0,0 +1,164 @@
# acl.m4 - check for access control list (ACL) primitives
# serial 15
# Copyright (C) 2002, 2004-2013 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# Written by Paul Eggert and Jim Meyering.
AC_DEFUN([gl_FUNC_ACL],
[
AC_ARG_ENABLE([acl],
AS_HELP_STRING([--disable-acl], [do not support ACLs]),
, [enable_acl=auto])
LIB_ACL=
use_acl=0
if test "x$enable_acl" != "xno"; then
dnl On all platforms, the ACL related API is declared in <sys/acl.h>.
AC_CHECK_HEADERS([sys/acl.h])
if test $ac_cv_header_sys_acl_h = yes; then
ac_save_LIBS=$LIBS
dnl Test for POSIX-draft-like API (Linux, FreeBSD, Mac OS X, IRIX, Tru64).
dnl -lacl is needed on Linux, -lpacl is needed on OSF/1.
if test $use_acl = 0; then
AC_SEARCH_LIBS([acl_get_file], [acl pacl],
[if test "$ac_cv_search_acl_get_file" != "none required"; then
LIB_ACL=$ac_cv_search_acl_get_file
fi
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_fd_np acl_delete_file_np \
acl_copy_ext_native acl_create_entry_np \
acl_to_short_text acl_free_text])
# If the acl_get_file bug is detected, don't enable the ACL support.
gl_ACL_GET_FILE([use_acl=1], [])
if test $use_acl = 1; then
dnl On Linux, additional API is declared in <acl/libacl.h>.
AC_CHECK_HEADERS([acl/libacl.h])
AC_REPLACE_FUNCS([acl_entries])
AC_CACHE_CHECK([for ACL_FIRST_ENTRY],
[gl_cv_acl_ACL_FIRST_ENTRY],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[#include <sys/types.h>
#include <sys/acl.h>
int type = ACL_FIRST_ENTRY;]])],
[gl_cv_acl_ACL_FIRST_ENTRY=yes],
[gl_cv_acl_ACL_FIRST_ENTRY=no])])
if test $gl_cv_acl_ACL_FIRST_ENTRY = yes; then
AC_DEFINE([HAVE_ACL_FIRST_ENTRY], [1],
[Define to 1 if the constant ACL_FIRST_ENTRY exists.])
fi
dnl On Mac OS X, other types of ACLs are supported.
AC_CACHE_CHECK([for ACL_TYPE_EXTENDED],
[gl_cv_acl_ACL_TYPE_EXTENDED],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[#include <sys/types.h>
#include <sys/acl.h>
int type = ACL_TYPE_EXTENDED;]])],
[gl_cv_acl_ACL_TYPE_EXTENDED=yes],
[gl_cv_acl_ACL_TYPE_EXTENDED=no])])
if test $gl_cv_acl_ACL_TYPE_EXTENDED = yes; then
AC_DEFINE([HAVE_ACL_TYPE_EXTENDED], [1],
[Define to 1 if the ACL type ACL_TYPE_EXTENDED exists.])
fi
else
LIB_ACL=
fi
])
fi
dnl Test for Solaris API (Solaris, Cygwin).
if test $use_acl = 0; then
AC_CHECK_FUNCS([facl])
if test $ac_cv_func_facl = yes; then
AC_SEARCH_LIBS([acl_trivial], [sec],
[if test "$ac_cv_search_acl_trivial" != "none required"; then
LIB_ACL=$ac_cv_search_acl_trivial
fi
])
AC_CHECK_FUNCS([acl_trivial])
use_acl=1
fi
fi
dnl Test for HP-UX API.
if test $use_acl = 0; then
AC_CHECK_FUNCS([getacl])
if test $ac_cv_func_getacl = yes; then
use_acl=1
fi
dnl Test for HP-UX 11.11 API.
AC_CHECK_HEADERS([aclv.h], [], [], [#include <sys/types.h>])
fi
dnl Test for AIX API (AIX 5.3 or newer).
if test $use_acl = 0; then
AC_CHECK_FUNCS([aclx_get])
if test $ac_cv_func_aclx_get = yes; then
use_acl=1
fi
fi
dnl Test for older AIX API.
if test $use_acl = 0 || test "$ac_cv_func_aclx_get" = yes; then
AC_CHECK_FUNCS([statacl])
if test $ac_cv_func_statacl = yes; then
use_acl=1
fi
fi
dnl Test for NonStop Kernel API.
if test $use_acl = 0; then
AC_CHECK_FUNCS([aclsort])
if test $ac_cv_func_aclsort = yes; then
use_acl=1
fi
fi
LIBS=$ac_save_LIBS
fi
if test "x$enable_acl$use_acl" = "xyes0"; then
AC_MSG_ERROR([ACLs enabled but support not detected])
elif test "x$enable_acl$use_acl" = "xauto0"; then
AC_MSG_WARN([libacl development library was not found or not usable.])
AC_MSG_WARN([AC_PACKAGE_NAME will be built without ACL support.])
fi
fi
AC_SUBST([LIB_ACL])
AC_DEFINE_UNQUOTED([USE_ACL], [$use_acl],
[Define to nonzero if you want access control list support.])
USE_ACL=$use_acl
AC_SUBST([USE_ACL])
])
# gl_ACL_GET_FILE(IF-WORKS, IF-NOT)
# -------------------------------------
# If 'acl_get_file' works (does not have a particular bug),
# run IF-WORKS, otherwise, IF-NOT.
# This tests for a Darwin 8.7.0 bug, whereby acl_get_file returns NULL,
# but sets errno = ENOENT for an existing file or directory.
AC_DEFUN([gl_ACL_GET_FILE],
[
AC_CACHE_CHECK([for working acl_get_file], [gl_cv_func_working_acl_get_file],
[AC_RUN_IFELSE(
[AC_LANG_PROGRAM(
[[#include <sys/types.h>
#include <sys/acl.h>
#include <errno.h>
]],
[[if (!acl_get_file (".", ACL_TYPE_ACCESS) && errno == ENOENT)
return 1;
return 0;
]])],
[gl_cv_func_working_acl_get_file=yes],
[gl_cv_func_working_acl_get_file=no],
[gl_cv_func_working_acl_get_file=cross-compiling])])
AS_IF([test $gl_cv_func_working_acl_get_file = yes], [$1], [$2])
])

137
m4/errno_h.m4 Normal file
View file

@ -0,0 +1,137 @@
# errno_h.m4 serial 12
dnl Copyright (C) 2004, 2006, 2008-2013 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
AC_DEFUN_ONCE([gl_HEADER_ERRNO_H],
[
AC_REQUIRE([AC_PROG_CC])
AC_CACHE_CHECK([for complete errno.h], [gl_cv_header_errno_h_complete], [
AC_EGREP_CPP([booboo],[
#include <errno.h>
#if !defined ETXTBSY
booboo
#endif
#if !defined ENOMSG
booboo
#endif
#if !defined EIDRM
booboo
#endif
#if !defined ENOLINK
booboo
#endif
#if !defined EPROTO
booboo
#endif
#if !defined EMULTIHOP
booboo
#endif
#if !defined EBADMSG
booboo
#endif
#if !defined EOVERFLOW
booboo
#endif
#if !defined ENOTSUP
booboo
#endif
#if !defined ENETRESET
booboo
#endif
#if !defined ECONNABORTED
booboo
#endif
#if !defined ESTALE
booboo
#endif
#if !defined EDQUOT
booboo
#endif
#if !defined ECANCELED
booboo
#endif
#if !defined EOWNERDEAD
booboo
#endif
#if !defined ENOTRECOVERABLE
booboo
#endif
#if !defined EILSEQ
booboo
#endif
],
[gl_cv_header_errno_h_complete=no],
[gl_cv_header_errno_h_complete=yes])
])
if test $gl_cv_header_errno_h_complete = yes; then
ERRNO_H=''
else
gl_NEXT_HEADERS([errno.h])
ERRNO_H='errno.h'
fi
AC_SUBST([ERRNO_H])
AM_CONDITIONAL([GL_GENERATE_ERRNO_H], [test -n "$ERRNO_H"])
gl_REPLACE_ERRNO_VALUE([EMULTIHOP])
gl_REPLACE_ERRNO_VALUE([ENOLINK])
gl_REPLACE_ERRNO_VALUE([EOVERFLOW])
])
# Assuming $1 = EOVERFLOW.
# The EOVERFLOW errno value ought to be defined in <errno.h>, according to
# POSIX. But some systems (like OpenBSD 4.0 or AIX 3) don't define it, and
# some systems (like OSF/1) define it when _XOPEN_SOURCE_EXTENDED is defined.
# Check for the value of EOVERFLOW.
# Set the variables EOVERFLOW_HIDDEN and EOVERFLOW_VALUE.
AC_DEFUN([gl_REPLACE_ERRNO_VALUE],
[
if test -n "$ERRNO_H"; then
AC_CACHE_CHECK([for ]$1[ value], [gl_cv_header_errno_h_]$1, [
AC_EGREP_CPP([yes],[
#include <errno.h>
#ifdef ]$1[
yes
#endif
],
[gl_cv_header_errno_h_]$1[=yes],
[gl_cv_header_errno_h_]$1[=no])
if test $gl_cv_header_errno_h_]$1[ = no; then
AC_EGREP_CPP([yes],[
#define _XOPEN_SOURCE_EXTENDED 1
#include <errno.h>
#ifdef ]$1[
yes
#endif
], [gl_cv_header_errno_h_]$1[=hidden])
if test $gl_cv_header_errno_h_]$1[ = hidden; then
dnl The macro exists but is hidden.
dnl Define it to the same value.
AC_COMPUTE_INT([gl_cv_header_errno_h_]$1, $1, [
#define _XOPEN_SOURCE_EXTENDED 1
#include <errno.h>
/* The following two lines are a workaround against an autoconf-2.52 bug. */
#include <stdio.h>
#include <stdlib.h>
])
fi
fi
])
case $gl_cv_header_errno_h_]$1[ in
yes | no)
]$1[_HIDDEN=0; ]$1[_VALUE=
;;
*)
]$1[_HIDDEN=1; ]$1[_VALUE="$gl_cv_header_errno_h_]$1["
;;
esac
AC_SUBST($1[_HIDDEN])
AC_SUBST($1[_VALUE])
fi
])
dnl Autoconf >= 2.61 has AC_COMPUTE_INT built-in.
dnl Remove this when we can assume autoconf >= 2.61.
m4_ifdef([AC_COMPUTE_INT], [], [
AC_DEFUN([AC_COMPUTE_INT], [_AC_COMPUTE_INT([$2],[$1],[$3],[$4])])
])

View file

@ -56,6 +56,7 @@ AC_DEFUN([gl_EARLY],
# Code from module dtotimespec:
# Code from module dup2:
# Code from module environ:
# Code from module errno:
# Code from module euidaccess:
# Code from module execinfo:
# Code from module extensions:
@ -94,6 +95,7 @@ AC_DEFUN([gl_EARLY],
# Code from module pselect:
# Code from module pthread_sigmask:
# Code from module putenv:
# Code from module qacl:
# Code from module readlink:
# Code from module readlinkat:
# Code from module root-uid:
@ -179,6 +181,7 @@ AC_DEFUN([gl_INIT],
gl_UNISTD_MODULE_INDICATOR([dup2])
gl_ENVIRON
gl_UNISTD_MODULE_INDICATOR([environ])
gl_HEADER_ERRNO_H
gl_EXECINFO_H
AC_REQUIRE([gl_EXTERN_INLINE])
gl_FUNC_FACCESSAT
@ -287,6 +290,7 @@ AC_DEFUN([gl_INIT],
gl_PREREQ_PUTENV
fi
gl_STDLIB_MODULE_INDICATOR([putenv])
gl_FUNC_ACL
gl_FUNC_READLINK
if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then
AC_LIBOBJ([readlink])
@ -733,6 +737,10 @@ AC_DEFUN([gl_FILE_LIST], [
build-aux/snippet/arg-nonnull.h
build-aux/snippet/c++defs.h
build-aux/snippet/warn-on-use.h
lib/acl-errno-valid.c
lib/acl-internal.h
lib/acl.h
lib/acl_entries.c
lib/alloca.in.h
lib/allocator.c
lib/allocator.h
@ -751,6 +759,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/dtoastr.c
lib/dtotimespec.c
lib/dup2.c
lib/errno.in.h
lib/euidaccess.c
lib/execinfo.c
lib/execinfo.in.h
@ -758,6 +767,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/fcntl.in.h
lib/fdatasync.c
lib/fdopendir.c
lib/file-has-acl.c
lib/filemode.c
lib/filemode.h
lib/fpending.c
@ -792,6 +802,8 @@ AC_DEFUN([gl_FILE_LIST], [
lib/pselect.c
lib/pthread_sigmask.c
lib/putenv.c
lib/qcopy-acl.c
lib/qset-acl.c
lib/readlink.c
lib/readlinkat.c
lib/root-uid.h
@ -843,6 +855,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/verify.h
lib/xalloc-oversized.h
m4/00gnulib.m4
m4/acl.m4
m4/alloca.m4
m4/c-strtod.m4
m4/clock_time.m4
@ -850,6 +863,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/dirent_h.m4
m4/dup2.m4
m4/environ.m4
m4/errno_h.m4
m4/euidaccess.m4
m4/execinfo.m4
m4/extensions.m4

View file

@ -1,3 +1,9 @@
2013-05-07 Paul Eggert <eggert@cs.ucla.edu>
Use Gnulib ACL implementation, for benefit of Solaris etc. (Bug#14295)
* config.nt (HAVE_ACL_SET_FILE): Rename from HAVE_POSIX_ACL.
* inc/ms-w32.h (EOPNOTSUPP): New macro.
2013-04-09 Ken Brown <kbrown@cornell.edu>
* emacs.rc: Use 64-bit manifest for 64-bit Cygwin build.

View file

@ -756,7 +756,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#undef HAVE_PNG_H
/* Define to 1 if using POSIX ACL support. */
#define HAVE_POSIX_ACL 1
#define HAVE_ACL_SET_FILE 1
/* Define to 1 if you have the `posix_memalign' function. */
#undef HAVE_POSIX_MEMALIGN

View file

@ -346,6 +346,12 @@ extern struct tm *localtime_r (time_t const * restrict, struct tm * restrict);
#define ENOTSUP ENOSYS
#endif
/* WINDOWSNT <errno.h> doesn't define EOPNOTSUPP, and we don't have
'configure' working yet so we can't rely on the Gnulib replacement
errno.h defining EOPNOTSUPP. Work around the problem by defining
it here. */
#define EOPNOTSUPP 130
#ifdef _MSC_VER
typedef int sigset_t;
typedef int ssize_t;

View file

@ -1,5 +1,16 @@
2013-05-07 Paul Eggert <eggert@cs.ucla.edu>
Use Gnulib ACL implementation, for benefit of Solaris etc. (Bug#14295)
* Makefile.in (LIB_ACL): New macro.
(LIBACL_LIBS): Remove.
(LIBES): Use LIB_ACL, not LIBACL_LIBS.
* fileio.c: Include <acl.h>.
Use HAVE_ACL_SET_FILE rather than HAVE_POSIX_ACL.
(ACL_NOT_WELL_SUPPORTED): Remove. All uses replaced by
!acl_errno_valid.
(Fcopy_file) [!WINDOWSNT]: Use qcopy_acl instead of rolling
it ourselves.
* unexelf.c: Don't assume ElfW (Half) fits in int.
(entry_address, find_section, unexec): Use ptrdiff_t, not int,
when dealing with ElfW (Half) values, since they can exceed 2**31

View file

@ -137,6 +137,7 @@ LIBOTF_LIBS = @LIBOTF_LIBS@
M17N_FLT_CFLAGS = @M17N_FLT_CFLAGS@
M17N_FLT_LIBS = @M17N_FLT_LIBS@
LIB_ACL=@LIB_ACL@
LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
LIB_EACCESS=@LIB_EACCESS@
LIB_FDATASYNC=@LIB_FDATASYNC@
@ -288,8 +289,6 @@ LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
LIBACL_LIBS = @LIBACL_LIBS@
LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@
INTERVALS_H = dispextern.h intervals.h composite.h
@ -397,13 +396,13 @@ ALLOBJS = $(VMLIMIT_OBJ) $(obj) $(otherobj)
## Construct full set of libraries to be linked.
LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
$(LIBX_OTHER) $(LIBSOUND) \
$(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
$(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_ACL) $(LIB_CLOCK_GETTIME) \
$(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
$(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) \
$(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
$(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
$(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
$(LIBACL_LIBS) $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(LIB_PTHREAD_SIGMASK) \
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(LIB_PTHREAD_SIGMASK) \
$(LIB_MATH)
all: emacs$(EXEEXT) $(OTHER_FILES)

View file

@ -36,7 +36,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <selinux/context.h>
#endif
#ifdef HAVE_POSIX_ACL
#ifdef HAVE_ACL_SET_FILE
#include <sys/acl.h>
#endif
@ -81,26 +81,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#define DRIVE_LETTER(x) c_tolower (x)
#endif
#ifdef HAVE_POSIX_ACL
/* FIXME: this macro was copied from gnulib's private acl-internal.h
header file. */
/* 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)
#elif defined WINDOWSNT
#define ACL_NOT_WELL_SUPPORTED(Err) ((Err) == ENOTSUP)
#else
#define ACL_NOT_WELL_SUPPORTED(Err) \
((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY)
#endif
#endif /* HAVE_POSIX_ACL */
#include "systime.h"
#include <acl.h>
#include <allocator.h>
#include <careadlinkat.h>
#include <stat-time.h>
@ -1988,7 +1970,7 @@ entries (depending on how Emacs was built). */)
security_context_t con;
int conlength = 0;
#endif
#ifdef HAVE_POSIX_ACL
#ifdef WINDOWSNT
acl_t acl = NULL;
#endif
@ -2028,11 +2010,9 @@ entries (depending on how Emacs was built). */)
#ifdef WINDOWSNT
if (!NILP (preserve_extended_attributes))
{
#ifdef HAVE_POSIX_ACL
acl = acl_get_file (SDATA (encoded_file), ACL_TYPE_ACCESS);
if (acl == NULL && !ACL_NOT_WELL_SUPPORTED (errno))
if (acl == NULL && acl_errno_valid (errno))
report_file_error ("Getting ACL", Fcons (file, Qnil));
#endif
}
if (!CopyFile (SDATA (encoded_file),
SDATA (encoded_newname),
@ -2069,17 +2049,15 @@ entries (depending on how Emacs was built). */)
/* Restore original attributes. */
SetFileAttributes (filename, attributes);
}
#ifdef HAVE_POSIX_ACL
if (acl != NULL)
{
bool fail =
acl_set_file (SDATA (encoded_newname), ACL_TYPE_ACCESS, acl) != 0;
if (fail && !ACL_NOT_WELL_SUPPORTED (errno))
if (fail && acl_errno_valid (errno))
report_file_error ("Setting ACL", Fcons (newname, Qnil));
acl_free (acl);
}
#endif
#else /* not WINDOWSNT */
immediate_quit = 1;
ifd = emacs_open (SSDATA (encoded_file), O_RDONLY, 0);
@ -2103,12 +2081,6 @@ entries (depending on how Emacs was built). */)
report_file_error ("Doing fgetfilecon", Fcons (file, Qnil));
}
#endif
#ifdef HAVE_POSIX_ACL
acl = acl_get_fd (ifd);
if (acl == NULL && !ACL_NOT_WELL_SUPPORTED (errno))
report_file_error ("Getting ACL", Fcons (file, Qnil));
#endif
}
if (out_st.st_mode != 0
@ -2156,7 +2128,7 @@ entries (depending on how Emacs was built). */)
immediate_quit = 0;
#ifndef MSDOS
/* Preserve the original file modes, and if requested, also its
/* Preserve the original file permissions, and if requested, also its
owner and group. */
{
mode_t mode_mask = 07777;
@ -2173,8 +2145,16 @@ entries (depending on how Emacs was built). */)
mode_mask |= 02000;
}
}
if (fchmod (ofd, st.st_mode & mode_mask) != 0)
report_file_error ("Doing chmod", Fcons (newname, Qnil));
switch (!NILP (preserve_extended_attributes)
? qcopy_acl (SSDATA (encoded_file), ifd,
SSDATA (encoded_newname), ofd,
st.st_mode & mode_mask)
: fchmod (ofd, st.st_mode & mode_mask))
{
case -2: report_file_error ("Copying permissions from", list1 (file));
case -1: report_file_error ("Copying permissions to", list1 (newname));
}
}
#endif /* not MSDOS */
@ -2191,17 +2171,6 @@ entries (depending on how Emacs was built). */)
}
#endif
#ifdef HAVE_POSIX_ACL
if (acl != NULL)
{
bool fail = acl_set_fd (ofd, acl) != 0;
if (fail && !ACL_NOT_WELL_SUPPORTED (errno))
report_file_error ("Setting ACL", Fcons (newname, Qnil));
acl_free (acl);
}
#endif
if (!NILP (keep_time))
{
EMACS_TIME atime = get_stat_atime (&st);
@ -3111,7 +3080,7 @@ was unable to determine the ACL entries. */)
{
Lisp_Object absname;
Lisp_Object handler;
#ifdef HAVE_POSIX_ACL
#ifdef HAVE_ACL_SET_FILE
acl_t acl;
Lisp_Object acl_string;
char *str;
@ -3126,7 +3095,7 @@ was unable to determine the ACL entries. */)
if (!NILP (handler))
return call2 (handler, Qfile_acl, absname);
#ifdef HAVE_POSIX_ACL
#ifdef HAVE_ACL_SET_FILE
absname = ENCODE_FILE (absname);
acl = acl_get_file (SSDATA (absname), ACL_TYPE_ACCESS);
@ -3164,7 +3133,7 @@ support. */)
{
Lisp_Object absname;
Lisp_Object handler;
#ifdef HAVE_POSIX_ACL
#ifdef HAVE_ACL_SET_FILE
Lisp_Object encoded_absname;
acl_t acl;
bool fail;
@ -3178,7 +3147,7 @@ support. */)
if (!NILP (handler))
return call3 (handler, Qset_file_acl, absname, acl_string);
#ifdef HAVE_POSIX_ACL
#ifdef HAVE_ACL_SET_FILE
if (STRINGP (acl_string))
{
acl = acl_from_text (SSDATA (acl_string));
@ -3193,7 +3162,7 @@ support. */)
fail = (acl_set_file (SSDATA (encoded_absname), ACL_TYPE_ACCESS,
acl)
!= 0);
if (fail && !ACL_NOT_WELL_SUPPORTED (errno))
if (fail && acl_errno_valid (errno))
report_file_error ("Setting ACL", Fcons (absname, Qnil));
acl_free (acl);