aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorEli Zaretskii <[email protected]>2013-05-15 19:15:07 +0300
committerEli Zaretskii <[email protected]>2013-05-15 19:15:07 +0300
commitd14365f9411ab4b20db6c455aa9cf24ce6a0bcb1 (patch)
treef547c3f203c903b118348061eb0fd2359c94d324 /lib
parentf94b5742a3e9bc7114c6e2cd85a2371381cba4d3 (diff)
parente3772e9833f971a450562350dc233bf00be7c5eb (diff)
Merge from trunk, configury not fixed yet.
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/acl-errno-valid.c52
-rw-r--r--lib/acl-internal.h250
-rw-r--r--lib/acl.h30
-rw-r--r--lib/acl_entries.c75
-rw-r--r--lib/errno.in.h279
-rw-r--r--lib/file-has-acl.c918
-rw-r--r--lib/gnulib.mk46
-rw-r--r--lib/makefile.w32-in11
-rw-r--r--lib/qcopy-acl.c583
-rw-r--r--lib/qset-acl.c676
-rw-r--r--lib/utimens.c24
12 files changed, 2936 insertions, 10 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 2ba63f3152..f76d0d3928 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,8 +1,10 @@
BUILT_SOURCES =
+CLEANFILES =
EXTRA_DIST =
MOSTLYCLEANDIRS =
MOSTLYCLEANFILES =
noinst_LIBRARIES =
+SUFFIXES =
AM_CFLAGS = $(PROFILING_CFLAGS) $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src
diff --git a/lib/acl-errno-valid.c b/lib/acl-errno-valid.c
new file mode 100644
index 0000000000..a391d077f5
--- /dev/null
+++ b/lib/acl-errno-valid.c
@@ -0,0 +1,52 @@
+/* Test whether ACLs are well supported on this system.
+
+ Copyright 2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Paul Eggert. */
+
+#include <config.h>
+
+#include <acl.h>
+
+#include <errno.h>
+
+/* Return true if errno value ERRNUM indicates that ACLs are well
+ supported on this system. ERRNUM should be an errno value obtained
+ after an ACL-related system call fails. */
+bool
+acl_errno_valid (int errnum)
+{
+ /* Recognize some common errors such as from an NFS mount that does
+ not support ACLs, even when local drives do. */
+ switch (errnum)
+ {
+ case EBUSY: return false;
+ case EINVAL: return false;
+#if defined __APPLE__ && defined __MACH__
+ case ENOENT: return false;
+#endif
+ case ENOSYS: return false;
+
+#if defined ENOTSUP && ENOTSUP != EOPNOTSUPP
+# if ENOTSUP != ENOSYS /* Needed for the MS-Windows port of GNU Emacs. */
+ case ENOTSUP: return false;
+# endif
+#endif
+
+ case EOPNOTSUPP: return false;
+ default: return true;
+ }
+}
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
new file mode 100644
index 0000000000..7e6d77a5fd
--- /dev/null
+++ b/lib/acl-internal.h
@@ -0,0 +1,250 @@
+/* Internal implementation of access control lists.
+
+ Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
+
+#include "acl.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* All systems define the ACL related API in <sys/acl.h>. */
+#if HAVE_SYS_ACL_H
+# include <sys/acl.h>
+#endif
+#if defined HAVE_FACL && ! defined GETACLCNT && defined ACL_CNT
+# define GETACLCNT ACL_CNT
+#endif
+
+/* On Linux, additional ACL related API is available in <acl/libacl.h>. */
+#ifdef HAVE_ACL_LIBACL_H
+# include <acl/libacl.h>
+#endif
+
+/* On HP-UX >= 11.11, additional ACL API is available in <aclv.h>. */
+#if HAVE_ACLV_H
+# include <sys/types.h>
+# include <aclv.h>
+/* HP-UX 11.11 lacks these declarations. */
+extern int acl (char *, int, int, struct acl *);
+extern int aclsort (int, int, struct acl *);
+#endif
+
+#include <errno.h>
+
+#include <limits.h>
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#ifndef HAVE_FCHMOD
+# define HAVE_FCHMOD false
+# define fchmod(fd, mode) (-1)
+#endif
+
+_GL_INLINE_HEADER_BEGIN
+#ifndef ACL_INTERNAL_INLINE
+# define ACL_INTERNAL_INLINE _GL_INLINE
+#endif
+
+#if USE_ACL
+
+# if HAVE_ACL_GET_FILE
+/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
+/* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
+
+# ifndef MIN_ACL_ENTRIES
+# define MIN_ACL_ENTRIES 4
+# endif
+
+/* POSIX 1003.1e (draft 17) */
+# ifdef HAVE_ACL_GET_FD
+/* Most platforms have a 1-argument acl_get_fd, only OSF/1 has a 2-argument
+ macro(!). */
+# if HAVE_ACL_FREE_TEXT /* OSF/1 */
+ACL_INTERNAL_INLINE acl_t
+rpl_acl_get_fd (int fd)
+{
+ return acl_get_fd (fd, ACL_TYPE_ACCESS);
+}
+# undef acl_get_fd
+# define acl_get_fd rpl_acl_get_fd
+# endif
+# else
+# define HAVE_ACL_GET_FD false
+# undef acl_get_fd
+# define acl_get_fd(fd) (NULL)
+# endif
+
+/* POSIX 1003.1e (draft 17) */
+# ifdef HAVE_ACL_SET_FD
+/* Most platforms have a 2-argument acl_set_fd, only OSF/1 has a 3-argument
+ macro(!). */
+# if HAVE_ACL_FREE_TEXT /* OSF/1 */
+ACL_INTERNAL_INLINE int
+rpl_acl_set_fd (int fd, acl_t acl)
+{
+ return acl_set_fd (fd, ACL_TYPE_ACCESS, acl);
+}
+# undef acl_set_fd
+# define acl_set_fd rpl_acl_set_fd
+# endif
+# else
+# define HAVE_ACL_SET_FD false
+# undef acl_set_fd
+# define acl_set_fd(fd, acl) (-1)
+# endif
+
+/* POSIX 1003.1e (draft 13) */
+# if ! HAVE_ACL_FREE_TEXT
+# define acl_free_text(buf) acl_free (buf)
+# endif
+
+/* Linux-specific */
+# ifndef HAVE_ACL_EXTENDED_FILE
+# define HAVE_ACL_EXTENDED_FILE false
+# define acl_extended_file(name) (-1)
+# endif
+
+/* Linux-specific */
+# ifndef HAVE_ACL_FROM_MODE
+# define HAVE_ACL_FROM_MODE false
+# define acl_from_mode(mode) (NULL)
+# endif
+
+/* Set to 1 if a file's mode is implicit by the ACL.
+ Set to 0 if a file's mode is stored independently from the ACL. */
+# if (HAVE_ACL_COPY_EXT_NATIVE && HAVE_ACL_CREATE_ENTRY_NP) || defined __sgi /* Mac OS X, IRIX */
+# define MODE_INSIDE_ACL 0
+# else
+# define MODE_INSIDE_ACL 1
+# endif
+
+/* Return the number of entries in ACL.
+ Return -1 and set errno upon failure to determine it. */
+/* Define a replacement for acl_entries if needed. (Only Linux has it.) */
+# if !HAVE_ACL_ENTRIES
+# define acl_entries rpl_acl_entries
+extern int acl_entries (acl_t);
+# endif
+
+# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
+/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
+ Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial. */
+extern int acl_extended_nontrivial (acl_t);
+# else
+/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS.
+ Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode.
+ Return -1 and set errno upon failure to determine it. */
+extern int acl_access_nontrivial (acl_t);
+# endif
+
+# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+
+/* Set to 1 if a file's mode is implicit by the ACL.
+ Set to 0 if a file's mode is stored independently from the ACL. */
+# if defined __CYGWIN__ /* Cygwin */
+# define MODE_INSIDE_ACL 0
+# else /* Solaris */
+# define MODE_INSIDE_ACL 1
+# endif
+
+/* Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
+extern int acl_nontrivial (int count, aclent_t *entries);
+
+# ifdef ACE_GETACL /* Solaris 10 */
+
+/* Test an ACL retrieved with ACE_GETACL.
+ Return 1 if the given ACL, consisting of COUNT entries, is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
+extern int acl_ace_nontrivial (int count, ace_t *entries);
+
+/* Definitions for when the built executable is executed on Solaris 10
+ (newer version) or Solaris 11. */
+/* For a_type. */
+# define OLD_ALLOW 0
+# define OLD_DENY 1
+# define NEW_ACE_ACCESS_ALLOWED_ACE_TYPE 0 /* replaces ALLOW */
+# define NEW_ACE_ACCESS_DENIED_ACE_TYPE 1 /* replaces DENY */
+/* For a_flags. */
+# define OLD_ACE_OWNER 0x0100
+# define OLD_ACE_GROUP 0x0200
+# define OLD_ACE_OTHER 0x0400
+# define NEW_ACE_OWNER 0x1000
+# define NEW_ACE_GROUP 0x2000
+# define NEW_ACE_IDENTIFIER_GROUP 0x0040
+# define NEW_ACE_EVERYONE 0x4000
+/* For a_access_mask. */
+# define NEW_ACE_READ_DATA 0x001 /* corresponds to 'r' */
+# define NEW_ACE_WRITE_DATA 0x002 /* corresponds to 'w' */
+# define NEW_ACE_APPEND_DATA 0x004
+# define NEW_ACE_READ_NAMED_ATTRS 0x008
+# define NEW_ACE_WRITE_NAMED_ATTRS 0x010
+# define NEW_ACE_EXECUTE 0x020
+# define NEW_ACE_DELETE_CHILD 0x040
+# define NEW_ACE_READ_ATTRIBUTES 0x080
+# define NEW_ACE_WRITE_ATTRIBUTES 0x100
+# define NEW_ACE_DELETE 0x10000
+# define NEW_ACE_READ_ACL 0x20000
+# define NEW_ACE_WRITE_ACL 0x40000
+# define NEW_ACE_WRITE_OWNER 0x80000
+# define NEW_ACE_SYNCHRONIZE 0x100000
+
+# endif
+
+# elif HAVE_GETACL /* HP-UX */
+
+/* Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
+extern int acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb);
+
+# if HAVE_ACLV_H /* HP-UX >= 11.11 */
+
+/* Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
+extern int aclv_nontrivial (int count, struct acl *entries);
+
+# endif
+
+# elif HAVE_ACLX_GET && 0 /* AIX */
+
+/* TODO */
+
+# elif HAVE_STATACL /* older AIX */
+
+/* Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
+extern int acl_nontrivial (struct acl *a);
+
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+/* Return 1 if the given ACL is non-trivial.
+ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */
+extern int acl_nontrivial (int count, struct acl *entries);
+
+# endif
+
+#endif
+
+_GL_INLINE_HEADER_END
diff --git a/lib/acl.h b/lib/acl.h
new file mode 100644
index 0000000000..056bf7f30f
--- /dev/null
+++ b/lib/acl.h
@@ -0,0 +1,30 @@
+/* acl.c - access control lists
+
+ Copyright (C) 2002, 2008-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Paul Eggert. */
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST;
+int file_has_acl (char const *, struct stat const *);
+int qset_acl (char const *, int, mode_t);
+int set_acl (char const *, int, mode_t);
+int qcopy_acl (char const *, int, char const *, int, mode_t);
+int copy_acl (char const *, int, char const *, int, mode_t);
+int chmod_or_fchmod (char const *, int, mode_t);
diff --git a/lib/acl_entries.c b/lib/acl_entries.c
new file mode 100644
index 0000000000..7ca7b9d5b4
--- /dev/null
+++ b/lib/acl_entries.c
@@ -0,0 +1,75 @@
+/* Return the number of entries in an ACL.
+
+ Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Paul Eggert and Andreas Gruenbacher. */
+
+#include <config.h>
+
+#include "acl-internal.h"
+
+/* This file assumes POSIX-draft like ACLs
+ (Linux, FreeBSD, Mac OS X, IRIX, Tru64). */
+
+/* Return the number of entries in ACL.
+ Return -1 and set errno upon failure to determine it. */
+
+int
+acl_entries (acl_t acl)
+{
+ int count = 0;
+
+ if (acl != NULL)
+ {
+#if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD, Mac OS X */
+# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
+ /* acl_get_entry returns 0 when it successfully fetches an entry,
+ and -1/EINVAL at the end. */
+ acl_entry_t ace;
+ int got_one;
+
+ for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
+ got_one >= 0;
+ got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
+ count++;
+# else /* Linux, FreeBSD */
+ /* acl_get_entry returns 1 when it successfully fetches an entry,
+ and 0 at the end. */
+ acl_entry_t ace;
+ int got_one;
+
+ for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace);
+ got_one > 0;
+ got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace))
+ count++;
+ if (got_one < 0)
+ return -1;
+# endif
+#else /* IRIX, Tru64 */
+# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */
+ /* Don't use acl_get_entry: it is undocumented. */
+ count = acl->acl_cnt;
+# endif
+# if HAVE_ACL_FREE_TEXT /* Tru64 */
+ /* Don't use acl_get_entry: it takes only one argument and does not
+ work. */
+ count = acl->acl_num;
+# endif
+#endif
+ }
+
+ return count;
+}
diff --git a/lib/errno.in.h b/lib/errno.in.h
new file mode 100644
index 0000000000..49b35464b9
--- /dev/null
+++ b/lib/errno.in.h
@@ -0,0 +1,279 @@
+/* A POSIX-like <errno.h>.
+
+ Copyright (C) 2008-2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _@GUARD_PREFIX@_ERRNO_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* The include_next requires a split double-inclusion guard. */
+#@INCLUDE_NEXT@ @NEXT_ERRNO_H@
+
+#ifndef _@GUARD_PREFIX@_ERRNO_H
+#define _@GUARD_PREFIX@_ERRNO_H
+
+
+/* On native Windows platforms, many macros are not defined. */
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+/* These are the same values as defined by MSVC 10, for interoperability. */
+
+# ifndef ENOMSG
+# define ENOMSG 122
+# define GNULIB_defined_ENOMSG 1
+# endif
+
+# ifndef EIDRM
+# define EIDRM 111
+# define GNULIB_defined_EIDRM 1
+# endif
+
+# ifndef ENOLINK
+# define ENOLINK 121
+# define GNULIB_defined_ENOLINK 1
+# endif
+
+# ifndef EPROTO
+# define EPROTO 134
+# define GNULIB_defined_EPROTO 1
+# endif
+
+# ifndef EBADMSG
+# define EBADMSG 104
+# define GNULIB_defined_EBADMSG 1
+# endif
+
+# ifndef EOVERFLOW
+# define EOVERFLOW 132
+# define GNULIB_defined_EOVERFLOW 1
+# endif
+
+# ifndef ENOTSUP
+# define ENOTSUP 129
+# define GNULIB_defined_ENOTSUP 1
+# endif
+
+# ifndef ENETRESET
+# define ENETRESET 117
+# define GNULIB_defined_ENETRESET 1
+# endif
+
+# ifndef ECONNABORTED
+# define ECONNABORTED 106
+# define GNULIB_defined_ECONNABORTED 1
+# endif
+
+# ifndef ECANCELED
+# define ECANCELED 105
+# define GNULIB_defined_ECANCELED 1
+# endif
+
+# ifndef EOWNERDEAD
+# define EOWNERDEAD 133
+# define GNULIB_defined_EOWNERDEAD 1
+# endif
+
+# ifndef ENOTRECOVERABLE
+# define ENOTRECOVERABLE 127
+# define GNULIB_defined_ENOTRECOVERABLE 1
+# endif
+
+# ifndef EINPROGRESS
+# define EINPROGRESS 112
+# define EALREADY 103
+# define ENOTSOCK 128
+# define EDESTADDRREQ 109
+# define EMSGSIZE 115
+# define EPROTOTYPE 136
+# define ENOPROTOOPT 123
+# define EPROTONOSUPPORT 135
+# define EOPNOTSUPP 130
+# define EAFNOSUPPORT 102
+# define EADDRINUSE 100
+# define EADDRNOTAVAIL 101
+# define ENETDOWN 116
+# define ENETUNREACH 118
+# define ECONNRESET 108
+# define ENOBUFS 119
+# define EISCONN 113
+# define ENOTCONN 126
+# define ETIMEDOUT 138
+# define ECONNREFUSED 107
+# define ELOOP 114
+# define EHOSTUNREACH 110
+# define EWOULDBLOCK 140
+# define GNULIB_defined_ESOCK 1
+# endif
+
+# ifndef ETXTBSY
+# define ETXTBSY 139
+# define ENODATA 120 /* not required by POSIX */
+# define ENOSR 124 /* not required by POSIX */
+# define ENOSTR 125 /* not required by POSIX */
+# define ETIME 137 /* not required by POSIX */
+# define EOTHER 131 /* not required by POSIX */
+# define GNULIB_defined_ESTREAMS 1
+# endif
+
+/* These are intentionally the same values as the WSA* error numbers, defined
+ in <winsock2.h>. */
+# define ESOCKTNOSUPPORT 10044 /* not required by POSIX */
+# define EPFNOSUPPORT 10046 /* not required by POSIX */
+# define ESHUTDOWN 10058 /* not required by POSIX */
+# define ETOOMANYREFS 10059 /* not required by POSIX */
+# define EHOSTDOWN 10064 /* not required by POSIX */
+# define EPROCLIM 10067 /* not required by POSIX */
+# define EUSERS 10068 /* not required by POSIX */
+# define EDQUOT 10069
+# define ESTALE 10070
+# define EREMOTE 10071 /* not required by POSIX */
+# define GNULIB_defined_EWINSOCK 1
+
+# endif
+
+
+/* On OSF/1 5.1, when _XOPEN_SOURCE_EXTENDED is not defined, the macros
+ EMULTIHOP, ENOLINK, EOVERFLOW are not defined. */
+# if @EMULTIHOP_HIDDEN@
+# define EMULTIHOP @EMULTIHOP_VALUE@
+# define GNULIB_defined_EMULTIHOP 1
+# endif
+# if @ENOLINK_HIDDEN@
+# define ENOLINK @ENOLINK_VALUE@
+# define GNULIB_defined_ENOLINK 1
+# endif
+# if @EOVERFLOW_HIDDEN@
+# define EOVERFLOW @EOVERFLOW_VALUE@
+# define GNULIB_defined_EOVERFLOW 1
+# endif
+
+
+/* On OpenBSD 4.0 and on native Windows, the macros ENOMSG, EIDRM, ENOLINK,
+ EPROTO, EMULTIHOP, EBADMSG, EOVERFLOW, ENOTSUP, ECANCELED are not defined.
+ Likewise, on NonStop Kernel, EDQUOT is not defined.
+ Define them here. Values >= 2000 seem safe to use: Solaris ESTALE = 151,
+ HP-UX EWOULDBLOCK = 246, IRIX EDQUOT = 1133.
+
+ Note: When one of these systems defines some of these macros some day,
+ binaries will have to be recompiled so that they recognizes the new
+ errno values from the system. */
+
+# ifndef ENOMSG
+# define ENOMSG 2000
+# define GNULIB_defined_ENOMSG 1
+# endif
+
+# ifndef EIDRM
+# define EIDRM 2001
+# define GNULIB_defined_EIDRM 1
+# endif
+
+# ifndef ENOLINK
+# define ENOLINK 2002
+# define GNULIB_defined_ENOLINK 1
+# endif
+
+# ifndef EPROTO
+# define EPROTO 2003
+# define GNULIB_defined_EPROTO 1
+# endif
+
+# ifndef EMULTIHOP
+# define EMULTIHOP 2004
+# define GNULIB_defined_EMULTIHOP 1
+# endif
+
+# ifndef EBADMSG
+# define EBADMSG 2005
+# define GNULIB_defined_EBADMSG 1
+# endif
+
+# ifndef EOVERFLOW
+# define EOVERFLOW 2006
+# define GNULIB_defined_EOVERFLOW 1
+# endif
+
+# ifndef ENOTSUP
+# define ENOTSUP 2007
+# define GNULIB_defined_ENOTSUP 1
+# endif
+
+# ifndef ENETRESET
+# define ENETRESET 2011
+# define GNULIB_defined_ENETRESET 1
+# endif
+
+# ifndef ECONNABORTED
+# define ECONNABORTED 2012
+# define GNULIB_defined_ECONNABORTED 1
+# endif
+
+# ifndef ESTALE
+# define ESTALE 2009
+# define GNULIB_defined_ESTALE 1
+# endif
+
+# ifndef EDQUOT
+# define EDQUOT 2010
+# define GNULIB_defined_EDQUOT 1
+# endif
+
+# ifndef ECANCELED
+# define ECANCELED 2008
+# define GNULIB_defined_ECANCELED 1
+# endif
+
+/* On many platforms, the macros EOWNERDEAD and ENOTRECOVERABLE are not
+ defined. */
+
+# ifndef EOWNERDEAD
+# if defined __sun
+ /* Use the same values as defined for Solaris >= 8, for
+ interoperability. */
+# define EOWNERDEAD 58
+# define ENOTRECOVERABLE 59
+# elif (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ /* We have a conflict here: pthreads-win32 defines these values
+ differently than MSVC 10. It's hairy to decide which one to use. */
+# if defined __MINGW32__ && !defined USE_WINDOWS_THREADS
+ /* Use the same values as defined by pthreads-win32, for
+ interoperability. */
+# define EOWNERDEAD 43
+# define ENOTRECOVERABLE 44
+# else
+ /* Use the same values as defined by MSVC 10, for
+ interoperability. */
+# define EOWNERDEAD 133
+# define ENOTRECOVERABLE 127
+# endif
+# else
+# define EOWNERDEAD 2013
+# define ENOTRECOVERABLE 2014
+# endif
+# define GNULIB_defined_EOWNERDEAD 1
+# define GNULIB_defined_ENOTRECOVERABLE 1
+# endif
+
+# ifndef EILSEQ
+# define EILSEQ 2015
+# define GNULIB_defined_EILSEQ 1
+# endif
+
+#endif /* _@GUARD_PREFIX@_ERRNO_H */
+#endif /* _@GUARD_PREFIX@_ERRNO_H */
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
new file mode 100644
index 0000000000..bb8bae1286
--- /dev/null
+++ b/lib/file-has-acl.c
@@ -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;
+}
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
index 4b754ec21d..4a84b1db26 100644
--- a/lib/gnulib.mk
+++ b/lib/gnulib.mk
@@ -21,7 +21,7 @@
# the same distribution terms as the rest of that program.
#
# Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
MOSTLYCLEANFILES += core *.stackdump
@@ -217,6 +217,40 @@ EXTRA_libgnu_a_SOURCES += dup2.c
## end gnulib module dup2
+## begin gnulib module errno
+
+BUILT_SOURCES += $(ERRNO_H)
+
+# We need the following in order to create <errno.h> when the system
+# doesn't have one that is POSIX compliant.
+if GL_GENERATE_ERRNO_H
+errno.h: errno.in.h $(top_builddir)/config.status
+ $(AM_V_GEN)rm -f $@-t $@ && \
+ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \
+ sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+ -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+ -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+ -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+ -e 's|@''NEXT_ERRNO_H''@|$(NEXT_ERRNO_H)|g' \
+ -e 's|@''EMULTIHOP_HIDDEN''@|$(EMULTIHOP_HIDDEN)|g' \
+ -e 's|@''EMULTIHOP_VALUE''@|$(EMULTIHOP_VALUE)|g' \
+ -e 's|@''ENOLINK_HIDDEN''@|$(ENOLINK_HIDDEN)|g' \
+ -e 's|@''ENOLINK_VALUE''@|$(ENOLINK_VALUE)|g' \
+ -e 's|@''EOVERFLOW_HIDDEN''@|$(EOVERFLOW_HIDDEN)|g' \
+ -e 's|@''EOVERFLOW_VALUE''@|$(EOVERFLOW_VALUE)|g' \
+ < $(srcdir)/errno.in.h; \
+ } > $@-t && \
+ mv $@-t $@
+else
+errno.h: $(top_builddir)/config.status
+ rm -f $@
+endif
+MOSTLYCLEANFILES += errno.h errno.h-t
+
+EXTRA_DIST += errno.in.h
+
+## end gnulib module errno
+
## begin gnulib module euidaccess
if gl_GNULIB_ENABLED_euidaccess
@@ -561,6 +595,16 @@ EXTRA_libgnu_a_SOURCES += putenv.c
## end gnulib module putenv
+## begin gnulib module qacl
+
+libgnu_a_SOURCES += acl-errno-valid.c file-has-acl.c qcopy-acl.c qset-acl.c
+
+EXTRA_DIST += acl-internal.h acl.h acl_entries.c
+
+EXTRA_libgnu_a_SOURCES += acl_entries.c
+
+## end gnulib module qacl
+
## begin gnulib module readlink
diff --git a/lib/makefile.w32-in b/lib/makefile.w32-in
index cd62fbcd13..537b730b40 100644
--- a/lib/makefile.w32-in
+++ b/lib/makefile.w32-in
@@ -23,7 +23,8 @@ ALL = gnulib
LOCAL_FLAGS = -I. -I../nt/inc -I../src
LIBS =
-GNULIBOBJS = $(BLD)/c-ctype.$(O) \
+GNULIBOBJS = $(BLD)/acl-errno-valid.$(O) \
+ $(BLD)/c-ctype.$(O) \
$(BLD)/c-strcasecmp.$(O) \
$(BLD)/c-strncasecmp.$(O) \
$(BLD)/close-stream.$(O) \
@@ -77,6 +78,9 @@ GNU_LIB = .
SRC = $(EMACS_ROOT)/src
NT_INC = $(EMACS_ROOT)/nt/inc
+ACL_H = $(GNU_LIB)/acl.h \
+ $(NT_INC)/sys/stat.h \
+ $(NT_INC)/stdbool.h
C_CTYPE_H = $(GNU_LIB)/c-ctype.h \
$(NT_INC)/stdbool.h
MS_W32_H = $(NT_INC)/ms-w32.h \
@@ -109,6 +113,11 @@ SIG2STR_H = $(GNU_LIB)/sig2str.h \
STAT_TIME_H = $(GNU_LIB)/stat-time.h \
$(NT_INC)/sys/stat.h
+$(BLD)/acl-errno-valid.$(O) : \
+ $(GNU_LIB)/acl-errno-valid.c \
+ $(ACL_H) \
+ $(CONFIG_H)
+
$(BLD)/c-ctype.$(O) : \
$(GNU_LIB)/c-ctype.c \
$(CONFIG_H) \
diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c
new file mode 100644
index 0000000000..25a2ff154d
--- /dev/null
+++ b/lib/qcopy-acl.c
@@ -0,0 +1,583 @@
+/* copy-acl.c - copy access control list from one file to another file
+
+ Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
+
+#include <config.h>
+
+#include "acl.h"
+
+#include "acl-internal.h"
+
+
+/* Copy access control lists from one file to another. If SOURCE_DESC is
+ a valid file descriptor, use file descriptor operations, else use
+ filename based operations on SRC_NAME. Likewise for DEST_DESC and
+ DST_NAME.
+ If access control lists are not available, fchmod the target file to
+ MODE. Also sets the non-permission bits of the destination file
+ (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
+ Return 0 if successful.
+ Return -2 and set errno for an error relating to the source file.
+ Return -1 and set errno for an error relating to the destination file. */
+
+int
+qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
+ int dest_desc, mode_t mode)
+{
+#if USE_ACL && HAVE_ACL_GET_FILE
+ /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
+ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
+# if !HAVE_ACL_TYPE_EXTENDED
+ /* Linux, FreeBSD, IRIX, Tru64 */
+
+ acl_t acl;
+ int ret;
+
+ if (HAVE_ACL_GET_FD && source_desc != -1)
+ acl = acl_get_fd (source_desc);
+ else
+ acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
+ if (acl == NULL)
+ {
+ if (! acl_errno_valid (errno))
+ return qset_acl (dst_name, dest_desc, mode);
+ else
+ return -2;
+ }
+
+ if (HAVE_ACL_SET_FD && dest_desc != -1)
+ ret = acl_set_fd (dest_desc, acl);
+ else
+ ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
+ if (ret != 0)
+ {
+ int saved_errno = errno;
+
+ if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl))
+ {
+ acl_free (acl);
+ return chmod_or_fchmod (dst_name, dest_desc, mode);
+ }
+ else
+ {
+ acl_free (acl);
+ chmod_or_fchmod (dst_name, dest_desc, mode);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+ else
+ acl_free (acl);
+
+ if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ /* We did not call chmod so far, and either the mode and the ACL are
+ separate or special bits are to be set which don't fit into ACLs. */
+
+ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
+ return -1;
+ }
+
+ if (S_ISDIR (mode))
+ {
+ acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
+ if (acl == NULL)
+ return -2;
+
+ if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
+ {
+ int saved_errno = errno;
+
+ acl_free (acl);
+ errno = saved_errno;
+ return -1;
+ }
+ else
+ acl_free (acl);
+ }
+ return 0;
+
+# else /* HAVE_ACL_TYPE_EXTENDED */
+ /* Mac OS X */
+
+ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
+ and acl_get_file (name, ACL_TYPE_DEFAULT)
+ always return NULL / EINVAL. You have to use
+ acl_get_file (name, ACL_TYPE_EXTENDED)
+ or acl_get_fd (open (name, ...))
+ to retrieve an ACL.
+ On the other hand,
+ acl_set_file (name, ACL_TYPE_ACCESS, acl)
+ and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
+ have the same effect as
+ acl_set_file (name, ACL_TYPE_EXTENDED, acl):
+ Each of these calls sets the file's ACL. */
+
+ acl_t acl;
+ int ret;
+
+ if (HAVE_ACL_GET_FD && source_desc != -1)
+ acl = acl_get_fd (source_desc);
+ else
+ acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
+ if (acl == NULL)
+ {
+ if (!acl_errno_valid (errno))
+ return qset_acl (dst_name, dest_desc, mode);
+ else
+ return -2;
+ }
+
+ if (HAVE_ACL_SET_FD && dest_desc != -1)
+ ret = acl_set_fd (dest_desc, acl);
+ else
+ ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
+ if (ret != 0)
+ {
+ int saved_errno = errno;
+
+ if (!acl_errno_valid (saved_errno) && !acl_extended_nontrivial (acl))
+ {
+ acl_free (acl);
+ return chmod_or_fchmod (dst_name, dest_desc, mode);
+ }
+ else
+ {
+ acl_free (acl);
+ chmod_or_fchmod (dst_name, dest_desc, mode);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+ else
+ acl_free (acl);
+
+ /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
+ return chmod_or_fchmod (dst_name, dest_desc, mode);
+
+# endif
+
+#elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+
+ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
+ of Unixware. The acl() call returns the access and default ACL both
+ at once. */
+# ifdef ACE_GETACL
+ int ace_count;
+ ace_t *ace_entries;
+# endif
+ int count;
+ aclent_t *entries;
+ int did_chmod;
+ int saved_errno;
+ int ret;
+
+# ifdef ACE_GETACL
+ /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
+ file systems (whereas the other ones are used in UFS file systems).
+ There is an API
+ pathconf (name, _PC_ACL_ENABLED)
+ fpathconf (desc, _PC_ACL_ENABLED)
+ that allows to determine which of the two kinds of ACLs is supported
+ for the given file. But some file systems may implement this call
+ incorrectly, so better not use it.
+ When fetching the source ACL, we simply fetch both ACL types.
+ When setting the destination ACL, we try either ACL types, assuming
+ that the kernel will translate the ACL from one form to the other.
+ (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
+ the description of ENOTSUP.) */
+ for (;;)
+ {
+ ace_count = (source_desc != -1
+ ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
+ : acl (src_name, ACE_GETACLCNT, 0, NULL));
+
+ if (ace_count < 0)
+ {
+ if (errno == ENOSYS || errno == EINVAL)
+ {
+ ace_count = 0;
+ ace_entries = NULL;
+ break;
+ }
+ else
+ return -2;
+ }
+
+ if (ace_count == 0)
+ {
+ ace_entries = NULL;
+ break;
+ }
+
+ ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
+ if (ace_entries == NULL)
+ {
+ errno = ENOMEM;
+ return -2;
+ }
+
+ ret = (source_desc != -1
+ ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
+ : acl (src_name, ACE_GETACL, ace_count, ace_entries));
+ if (ret < 0)
+ {
+ free (ace_entries);
+ if (errno == ENOSYS || errno == EINVAL)
+ {
+ ace_count = 0;
+ ace_entries = NULL;
+ break;
+ }
+ else
+ return -2;
+ }
+ if (ret == ace_count)
+ break;
+ /* Huh? The number of ACL entries changed since the last call.
+ Repeat. */
+ }
+# endif
+
+ for (;;)
+ {
+ count = (source_desc != -1
+ ? facl (source_desc, GETACLCNT, 0, NULL)
+ : acl (src_name, GETACLCNT, 0, NULL));
+
+ if (count < 0)
+ {
+ if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
+ {
+ count = 0;
+ entries = NULL;
+ break;
+ }
+ else
+ return -2;
+ }
+
+ if (count == 0)
+ {
+ entries = NULL;
+ break;
+ }
+
+ entries = (aclent_t *) malloc (count * sizeof (aclent_t));
+ if (entries == NULL)
+ {
+ errno = ENOMEM;
+ return -2;
+ }
+
+ if ((source_desc != -1
+ ? facl (source_desc, GETACL, count, entries)
+ : acl (src_name, GETACL, count, entries))
+ == count)
+ break;
+ /* Huh? The number of ACL entries changed since the last call.
+ Repeat. */
+ }
+
+ /* Is there an ACL of either kind? */
+# ifdef ACE_GETACL
+ if (ace_count == 0)
+# endif
+ if (count == 0)
+ return qset_acl (dst_name, dest_desc, mode);
+
+ did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
+ saved_errno = 0; /* the first non-ignorable error code */
+
+ if (!MODE_INSIDE_ACL)
+ {
+ /* On Cygwin, it is necessary to call chmod before acl, because
+ chmod can change the contents of the ACL (in ways that don't
+ change the allowed accesses, but still visible). */
+ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
+ saved_errno = errno;
+ did_chmod = 1;
+ }
+
+ /* If both ace_entries and entries are available, try SETACL before
+ ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
+ can. */
+
+ if (count > 0)
+ {
+ ret = (dest_desc != -1
+ ? facl (dest_desc, SETACL, count, entries)
+ : acl (dst_name, SETACL, count, entries));
+ if (ret < 0 && saved_errno == 0)
+ {
+ saved_errno = errno;
+ if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ && !acl_nontrivial (count, entries))
+ saved_errno = 0;
+ }
+ else
+ did_chmod = 1;
+ }
+ free (entries);
+
+# ifdef ACE_GETACL
+ if (ace_count > 0)
+ {
+ ret = (dest_desc != -1
+ ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
+ : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
+ if (ret < 0 && saved_errno == 0)
+ {
+ saved_errno = errno;
+ if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
+ && !acl_ace_nontrivial (ace_count, ace_entries))
+ saved_errno = 0;
+ }
+ }
+ free (ace_entries);
+# endif
+
+ if (MODE_INSIDE_ACL
+ && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
+ {
+ /* We did not call chmod so far, and either the mode and the ACL are
+ separate or special bits are to be set which don't fit into ACLs. */
+
+ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
+ {
+ if (saved_errno == 0)
+ saved_errno = errno;
+ }
+ }
+
+ if (saved_errno)
+ {
+ errno = saved_errno;
+ return -1;
+ }
+ return 0;
+
+#elif USE_ACL && HAVE_GETACL /* HP-UX */
+
+ struct acl_entry entries[NACLENTRIES];
+ int count;
+# if HAVE_ACLV_H
+ struct acl aclv_entries[NACLVENTRIES];
+ int aclv_count;
+# endif
+ int did_chmod;
+ int saved_errno;
+ int ret;
+
+ count = (source_desc != -1
+ ? fgetacl (source_desc, NACLENTRIES, entries)
+ : getacl (src_name, NACLENTRIES, entries));
+
+ if (count < 0)
+ {
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
+ count = 0;
+ else
+ return -2;
+ }
+ else if (count > 0)
+ {
+ if (count > NACLENTRIES)
+ /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
+ abort ();
+ }
+
+# if HAVE_ACLV_H
+ aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries);
+
+ if (aclv_count < 0)
+ {
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ count = 0;
+ else
+ return -2;
+ }
+ else if (aclv_count > 0)
+ {
+ if (aclv_count > NACLVENTRIES)
+ /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */
+ abort ();
+ }
+# endif
+
+ if (count == 0)
+# if HAVE_ACLV_H
+ if (aclv_count == 0)
+# endif
+ return qset_acl (dst_name, dest_desc, mode);
+
+ did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
+ saved_errno = 0; /* the first non-ignorable error code */
+
+ if (count > 0)
+ {
+ ret = (dest_desc != -1
+ ? fsetacl (dest_desc, count, entries)
+ : setacl (dst_name, count, entries));
+ if (ret < 0 && saved_errno == 0)
+ {
+ saved_errno = errno;
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
+ {
+ struct stat source_statbuf;
+
+ if ((source_desc != -1
+ ? fstat (source_desc, &source_statbuf)
+ : stat (src_name, &source_statbuf)) == 0)
+ {
+ if (!acl_nontrivial (count, entries, &source_statbuf))
+ saved_errno = 0;
+ }
+ else
+ saved_errno = errno;
+ }
+ }
+ else
+ did_chmod = 1;
+ }
+
+# if HAVE_ACLV_H
+ if (aclv_count > 0)
+ {
+ ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries);
+ if (ret < 0 && saved_errno == 0)
+ {
+ saved_errno = errno;
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ {
+ if (!aclv_nontrivial (aclv_count, aclv_entries))
+ saved_errno = 0;
+ }
+ }
+ else
+ did_chmod = 1;
+ }
+# endif
+
+ if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
+ {
+ /* We did not call chmod so far, and special bits are to be set which
+ don't fit into ACLs. */
+
+ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
+ {
+ if (saved_errno == 0)
+ saved_errno = errno;
+ }
+ }
+
+ if (saved_errno)
+ {
+ errno = saved_errno;
+ return -1;
+ }
+ return 0;
+
+#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
+
+ /* TODO */
+
+#elif USE_ACL && HAVE_STATACL /* older AIX */
+
+ union { struct acl a; char room[4096]; } u;
+ int ret;
+
+ if ((source_desc != -1
+ ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
+ : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
+ < 0)
+ return -2;
+
+ ret = (dest_desc != -1
+ ? fchacl (dest_desc, &u.a, u.a.acl_len)
+ : chacl (dst_name, &u.a, u.a.acl_len));
+ if (ret < 0)
+ {
+ int saved_errno = errno;
+
+ chmod_or_fchmod (dst_name, dest_desc, mode);
+ errno = saved_errno;
+ return -1;
+ }
+
+ /* No need to call chmod_or_fchmod at this point, since the mode bits
+ S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
+
+ return 0;
+
+#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
+
+ struct acl entries[NACLENTRIES];
+ int count;
+ int ret;
+
+ count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries);
+
+ if (count < 0)
+ {
+ if (0)
+ count = 0;
+ else
+ return -2;
+ }
+ else if (count > 0)
+ {
+ if (count > NACLENTRIES)
+ /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
+ abort ();
+ }
+
+ if (count == 0)
+ return qset_acl (dst_name, dest_desc, mode);
+
+ ret = acl ((char *) dst_name, ACL_SET, count, entries);
+ if (ret < 0)
+ {
+ int saved_errno = errno;
+
+ if (0)
+ {
+ if (!acl_nontrivial (count, entries))
+ return chmod_or_fchmod (dst_name, dest_desc, mode);
+ }
+
+ chmod_or_fchmod (dst_name, dest_desc, mode);
+ errno = saved_errno;
+ return -1;
+ }
+
+ if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+ {
+ /* We did not call chmod so far, and either the mode and the ACL are
+ separate or special bits are to be set which don't fit into ACLs. */
+
+ return chmod_or_fchmod (dst_name, dest_desc, mode);
+ }
+ return 0;
+
+#else
+
+ return qset_acl (dst_name, dest_desc, mode);
+
+#endif
+}
diff --git a/lib/qset-acl.c b/lib/qset-acl.c
new file mode 100644
index 0000000000..7bde2c15b0
--- /dev/null
+++ b/lib/qset-acl.c
@@ -0,0 +1,676 @@
+/* qset-acl.c - set access control list equivalent to a mode
+
+ Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */
+
+#include <config.h>
+
+#define ACL_INTERNAL_INLINE _GL_EXTERN_INLINE
+
+#include "acl.h"
+
+#include "acl-internal.h"
+
+
+/* If DESC is a valid file descriptor use fchmod to change the
+ file's mode to MODE on systems that have fchmod. On systems
+ that don't have fchmod and if DESC is invalid, use chmod on
+ NAME instead.
+ Return 0 if successful. Return -1 and set errno upon failure. */
+
+int
+chmod_or_fchmod (const char *name, int desc, mode_t mode)
+{
+ if (HAVE_FCHMOD && desc != -1)
+ return fchmod (desc, mode);
+ else
+ return chmod (name, mode);
+}
+
+/* Set the access control lists of a file. If DESC is a valid file
+ descriptor, use file descriptor operations where available, else use
+ filename based operations on NAME. If access control lists are not
+ available, fchmod the target file to MODE. Also sets the
+ non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
+ to those from MODE if any are set.
+ Return 0 if successful. Return -1 and set errno upon failure. */
+
+int
+qset_acl (char const *name, int desc, mode_t mode)
+{
+#if USE_ACL
+# if HAVE_ACL_GET_FILE
+ /* POSIX 1003.1e draft 17 (abandoned) specific version. */
+ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
+# if !HAVE_ACL_TYPE_EXTENDED
+ /* Linux, FreeBSD, IRIX, Tru64 */
+
+ /* We must also have acl_from_text and acl_delete_def_file.
+ (acl_delete_def_file could be emulated with acl_init followed
+ by acl_set_file, but acl_set_file with an empty acl is
+ unspecified.) */
+
+# ifndef HAVE_ACL_FROM_TEXT
+# error Must have acl_from_text (see POSIX 1003.1e draft 17).
+# endif
+# ifndef HAVE_ACL_DELETE_DEF_FILE
+# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
+# endif
+
+ acl_t acl;
+ int ret;
+
+ if (HAVE_ACL_FROM_MODE) /* Linux */
+ {
+ acl = acl_from_mode (mode);
+ if (!acl)
+ return -1;
+ }
+ else /* FreeBSD, IRIX, Tru64 */
+ {
+ /* If we were to create the ACL using the functions acl_init(),
+ acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
+ acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
+ would need to create a qualifier. I don't know how to do this.
+ So create it using acl_from_text(). */
+
+# if HAVE_ACL_FREE_TEXT /* Tru64 */
+ char acl_text[] = "u::---,g::---,o::---,";
+# else /* FreeBSD, IRIX */
+ char acl_text[] = "u::---,g::---,o::---";
+# endif
+
+ if (mode & S_IRUSR) acl_text[ 3] = 'r';
+ if (mode & S_IWUSR) acl_text[ 4] = 'w';
+ if (mode & S_IXUSR) acl_text[ 5] = 'x';
+ if (mode & S_IRGRP) acl_text[10] = 'r';
+ if (mode & S_IWGRP) acl_text[11] = 'w';
+ if (mode & S_IXGRP) acl_text[12] = 'x';
+ if (mode & S_IROTH) acl_text[17] = 'r';
+ if (mode & S_IWOTH) acl_text[18] = 'w';
+ if (mode & S_IXOTH) acl_text[19] = 'x';
+
+ acl = acl_from_text (acl_text);
+ if (!acl)
+ return -1;
+ }
+ if (HAVE_ACL_SET_FD && desc != -1)
+ ret = acl_set_fd (desc, acl);
+ else
+ ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
+ if (ret != 0)
+ {
+ int saved_errno = errno;
+ acl_free (acl);
+ if (! acl_errno_valid (errno))
+ return chmod_or_fchmod (name, desc, mode);
+ errno = saved_errno;
+ return -1;
+ }
+ else
+ acl_free (acl);
+
+ if (S_ISDIR (mode) && acl_delete_def_file (name))
+ return -1;
+
+ if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ /* We did not call chmod so far, and either the mode and the ACL are
+ separate or special bits are to be set which don't fit into ACLs. */
+ return chmod_or_fchmod (name, desc, mode);
+ }
+ return 0;
+
+# else /* HAVE_ACL_TYPE_EXTENDED */
+ /* Mac OS X */
+
+ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
+ and acl_get_file (name, ACL_TYPE_DEFAULT)
+ always return NULL / EINVAL. You have to use
+ acl_get_file (name, ACL_TYPE_EXTENDED)
+ or acl_get_fd (open (name, ...))
+ to retrieve an ACL.
+ On the other hand,
+ acl_set_file (name, ACL_TYPE_ACCESS, acl)
+ and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
+ have the same effect as
+ acl_set_file (name, ACL_TYPE_EXTENDED, acl):
+ Each of these calls sets the file's ACL. */
+
+ acl_t acl;
+ int ret;
+
+ /* Remove the ACL if the file has ACLs. */
+ if (HAVE_ACL_GET_FD && desc != -1)
+ acl = acl_get_fd (desc);
+ else
+ acl = acl_get_file (name, ACL_TYPE_EXTENDED);
+ if (acl)
+ {
+ acl_free (acl);
+
+ acl = acl_init (0);
+ if (acl)
+ {
+ if (HAVE_ACL_SET_FD && desc != -1)
+ ret = acl_set_fd (desc, acl);
+ else
+ ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
+ if (ret != 0)
+ {
+ int saved_errno = errno;
+ acl_free (acl);
+ if (! acl_errno_valid (saved_errno))
+ return chmod_or_fchmod (name, desc, mode);
+ errno = saved_errno;
+ return -1;
+ }
+ acl_free (acl);
+ }
+ }
+
+ /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
+ return chmod_or_fchmod (name, desc, mode);
+# endif
+
+# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+
+ int done_setacl = 0;
+
+# ifdef ACE_GETACL
+ /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
+ file systems (whereas the other ones are used in UFS file systems). */
+
+ /* The flags in the ace_t structure changed in a binary incompatible way
+ when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
+ How to distinguish the two conventions at runtime?
+ We fetch the existing ACL. In the old convention, usually three ACEs have
+ a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
+ In the new convention, these values are not used. */
+ int convention;
+
+ {
+ /* Initially, try to read the entries into a stack-allocated buffer.
+ Use malloc if it does not fit. */
+ enum
+ {
+ alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
+ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
+ };
+ ace_t buf[alloc_init];
+ size_t alloc = alloc_init;
+ ace_t *entries = buf;
+ ace_t *malloced = NULL;
+ int count;
+
+ for (;;)
+ {
+ count = (desc != -1
+ ? facl (desc, ACE_GETACL, alloc, entries)
+ : acl (name, ACE_GETACL, alloc, entries));
+ if (count < 0 && errno == ENOSPC)
+ {
+ /* Increase the size of the buffer. */
+ free (malloced);
+ if (alloc > alloc_max / 2)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ alloc = 2 * alloc; /* <= alloc_max */
+ entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
+ if (entries == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }
+
+ if (count <= 0)
+ convention = -1;
+ else
+ {
+ int i;
+
+ convention = 0;
+ for (i = 0; i < count; i++)
+ if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
+ {
+ convention = 1;
+ break;
+ }
+ }
+ free (malloced);
+ }
+
+ if (convention >= 0)
+ {
+ ace_t entries[6];
+ int count;
+ int ret;
+
+ if (convention)
+ {
+ /* Running on Solaris 10. */
+ entries[0].a_type = OLD_ALLOW;
+ entries[0].a_flags = OLD_ACE_OWNER;
+ entries[0].a_who = 0; /* irrelevant */
+ entries[0].a_access_mask = (mode >> 6) & 7;
+ entries[1].a_type = OLD_ALLOW;
+ entries[1].a_flags = OLD_ACE_GROUP;
+ entries[1].a_who = 0; /* irrelevant */
+ entries[1].a_access_mask = (mode >> 3) & 7;
+ entries[2].a_type = OLD_ALLOW;
+ entries[2].a_flags = OLD_ACE_OTHER;
+ entries[2].a_who = 0;
+ entries[2].a_access_mask = mode & 7;
+ count = 3;
+ }
+ else
+ {
+ /* Running on Solaris 10 (newer version) or Solaris 11.
+ The details here were found through "/bin/ls -lvd somefiles". */
+ entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
+ entries[0].a_flags = NEW_ACE_OWNER;
+ entries[0].a_who = 0; /* irrelevant */
+ entries[0].a_access_mask = 0;
+ entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
+ entries[1].a_flags = NEW_ACE_OWNER;
+ entries[1].a_who = 0; /* irrelevant */
+ entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
+ | NEW_ACE_WRITE_ATTRIBUTES
+ | NEW_ACE_WRITE_ACL
+ | NEW_ACE_WRITE_OWNER;
+ if (mode & 0400)
+ entries[1].a_access_mask |= NEW_ACE_READ_DATA;
+ else
+ entries[0].a_access_mask |= NEW_ACE_READ_DATA;
+ if (mode & 0200)
+ entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+ else
+ entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+ if (mode & 0100)
+ entries[1].a_access_mask |= NEW_ACE_EXECUTE;
+ else
+ entries[0].a_access_mask |= NEW_ACE_EXECUTE;
+ entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
+ entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
+ entries[2].a_who = 0; /* irrelevant */
+ entries[2].a_access_mask = 0;
+ entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
+ entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
+ entries[3].a_who = 0; /* irrelevant */
+ entries[3].a_access_mask = 0;
+ if (mode & 0040)
+ entries[3].a_access_mask |= NEW_ACE_READ_DATA;
+ else
+ entries[2].a_access_mask |= NEW_ACE_READ_DATA;
+ if (mode & 0020)
+ entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+ else
+ entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+ if (mode & 0010)
+ entries[3].a_access_mask |= NEW_ACE_EXECUTE;
+ else
+ entries[2].a_access_mask |= NEW_ACE_EXECUTE;
+ entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
+ entries[4].a_flags = NEW_ACE_EVERYONE;
+ entries[4].a_who = 0;
+ entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
+ | NEW_ACE_WRITE_ATTRIBUTES
+ | NEW_ACE_WRITE_ACL
+ | NEW_ACE_WRITE_OWNER;
+ entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
+ entries[5].a_flags = NEW_ACE_EVERYONE;
+ entries[5].a_who = 0;
+ entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
+ | NEW_ACE_READ_ATTRIBUTES
+ | NEW_ACE_READ_ACL
+ | NEW_ACE_SYNCHRONIZE;
+ if (mode & 0004)
+ entries[5].a_access_mask |= NEW_ACE_READ_DATA;
+ else
+ entries[4].a_access_mask |= NEW_ACE_READ_DATA;
+ if (mode & 0002)
+ entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+ else
+ entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
+ if (mode & 0001)
+ entries[5].a_access_mask |= NEW_ACE_EXECUTE;
+ else
+ entries[4].a_access_mask |= NEW_ACE_EXECUTE;
+ count = 6;
+ }
+ if (desc != -1)
+ ret = facl (desc, ACE_SETACL, count, entries);
+ else
+ ret = acl (name, ACE_SETACL, count, entries);
+ if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
+ {
+ if (errno == ENOSYS)
+ return chmod_or_fchmod (name, desc, mode);
+ return -1;
+ }
+ if (ret == 0)
+ done_setacl = 1;
+ }
+# endif
+
+ if (!done_setacl)
+ {
+ aclent_t entries[3];
+ int ret;
+
+ entries[0].a_type = USER_OBJ;
+ entries[0].a_id = 0; /* irrelevant */
+ entries[0].a_perm = (mode >> 6) & 7;
+ entries[1].a_type = GROUP_OBJ;
+ entries[1].a_id = 0; /* irrelevant */
+ entries[1].a_perm = (mode >> 3) & 7;
+ entries[2].a_type = OTHER_OBJ;
+ entries[2].a_id = 0;
+ entries[2].a_perm = mode & 7;
+
+ if (desc != -1)
+ ret = facl (desc, SETACL,
+ sizeof (entries) / sizeof (aclent_t), entries);
+ else
+ ret = acl (name, SETACL,
+ sizeof (entries) / sizeof (aclent_t), entries);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == EOPNOTSUPP)
+ return chmod_or_fchmod (name, desc, mode);
+ return -1;
+ }
+ }
+
+ if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ /* We did not call chmod so far, so the special bits have not yet
+ been set. */
+ return chmod_or_fchmod (name, desc, mode);
+ }
+ return 0;
+
+# elif HAVE_GETACL /* HP-UX */
+
+ struct stat statbuf;
+ int ret;
+
+ if (desc != -1)
+ ret = fstat (desc, &statbuf);
+ else
+ ret = stat (name, &statbuf);
+ if (ret < 0)
+ return -1;
+
+ {
+ struct acl_entry entries[3];
+
+ entries[0].uid = statbuf.st_uid;
+ entries[0].gid = ACL_NSGROUP;
+ entries[0].mode = (mode >> 6) & 7;
+ entries[1].uid = ACL_NSUSER;
+ entries[1].gid = statbuf.st_gid;
+ entries[1].mode = (mode >> 3) & 7;
+ entries[2].uid = ACL_NSUSER;
+ entries[2].gid = ACL_NSGROUP;
+ entries[2].mode = mode & 7;
+
+ if (desc != -1)
+ ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
+ else
+ ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
+ }
+ if (ret < 0)
+ {
+ if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
+ return -1;
+
+# if HAVE_ACLV_H /* HP-UX >= 11.11 */
+ {
+ struct acl entries[4];
+
+ entries[0].a_type = USER_OBJ;
+ entries[0].a_id = 0; /* irrelevant */
+ entries[0].a_perm = (mode >> 6) & 7;
+ entries[1].a_type = GROUP_OBJ;
+ entries[1].a_id = 0; /* irrelevant */
+ entries[1].a_perm = (mode >> 3) & 7;
+ entries[2].a_type = CLASS_OBJ;
+ entries[2].a_id = 0;
+ entries[2].a_perm = (mode >> 3) & 7;
+ entries[3].a_type = OTHER_OBJ;
+ entries[3].a_id = 0;
+ entries[3].a_perm = mode & 7;
+
+ ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
+ if (ret > 0)
+ abort ();
+ if (ret < 0)
+ {
+ if (0)
+ return chmod_or_fchmod (name, desc, mode);
+ return -1;
+ }
+
+ ret = acl ((char *) name, ACL_SET,
+ sizeof (entries) / sizeof (struct acl), entries);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ return chmod_or_fchmod (name, desc, mode);
+ return -1;
+ }
+ }
+# else
+ return chmod_or_fchmod (name, desc, mode);
+# endif
+ }
+
+ if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+ {
+ /* We did not call chmod so far, so the special bits have not yet
+ been set. */
+ return chmod_or_fchmod (name, desc, mode);
+ }
+ return 0;
+
+# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
+
+ acl_type_list_t types;
+ size_t types_size = sizeof (types);
+ acl_type_t type;
+
+ if (aclx_gettypes (name, &types, &types_size) < 0
+ || types.num_entries == 0)
+ return chmod_or_fchmod (name, desc, mode);
+
+ /* XXX Do we need to clear all types of ACLs for the given file, or is it
+ sufficient to clear the first one? */
+ type = types.entries[0];
+ if (type.u64 == ACL_AIXC)
+ {
+ union { struct acl a; char room[128]; } u;
+ int ret;
+
+ u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
+ u.a.acl_mode = mode & ~(S_IXACL | 0777);
+ u.a.u_access = (mode >> 6) & 7;
+ u.a.g_access = (mode >> 3) & 7;
+ u.a.o_access = mode & 7;
+
+ if (desc != -1)
+ ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
+ type, &u.a, u.a.acl_len, mode);
+ else
+ ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
+ type, &u.a, u.a.acl_len, mode);
+ if (!(ret < 0 && errno == ENOSYS))
+ return ret;
+ }
+ else if (type.u64 == ACL_NFS4)
+ {
+ union { nfs4_acl_int_t a; char room[128]; } u;
+ nfs4_ace_int_t *ace;
+ int ret;
+
+ u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
+ u.a.aclEntryN = 0;
+ ace = &u.a.aclEntry[0];
+ {
+ ace->flags = ACE4_ID_SPECIAL;
+ ace->aceWho.special_whoid = ACE4_WHO_OWNER;
+ ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->aceFlags = 0;
+ ace->aceMask =
+ (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
+ | (mode & 0200
+ ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
+ | ACE4_ADD_SUBDIRECTORY
+ : 0)
+ | (mode & 0100 ? ACE4_EXECUTE : 0);
+ ace->aceWhoString[0] = '\0';
+ ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
+ ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
+ u.a.aclEntryN++;
+ }
+ {
+ ace->flags = ACE4_ID_SPECIAL;
+ ace->aceWho.special_whoid = ACE4_WHO_GROUP;
+ ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->aceFlags = 0;
+ ace->aceMask =
+ (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
+ | (mode & 0020
+ ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
+ | ACE4_ADD_SUBDIRECTORY
+ : 0)
+ | (mode & 0010 ? ACE4_EXECUTE : 0);
+ ace->aceWhoString[0] = '\0';
+ ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
+ ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
+ u.a.aclEntryN++;
+ }
+ {
+ ace->flags = ACE4_ID_SPECIAL;
+ ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
+ ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace->aceFlags = 0;
+ ace->aceMask =
+ (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
+ | (mode & 0002
+ ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
+ | ACE4_ADD_SUBDIRECTORY
+ : 0)
+ | (mode & 0001 ? ACE4_EXECUTE : 0);
+ ace->aceWhoString[0] = '\0';
+ ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
+ ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
+ u.a.aclEntryN++;
+ }
+ u.a.aclLength = (char *) ace - (char *) &u.a;
+
+ if (desc != -1)
+ ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
+ type, &u.a, u.a.aclLength, mode);
+ else
+ ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
+ type, &u.a, u.a.aclLength, mode);
+ if (!(ret < 0 && errno == ENOSYS))
+ return ret;
+ }
+
+ return chmod_or_fchmod (name, desc, mode);
+
+# elif HAVE_STATACL /* older AIX */
+
+ union { struct acl a; char room[128]; } u;
+ int ret;
+
+ u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
+ u.a.acl_mode = mode & ~(S_IXACL | 0777);
+ u.a.u_access = (mode >> 6) & 7;
+ u.a.g_access = (mode >> 3) & 7;
+ u.a.o_access = mode & 7;
+
+ if (desc != -1)
+ ret = fchacl (desc, &u.a, u.a.acl_len);
+ else
+ ret = chacl (name, &u.a, u.a.acl_len);
+
+ if (ret < 0 && errno == ENOSYS)
+ return chmod_or_fchmod (name, desc, mode);
+
+ return ret;
+
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+ struct acl entries[4];
+ int ret;
+
+ entries[0].a_type = USER_OBJ;
+ entries[0].a_id = 0; /* irrelevant */
+ entries[0].a_perm = (mode >> 6) & 7;
+ entries[1].a_type = GROUP_OBJ;
+ entries[1].a_id = 0; /* irrelevant */
+ entries[1].a_perm = (mode >> 3) & 7;
+ entries[2].a_type = CLASS_OBJ;
+ entries[2].a_id = 0;
+ entries[2].a_perm = (mode >> 3) & 7;
+ entries[3].a_type = OTHER_OBJ;
+ entries[3].a_id = 0;
+ entries[3].a_perm = mode & 7;
+
+ ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
+ if (ret > 0)
+ abort ();
+ if (ret < 0)
+ {
+ if (0)
+ return chmod_or_fchmod (name, desc, mode);
+ return -1;
+ }
+
+ ret = acl ((char *) name, ACL_SET,
+ sizeof (entries) / sizeof (struct acl), entries);
+ if (ret < 0)
+ {
+ if (0)
+ return chmod_or_fchmod (name, desc, mode);
+ return -1;
+ }
+
+ if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+ {
+ /* We did not call chmod so far, so the special bits have not yet
+ been set. */
+ return chmod_or_fchmod (name, desc, mode);
+ }
+ return 0;
+
+# else /* Unknown flavor of ACLs */
+ return chmod_or_fchmod (name, desc, mode);
+# endif
+#else /* !USE_ACL */
+ return chmod_or_fchmod (name, desc, mode);
+#endif
+}
diff --git a/lib/utimens.c b/lib/utimens.c
index 87123605ba..013843d6da 100644
--- a/lib/utimens.c
+++ b/lib/utimens.c
@@ -216,15 +216,19 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2])
if (0 <= utimensat_works_really)
{
int result;
-# if __linux__
+# if __linux__ || __sun
/* As recently as Linux kernel 2.6.32 (Dec 2009), several file
systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
but work if both times are either explicitly specified or
UTIME_NOW. Work around it with a preparatory [f]stat prior
to calling futimens/utimensat; fortunately, there is not much
timing impact due to the extra syscall even on file systems
- where UTIME_OMIT would have worked. FIXME: Simplify this in
- 2012, when file system bugs are no longer common. */
+ where UTIME_OMIT would have worked.
+
+ The same bug occurs in Solaris 11.1 (Apr 2013).
+
+ FIXME: Simplify this for Linux in 2016 and for Solaris in
+ 2024, when file system bugs are no longer common. */
if (adjustment_needed == 2)
{
if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
@@ -236,7 +240,7 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2])
/* Note that st is good, in case utimensat gives ENOSYS. */
adjustment_needed++;
}
-# endif /* __linux__ */
+# endif
# if HAVE_UTIMENSAT
if (fd < 0)
{
@@ -445,15 +449,19 @@ lutimens (char const *file, struct timespec const timespec[2])
if (0 <= lutimensat_works_really)
{
int result;
-# if __linux__
+# if __linux__ || __sun
/* As recently as Linux kernel 2.6.32 (Dec 2009), several file
systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
but work if both times are either explicitly specified or
UTIME_NOW. Work around it with a preparatory lstat prior to
calling utimensat; fortunately, there is not much timing
impact due to the extra syscall even on file systems where
- UTIME_OMIT would have worked. FIXME: Simplify this in 2012,
- when file system bugs are no longer common. */
+ UTIME_OMIT would have worked.
+
+ The same bug occurs in Solaris 11.1 (Apr 2013).
+
+ FIXME: Simplify this for Linux in 2016 and for Solaris in
+ 2024, when file system bugs are no longer common. */
if (adjustment_needed == 2)
{
if (lstat (file, &st))
@@ -465,7 +473,7 @@ lutimens (char const *file, struct timespec const timespec[2])
/* Note that st is good, in case utimensat gives ENOSYS. */
adjustment_needed++;
}
-# endif /* __linux__ */
+# endif
result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
# ifdef __linux__
/* Work around a kernel bug: