Use Gnulib ACL implementation, for benefit of Solaris etc.
authorPaul Eggert <eggert@cs.ucla.edu>
Tue, 7 May 2013 21:34:03 +0000 (14:34 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Tue, 7 May 2013 21:34:03 +0000 (14:34 -0700)
* 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

26 files changed:
ChangeLog
admin/ChangeLog
admin/merge-gnulib
configure.ac
etc/ChangeLog
etc/NEWS
lib/Makefile.am
lib/acl-errno-valid.c [new file with mode: 0644]
lib/acl-internal.h [new file with mode: 0644]
lib/acl.h [new file with mode: 0644]
lib/acl_entries.c [new file with mode: 0644]
lib/errno.in.h [new file with mode: 0644]
lib/file-has-acl.c [new file with mode: 0644]
lib/gnulib.mk
lib/makefile.w32-in
lib/qcopy-acl.c [new file with mode: 0644]
lib/qset-acl.c [new file with mode: 0644]
m4/acl.m4 [new file with mode: 0644]
m4/errno_h.m4 [new file with mode: 0644]
m4/gnulib-comp.m4
nt/ChangeLog
nt/config.nt
nt/inc/ms-w32.h
src/ChangeLog
src/Makefile.in
src/fileio.c

index 4636d43..9e06df6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index 196acd4..ca25bf4 100644 (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).
index 1007491..c8bfe0d 100755 (executable)
@@ -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
index e3a1027..fe70676 100644 (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
index 4e9ba15..46564ae 100644 (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.
index 4261f23..fd65bad 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -23,10 +23,10 @@ so we will look at it and add it to the manual.
 \f
 * 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.
index d8979a1..c72e090 100644 (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
diff --git a/lib/acl-errno-valid.c b/lib/acl-errno-valid.c
new file mode 100644 (file)
index 0000000..a391d07
--- /dev/null
@@ -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;
+    }
+}
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
new file mode 100644 (file)
index 0000000..7e6d77a
--- /dev/null
@@ -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
diff --git a/lib/acl.h b/lib/acl.h
new file mode 100644 (file)
index 0000000..056bf7f
--- /dev/null
+++ b/lib/acl.h
@@ -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);
diff --git a/lib/acl_entries.c b/lib/acl_entries.c
new file mode 100644 (file)
index 0000000..7ca7b9d
--- /dev/null
@@ -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;
+}
diff --git a/lib/errno.in.h b/lib/errno.in.h
new file mode 100644 (file)
index 0000000..49b3546
--- /dev/null
@@ -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 */
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
new file mode 100644 (file)
index 0000000..bb8bae1
--- /dev/null
@@ -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;
+}
index 4b754ec..4a84b1d 100644 (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
 
 
index cd62fbc..5e2083a 100644 (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) \
diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c
new file mode 100644 (file)
index 0000000..25a2ff1
--- /dev/null
@@ -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
+}
diff --git a/lib/qset-acl.c b/lib/qset-acl.c
new file mode 100644 (file)
index 0000000..7bde2c1
--- /dev/null
@@ -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
+}
diff --git a/m4/acl.m4 b/m4/acl.m4
new file mode 100644 (file)
index 0000000..5b1fb65
--- /dev/null
+++ b/m4/acl.m4
@@ -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])
+])
diff --git a/m4/errno_h.m4 b/m4/errno_h.m4
new file mode 100644 (file)
index 0000000..c813ea5
--- /dev/null
@@ -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])])
+])
index d304f10..344b776 100644 (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
index acc7448..a025e1c 100644 (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.
index 347a1b0..2c7986f 100644 (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
index c67f07b..9ff0399 100644 (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;
index 55b7184..1085bc0 100644 (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
index 1222b5a..ad81a8d 100644 (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)
index 4a792d4..fe1bce1 100644 (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);