unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#14295: Support copy-file ACLs for Solaris etc.
@ 2013-04-28  3:33 Paul Eggert
  2013-04-28 17:16 ` Eli Zaretskii
  0 siblings, 1 reply; 5+ messages in thread
From: Paul Eggert @ 2013-04-28  3:33 UTC (permalink / raw)
  To: 14295

[-- Attachment #1: Type: text/plain, Size: 569 bytes --]

Tags: patch

Recently copy-file was augmented to add support for
copying ACLs on abandoned-POSIX-style hosts.  Here's
an additional patch to add support for copying ACLs on
Solaris, HP-UX, etc.  Basically, the idea is to use
Gnulib's support for ACLs.  I've tested this on
GNU/Linux and Solaris, but not on Microsoft platforms,
and am CC'ing this to Eli as a heads-up for that.

This changes the 'configure' option from --without-acl to
--disable-acl if one wishes to disable ACL support when
configuring Emacs; this is the option spelling that other GNU
packages use.

[-- Attachment #2: acl.txt --]
[-- Type: text/plain, Size: 104700 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2013-04-27 19:30:33 +0000
+++ ChangeLog	2013-04-27 19:40:49 +0000
@@ -1,5 +1,16 @@
 2013-04-27  Paul Eggert  <eggert@cs.ucla.edu>
 
+	Use Gnulib ACL implementation, for benefit of Solaris etc.
+	* configure.ac: Remove -with-acl option, since Gnulib does that for
+	us now.
+	(LIBACL_LIBS): Remove; no longer needed.
+	* lib/Makefile.am (CLEANFILES, SUFFIXES): New (empty) macros,
+	for the benefit of the new ACL implementation.
+	* lib/acl-errno-valid.c, lib/acl-internal.h, lib/acl.h:
+	* lib/acl_entries.c, lib/file-has-acl.c, lib/qcopy-acl.c:
+	* lib/qset-acl.c, m4/acl.m4: New files, taken from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 	Merge from gnulib, incorporating:
 	2013-04-27 alignof, intprops, malloca: port better to IBM's C compiler
 

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2013-04-02 01:18:40 +0000
+++ admin/ChangeLog	2013-04-17 05:21:33 +0000
@@ -1,3 +1,8 @@
+2013-04-17  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use Gnulib ACL implementation, for benefit of Solaris etc.
+	* merge-gnulib (GNULIB_MODULES): Add qacl.
+
 2013-04-01  Paul Eggert  <eggert@cs.ucla.edu>
 
 	Use UTF-8 for most files with non-ASCII characters (Bug#13936).

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2013-03-13 18:42:22 +0000
+++ admin/merge-gnulib	2013-04-17 05:21:33 +0000
@@ -33,7 +33,7 @@
   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

=== modified file 'configure.ac'
--- configure.ac	2013-04-26 19:31:09 +0000
+++ configure.ac	2013-04-27 19:40:49 +0000
@@ -199,7 +199,6 @@
 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 @@
   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

=== modified file 'etc/ChangeLog'
--- etc/ChangeLog	2013-04-24 16:50:14 +0000
+++ etc/ChangeLog	2013-04-27 19:40:49 +0000
@@ -1,3 +1,9 @@
+2013-04-27  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use Gnulib ACL implementation, for benefit of Solaris etc.
+	* NEWS: Emacs is no longer limited to POSIX ACLs.  --disable-acl,
+	not --without-acl, since we're now using Gnulib's implementation.
+
 2013-04-24  Tassilo Horn  <tsdh@gnu.org>
 
 	* themes/tsdh-dark-theme.el (tsdh-dark): Add ido faces and remove

=== modified file 'etc/NEWS'
--- etc/NEWS	2013-04-27 20:55:00 +0000
+++ etc/NEWS	2013-04-28 00:00:19 +0000
@@ -23,10 +23,10 @@
 \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.

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2013-04-07 06:21:40 +0000
+++ lib/Makefile.am	2013-04-17 05:21:33 +0000
@@ -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

=== added file 'lib/acl-errno-valid.c'
--- lib/acl-errno-valid.c	1970-01-01 00:00:00 +0000
+++ lib/acl-errno-valid.c	2013-04-17 05:21:33 +0000
@@ -0,0 +1,48 @@
+/* Test whether ACLs are well supported on this system.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <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
+    case ENOTSUP: return false;
+#endif
+    case EOPNOTSUPP: return false;
+    default: return true;
+    }
+}

=== added file 'lib/acl-internal.h'
--- lib/acl-internal.h	1970-01-01 00:00:00 +0000
+++ lib/acl-internal.h	2013-04-17 05:21:33 +0000
@@ -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

=== added file 'lib/acl.h'
--- lib/acl.h	1970-01-01 00:00:00 +0000
+++ lib/acl.h	2013-04-17 05:21:33 +0000
@@ -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);

=== added file 'lib/acl_entries.c'
--- lib/acl_entries.c	1970-01-01 00:00:00 +0000
+++ lib/acl_entries.c	2013-04-17 05:21:33 +0000
@@ -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;
+}

=== added file 'lib/file-has-acl.c'
--- lib/file-has-acl.c	1970-01-01 00:00:00 +0000
+++ lib/file-has-acl.c	2013-04-18 01:35:08 +0000
@@ -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;
+}

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2013-03-13 18:42:22 +0000
+++ lib/gnulib.mk	2013-04-27 20:24:10 +0000
@@ -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=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 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
@@ -561,6 +561,16 @@
 
 ## 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
 
 

=== added file 'lib/qcopy-acl.c'
--- lib/qcopy-acl.c	1970-01-01 00:00:00 +0000
+++ lib/qcopy-acl.c	2013-04-17 05:21:33 +0000
@@ -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
+}

=== added file 'lib/qset-acl.c'
--- lib/qset-acl.c	1970-01-01 00:00:00 +0000
+++ lib/qset-acl.c	2013-04-28 00:46:36 +0000
@@ -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
+}

=== added file 'm4/acl.m4'
--- m4/acl.m4	1970-01-01 00:00:00 +0000
+++ m4/acl.m4	2013-04-17 05:21:33 +0000
@@ -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])
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2013-03-13 18:42:22 +0000
+++ m4/gnulib-comp.m4	2013-04-27 20:24:10 +0000
@@ -94,6 +94,7 @@
   # 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:
@@ -287,6 +288,7 @@
     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 +735,10 @@
   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
@@ -758,6 +764,7 @@
   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 +799,8 @@
   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 +852,7 @@
   lib/verify.h
   lib/xalloc-oversized.h
   m4/00gnulib.m4
+  m4/acl.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2013-04-09 02:38:56 +0000
+++ nt/ChangeLog	2013-04-17 05:21:33 +0000
@@ -1,3 +1,8 @@
+2013-04-17  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use Gnulib ACL implementation, for benefit of Solaris etc.
+	* config.nt (HAVE_ACL_SET_FILE): Rename from HAVE_POSIX_ACL.
+
 2013-04-09  Ken Brown  <kbrown@cornell.edu>
 
 	* emacs.rc: Use 64-bit manifest for 64-bit Cygwin build.

=== modified file 'nt/config.nt'
--- nt/config.nt	2013-03-30 07:10:58 +0000
+++ nt/config.nt	2013-04-17 05:21:33 +0000
@@ -756,7 +756,7 @@
 #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

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2013-04-27 21:12:17 +0000
+++ src/ChangeLog	2013-04-28 00:00:19 +0000
@@ -1,3 +1,10 @@
+2013-04-27  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use Gnulib ACL implementation, for benefit of Solaris etc.
+	* Makefile.in (LIB_ACL): New macro.
+	(LIBACL_LIBS): Remove.
+	(LIBES): Use LIB_ACL, not LIBACL_LIBS.
+
 2013-04-27  Juri Linkov  <juri@jurta.org>
 
 	* callint.c (Fcall_interactively): Call `Qread_number' for

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2013-04-22 05:18:30 +0000
+++ src/Makefile.in	2013-04-27 19:40:49 +0000
@@ -137,6 +137,7 @@
 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@
@@ -282,8 +283,6 @@
 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
@@ -391,13 +390,13 @@
 ## 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) \
    $(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)

=== modified file 'src/fileio.c'
--- src/fileio.c	2013-04-07 16:18:41 +0000
+++ src/fileio.c	2013-04-17 05:21:33 +0000
@@ -36,7 +36,7 @@
 #include <selinux/context.h>
 #endif
 
-#ifdef HAVE_POSIX_ACL
+#ifdef HAVE_ACL_SET_FILE
 #include <sys/acl.h>
 #endif
 
@@ -81,26 +81,8 @@
 #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,9 +1970,6 @@
   security_context_t con;
   int conlength = 0;
 #endif
-#ifdef HAVE_POSIX_ACL
-  acl_t acl = NULL;
-#endif
 
   encoded_file = encoded_newname = Qnil;
   GCPRO4 (file, newname, encoded_file, encoded_newname);
@@ -2026,14 +2005,6 @@
     out_st.st_mode = 0;
 
 #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))
-	report_file_error ("Getting ACL", Fcons (file, Qnil));
-#endif
-    }
   if (!CopyFile (SDATA (encoded_file),
 		 SDATA (encoded_newname),
 		 FALSE))
@@ -2069,17 +2040,6 @@
       /* 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))
-	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 +2063,6 @@
 	    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 +2110,7 @@
   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 +2127,16 @@
 	      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 +2153,6 @@
     }
 #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 +3062,7 @@
 {
   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 +3077,7 @@
   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 +3115,7 @@
 {
   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 +3129,7 @@
   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 +3144,7 @@
       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);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#14295: Support copy-file ACLs for Solaris etc.
  2013-04-28  3:33 bug#14295: Support copy-file ACLs for Solaris etc Paul Eggert
@ 2013-04-28 17:16 ` Eli Zaretskii
  2013-04-29  6:15   ` Paul Eggert
  0 siblings, 1 reply; 5+ messages in thread
From: Eli Zaretskii @ 2013-04-28 17:16 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 14295

> Date: Sat, 27 Apr 2013 20:33:19 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: Eli Zaretskii <eliz@gnu.org>
> 
> Recently copy-file was augmented to add support for
> copying ACLs on abandoned-POSIX-style hosts.  Here's
> an additional patch to add support for copying ACLs on
> Solaris, HP-UX, etc.  Basically, the idea is to use
> Gnulib's support for ACLs.  I've tested this on
> GNU/Linux and Solaris, but not on Microsoft platforms,
> and am CC'ing this to Eli as a heads-up for that.

Thanks; see a few comments below.

> This changes the 'configure' option from --without-acl to
> --disable-acl if one wishes to disable ACL support when
> configuring Emacs; this is the option spelling that other GNU
> packages use.

How hard would it to support both?

> +bool
> +acl_errno_valid (int errnum)
> +{
> +  /* Recognize some common errors such as from an NFS mount that does
> +     not support ACLs, even when local drives do.  */
> +  switch (errnum)
> +    {
> +    case EBUSY: return false;
> +    case EINVAL: return false;
> +#if defined __APPLE__ && defined __MACH__
> +    case ENOENT: return false;
> +#endif
> +    case ENOSYS: return false;
> +#if defined ENOTSUP && ENOTSUP != EOPNOTSUPP
> +    case ENOTSUP: return false;
> +#endif
> +    case EOPNOTSUPP: return false;
> +    default: return true;
> +    }
> +}

