summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2015-05-27 17:46:15 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2015-05-27 17:55:06 -0700
commit6c52e9b93b68795d1876718d8f3c1d57bf7f6d91 (patch)
treeb2d93b1121c05cfffa59df9325139cdf56eed6c0 /lib
parentcc41938ae0f5694d657f360f19dd9ac166271db2 (diff)
downloademacs-6c52e9b93b68795d1876718d8f3c1d57bf7f6d91.tar.gz
Merge from gnulib
This incorporates: 2015-05-27 qacl: Reimplement qset_acl and qcopy_acl (Bug#20666) 2015-05-27 file-has-acl: Split feature tests again (Bug#20667) 2015-05-27 string: fix build failure on BSD/OSX with FORTIFY_SOURCE 2015-05-26 stdio: limit __gnu_printf__ witness to gcc 4.4+ 2015-05-26 inttypes: force correct mingw PRIdMAX even without <stdio.h> 2015-05-26 stdio: fix probe on mingw under gcc 5.1 * admin/merge-gnulib (GNULIB_MODULES): Replace qacl with qcopy-acl, since we don't need the rest of qacl. * doc/misc/texinfo.tex, lib/acl-internal.c, lib/acl-internal.h: * lib/inttypes.in.h, lib/qcopy-acl.c, lib/qset-acl.c: * lib/string.in.h, m4/acl.m4, m4/stdio_h.m4: Get latest versions from gnulib. * lib/get-permissions.c, lib/set-permissions.c: New files. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * nt/gnulib.mk: Merge lib/gnulib.mk changes by hand.
Diffstat (limited to 'lib')
-rw-r--r--lib/acl-internal.c31
-rw-r--r--lib/acl-internal.h57
-rw-r--r--lib/get-permissions.c280
-rw-r--r--lib/gnulib.mk23
-rw-r--r--lib/inttypes.in.h4
-rw-r--r--lib/qcopy-acl.c531
-rw-r--r--lib/set-permissions.c (renamed from lib/qset-acl.c)805
-rw-r--r--lib/string.in.h21
8 files changed, 884 insertions, 868 deletions
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index d9bd4461ea6..1a2f8c44bf7 100644
--- a/lib/acl-internal.c
+++ b/lib/acl-internal.c
@@ -467,3 +467,34 @@ acl_nontrivial (int count, struct acl *entries)
}
#endif
+
+void
+free_permission_context (struct permission_context *ctx)
+{
+#ifdef USE_ACL
+# if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
+ if (ctx->acl)
+ acl_free (ctx->acl);
+# if !HAVE_ACL_TYPE_EXTENDED
+ if (ctx->default_acl)
+ acl_free (ctx->default_acl);
+# endif
+
+# elif defined GETACL /* Solaris, Cygwin */
+ free (ctx->entries);
+# ifdef ACE_GETACL
+ free (ctx->ace_entries);
+# endif
+
+# elif HAVE_GETACL /* HP-UX */
+
+# if HAVE_ACLV_H
+# endif
+
+# elif HAVE_STATACL /* older AIX */
+
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+# endif
+#endif
+}
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
index 9b9fae2e9e0..11fdea14042 100644
--- a/lib/acl-internal.h
+++ b/lib/acl-internal.h
@@ -133,12 +133,9 @@ rpl_acl_set_fd (int fd, acl_t acl)
# 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. */
+/* 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.
@@ -164,12 +161,9 @@ extern int acl_access_nontrivial (acl_t);
# 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. */
+/* 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.
@@ -248,6 +242,53 @@ extern int acl_nontrivial (int count, struct acl *entries);
# endif
+/* Set to 1 if a file's mode is implicit by the ACL. */
+# ifndef MODE_INSIDE_ACL
+# define MODE_INSIDE_ACL 1
+# endif
+
#endif
+struct permission_context {
+ mode_t mode;
+#ifdef USE_ACL
+# if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
+ acl_t acl;
+# if !HAVE_ACL_TYPE_EXTENDED
+ acl_t default_acl;
+# endif
+ bool acls_not_supported;
+
+# elif defined GETACL /* Solaris, Cygwin */
+ int count;
+ aclent_t *entries;
+# ifdef ACE_GETACL
+ int ace_count;
+ ace_t *ace_entries;
+# endif
+
+# elif HAVE_GETACL /* HP-UX */
+ struct acl_entry entries[NACLENTRIES];
+ int count;
+# if HAVE_ACLV_H
+ struct acl aclv_entries[NACLVENTRIES];
+ int aclv_count;
+# endif
+
+# elif HAVE_STATACL /* older AIX */
+ union { struct acl a; char room[4096]; } u;
+ bool have_u;
+
+# elif HAVE_ACLSORT /* NonStop Kernel */
+ struct acl entries[NACLENTRIES];
+ int count;
+
+# endif
+#endif
+};
+
+int get_permissions (const char *, int, mode_t, struct permission_context *);
+int set_permissions (struct permission_context *, const char *, int);
+void free_permission_context (struct permission_context *);
+
_GL_INLINE_HEADER_END
diff --git a/lib/get-permissions.c b/lib/get-permissions.c
new file mode 100644
index 00000000000..ccee1f1410e
--- /dev/null
+++ b/lib/get-permissions.c
@@ -0,0 +1,280 @@
+/* get-permissions.c - get permissions of a file
+
+ Copyright (C) 2002-2003, 2005-2015 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 <string.h>
+#include "acl.h"
+
+#include "acl-internal.h"
+
+/* Read the permissions of a file into CTX. If DESC is a valid file descriptor,
+ use file descriptor operations, else use filename based operations on NAME.
+ MODE is the file mode obtained from a previous stat call.
+ Return 0 if successful. Return -1 and set errno upon failure. */
+
+int
+get_permissions (const char *name, int desc, mode_t mode,
+ struct permission_context *ctx)
+{
+ memset (ctx, 0, sizeof(*ctx));
+ ctx->mode = 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 */
+
+ if (HAVE_ACL_GET_FD && desc != -1)
+ ctx->acl = acl_get_fd (desc);
+ else
+ ctx->acl = acl_get_file (name, ACL_TYPE_ACCESS);
+ if (ctx->acl == NULL)
+ return acl_errno_valid (errno) ? -1 : 0;
+
+ /* With POSIX ACLs, a file cannot have "no" acl; a file without
+ extended permissions has a "minimal" acl which is equivalent to the
+ file mode. */
+
+ if (S_ISDIR (mode))
+ {
+ ctx->default_acl = acl_get_file (name, ACL_TYPE_DEFAULT);
+ if (ctx->default_acl == NULL)
+ return -1;
+ }
+
+# 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. */
+
+ if (HAVE_ACL_GET_FD && desc != -1)
+ ctx->acl = acl_get_fd (desc);
+ else
+ ctx->acl = acl_get_file (name, ACL_TYPE_EXTENDED);
+ if (ctx->acl == NULL)
+ return acl_errno_valid (errno) ? -1 : 0;
+
+# 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
+ /* 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 (;;)
+ {
+ int ret;
+
+ if (desc != -1)
+ ret = facl (desc, ACE_GETACLCNT, 0, NULL);
+ else
+ ret = acl (name, ACE_GETACLCNT, 0, NULL);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == EINVAL)
+ ret = 0;
+ else
+ return -1;
+ }
+ ctx->ace_count = ret;
+
+ if (ctx->ace_count == 0)
+ break;
+
+ ctx->ace_entries = (ace_t *) malloc (ctx->ace_count * sizeof (ace_t));
+ if (ctx->ace_entries == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (desc != -1)
+ ret = facl (desc, ACE_GETACL, ctx->ace_count, ctx->ace_entries);
+ else
+ ret = acl (name, ACE_GETACL, ctx->ace_count, ctx->ace_entries);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == EINVAL)
+ {
+ free (ctx->ace_entries);
+ ctx->ace_entries = NULL;
+ ctx->ace_count = 0;
+ break;
+ }
+ else
+ return -1;
+ }
+ if (ret <= ctx->ace_count)
+ {
+ ctx->ace_count = ret;
+ break;
+ }
+ /* Huh? The number of ACL entries has increased since the last call.
+ Repeat. */
+ free (ctx->ace_entries);
+ ctx->ace_entries = NULL;
+ }
+# endif
+
+ for (;;)
+ {
+ int ret;
+
+ if (desc != -1)
+ ret = facl (desc, GETACLCNT, 0, NULL);
+ else
+ ret = acl (name, GETACLCNT, 0, NULL);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
+ ret = 0;
+ else
+ return -1;
+ }
+ ctx->count = ret;
+
+ if (ctx->count == 0)
+ break;
+
+ ctx->entries = (aclent_t *) malloc (ctx->count * sizeof (aclent_t));
+ if (ctx->entries == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (desc != -1)
+ ret = facl (desc, GETACL, ctx->count, ctx->entries);
+ else
+ ret = acl (name, GETACL, ctx->count, ctx->entries);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
+ {
+ free (ctx->entries);
+ ctx->entries = NULL;
+ ctx->count = 0;
+ break;
+ }
+ else
+ return -1;
+ }
+ if (ret <= ctx->count)
+ {
+ ctx->count = ret;
+ break;
+ }
+ /* Huh? The number of ACL entries has increased since the last call.
+ Repeat. */
+ free (ctx->entries);
+ ctx->entries = NULL;
+ }
+
+#elif USE_ACL && HAVE_GETACL /* HP-UX */
+
+ int ret;
+
+ if (desc != -1)
+ ret = fgetacl (desc, NACLENTRIES, ctx->entries);
+ else
+ ret = getacl (name, NACLENTRIES, ctx->entries);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
+ ret = 0;
+ else
+ return -1;
+ }
+ else if (ret > NACLENTRIES)
+ /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
+ abort ();
+ ctx->count = ret;
+
+# if HAVE_ACLV_H
+ ret = acl ((char *) name, ACL_GET, NACLVENTRIES, ctx->aclv_entries);
+ if (ret < 0)
+ {
+ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ ret = 0;
+ else
+ return -2;
+ }
+ else if (ret > NACLVENTRIES)
+ /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */
+ abort ();
+ ctx->aclv_count = ret;
+# endif
+
+#elif USE_ACL && HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
+
+ /* TODO (see set_permissions). */
+
+#elif USE_ACL && HAVE_STATACL /* older AIX */
+
+ if (desc != -1)
+ ret = fstatacl (desc, STX_NORMAL, &ctx->u.a, sizeof (ctx->u));
+ else
+ ret = statacl (name, STX_NORMAL, &ctx->u.a, sizeof (ctx->u));
+ if (ret == 0)
+ ctx->have_u = true;
+
+#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
+
+ int ret;
+
+ ret = acl ((char *) name, ACL_GET, NACLENTRIES, ctx->entries);
+ if (ret < 0)
+ return -1;
+ else if (ret > NACLENTRIES)
+ /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
+ abort ();
+ ctx->count = ret;
+
+#endif
+
+ return 0;
+
+}
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
index bec0d3bb19b..95f2f8b48a6 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=close --avoid=dup --avoid=fchdir --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=stdarg --avoid=stdbool --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog intprops largefile lstat manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stddef stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time time time_r timer-time timespec-add timespec-sub unsetenv update-copyright utimens vla 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=close --avoid=dup --avoid=fchdir --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=stdarg --avoid=stdbool --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog intprops largefile lstat manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time stdalign stddef stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time time time_r timer-time timespec-add timespec-sub unsetenv update-copyright utimens vla warnings
MOSTLYCLEANFILES += core *.stackdump
@@ -42,6 +42,17 @@ HAVE_INCLUDE_NEXT = (__GNUC__ || 60000000 <= __DECC_VER)
## end gnulib module absolute-header
+## begin gnulib module acl-permissions
+
+libgnu_a_SOURCES += acl-errno-valid.c acl-internal.c \
+ get-permissions.c set-permissions.c
+
+EXTRA_DIST += acl-internal.h acl.h acl_entries.c
+
+EXTRA_libgnu_a_SOURCES += acl_entries.c
+
+## end gnulib module acl-permissions
+
## begin gnulib module alloca-opt
BUILT_SOURCES += $(ALLOCA_H)
@@ -696,15 +707,11 @@ EXTRA_libgnu_a_SOURCES += putenv.c
## end gnulib module putenv
-## begin gnulib module qacl
-
-libgnu_a_SOURCES += acl-errno-valid.c acl-internal.c qcopy-acl.c qset-acl.c
+## begin gnulib module qcopy-acl
-EXTRA_DIST += acl-internal.h acl.h acl_entries.c
-
-EXTRA_libgnu_a_SOURCES += acl_entries.c
+libgnu_a_SOURCES += qcopy-acl.c
-## end gnulib module qacl
+## end gnulib module qcopy-acl
## begin gnulib module readlink
diff --git a/lib/inttypes.in.h b/lib/inttypes.in.h
index 13a72bee90c..78846f69683 100644
--- a/lib/inttypes.in.h
+++ b/lib/inttypes.in.h
@@ -51,6 +51,10 @@
#endif
/* Get CHAR_BIT. */
#include <limits.h>
+/* On mingw, __USE_MINGW_ANSI_STDIO only works if <stdio.h> is also included */
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# include <stdio.h>
+#endif
#if !(INT_MIN == INT32_MIN && INT_MAX == INT32_MAX)
# error "This file assumes that 'int' has exactly 32 bits. Please report your platform and compiler to <bug-gnulib@gnu.org>."
diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c
index bc258ba560b..c4507424719 100644
--- a/lib/qcopy-acl.c
+++ b/lib/qcopy-acl.c
@@ -39,534 +39,13 @@ 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;
+ struct permission_context ctx;
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);
+ ret = get_permissions (src_name, source_desc, mode, &ctx);
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
- && !acl_nontrivial (count, entries))
- saved_errno = 0;
- }
- 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
+ ret = set_permissions (&ctx, dst_name, dest_desc);
+ free_permission_context (&ctx);
+ return ret;
}
diff --git a/lib/qset-acl.c b/lib/set-permissions.c
index bb4b0b261a9..47cb91ca162 100644
--- a/lib/qset-acl.c
+++ b/lib/set-permissions.c
@@ -1,4 +1,4 @@
-/* qset-acl.c - set access control list equivalent to a mode
+/* set-permissions.c - set permissions of a file
Copyright (C) 2002-2003, 2005-2015 Free Software Foundation, Inc.
@@ -15,182 +15,43 @@
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. */
+ Written by Paul Eggert, Andreas Grünbacher, 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);
+# if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64 */
+static acl_t
+acl_from_mode (mode_t mode)
+{
+# if HAVE_ACL_FREE_TEXT /* Tru64 */
+ char acl_text[] = "u::---,g::---,o::---,";
+# else /* FreeBSD, IRIX */
+ char acl_text[] = "u::---,g::---,o::---";
# endif
-# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
-
- int done_setacl = 0;
+ 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';
+
+ return acl_from_text (acl_text);
+}
+# endif
+# if HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
+static int
+set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
+{
# 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). */
@@ -365,53 +226,53 @@ qset_acl (char const *name, int desc, mode_t mode)
if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
{
if (errno == ENOSYS)
- return chmod_or_fchmod (name, desc, mode);
+ {
+ *must_chmod = true;
+ return 0;
+ }
return -1;
}
if (ret == 0)
- done_setacl = 1;
+ return 0;
}
# 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;
+ {
+ 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;
-# elif HAVE_GETACL /* HP-UX */
+ 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)
+ {
+ *must_chmod = true;
+ return 0;
+ }
+ return -1;
+ }
+ }
+}
+#elif HAVE_GETACL /* HP-UX */
+static int
+context_acl_from_mode (struct permission_context *ctx, const char *name, int desc)
+{
struct stat statbuf;
int ret;
@@ -422,87 +283,60 @@ qset_acl (char const *name, int desc, mode_t mode)
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;
+ ctx->entries[0].uid = statbuf.st_uid;
+ ctx->entries[0].gid = ACL_NSGROUP;
+ ctx->entries[0].mode = (mode >> 6) & 7;
+ ctx->entries[1].uid = ACL_NSUSER;
+ ctx->entries[1].gid = statbuf.st_gid;
+ ctx->entries[1].mode = (mode >> 3) & 7;
+ ctx->entries[2].uid = ACL_NSUSER;
+ ctx->entries[2].gid = ACL_NSGROUP;
+ ctx->entries[2].mode = mode & 7;
+ ctx->count = 3;
+ return 0;
+}
# 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;
- }
+static int
+context_aclv_from_mode (struct permission_context *ctx)
+{
+ int ret;
- 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
- }
+ ctx->aclv_entries[0].a_type = USER_OBJ;
+ ctx->aclv_entries[0].a_id = 0; /* irrelevant */
+ ctx->aclv_entries[0].a_perm = (mode >> 6) & 7;
+ ctx->aclv_entries[1].a_type = GROUP_OBJ;
+ ctx->aclv_entries[1].a_id = 0; /* irrelevant */
+ ctx->aclv_entries[1].a_perm = (mode >> 3) & 7;
+ ctx->aclv_entries[2].a_type = CLASS_OBJ;
+ ctx->aclv_entries[2].a_id = 0;
+ ctx->aclv_entries[2].a_perm = (mode >> 3) & 7;
+ ctx->aclv_entries[3].a_type = OTHER_OBJ;
+ ctx->aclv_entries[3].a_id = 0;
+ ctx->aclv_entries[3].a_perm = mode & 7;
+ ctx->aclv_count = 4;
- 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;
+ ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
+ if (ret > 0)
+ abort ();
+ return ret;
+}
+#endif
# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
-
+static int
+set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
+{
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);
+ {
+ *must_chmod = true;
+ return 0;
+ }
/* XXX Do we need to clear all types of ACLs for the given file, or is it
sufficient to clear the first one? */
@@ -599,78 +433,401 @@ qset_acl (char const *name, int desc, mode_t mode)
return ret;
}
- return chmod_or_fchmod (name, desc, mode);
+ *must_chmod = true;
+ return 0;
+}
# elif HAVE_STATACL /* older AIX */
+static int
+context_acl_from_mode (struct permission_context *ctx)
+{
+ ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */
+ ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777);
+ ctx->u.a.u_access = (ctx->mode >> 6) & 7;
+ ctx->u.a.g_access = (ctx->mode >> 3) & 7;
+ ctx->u.a.o_access = ctx->mode & 7;
+ ctx->have_u = true;
+ return 0;
+}
- union { struct acl a; char room[128]; } u;
+# elif HAVE_ACLSORT /* NonStop Kernel */
+static int
+context_acl_from_mode (struct permission_context *ctx)
+{
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;
+ ctx->entries[0].a_type = USER_OBJ;
+ ctx->entries[0].a_id = 0; /* irrelevant */
+ ctx->entries[0].a_perm = (mode >> 6) & 7;
+ ctx->entries[1].a_type = GROUP_OBJ;
+ ctx->entries[1].a_id = 0; /* irrelevant */
+ ctx->entries[1].a_perm = (mode >> 3) & 7;
+ ctx->entries[2].a_type = CLASS_OBJ;
+ ctx->entries[2].a_id = 0;
+ ctx->entries[2].a_perm = (mode >> 3) & 7;
+ ctx->entries[3].a_type = OTHER_OBJ;
+ ctx->entries[3].a_id = 0;
+ ctx->entries[3].a_perm = mode & 7;
+ ctx->count = 4;
- if (desc != -1)
- ret = fchacl (desc, &u.a, u.a.acl_len);
+ ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
+ if (ret > 0)
+ abort ();
+ return ret;
+}
+# endif
+
+static int
+set_acls (struct permission_context *ctx, const char *name, int desc,
+ int from_mode, bool *must_chmod, bool *acls_set)
+{
+ int ret = 0;
+
+#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 */
+
+# 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
+
+ if (! ctx->acls_not_supported)
+ {
+ if (ret == 0 && from_mode)
+ {
+ if (ctx->acl)
+ acl_free (ctx->acl);
+ ctx->acl = acl_from_mode (ctx->mode);
+ if (ctx->acl == NULL)
+ ret = -1;
+ }
+
+ if (ret == 0 && ctx->acl)
+ {
+ if (HAVE_ACL_SET_FD && desc != -1)
+ ret = acl_set_fd (desc, ctx->acl);
+ else
+ ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl);
+ if (ret != 0)
+ {
+ if (! acl_errno_valid (errno))
+ {
+ ctx->acls_not_supported = true;
+ if (from_mode || acl_access_nontrivial (ctx->acl) == 0)
+ ret = 0;
+ }
+ }
+ else
+ {
+ *acls_set = true;
+ if (S_ISDIR(ctx->mode))
+ {
+ if (! from_mode && ctx->default_acl)
+ ret = acl_set_file (name, ACL_TYPE_DEFAULT,
+ ctx->default_acl);
+ else
+ ret = acl_delete_def_file (name);
+ }
+ }
+ }
+ }
+
+# 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. */
+
+ if (ctx->acl == NULL)
+ {
+ acl_t acl;
+
+ /* Remove ACLs 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 (acl);
+ 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);
+ acl_free (acl);
+ }
+ else
+ ret = -1;
+ }
+ }
else
- ret = chacl (name, &u.a, u.a.acl_len);
+ {
+ 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)
+ {
+ if (! acl_errno_valid (saved_errno) && ! acl_extended_nontrivial (acl))
+ ret = 0;
+ }
+ }
+ *acls_set = true;
- if (ret < 0 && errno == ENOSYS)
- return chmod_or_fchmod (name, desc, mode);
+# endif
- return ret;
+# elif defined GETACL /* Solaris, Cygwin, not HP-UX */
-# elif HAVE_ACLSORT /* NonStop Kernel */
+ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
+ of Unixware. The acl() call returns the access and default ACL both
+ at once. */
- struct acl entries[4];
- int ret;
+ /* If both ace_entries and entries are available, try SETACL before
+ ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
+ can. */
- 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;
+ if (from_mode)
+ return set_acls_from_mode (name, desc, ctx->mode, must_chmod);
- ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
- if (ret > 0)
- abort ();
- if (ret < 0)
+ if (ret == 0 && ctx->count)
{
- if (0)
- return chmod_or_fchmod (name, desc, mode);
- return -1;
+ if (desc != -1)
+ ret = facl (desc, SETACL, count, entries);
+ else
+ ret = acl (name, SETACL, count, entries);
+ if (ret < 0)
+ {
+ if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ && acl_nontrivial (count, entries) == 0)
+ ret = 0;
+ }
+ else
+ *acls_set = true;
}
- ret = acl ((char *) name, ACL_SET,
- sizeof (entries) / sizeof (struct acl), entries);
- if (ret < 0)
+# ifdef ACE_GETACL
+ if (ret == 0 && ctx->ace_count)
{
- if (0)
- return chmod_or_fchmod (name, desc, mode);
- return -1;
+ if (desc != -1)
+ ret = facl (desc, ACE_SETACL, ace_count, ace_entries);
+ else
+ ret = acl (name, ACE_SETACL, ace_count, ace_entries);
+ if (ret < 0)
+ {
+ if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
+ && acl_ace_nontrivial (ace_count, ace_entries) == 0)
+ ret = 0;
+ }
+ else
+ *acls_set = true;
}
+# endif
+
+#elif HAVE_GETACL /* HP-UX */
- if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+ if (from_mode)
+ ret = context_acl_from_mode (ctx, name, desc);
+
+ if (ret == 0 && ctx->count > 0)
{
- /* We did not call chmod so far, so the special bits have not yet
- been set. */
- return chmod_or_fchmod (name, desc, mode);
+ if (desc != -1)
+ ret = fsetacl (desc, ctx->count, ctx->entries);
+ else
+ ret = setacl (name, ctx->count, ctx->entries);
+ if (ret < 0)
+ {
+ if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
+ && (from_mode || !acl_nontrivial (ctx->count, ctx->entries, &source_statbuf)))
+ ret = 0;
+ }
+ else
+ *acls_set = true;
}
- return 0;
-# else /* Unknown flavor of ACLs */
- return chmod_or_fchmod (name, desc, mode);
+# if HAVE_ACLV_H
+ if (from_mode)
+ ret = context_aclv_from_mode (ctx);
+
+ if (ret == 0 && ctx->aclv_count > 0)
+ {
+ ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries);
+ if (ret < 0)
+ {
+ if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
+ && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries)))
+ ret = 0;
+ }
+ else
+ *acls_set = true;
+ }
+# endif
+
+# elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
+
+ /* TODO: Implement setting ACLs once get_permissions() reads them. */
+
+ if (from_mode)
+ ret = set_acls_from_mode (name, desc, mode, must_chmod);
+
+# elif HAVE_STATACL /* older AIX */
+
+ if (from_mode)
+ ret = context_acl_from_mode (ctx);
+
+ if (ret == 0 && ctx->have_u)
+ {
+ 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)
+ {
+ if (errno == ENOSYS && from_mode)
+ ret = 0;
+ }
+ else
+ *acls_set = true;
+ }
+
+# elif HAVE_ACLSORT /* NonStop Kernel */
+
+ if (from_mode)
+ ret = context_acl_from_mode (ctx);
+
+ if (ret == 0 && ctx->count)
+ {
+ ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries);
+ if (ret != 0)
+ {
+ if (!acl_nontrivial (ctx->count, ctx->entries))
+ ret = 0;
+ }
+ else
+ *acls_set = true;
+ }
+
+# else /* No ACLs */
+
+ /* Nothing to do. */
+
+#endif
+
+ return ret;
+}
+#endif
+
+/* 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 permissions in CTX on a file. If DESC is a valid file descriptor,
+ use file descriptor operations, else use filename based operations on NAME.
+ If access control lists are not available, fchmod the target file to the
+ mode in CTX. Also sets the non-permission bits of the destination file
+ (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set.
+ Return 0 if successful. Return -1 and set errno upon failure. */
+
+int
+set_permissions (struct permission_context *ctx, const char *name, int desc)
+{
+ bool acls_set _GL_UNUSED = false;
+ bool early_chmod;
+ bool must_chmod = false;
+ int ret = 0;
+
+#if USE_ACL
+# if HAVE_STATACL
+ /* older AIX */
+ /* There is no need to call chmod_or_fchmod, since the mode
+ bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
+
+ early_chmod = false;
+# else
+ /* All other plaforms */
+ /* 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). */
+
+ early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX)));
# endif
-#else /* !USE_ACL */
- return chmod_or_fchmod (name, desc, mode);
+#else
+ /* No ACLs */
+
+ early_chmod = true;
#endif
+
+ if (early_chmod)
+ {
+ ret = chmod_or_fchmod (name, desc, ctx->mode);
+ if (ret != 0)
+ return -1;
+ }
+
+#if USE_ACL
+ ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set);
+ if (! acls_set)
+ {
+ int saved_errno = ret ? errno : 0;
+
+ /* If we can't set an acl which we expect to be able to set, try setting
+ the permissions to ctx->mode. Doe to possible inherited permissions,
+ we cannot simply chmod. */
+
+ acls_set = false;
+ ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set);
+ if (! acls_set)
+ must_chmod = true;
+
+ if (saved_errno)
+ {
+ errno = saved_errno;
+ ret = -1;
+ }
+ }
+#endif
+
+ if (must_chmod && ! early_chmod)
+ {
+ int saved_errno = ret ? errno : 0;
+
+ ret = chmod_or_fchmod (name, desc, ctx->mode);
+
+ if (saved_errno)
+ {
+ errno = saved_errno;
+ ret = -1;
+ }
+ }
+
+ return ret;
}
diff --git a/lib/string.in.h b/lib/string.in.h
index ebd727eda14..2abd6bc0e10 100644
--- a/lib/string.in.h
+++ b/lib/string.in.h
@@ -15,16 +15,32 @@
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@_STRING_H
-
#if __GNUC__ >= 3
@PRAGMA_SYSTEM_HEADER@
#endif
@PRAGMA_COLUMNS@
+#if defined _GL_ALREADY_INCLUDING_STRING_H
+/* Special invocation convention:
+ - On OS X/NetBSD we have a sequence of nested includes
+ <string.h> -> <strings.h> -> "string.h"
+ In this situation system _chk variants due to -D_FORTIFY_SOURCE
+ might be used after any replacements defined here. */
+
+#@INCLUDE_NEXT@ @NEXT_STRING_H@
+
+#else
+/* Normal invocation convention. */
+
+#ifndef _@GUARD_PREFIX@_STRING_H
+
+#define _GL_ALREADY_INCLUDING_STRING_H
+
/* The include_next requires a split double-inclusion guard. */
#@INCLUDE_NEXT@ @NEXT_STRING_H@
+#undef _GL_ALREADY_INCLUDING_STRING_H
+
#ifndef _@GUARD_PREFIX@_STRING_H
#define _@GUARD_PREFIX@_STRING_H
@@ -1027,3 +1043,4 @@ _GL_WARN_ON_USE (strverscmp, "strverscmp is unportable - "
#endif /* _@GUARD_PREFIX@_STRING_H */
#endif /* _@GUARD_PREFIX@_STRING_H */
+#endif