This uses EOPNOTSUPP without #ifdef guards; is that universally
available?

>  #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))
> -	report_file_error ("Getting ACL", Fcons (file, Qnil));
> -#endif
> -    }
>    if (!CopyFile (SDATA (encoded_file),
>  		 SDATA (encoded_newname),
>  		 FALSE))
> @@ -2069,17 +2040,6 @@
>        /* 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))
> -	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 +2063,6 @@
>  	    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 +2110,7 @@
>    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 +2127,16 @@
>  	      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 */

This hunk looks wrong, or maybe I'm missing something: it looks like
you removed the ACL support code from the WINDOWSNT blocks, but added
the replacement qcopy_acl in a block that is not compiled on Windows
at all (and uses fchmod and st which are not initialized on Windows).

If I'm right, perhaps it is best to leave the WINDOWSNT parts alone:
since acl_get_file and acl_set_file are still being left in Emacs
sources elsewhere, there's no need to remove their calls here, and
leaving them alone will avoid the need to add qcopy_acl to w32.c.

> -#ifdef HAVE_POSIX_ACL
> +#ifdef HAVE_ACL_SET_FILE
>    absname = ENCODE_FILE (absname);
>  
>    acl = acl_get_file (SSDATA (absname), ACL_TYPE_ACCESS);

It sounds wrong to me to condition a call to acl_get_file with a macro
called HAVE_ACL_SET_FILE.

> @@ -3193,7 +3144,7 @@
>        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))

For this to work, acl-errno-valid.c will have to be compiled on
MS-Windows.  And for that, we will need to solve the EOPNOTSUPP issue
mentioned above, because Windows doesn't define it.





^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#14295: Support copy-file ACLs for Solaris etc.
  2013-04-28 17:16 ` Eli Zaretskii
@ 2013-04-29  6:15   ` Paul Eggert
  2013-04-29 17:07     ` Eli Zaretskii
  0 siblings, 1 reply; 5+ messages in thread
From: Paul Eggert @ 2013-04-29  6:15 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 14295

[-- Attachment #1: Type: text/plain, Size: 1328 bytes --]

> How hard would it to support both?

I don't know, and I'd rather not support --without-acl.  This should
have been an --enable/--disable option in the first place, as it's
enabling or disabling a feature, not building with or without a
package.  There's little point to Emacs departing from existing
GNU practice here.

> This uses EOPNOTSUPP without #ifdef guards; is that universally
> available?

Mostly, except for Windows, but to play it safe we can incorporate the
Gnulib errno module, which defines EOPNOTSUPP if it's not already
defined.

> If I'm right, perhaps it is best to leave the WINDOWSNT parts alone:

Sure, we can do that.

> It sounds wrong to me to condition a call to acl_get_file with a macro
> called HAVE_ACL_SET_FILE.

In practice both are available if either is; in particular, there's no
point calling acl_get_file if you can't invoke acl_set_file later.

> For this to work, acl-errno-valid.c will have to be compiled on
> MS-Windows.  And for that, we will need to solve the EOPNOTSUPP issue
> mentioned above, because Windows doesn't define it.

OK.  Revised patch attached, which tries to address the above.
It relies on Gnulib for EOPNOTSUPP, except for WINDOWSNT where
'configure' isn't working yet, and there it adds a line to
nt/config.nt to deal with this until we get 'configure' working.

[-- Attachment #2: acl.txt --]
[-- Type: text/plain, Size: 120031 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2013-04-29 04:48:30 +0000
+++ ChangeLog	2013-04-29 06:07:42 +0000
@@ -1,5 +1,19 @@
 2013-04-29  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.
+
 	Merge from gnulib, incorporating:
 	2013-04-28 extern-inline: work around bug in Sun c99
 

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2013-04-02 01:18:40 +0000
+++ admin/ChangeLog	2013-04-29 06:07:42 +0000
@@ -1,3 +1,9 @@
+2013-04-29  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).

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2013-03-13 18:42:22 +0000
+++ admin/merge-gnulib	2013-04-29 06:07:42 +0000
@@ -33,7 +33,7 @@
   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_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

=== modified file 'configure.ac'
--- configure.ac	2013-04-26 19:31:09 +0000
+++ configure.ac	2013-04-27 19:40:49 +0000
@@ -199,7 +199,6 @@
 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 @@
   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

=== modified file 'etc/ChangeLog'
--- etc/ChangeLog	2013-04-24 16:50:14 +0000
+++ etc/ChangeLog	2013-04-29 04:50:26 +0000
@@ -1,3 +1,9 @@
+2013-04-29  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-04-24  Tassilo Horn  <tsdh@gnu.org>
 
 	* themes/tsdh-dark-theme.el (tsdh-dark): Add ido faces and remove

=== modified file 'etc/NEWS'
--- etc/NEWS	2013-04-27 20:55:00 +0000
+++ etc/NEWS	2013-04-28 00:00:19 +0000
@@ -23,10 +23,10 @@
 \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.

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2013-04-07 06:21:40 +0000
+++ lib/Makefile.am	2013-04-17 05:21:33 +0000
@@ -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

=== added file 'lib/acl-errno-valid.c'
--- lib/acl-errno-valid.c	1970-01-01 00:00:00 +0000
+++ lib/acl-errno-valid.c	2013-04-17 05:21:33 +0000
@@ -0,0 +1,48 @@
+/* Test whether ACLs are well supported on this system.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <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
+    case ENOTSUP: return false;
+#endif
+    case EOPNOTSUPP: return false;
+    default: return true;
+    }
+}

=== added file 'lib/acl-internal.h'
--- lib/acl-internal.h	1970-01-01 00:00:00 +0000
+++ lib/acl-internal.h	2013-04-17 05:21:33 +0000
@@ -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

=== added file 'lib/acl.h'
--- lib/acl.h	1970-01-01 00:00:00 +0000
+++ lib/acl.h	2013-04-17 05:21:33 +0000
@@ -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);

=== added file 'lib/acl_entries.c'
--- lib/acl_entries.c	1970-01-01 00:00:00 +0000
+++ lib/acl_entries.c	2013-04-17 05:21:33 +0000
@@ -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;
+}

=== added file 'lib/errno.in.h'
--- lib/errno.in.h	1970-01-01 00:00:00 +0000
+++ lib/errno.in.h	2013-04-29 06:07:42 +0000
@@ -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 */

=== added file 'lib/file-has-acl.c'
--- lib/file-has-acl.c	1970-01-01 00:00:00 +0000
+++ lib/file-has-acl.c	2013-04-18 01:35:08 +0000
@@ -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;
+}

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2013-03-13 18:42:22 +0000
+++ lib/gnulib.mk	2013-04-29 06:07:42 +0000
@@ -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 @@
 
 ## 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 @@
 
 ## 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
 
 

=== modified file 'lib/makefile.w32-in'
--- lib/makefile.w32-in	2013-02-12 03:52:04 +0000
+++ lib/makefile.w32-in	2013-04-29 06:07:42 +0000
@@ -23,7 +23,8 @@
 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 @@
 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) \

=== added file 'lib/qcopy-acl.c'
--- lib/qcopy-acl.c	1970-01-01 00:00:00 +0000
+++ lib/qcopy-acl.c	2013-04-17 05:21:33 +0000
@@ -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
+}

=== added file 'lib/qset-acl.c'
--- lib/qset-acl.c	1970-01-01 00:00:00 +0000
+++ lib/qset-acl.c	2013-04-28 00:46:36 +0000
@@ -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
+}

=== added file 'm4/acl.m4'
--- m4/acl.m4	1970-01-01 00:00:00 +0000
+++ m4/acl.m4	2013-04-17 05:21:33 +0000
@@ -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])
+])

=== added file 'm4/errno_h.m4'
--- m4/errno_h.m4	1970-01-01 00:00:00 +0000
+++ m4/errno_h.m4	2013-04-29 06:07:42 +0000
@@ -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])])
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2013-03-13 18:42:22 +0000
+++ m4/gnulib-comp.m4	2013-04-29 06:07:42 +0000
@@ -56,6 +56,7 @@
   # 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 @@
   # 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 @@
   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 @@
     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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   m4/dirent_h.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/errno_h.m4
   m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2013-04-09 02:38:56 +0000
+++ nt/ChangeLog	2013-04-29 06:07:42 +0000
@@ -1,3 +1,9 @@
+2013-04-29  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.
+	(EOPNOTSUP): New macro.
+
 2013-04-09  Ken Brown  <kbrown@cornell.edu>
 
 	* emacs.rc: Use 64-bit manifest for 64-bit Cygwin build.

=== modified file 'nt/config.nt'
--- nt/config.nt	2013-03-30 07:10:58 +0000
+++ nt/config.nt	2013-04-29 06:07:42 +0000
@@ -756,7 +756,7 @@
 #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
@@ -1723,6 +1723,12 @@
 /* Define as `fork' if `vfork' does not work. */
 #undef vfork
 
+/* 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
+
 #include <conf_post.h>
 
 #endif /* EMACS_CONFIG_H */

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2013-04-28 18:21:01 +0000
+++ src/ChangeLog	2013-04-29 06:07:42 +0000
@@ -1,3 +1,16 @@
+2013-04-29  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.
+
 2013-04-28  Eli Zaretskii  <eliz@gnu.org>
 
 	* coding.c (decode_coding_gap): Don't remove the character before

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2013-04-22 05:18:30 +0000
+++ src/Makefile.in	2013-04-27 19:40:49 +0000
@@ -137,6 +137,7 @@
 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@
@@ -282,8 +283,6 @@
 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
@@ -391,13 +390,13 @@
 ## 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) \
    $(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)

=== modified file 'src/fileio.c'
--- src/fileio.c	2013-04-07 16:18:41 +0000
+++ src/fileio.c	2013-04-29 06:07:42 +0000
@@ -36,7 +36,7 @@
 #include <selinux/context.h>
 #endif
 
-#ifdef HAVE_POSIX_ACL
+#ifdef HAVE_ACL_SET_FILE
 #include <sys/acl.h>
 #endif
 
@@ -81,26 +81,8 @@
 #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 @@
   security_context_t con;
   int conlength = 0;
 #endif
-#ifdef HAVE_POSIX_ACL
+#ifdef WINDOWSNT
   acl_t acl = NULL;
 #endif
 
@@ -2028,11 +2010,9 @@
 #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 @@
       /* 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 @@
 	    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 @@
   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 @@
 	      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 @@
     }
 #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 @@
 {
   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 @@
   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 @@
 {
   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 @@
   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 @@
       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);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#14295: Support copy-file ACLs for Solaris etc.
  2013-04-29  6:15   ` Paul Eggert
@ 2013-04-29 17:07     ` Eli Zaretskii
  2013-05-07 21:36       ` Paul Eggert
  0 siblings, 1 reply; 5+ messages in thread
From: Eli Zaretskii @ 2013-04-29 17:07 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 14295

> Date: Sun, 28 Apr 2013 23:15:06 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: bug-gnu-emacs@gnu.org
> 
> > How hard would it to support both?
> 
> I don't know, and I'd rather not support --without-acl.  This should
> have been an --enable/--disable option in the first place, as it's
> enabling or disabling a feature, not building with or without a
> package.  There's little point to Emacs departing from existing
> GNU practice here.

I just wonder how many people are used to that option.  Maybe not too
many, since this is a new feature.

> > This uses EOPNOTSUPP without #ifdef guards; is that universally
> > available?
> 
> Mostly, except for Windows, but to play it safe we can incorporate the
> Gnulib errno module, which defines EOPNOTSUPP if it's not already
> defined.

That almost works, but there are 2 issues:

 . I'd prefer to have the definition of EOPNOTSUPP in nt/inc/ms-w32.h,
   to make synchronization of config.nt with src/config.in easier.

 . More importantly, ENOTSUP is defined on Windows with the same value
   as ENOSYS (see ms-w32.h), so acl-errno-valid.c will fail to compile
   due to 2 identical case values in a switch.

> > If I'm right, perhaps it is best to leave the WINDOWSNT parts alone:
> 
> Sure, we can do that.

Thanks.

> OK.  Revised patch attached, which tries to address the above.

It looks fine, other than the 2 minor issues above, thanks.

> It relies on Gnulib for EOPNOTSUPP, except for WINDOWSNT where
> 'configure' isn't working yet, and there it adds a line to
> nt/config.nt to deal with this until we get 'configure' working.

'configure' is already working, I just need time to finish up the
branch and merge it.  But even when that is done (hopefully in a few
days), we will not immediately abandon the current configury.  It will
take some time.





^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#14295: Support copy-file ACLs for Solaris etc.
  2013-04-29 17:07     ` Eli Zaretskii
@ 2013-05-07 21:36       ` Paul Eggert
  0 siblings, 0 replies; 5+ messages in thread
From: Paul Eggert @ 2013-05-07 21:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 14295-done

On 04/29/13 10:07, Eli Zaretskii wrote:

> That almost works, but there are 2 issues:
> 
>  . I'd prefer to have the definition of EOPNOTSUPP in nt/inc/ms-w32.h,
>    to make synchronization of config.nt with src/config.in easier.
> 
>  . More importantly, ENOTSUP is defined on Windows with the same value
>    as ENOSYS (see ms-w32.h), so acl-errno-valid.c will fail to compile
>    due to 2 identical case values in a switch.

Thanks, I fixed those, installed it as trunk bzr 112507,
and am marking this bug as done.






^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-05-07 21:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-04-28  3:33 bug#14295: Support copy-file ACLs for Solaris etc Paul Eggert
2013-04-28 17:16 ` Eli Zaretskii
2013-04-29  6:15   ` Paul Eggert
2013-04-29 17:07     ` Eli Zaretskii
2013-05-07 21:36       ` Paul Eggert

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).