[tar] source: add latest xattrs/selinux/acls upstream implementation
Pavel Raiskup
praiskup at fedoraproject.org
Tue Feb 19 12:22:32 UTC 2013
commit 06e0a0b14098e2a012b32a20b8d66475e23630bb
Author: Pavel Raiskup <praiskup at redhat.com>
Date: Tue Feb 19 10:32:23 2013 +0100
source: add latest xattrs/selinux/acls upstream implementation
Version: 2:1.26-17
tar-1.26-selinux-gnulib.patch | 3603 +++++++++++++++++++++++++++++++++++++++++
tar-1.26-xattrs.patch | 3233 ++++++++++++++++++++++++++++++++++++
tar.spec | 12 +
3 files changed, 6848 insertions(+), 0 deletions(-)
---
diff --git a/tar-1.26-selinux-gnulib.patch b/tar-1.26-selinux-gnulib.patch
new file mode 100644
index 0000000..78783a9
--- /dev/null
+++ b/tar-1.26-selinux-gnulib.patch
@@ -0,0 +1,3603 @@
+From f733da5249b0b119cb551c73b322439bcbbc013a Mon Sep 17 00:00:00 2001
+From: Pavel Raiskup <praiskup at redhat.com>
+Date: Tue, 19 Feb 2013 10:41:10 +0100
+Subject: [PATCH] x
+
+---
+ build-aux/snippet/unused-parameter.h | 38 ++
+ gnu/Makefile.am | 93 +++-
+ gnu/acl-internal.h | 267 ++++++++++
+ gnu/acl.h | 30 ++
+ gnu/acl_entries.c | 77 +++
+ gnu/copy-acl.c | 620 +++++++++++++++++++++++
+ gnu/file-has-acl.c | 920 +++++++++++++++++++++++++++++++++++
+ gnu/getfilecon.c | 88 ++++
+ gnu/se-context.in.h | 30 ++
+ gnu/se-selinux.in.h | 99 ++++
+ gnu/selinux-at.c | 74 +++
+ gnu/selinux-at.h | 54 ++
+ gnu/set-mode-acl.c | 699 ++++++++++++++++++++++++++
+ m4/acl.m4 | 165 +++++++
+ m4/gnulib-comp.m4 | 27 +-
+ m4/selinux-context-h.m4 | 22 +
+ m4/selinux-selinux-h.m4 | 69 +++
+ 17 files changed, 3365 insertions(+), 7 deletions(-)
+ create mode 100644 build-aux/snippet/unused-parameter.h
+ create mode 100644 gnu/acl-internal.h
+ create mode 100644 gnu/acl.h
+ create mode 100644 gnu/acl_entries.c
+ create mode 100644 gnu/copy-acl.c
+ create mode 100644 gnu/file-has-acl.c
+ create mode 100644 gnu/getfilecon.c
+ create mode 100644 gnu/se-context.in.h
+ create mode 100644 gnu/se-selinux.in.h
+ create mode 100644 gnu/selinux-at.c
+ create mode 100644 gnu/selinux-at.h
+ create mode 100644 gnu/set-mode-acl.c
+ create mode 100644 m4/acl.m4
+ create mode 100644 m4/selinux-context-h.m4
+ create mode 100644 m4/selinux-selinux-h.m4
+
+diff --git a/build-aux/snippet/unused-parameter.h b/build-aux/snippet/unused-parameter.h
+new file mode 100644
+index 0000000..6b60482
+--- /dev/null
++++ b/build-aux/snippet/unused-parameter.h
+@@ -0,0 +1,38 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* A C macro for declaring that specific function parameters are not used.
++ Copyright (C) 2008-2012 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/>. */
++
++/* _GL_UNUSED_PARAMETER is a marker that can be appended to function parameter
++ declarations for parameters that are not used. This helps to reduce
++ warnings, such as from GCC -Wunused-parameter. The syntax is as follows:
++ type param _GL_UNUSED_PARAMETER
++ or more generally
++ param_decl _GL_UNUSED_PARAMETER
++ For example:
++ int param _GL_UNUSED_PARAMETER
++ int *(*param)(void) _GL_UNUSED_PARAMETER
++ Other possible, but obscure and discouraged syntaxes:
++ int _GL_UNUSED_PARAMETER *(*param)(void)
++ _GL_UNUSED_PARAMETER int *(*param)(void)
++ */
++#ifndef _GL_UNUSED_PARAMETER
++# if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
++# define _GL_UNUSED_PARAMETER __attribute__ ((__unused__))
++# else
++# define _GL_UNUSED_PARAMETER
++# endif
++#endif
+diff --git a/gnu/Makefile.am b/gnu/Makefile.am
+index 06eb0d9..77d75d2 100644
+--- a/gnu/Makefile.am
++++ b/gnu/Makefile.am
+@@ -1,6 +1,3 @@
+-# -*- buffer-read-only: t -*- vi: set ro:
+-# DO NOT EDIT! GENERATED AUTOMATICALLY!
+-## DO NOT EDIT! GENERATED AUTOMATICALLY!
+ ## Process this file with automake to produce Makefile.in.
+ # Copyright (C) 2002-2011 Free Software Foundation, Inc.
+ #
+@@ -38,8 +35,17 @@ libgnu_a_LIBADD = $(gl_LIBOBJS)
+ libgnu_a_DEPENDENCIES = $(gl_LIBOBJS)
+ EXTRA_libgnu_a_SOURCES =
+
+-## begin gnulib module alloca
++## begin gnulib module acl
++
++libgnu_a_SOURCES += set-mode-acl.c copy-acl.c file-has-acl.c
++
++EXTRA_DIST += acl-internal.h acl.h acl_entries.c
+
++EXTRA_libgnu_a_SOURCES += acl_entries.c
++
++## end gnulib module acl
++
++## begin gnulib module alloca
+
+ EXTRA_DIST += alloca.c
+
+@@ -179,6 +185,31 @@ EXTRA_DIST += $(top_srcdir)/build-aux/c++defs.h
+
+ ## end gnulib module c++defs
+
++## begin gnulib module snippet/unused-parameter
++
++# The BUILT_SOURCES created by this Makefile snippet are not used via #include
++# statements but through direct file reference. Therefore this snippet must be
++# present in all Makefile.am that need it. This is ensured by the applicability
++# 'all' defined above.
++
++BUILT_SOURCES += unused-parameter.h
++# The unused-parameter.h that gets inserted into generated .h files is the same
++# as build-aux/snippet/unused-parameter.h, except that it has the copyright
++# header cut off.
++unused-parameter.h: $(top_srcdir)/build-aux/snippet/unused-parameter.h
++ $(AM_V_GEN)rm -f $@-t $@ && \
++ sed -n -e '/GL_UNUSED_PARAMETER/,$$p' \
++ < $(top_srcdir)/build-aux/snippet/unused-parameter.h \
++ > $@-t && \
++ mv $@-t $@
++MOSTLYCLEANFILES += unused-parameter.h unused-parameter.h-t
++
++UNUSED_PARAMETER_H=unused-parameter.h
++
++EXTRA_DIST += $(top_srcdir)/build-aux/snippet/unused-parameter.h
++
++## end gnulib module snippet/unused-parameter
++
+ ## begin gnulib module c-ctype
+
+ libgnu_a_SOURCES += c-ctype.h c-ctype.c
+@@ -1386,6 +1417,60 @@ EXTRA_libgnu_a_SOURCES += savedir.c
+
+ ## end gnulib module savedir
+
++## begin gnulib module selinux-at
++
++
++EXTRA_DIST += at-func.c selinux-at.c selinux-at.h
++
++EXTRA_libgnu_a_SOURCES += at-func.c selinux-at.c
++
++## end gnulib module selinux-at
++
++## begin gnulib module selinux-h
++
++libgnu_a_SOURCES += se-context.in.h se-selinux.in.h
++
++BUILT_SOURCES += selinux/selinux.h
++selinux/selinux.h: se-selinux.in.h $(top_builddir)/config.status $(UNUSED_PARAMETER_H)
++ $(AM_V_at)$(MKDIR_P) selinux
++ $(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_SELINUX_SELINUX_H''@|$(NEXT_SELINUX_SELINUX_H)|g' \
++ -e '/definition of _GL_UNUSED_PARAMETER/r $(UNUSED_PARAMETER_H)' \
++ < $(srcdir)/se-selinux.in.h; \
++ } > $@-t && \
++ chmod a-x $@-t && \
++ mv $@-t $@
++MOSTLYCLEANFILES += selinux/selinux.h selinux/selinux.h-t
++
++BUILT_SOURCES += $(SELINUX_CONTEXT_H)
++if GL_GENERATE_SELINUX_CONTEXT_H
++selinux/context.h: se-context.in.h $(top_builddir)/config.status $(UNUSED_PARAMETER_H)
++ $(AM_V_at)$(MKDIR_P) selinux
++ $(AM_V_GEN)rm -f $@-t $@ && \
++ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \
++ sed -e '/definition of _GL_UNUSED_PARAMETER/r $(UNUSED_PARAMETER_H)' \
++ < $(srcdir)/se-context.in.h; \
++ } > $@-t && \
++ chmod a-x $@-t && \
++ mv $@-t $@
++else
++selinux/context.h: $(top_builddir)/config.status
++ rm -f $@
++endif
++MOSTLYCLEANFILES += selinux/context.h selinux/context.h-t
++MOSTLYCLEANDIRS += selinux
++
++EXTRA_DIST += getfilecon.c
++
++EXTRA_libgnu_a_SOURCES += getfilecon.c
++
++## end gnulib module selinux-h
++
+ ## begin gnulib module setenv
+
+
+diff --git a/gnu/acl-internal.h b/gnu/acl-internal.h
+new file mode 100644
+index 0000000..07309e0
+--- /dev/null
++++ b/gnu/acl-internal.h
+@@ -0,0 +1,267 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* Internal implementation of access control lists.
++
++ Copyright (C) 2002-2003, 2005-2012 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 "error.h"
++#include "quote.h"
++
++#include <errno.h>
++#ifndef ENOSYS
++# define ENOSYS (-1)
++#endif
++#ifndef ENOTSUP
++# define ENOTSUP (-1)
++#endif
++
++#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
++
++/* Recognize some common errors such as from an NFS mount that does
++ not support ACLs, even when local drives do. */
++#if defined __APPLE__ && defined __MACH__ /* Mac OS X */
++# define ACL_NOT_WELL_SUPPORTED(Err) \
++ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == ENOENT)
++#elif defined EOPNOTSUPP /* Tru64 NFS */
++# define ACL_NOT_WELL_SUPPORTED(Err) \
++ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == EOPNOTSUPP)
++#else
++# define ACL_NOT_WELL_SUPPORTED(Err) \
++ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY)
++#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 */
++static 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 */
++static 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
+diff --git a/gnu/acl.h b/gnu/acl.h
+new file mode 100644
+index 0000000..d808a90
+--- /dev/null
++++ b/gnu/acl.h
+@@ -0,0 +1,30 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* acl.c - access control lists
++
++ Copyright (C) 2002, 2008-2012 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 <sys/types.h>
++#include <sys/stat.h>
++
++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/gnu/acl_entries.c b/gnu/acl_entries.c
+new file mode 100644
+index 0000000..11adc22
+--- /dev/null
++++ b/gnu/acl_entries.c
+@@ -0,0 +1,77 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* Return the number of entries in an ACL.
++
++ Copyright (C) 2002-2003, 2005-2012 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/gnu/copy-acl.c b/gnu/copy-acl.c
+new file mode 100644
+index 0000000..a4d82f7
+--- /dev/null
++++ b/gnu/copy-acl.c
+@@ -0,0 +1,620 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* copy-acl.c - copy access control list from one file to another file
++
++ Copyright (C) 2002-2003, 2005-2012 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"
++
++#include "gettext.h"
++#define _(msgid) gettext (msgid)
++
++
++/* 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_NOT_WELL_SUPPORTED (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_NOT_WELL_SUPPORTED (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_NOT_WELL_SUPPORTED (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_NOT_WELL_SUPPORTED (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
++}
++
++
++/* 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, otherwise output a diagnostic and return a
++ negative error code. */
++
++int
++copy_acl (const char *src_name, int source_desc, const char *dst_name,
++ int dest_desc, mode_t mode)
++{
++ int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
++ switch (ret)
++ {
++ case -2:
++ error (0, errno, "%s", quote (src_name));
++ break;
++
++ case -1:
++ error (0, errno, _("preserving permissions for %s"), quote (dst_name));
++ break;
++
++ default:
++ break;
++ }
++ return ret;
++}
+diff --git a/gnu/file-has-acl.c b/gnu/file-has-acl.c
+new file mode 100644
+index 0000000..17872a5
+--- /dev/null
++++ b/gnu/file-has-acl.c
+@@ -0,0 +1,920 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* Test whether a file has a nontrivial access control list.
++
++ Copyright (C) 2002-2003, 2005-2012 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_NOT_WELL_SUPPORTED (errno) ? 0 : -1;
++ 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/gnu/getfilecon.c b/gnu/getfilecon.c
+new file mode 100644
+index 0000000..4a0f40d
+--- /dev/null
++++ b/gnu/getfilecon.c
+@@ -0,0 +1,88 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* wrap getfilecon, lgetfilecon, and fgetfilecon
++ Copyright (C) 2009-2012 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/>. */
++
++/* written by Jim Meyering */
++
++#include <config.h>
++
++#include <selinux/selinux.h>
++
++#include <sys/types.h>
++#include <errno.h>
++#include <string.h>
++
++/* FIXME: remove this once there is an errno-gnu module
++ that guarantees the definition of ENODATA. */
++#ifndef ENODATA
++# define ENODATA ENOTSUP
++#endif
++
++#undef getfilecon
++#undef lgetfilecon
++#undef fgetfilecon
++int getfilecon (char const *file, security_context_t *con);
++int lgetfilecon (char const *file, security_context_t *con);
++int fgetfilecon (int fd, security_context_t *con);
++
++/* getfilecon, lgetfilecon, and fgetfilecon can all misbehave, be it
++ via an old version of libselinux where these would return 0 and set the
++ result context to NULL, or via a modern kernel+lib operating on a file
++ from a disk whose attributes were set by a kernel from around 2006.
++ In that latter case, the functions return a length of 10 for the
++ "unlabeled" context. Map both failures to a return value of -1, and
++ set errno to ENOTSUP in the first case, and ENODATA in the latter. */
++
++static inline int
++map_to_failure (int ret, security_context_t *con)
++{
++ if (ret == 0)
++ {
++ errno = ENOTSUP;
++ return -1;
++ }
++
++ if (ret == 10 && strcmp (*con, "unlabeled") == 0)
++ {
++ freecon (*con);
++ errno = ENODATA;
++ return -1;
++ }
++
++ return ret;
++}
++
++int
++rpl_getfilecon (char const *file, security_context_t *con)
++{
++ int ret = getfilecon (file, con);
++ return map_to_failure (ret, con);
++}
++
++int
++rpl_lgetfilecon (char const *file, security_context_t *con)
++{
++ int ret = lgetfilecon (file, con);
++ return map_to_failure (ret, con);
++}
++
++int
++rpl_fgetfilecon (int fd, security_context_t *con)
++{
++ int ret = fgetfilecon (fd, con);
++ return map_to_failure (ret, con);
++}
+diff --git a/gnu/se-context.in.h b/gnu/se-context.in.h
+new file mode 100644
+index 0000000..adb13ba
+--- /dev/null
++++ b/gnu/se-context.in.h
+@@ -0,0 +1,30 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++#ifndef SELINUX_CONTEXT_H
++# define SELINUX_CONTEXT_H
++
++# include <errno.h>
++
++/* The definition of _GL_UNUSED_PARAMETER is copied here. */
++
++typedef int context_t;
++static inline context_t context_new (char const *s _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return 0; }
++static inline char *context_str (context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return (void *) 0; }
++static inline void context_free (context_t c _GL_UNUSED_PARAMETER) {}
++
++static inline int context_user_set (context_t sc _GL_UNUSED_PARAMETER,
++ char const *s _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int context_role_set (context_t sc _GL_UNUSED_PARAMETER,
++ char const *s _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int context_range_set (context_t sc _GL_UNUSED_PARAMETER,
++ char const *s _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int context_type_set (context_t sc _GL_UNUSED_PARAMETER,
++ char const *s _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++
++#endif
+diff --git a/gnu/se-selinux.in.h b/gnu/se-selinux.in.h
+new file mode 100644
+index 0000000..34205a1
+--- /dev/null
++++ b/gnu/se-selinux.in.h
+@@ -0,0 +1,99 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* Replacement <selinux/selinux.h> for platforms that lack it.
++ Copyright (C) 2008-2012 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/>. */
++
++#ifndef _ at GUARD_PREFIX@_SELINUX_SELINUX_H
++# define _ at GUARD_PREFIX@_SELINUX_SELINUX_H
++
++# if __GNUC__ >= 3
++ at PRAGMA_SYSTEM_HEADER@
++# endif
++ at PRAGMA_COLUMNS@
++
++# if HAVE_SELINUX_SELINUX_H
++
++#@INCLUDE_NEXT@ @NEXT_SELINUX_SELINUX_H@
++
++# else
++
++# include <sys/types.h>
++# include <errno.h>
++
++/* The definition of _GL_UNUSED_PARAMETER is copied here. */
++
++# if !GNULIB_defined_security_types
++
++typedef unsigned short security_class_t;
++# define security_context_t char*
++# define is_selinux_enabled() 0
++
++static inline int getcon (security_context_t *con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline void freecon (security_context_t con _GL_UNUSED_PARAMETER) {}
++
++
++static inline int getfscreatecon (security_context_t *con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int setfscreatecon (security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int matchpathcon (char const *file _GL_UNUSED_PARAMETER,
++ mode_t m _GL_UNUSED_PARAMETER,
++ security_context_t *con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int getfilecon (char const *file _GL_UNUSED_PARAMETER,
++ security_context_t *con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int lgetfilecon (char const *file _GL_UNUSED_PARAMETER,
++ security_context_t *con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int fgetfilecon (int fd,
++ security_context_t *con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int setfilecon (char const *file _GL_UNUSED_PARAMETER,
++ security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int lsetfilecon (char const *file _GL_UNUSED_PARAMETER,
++ security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int fsetfilecon (int fd _GL_UNUSED_PARAMETER,
++ security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++
++static inline int security_check_context
++ (security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int security_check_context_raw
++ (security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int setexeccon (security_context_t con _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int security_compute_create
++ (security_context_t scon _GL_UNUSED_PARAMETER,
++ security_context_t tcon _GL_UNUSED_PARAMETER,
++ security_class_t tclass _GL_UNUSED_PARAMETER,
++ security_context_t *newcon _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++static inline int matchpathcon_init_prefix
++ (char const *path _GL_UNUSED_PARAMETER,
++ char const *prefix _GL_UNUSED_PARAMETER)
++ { errno = ENOTSUP; return -1; }
++
++# define GNULIB_defined_security_types 1
++# endif
++
++# endif
++#endif /* _ at GUARD_PREFIX@_SELINUX_SELINUX_H */
+diff --git a/gnu/selinux-at.c b/gnu/selinux-at.c
+new file mode 100644
+index 0000000..f6619fa
+--- /dev/null
++++ b/gnu/selinux-at.c
+@@ -0,0 +1,74 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* openat-style fd-relative functions for SE Linux
++ Copyright (C) 2007, 2009-2012 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 Jim Meyering */
++
++#include <config.h>
++
++#include "selinux-at.h"
++#include "openat.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <fcntl.h>
++
++#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
++#include "save-cwd.h"
++
++#include "openat-priv.h"
++
++#define AT_FUNC_NAME getfileconat
++#define AT_FUNC_F1 getfilecon
++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t *con
++#define AT_FUNC_POST_FILE_ARGS , con
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++#define AT_FUNC_NAME lgetfileconat
++#define AT_FUNC_F1 lgetfilecon
++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t *con
++#define AT_FUNC_POST_FILE_ARGS , con
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++#define AT_FUNC_NAME setfileconat
++#define AT_FUNC_F1 setfilecon
++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t con
++#define AT_FUNC_POST_FILE_ARGS , con
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++#define AT_FUNC_NAME lsetfileconat
++#define AT_FUNC_F1 lsetfilecon
++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t con
++#define AT_FUNC_POST_FILE_ARGS , con
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
+diff --git a/gnu/selinux-at.h b/gnu/selinux-at.h
+new file mode 100644
+index 0000000..4ab3109
+--- /dev/null
++++ b/gnu/selinux-at.h
+@@ -0,0 +1,54 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* Prototypes for openat-style fd-relative SELinux functions
++ Copyright (C) 2007, 2009-2012 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/>. */
++
++#include <selinux/selinux.h>
++#include <selinux/context.h>
++
++/* These are the dir-fd-relative variants of the functions without the
++ "at" suffix. For example, getfileconat (AT_FDCWD, file, &c) is usually
++ equivalent to getfilecon (file, &c). The emulation is accomplished
++ by first attempting getfilecon ("/proc/self/fd/DIR_FD/FILE", &c).
++ Failing that, simulate it via save_cwd/fchdir/getfilecon/restore_cwd.
++ If either the save_cwd or the restore_cwd fails (relatively unlikely),
++ then give a diagnostic and exit nonzero. */
++
++/* dir-fd-relative getfilecon. Set *CON to the SELinux security context
++ of the file specified by DIR_FD and FILE and return the length of *CON.
++ DIR_FD and FILE are interpreted as for fstatat[*]. A non-NULL *CON
++ must be freed with freecon. Upon error, set *CON to NULL, set errno
++ and return -1.
++ [*] with flags=0 here, with flags=AT_SYMLINK_NOFOLLOW for lgetfileconat */
++int getfileconat (int dir_fd, char const *file, security_context_t *con);
++
++/* dir-fd-relative lgetfilecon. This function is just like getfileconat,
++ except when DIR_FD and FILE specify a symlink: lgetfileconat operates on
++ the symlink, while getfileconat operates on the referent of the symlink. */
++int lgetfileconat (int dir_fd, char const *file, security_context_t *con);
++
++/* dir-fd-relative setfilecon. Set the SELinux security context of
++ the file specified by DIR_FD and FILE to CON. DIR_FD and FILE are
++ interpreted as for fstatat[*]. Upon success, return 0.
++ Otherwise, return -1 and set errno. */
++int setfileconat (int dir_fd, char const *file, security_context_t con);
++
++/* dir-fd-relative lsetfilecon. This function is just like setfileconat,
++ except that rather than dereferencing a symlink, this function affects it. */
++/* dir-fd-relative lsetfilecon. This function is just like setfileconat,
++ except when DIR_FD and FILE specify a symlink: lsetfileconat operates on
++ the symlink, while setfileconat operates on the referent of the symlink. */
++int lsetfileconat (int dir_fd, char const *file, security_context_t con);
+diff --git a/gnu/set-mode-acl.c b/gnu/set-mode-acl.c
+new file mode 100644
+index 0000000..edc8e26
+--- /dev/null
++++ b/gnu/set-mode-acl.c
+@@ -0,0 +1,699 @@
++/* -*- buffer-read-only: t -*- vi: set ro: */
++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
++/* set-mode-acl.c - set access control list equivalent to a mode
++
++ Copyright (C) 2002-2003, 2005-2012 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>
++
++#include "acl.h"
++
++#include "acl-internal.h"
++
++#include "gettext.h"
++#define _(msgid) gettext (msgid)
++
++
++/* If DESC is a valid file descriptor use fchmod to change the
++ file's mode to MODE on systems that have fchown. On systems
++ that don't have fchown and if DESC is invalid, use chown 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_NOT_WELL_SUPPORTED (errno))
++ return chmod_or_fchmod (name, desc, mode);
++ else
++ {
++ 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_NOT_WELL_SUPPORTED (saved_errno))
++ return chmod_or_fchmod (name, desc, mode);
++ else
++ {
++ 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
++}
++
++/* As with qset_acl, but also output a diagnostic on failure. */
++
++int
++set_acl (char const *name, int desc, mode_t mode)
++{
++ int ret = qset_acl (name, desc, mode);
++ if (ret != 0)
++ error (0, errno, _("setting permissions for %s"), quote (name));
++ return ret;
++}
+diff --git a/m4/acl.m4 b/m4/acl.m4
+new file mode 100644
+index 0000000..19aa548
+--- /dev/null
++++ b/m4/acl.m4
+@@ -0,0 +1,165 @@
++# acl.m4 - check for access control list (ACL) primitives
++# serial 14
++
++# Copyright (C) 2002, 2004-2012 Free Software Foundation, Inc.
++# This file is free software; the Free Software Foundation
++# gives unlimited permission to copy and/or distribute it,
++# with or without modifications, as long as this notice is preserved.
++
++# Written by Paul Eggert and Jim Meyering.
++
++AC_DEFUN([gl_FUNC_ACL],
++[
++ AC_ARG_ENABLE([acl],
++ AS_HELP_STRING([--disable-acl], [do not support ACLs]),
++ , [enable_acl=auto])
++
++ LIB_ACL=
++ use_acl=0
++ AC_REQUIRE([AC_C_INLINE])
++ if test "x$enable_acl" != "xno"; then
++ dnl On all platforms, the ACL related API is declared in <sys/acl.h>.
++ AC_CHECK_HEADERS([sys/acl.h])
++ if test $ac_cv_header_sys_acl_h = yes; then
++ ac_save_LIBS=$LIBS
++
++ dnl Test for POSIX-draft-like API (Linux, FreeBSD, Mac OS X, IRIX, Tru64).
++ dnl -lacl is needed on Linux, -lpacl is needed on OSF/1.
++ if test $use_acl = 0; then
++ AC_SEARCH_LIBS([acl_get_file], [acl pacl],
++ [if test "$ac_cv_search_acl_get_file" != "none required"; then
++ LIB_ACL=$ac_cv_search_acl_get_file
++ fi
++ AC_CHECK_FUNCS(
++ [acl_get_file acl_get_fd acl_set_file acl_set_fd \
++ acl_free acl_from_mode acl_from_text \
++ acl_delete_def_file acl_extended_file \
++ acl_delete_fd_np acl_delete_file_np \
++ acl_copy_ext_native acl_create_entry_np \
++ acl_to_short_text acl_free_text])
++ # If the acl_get_file bug is detected, don't enable the ACL support.
++ gl_ACL_GET_FILE([use_acl=1], [])
++ if test $use_acl = 1; then
++ dnl On Linux, additional API is declared in <acl/libacl.h>.
++ AC_CHECK_HEADERS([acl/libacl.h])
++ AC_REPLACE_FUNCS([acl_entries])
++ AC_CACHE_CHECK([for ACL_FIRST_ENTRY],
++ [gl_cv_acl_ACL_FIRST_ENTRY],
++ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
++[[#include <sys/types.h>
++#include <sys/acl.h>
++int type = ACL_FIRST_ENTRY;]])],
++ [gl_cv_acl_ACL_FIRST_ENTRY=yes],
++ [gl_cv_acl_ACL_FIRST_ENTRY=no])])
++ if test $gl_cv_acl_ACL_FIRST_ENTRY = yes; then
++ AC_DEFINE([HAVE_ACL_FIRST_ENTRY], [1],
++ [Define to 1 if the constant ACL_FIRST_ENTRY exists.])
++ fi
++ dnl On Mac OS X, other types of ACLs are supported.
++ AC_CACHE_CHECK([for ACL_TYPE_EXTENDED],
++ [gl_cv_acl_ACL_TYPE_EXTENDED],
++ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
++[[#include <sys/types.h>
++#include <sys/acl.h>
++int type = ACL_TYPE_EXTENDED;]])],
++ [gl_cv_acl_ACL_TYPE_EXTENDED=yes],
++ [gl_cv_acl_ACL_TYPE_EXTENDED=no])])
++ if test $gl_cv_acl_ACL_TYPE_EXTENDED = yes; then
++ AC_DEFINE([HAVE_ACL_TYPE_EXTENDED], [1],
++ [Define to 1 if the ACL type ACL_TYPE_EXTENDED exists.])
++ fi
++ else
++ LIB_ACL=
++ fi
++ ])
++ fi
++
++ dnl Test for Solaris API (Solaris, Cygwin).
++ if test $use_acl = 0; then
++ AC_CHECK_FUNCS([facl])
++ if test $ac_cv_func_facl = yes; then
++ AC_SEARCH_LIBS([acl_trivial], [sec],
++ [if test "$ac_cv_search_acl_trivial" != "none required"; then
++ LIB_ACL=$ac_cv_search_acl_trivial
++ fi
++ ])
++ AC_CHECK_FUNCS([acl_trivial])
++ use_acl=1
++ fi
++ fi
++
++ dnl Test for HP-UX API.
++ if test $use_acl = 0; then
++ AC_CHECK_FUNCS([getacl])
++ if test $ac_cv_func_getacl = yes; then
++ use_acl=1
++ fi
++ dnl Test for HP-UX 11.11 API.
++ AC_CHECK_HEADERS([aclv.h], [], [], [#include <sys/types.h>])
++ fi
++
++ dnl Test for AIX API (AIX 5.3 or newer).
++ if test $use_acl = 0; then
++ AC_CHECK_FUNCS([aclx_get])
++ if test $ac_cv_func_aclx_get = yes; then
++ use_acl=1
++ fi
++ fi
++
++ dnl Test for older AIX API.
++ if test $use_acl = 0 || test "$ac_cv_func_aclx_get" = yes; then
++ AC_CHECK_FUNCS([statacl])
++ if test $ac_cv_func_statacl = yes; then
++ use_acl=1
++ fi
++ fi
++
++ dnl Test for NonStop Kernel API.
++ if test $use_acl = 0; then
++ AC_CHECK_FUNCS([aclsort])
++ if test $ac_cv_func_aclsort = yes; then
++ use_acl=1
++ fi
++ fi
++
++ LIBS=$ac_save_LIBS
++ fi
++ if test "x$enable_acl$use_acl" = "xyes0"; then
++ AC_MSG_ERROR([ACLs enabled but support not detected])
++ elif test "x$enable_acl$use_acl" = "xauto0"; then
++ AC_MSG_WARN([libacl development library was not found or not usable.])
++ AC_MSG_WARN([AC_PACKAGE_NAME will be built without ACL support.])
++ fi
++ fi
++ AC_SUBST([LIB_ACL])
++ AC_DEFINE_UNQUOTED([USE_ACL], [$use_acl],
++ [Define to nonzero if you want access control list support.])
++ USE_ACL=$use_acl
++ AC_SUBST([USE_ACL])
++])
++
++# gl_ACL_GET_FILE(IF-WORKS, IF-NOT)
++# -------------------------------------
++# If 'acl_get_file' works (does not have a particular bug),
++# run IF-WORKS, otherwise, IF-NOT.
++# This tests for a Darwin 8.7.0 bug, whereby acl_get_file returns NULL,
++# but sets errno = ENOENT for an existing file or directory.
++AC_DEFUN([gl_ACL_GET_FILE],
++[
++ AC_CACHE_CHECK([for working acl_get_file], [gl_cv_func_working_acl_get_file],
++ [AC_RUN_IFELSE(
++ [AC_LANG_PROGRAM(
++ [[#include <sys/types.h>
++ #include <sys/acl.h>
++ #include <errno.h>
++ ]],
++ [[if (!acl_get_file (".", ACL_TYPE_ACCESS) && errno == ENOENT)
++ return 1;
++ return 0;
++ ]])],
++ [gl_cv_func_working_acl_get_file=yes],
++ [gl_cv_func_working_acl_get_file=no],
++ [gl_cv_func_working_acl_get_file=cross-compiling])])
++
++ AS_IF([test $gl_cv_func_working_acl_get_file = yes], [$1], [$2])
++])
+diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
+index 837538e..147ecc0 100644
+--- a/m4/gnulib-comp.m4
++++ b/m4/gnulib-comp.m4
+@@ -1,6 +1,3 @@
+-# -*- buffer-read-only: t -*- vi: set ro:
+-# DO NOT EDIT! GENERATED AUTOMATICALLY!
+-# DO NOT EDIT! GENERATED AUTOMATICALLY!
+ # Copyright (C) 2002-2011 Free Software Foundation, Inc.
+ #
+ # This file is free software, distributed under the terms of the GNU
+@@ -256,6 +253,7 @@ AC_DEFUN([gl_INIT],
+ gl_source_base='gnu'
+ # Code from module alloca:
+ # Code from module alloca-opt:
++ gl_FUNC_ACL
+ gl_FUNC_ALLOCA
+ # Code from module areadlink:
+ # Code from module areadlinkat:
+@@ -565,6 +563,15 @@ AC_DEFUN([gl_INIT],
+ gl_SAVE_CWD
+ # Code from module savedir:
+ gl_SAVEDIR
++ # Code from module acl:
++ AC_CHECK_HEADERS([selinux/flask.h])
++ AC_LIBOBJ([selinux-at])
++ gl_HEADERS_SELINUX_SELINUX_H
++ gl_HEADERS_SELINUX_CONTEXT_H
++ AC_REQUIRE([AC_C_INLINE])
++ if test "$with_selinux" != no && test "$ac_cv_header_selinux_selinux_h" = yes; then
++ AC_LIBOBJ([getfilecon])
++ fi
+ # Code from module setenv:
+ gl_FUNC_SETENV
+ gl_STDLIB_MODULE_INDICATOR([setenv])
+@@ -875,10 +882,14 @@ AC_DEFUN([gltests_LIBSOURCES], [
+ AC_DEFUN([gl_FILE_LIST], [
+ build-aux/arg-nonnull.h
+ build-aux/c++defs.h
++ build-aux/snippet/unused-parameter.h
+ build-aux/config.rpath
+ build-aux/gitlog-to-changelog
+ build-aux/warn-on-use.h
+ doc/parse-datetime.texi
++ lib/acl-internal.h
++ lib/acl.h
++ lib/acl_entries.c
+ lib/alloca.c
+ lib/alloca.in.h
+ lib/anytostr.c
+@@ -928,6 +939,7 @@ AC_DEFUN([gl_FILE_LIST], [
+ lib/closeout.c
+ lib/closeout.h
+ lib/config.charset
++ lib/copy-acl.c
+ lib/dirent--.h
+ lib/dirent-safer.h
+ lib/dirent.in.h
+@@ -955,6 +967,7 @@ AC_DEFUN([gl_FILE_LIST], [
+ lib/fd-safer.c
+ lib/fdopendir.c
+ lib/fdutimensat.c
++ lib/file-has-acl.c
+ lib/fileblocks.c
+ lib/filenamecat-lgpl.c
+ lib/filenamecat.h
+@@ -1083,6 +1096,11 @@ AC_DEFUN([gl_FILE_LIST], [
+ lib/save-cwd.h
+ lib/savedir.c
+ lib/savedir.h
++ lib/se-context.in.h
++ lib/se-selinux.in.h
++ lib/selinux-at.c
++ lib/selinux-at.h
++ lib/set-mode-acl.c
+ lib/setenv.c
+ lib/size_max.h
+ lib/sleep.c
+@@ -1176,6 +1194,7 @@ AC_DEFUN([gl_FILE_LIST], [
+ lib/xvasprintf.c
+ lib/xvasprintf.h
+ m4/00gnulib.m4
++ m4/acl.m4
+ m4/alloca.m4
+ m4/argmatch.m4
+ m4/argp.m4
+@@ -1314,6 +1333,8 @@ AC_DEFUN([gl_FILE_LIST], [
+ m4/safe-write.m4
+ m4/save-cwd.m4
+ m4/savedir.m4
++ m4/selinux-context-h.m4
++ m4/selinux-selinux-h.m4
+ m4/setenv.m4
+ m4/size_max.m4
+ m4/sleep.m4
+diff --git a/m4/selinux-context-h.m4 b/m4/selinux-context-h.m4
+new file mode 100644
+index 0000000..7ad67bb
+--- /dev/null
++++ b/m4/selinux-context-h.m4
+@@ -0,0 +1,22 @@
++# serial 3 -*- Autoconf -*-
++# Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc.
++# This file is free software; the Free Software Foundation
++# gives unlimited permission to copy and/or distribute it,
++# with or without modifications, as long as this notice is preserved.
++
++# From Jim Meyering
++# Provide <selinux/context.h>, if necessary.
++
++AC_DEFUN([gl_HEADERS_SELINUX_CONTEXT_H],
++[
++ AC_REQUIRE([gl_LIBSELINUX])
++ if test "$with_selinux" != no; then
++ AC_CHECK_HEADERS([selinux/context.h],
++ [SELINUX_CONTEXT_H=],
++ [SELINUX_CONTEXT_H=selinux/context.h])
++ else
++ SELINUX_CONTEXT_H=selinux/context.h
++ fi
++ AC_SUBST([SELINUX_CONTEXT_H])
++ AM_CONDITIONAL([GL_GENERATE_SELINUX_CONTEXT_H], [test -n "$SELINUX_CONTEXT_H"])
++])
+diff --git a/m4/selinux-selinux-h.m4 b/m4/selinux-selinux-h.m4
+new file mode 100644
+index 0000000..ed5215b
+--- /dev/null
++++ b/m4/selinux-selinux-h.m4
+@@ -0,0 +1,69 @@
++# serial 5 -*- Autoconf -*-
++# Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc.
++# This file is free software; the Free Software Foundation
++# gives unlimited permission to copy and/or distribute it,
++# with or without modifications, as long as this notice is preserved.
++
++# From Jim Meyering
++# Provide <selinux/selinux.h>, if necessary.
++# If it is already present, provide wrapper functions to guard against
++# misbehavior from getfilecon, lgetfilecon, and fgetfilecon.
++
++AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H],
++[
++ AC_REQUIRE([gl_LIBSELINUX])
++ if test "$with_selinux" != no; then
++ AC_CHECK_HEADERS([selinux/selinux.h])
++
++ if test "$ac_cv_header_selinux_selinux_h" = yes; then
++ # We do have <selinux/selinux.h>, so do compile getfilecon.c
++ # and arrange to use its wrappers.
++ gl_CHECK_NEXT_HEADERS([selinux/selinux.h])
++ AC_DEFINE([getfilecon], [rpl_getfilecon],
++ [Always use our getfilecon wrapper.])
++ AC_DEFINE([lgetfilecon], [rpl_lgetfilecon],
++ [Always use our lgetfilecon wrapper.])
++ AC_DEFINE([fgetfilecon], [rpl_fgetfilecon],
++ [Always use our fgetfilecon wrapper.])
++ fi
++
++ case "$ac_cv_search_setfilecon:$ac_cv_header_selinux_selinux_h" in
++ no:*) # already warned
++ ;;
++ *:no)
++ AC_MSG_WARN([libselinux was found but selinux/selinux.h is missing.])
++ AC_MSG_WARN([AC_PACKAGE_NAME will be compiled without SELinux support.])
++ esac
++ else
++ # Do as if <selinux/selinux.h> does not exist, even if
++ # AC_CHECK_HEADERS_ONCE has already determined that it exists.
++ AC_DEFINE([HAVE_SELINUX_SELINUX_H], [0])
++ fi
++])
++
++AC_DEFUN([gl_LIBSELINUX],
++[
++ AC_REQUIRE([AC_CANONICAL_HOST])
++ AC_REQUIRE([AC_CANONICAL_BUILD])
++
++ AC_ARG_WITH([selinux],
++ AS_HELP_STRING([--without-selinux], [do not use SELinux, even on systems with SELinux]),
++ [], [with_selinux=maybe])
++
++ LIB_SELINUX=
++ if test "$with_selinux" != no; then
++ gl_save_LIBS=$LIBS
++ AC_SEARCH_LIBS([setfilecon], [selinux],
++ [test "$ac_cv_search_setfilecon" = "none required" ||
++ LIB_SELINUX=$ac_cv_search_setfilecon])
++ LIBS=$gl_save_LIBS
++ fi
++ AC_SUBST([LIB_SELINUX])
++
++ # Warn if SELinux is found but libselinux is absent;
++ if test "$ac_cv_search_setfilecon" = no &&
++ test "$host" = "$build" && test -d /selinux; then
++ AC_MSG_WARN([This system supports SELinux but libselinux is missing.])
++ AC_MSG_WARN([AC_PACKAGE_NAME will be compiled without SELinux support.])
++ fi
++])
+--
+1.8.1.2
+
diff --git a/tar-1.26-xattrs.patch b/tar-1.26-xattrs.patch
new file mode 100644
index 0000000..80ef5a2
--- /dev/null
+++ b/tar-1.26-xattrs.patch
@@ -0,0 +1,3233 @@
+diff --git a/NEWS b/NEWS
+index 12c1dd6..8aeae33 100644
+--- a/NEWS
++++ b/NEWS
+@@ -1,4 +1,4 @@
+-GNU tar NEWS - User visible changes. 2011-03-12
++GNU tar NEWS - User visible changes. 2012-11-19
+ Please send GNU tar bug reports to <bug-tar at gnu.org>
+
+
+@@ -22,6 +22,17 @@ zero-sized files.
+ When invoked with these two options, tar 1.25 would add only the
+ top-level directory to the archive, but not its contents.
+
++* Support for POSIX ACLs, extended attributes and SELinux context.
++
++Starting with this version tar is able to store, extract and list
++extended file attributes, POSIX.1e ACLs and SELinux context. This is
++controlled by the command line options --xattrs, --acls and --selinux,
++correspondingly. Each of these options has a `--no-' counterpart
++(e.g. --no-xattrs), which disables the corresponding feature.
++Additionally, the options --xattrs-include and --xattrs-exclude allow
++you to selectively control for which files to store (or extract) the
++extended attributes.
++
+
+ version 1.25 - Sergey Poznyakoff, 2010-11-07
+
+diff --git a/THANKS b/THANKS
+index 0364c50..54c96a0 100644
+--- a/THANKS
++++ b/THANKS
+@@ -296,6 +296,7 @@ Kimmy Posey kimmyd at bnr.ca
+ Koji Kishi kis at rqa.sony.co.jp
+ Konno Hiroharu konno at pac.co.jp
+ Kurt Jaeger pi at lf.net
++James Antill jantill at redhat.com
+ Larry Creech lcreech at lonestar.rcclub.org
+ Larry Schwimmer rosebud at cyclone.stanford.edu
+ Lasse Collin lasse.collin at tukaani.org
+@@ -374,6 +375,7 @@ Oswald P. Backus IV backus at lks.csi.com
+ Pascal Meheut pascal at cnam.cnam.fr
+ Patrick Fulconis fulco at sig.uvsq.fr
+ Patrick Timmons timmons at electech.polymtl.ca
++Pavel Raiskup praiskup at redhat.com
+ Paul Eggert eggert at twinsun.com
+ Paul Kanz paul at icx.com
+ Paul Mitchell P.Mitchell at surrey.ac.uk
+diff --git a/acinclude.m4 b/acinclude.m4
+index 10a27e5..30381d3 100644
+--- a/acinclude.m4
++++ b/acinclude.m4
+@@ -24,3 +24,29 @@ AC_DEFUN([TAR_COMPR_PROGRAM],[
+ [tar_compr_var=m4_if($2,,$1,$2)])
+ AC_DEFINE_UNQUOTED(tar_compr_define, "$tar_compr_var",
+ [Define to the program name of ]$1[ compressor program])])
++
++# Provide <attr/xattr.h>, if necessary
++
++AC_DEFUN([TAR_HEADERS_ATTR_XATTR_H],
++[
++ AC_ARG_WITH([xattrs],
++ AS_HELP_STRING([--without-xattrs], [don't use linux extended attributes]),
++ [], [with_xattrs=maybe]
++ )
++
++ AC_CHECK_HEADERS([attr/xattr.h])
++ AM_CONDITIONAL([TAR_COND_XATTR_H],[test "$ac_cv_header_attr_xattr_h" = yes])
++ if test "$ac_cv_header_attr_xattr_h" = yes; then
++ AC_CHECK_FUNCS(getxattr fgetxattr lgetxattr \
++ setxattr fsetxattr lsetxattr \
++ listxattr flistxattr llistxattr,
++ # only when functions are present
++ AC_DEFINE([HAVE_ATTR_XATTR_H], [1],
++ [define to 1 if we have <attr/xattr.h> header])
++ if test "$with_xattrs" != no; then
++ AC_DEFINE([HAVE_XATTRS],,[Define when we have working linux xattrs.])
++ fi
++ )
++ fi
++])
++
+\ No newline at end of file
+diff --git a/configure.ac b/configure.ac
+index db69cb8..9b3e0c8 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -70,6 +70,29 @@ if test $diff_cv_st_fstype_string = yes; then
+ [Define if struct stat has a char st_fstype[] member.])
+ fi
+
++# even if we use gnulib's acl.h with integrated m4 file later on (used because
++# of very useful file_has_acl() function) we need following checks that restrict
++# tar to use POSIX.1e ACLs only.
++AC_ARG_WITH([posix-acls],
++ AS_HELP_STRING([--without-posix-acls],
++ [do not use POSIX.1e access control lists]),
++ [with_posix_acls=no])
++if test "x$with_posix_acls" != "xno"; then
++ AC_CHECK_HEADERS(sys/acl.h,, [with_posix_acl=no])
++ AC_SEARCH_LIBS([acl_get_file], [acl pacl],, [with_posix_acl=no])
++ AC_SEARCH_LIBS([acl_get_fd], [acl pacl],, [with_posix_acl=no])
++ AC_SEARCH_LIBS([acl_set_file], [acl pacl],, [with_posix_acl=no])
++ AC_SEARCH_LIBS([acl_set_fd], [acl pacl],, [with_posix_acl=no])
++ AC_SEARCH_LIBS([acl_to_text], [acl pacl],, [with_posix_acl=no])
++ AC_SEARCH_LIBS([acl_from_text], [acl pacl],, [with_posix_acl=no])
++ if test "x$with_posix_acls" != xno; then
++ AC_DEFINE(HAVE_POSIX_ACLS,,[Define when we have working POSIX acls])
++ fi
++else
++ # disable acls in gnulib's checks
++ export enable_acl=no
++fi
++
+ AC_TYPE_SIGNAL
+ AC_TYPE_MODE_T
+ AC_TYPE_PID_T
+@@ -90,7 +113,10 @@ gl_INIT
+ # paxutils modules
+ tar_PAXUTILS
+
++TAR_HEADERS_ATTR_XATTR_H
++
+ AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
++
+ AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
+ AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
+ AC_CHECK_DECLS([time],,, [#include <time.h>])
+diff --git a/lib/Makefile.am b/lib/Makefile.am
+index efd6826..d73fac8 100644
+--- a/lib/Makefile.am
++++ b/lib/Makefile.am
+@@ -28,11 +28,24 @@ BUILT_SOURCES = rmt-command.h
+ CLEANFILES = rmt-command.h rmt-command.h-t
+ INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu
+
+-noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h
++noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h xattr-at.h
+ libtar_a_SOURCES = \
+ paxerror.c paxexit-status.c paxlib.h paxnames.c \
+ prepargs.c prepargs.h \
+ rtapelib.c \
+ rmt.h \
+ stdopen.c stdopen.h \
+- system.h system-ioctl.h
++ system.h system-ioctl.h \
++ xattr-at.c
++
++if !TAR_COND_XATTR_H
++BUILT_SOURCES += attr/xattr.h
++attr/xattr.h: attr-xattr.in.h $(top_builddir)/config.status
++ $(AM_V_at)$(MKDIR_P) attr
++ $(AM_V_GEN)rm -f $@-t $@ && \
++ cp $(srcdir)/attr-xattr.in.h attr/xattr.h
++
++MOSTLYCLEANFILES = attr/xattr.h attr/xattr.h
++endif
++
++EXTRA_DIST = attr-xattr.in.h
+diff --git a/lib/attr-xattr.in.h b/lib/attr-xattr.in.h
+new file mode 100644
+index 0000000..b5796fe
+--- /dev/null
++++ b/lib/attr-xattr.in.h
+@@ -0,0 +1,58 @@
++/* Replacement <attr/xattr.h> for platforms that lack it.
++ Copyright (C) 2012 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/>. */
++
++#ifndef TAR_ATTR_XATTR_H
++#define TAR_ATTR_XATTR_H
++#include <errno.h>
++
++/* setting */
++static inline int setxattr (const char *path, const char *name, const void
++ *value, size_t size, int flags)
++{ errno = ENOTSUP; return -1; }
++
++static inline int lsetxattr (const char *path, const char *name, const void
++ *value, size_t size, int flags)
++{ errno = ENOTSUP; return -1; }
++
++static inline int fsetxattr (int filedes, const char *name, const void *value,
++ size_t size, int flags)
++{ errno = ENOTSUP; return -1; }
++
++
++/* getting */
++static inline ssize_t getxattr (const char *path, const char *name, void *value,
++ size_t size)
++{ errno = ENOTSUP; return -1; }
++static inline ssize_t lgetxattr (const char *path, const char *name, void
++ *value, size_t size)
++{ errno = ENOTSUP; return -1; }
++static inline ssize_t fgetxattr (int filedes, const char *name, void *value,
++ size_t size)
++{ errno = ENOTSUP; return -1; }
++
++
++/* listing */
++static inline ssize_t listxattr (const char *path, char *list, size_t size)
++{ errno = ENOTSUP; return -1; }
++
++static inline ssize_t llistxattr (const char *path, char *list, size_t size)
++{ errno = ENOTSUP; return -1; }
++
++static inline ssize_t flistxattr (int filedes, char *list, size_t size)
++{ errno = ENOTSUP; return -1; }
++
++#endif
++
+diff --git a/lib/xattr-at.c b/lib/xattr-at.c
+new file mode 100644
+index 0000000..746578c
+--- /dev/null
++++ b/lib/xattr-at.c
+@@ -0,0 +1,110 @@
++/* openat-style fd-relative functions for operating with extended file
++ attributes.
++
++ Copyright (C) 2012 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/>. */
++
++#include <config.h>
++
++#include "xattr-at.h"
++#include "openat.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <fcntl.h>
++
++#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
++#include "save-cwd.h"
++
++#include "openat-priv.h"
++
++/* setxattrat */
++#define AT_FUNC_NAME setxattrat
++#define AT_FUNC_F1 setxattr
++#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, const void *value \
++ , size_t size, int flags
++#define AT_FUNC_POST_FILE_ARGS , name, value, size, flags
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* lsetxattrat */
++#define AT_FUNC_NAME lsetxattrat
++#define AT_FUNC_F1 lsetxattr
++#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, const void *value \
++ , size_t size, int flags
++#define AT_FUNC_POST_FILE_ARGS , name, value, size, flags
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* getxattrat */
++#define AT_FUNC_NAME getxattrat
++#define AT_FUNC_RESULT ssize_t
++#define AT_FUNC_F1 getxattr
++#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, void *value \
++ , size_t size
++#define AT_FUNC_POST_FILE_ARGS , name, value, size
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_RESULT
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* lgetxattrat */
++#define AT_FUNC_NAME lgetxattrat
++#define AT_FUNC_RESULT ssize_t
++#define AT_FUNC_F1 lgetxattr
++#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, void *value \
++ , size_t size
++#define AT_FUNC_POST_FILE_ARGS , name, value, size
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_RESULT
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* listxattrat */
++#define AT_FUNC_NAME listxattrat
++#define AT_FUNC_RESULT ssize_t
++#define AT_FUNC_F1 listxattr
++#define AT_FUNC_POST_FILE_PARAM_DECLS , char *list , size_t size
++#define AT_FUNC_POST_FILE_ARGS , list , size
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_RESULT
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* llistxattrat */
++#define AT_FUNC_NAME llistxattrat
++#define AT_FUNC_RESULT ssize_t
++#define AT_FUNC_F1 llistxattr
++#define AT_FUNC_POST_FILE_PARAM_DECLS , char *list , size_t size
++#define AT_FUNC_POST_FILE_ARGS , list , size
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_RESULT
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
+diff --git a/lib/xattr-at.h b/lib/xattr-at.h
+new file mode 100644
+index 0000000..360245c
+--- /dev/null
++++ b/lib/xattr-at.h
+@@ -0,0 +1,66 @@
++/* Prototypes for openat-style fd-relative functions for operating with
++ extended file attributes.
++
++ Copyright (C) 2012 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/>. */
++
++#ifndef XATTRS_AT_H
++#define XATTRS_AT_H
++
++#include <sys/types.h>
++#include <attr/xattr.h>
++
++/* These are the dir-fd-relative variants of the functions without the
++ "at" suffix. For example, setxattrat (AT_FDCWD, path, name, value, size,
++ flags &c) is usually equivalent to setxattr (file, name, value, size,
++ flags). For more info use the setxattr(2), getxattr(2) or listxattr(2)
++ manpages. */
++
++/* dir-fd-relative setxattr. Operation sets the VALUE of the extended
++ attribute identified by NAME and associated with the given PATH in the
++ filesystem relatively to directory identified by DIR_FD. See the
++ setxattr(2) manpage for the description of all parameters. */
++int setxattrat (int dir_fd, const char *path, const char *name,
++ const void *value, size_t size, int flags);
++
++/* dir-fd-relative lsetxattr. This function is just like setxattrat,
++ except when DIR_FD and FILE specify a symlink: lsetxattrat operates on the
++ symlink, while the setxattrat operates on the referent of the symlink. */
++int lsetxattrat (int dir_fd, const char *path, const char *name,
++ const void *value, size_t size, int flags);
++
++/* dir-fd-relative getxattr. Operation gets the VALUE of the extended
++ attribute idenfified by NAME and associated with the given PATH in the
++ filesystem relatively to directory identified by DIR_FD. For more info
++ about all parameters see the getxattr(2) manpage. */
++ssize_t getxattrat (int dir_fd, const char *path, const char *name,
++ void *value, size_t size);
++
++/* dir-fd-relative lgetxattr. This function is just like getxattrat,
++ except when DIR_FD and FILE specify a symlink: lgetxattrat operates on the
++ symlink, while the getxattrat operates on the referent of the symlink. */
++ssize_t lgetxattrat (int dir_fd, const char *path, const char *name,
++ void *value, size_t size);
++
++/* dir-fd-relative listxattr. Obtain the list of extended attrubtes names. For
++ more info see the listxattr(2) manpage. */
++ssize_t listxattrat (int dir_fd, const char *path, char *list, size_t size);
++
++/* dir-fd-relative llistxattr. This function is just like listxattrat,
++ except when DIR_FD and FILE specify a symlink: llistxattr operates on the
++ symlink, while the listxattrat operates on the referent of the symlink. */
++ssize_t llistxattrat (int dir_fd, const char *path, char *list, size_t size);
++
++#endif /* XATTRS_AT_H */
+diff --git a/src/Makefile.am b/src/Makefile.am
+index de310f4..782df19 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -20,7 +20,7 @@
+
+ bin_PROGRAMS = tar
+
+-noinst_HEADERS = arith.h common.h tar.h
++noinst_HEADERS = arith.h common.h tar.h xattrs.h
+ tar_SOURCES = \
+ buffer.c\
+ checkpoint.c\
+@@ -42,10 +42,11 @@ tar_SOURCES = \
+ unlink.c\
+ update.c\
+ utf8.c\
+- warning.c
++ warning.c\
++ xattrs.c
+
+ INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
+
+ LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
+
+-tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
++tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) $(LIB_SELINUX)
+diff --git a/src/common.h b/src/common.h
+index 2409413..16ba401 100644
+--- a/src/common.h
++++ b/src/common.h
+@@ -1,8 +1,8 @@
+ /* Common declarations for the tar program.
+
+ Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
+- 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
+- Inc.
++ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
++ 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
+@@ -91,6 +91,11 @@ enum subcommand
+
+ GLOBAL enum subcommand subcommand_option;
+
++#define READ_LIKE_SUBCOMMAND \
++ (subcommand_option == EXTRACT_SUBCOMMAND \
++ || subcommand_option == DIFF_SUBCOMMAND \
++ || subcommand_option == LIST_SUBCOMMAND)
++
+ /* Selected format for output archive. */
+ GLOBAL enum archive_format archive_format;
+
+@@ -254,6 +259,15 @@ GLOBAL int same_owner_option;
+ /* If positive, preserve permissions when extracting. */
+ GLOBAL int same_permissions_option;
+
++/* If positive, save the SELinux context. */
++GLOBAL int selinux_context_option;
++
++/* If positive, save the ACLs. */
++GLOBAL int acls_option;
++
++/* If positive, save the user and root xattrs. */
++GLOBAL int xattrs_option;
++
+ /* When set, strip the given number of file name components from the file name
+ before extracting */
+ GLOBAL size_t strip_name_components;
+@@ -708,6 +722,9 @@ extern char *output_start;
+
+ void update_archive (void);
+
++/* Module attrs.c. */
++#include "xattrs.h"
++
+ /* Module xheader.c. */
+
+ void xheader_decode (struct tar_stat_info *stat);
+@@ -728,6 +745,12 @@ bool xheader_string_end (struct xheader *xhdr, char const *keyword);
+ bool xheader_keyword_deleted_p (const char *kw);
+ char *xheader_format_name (struct tar_stat_info *st, const char *fmt,
+ size_t n);
++void xheader_xattr_init (struct tar_stat_info *st);
++void xheader_xattr_free (struct xattr_array *vals, size_t sz);
++void xheader_xattr_copy (const struct tar_stat_info *st,
++ struct xattr_array **vals, size_t *sz);
++void xheader_xattr_add (struct tar_stat_info *st,
++ const char *key, const char *val, size_t len);
+
+ /* Module system.c */
+
+@@ -809,6 +832,7 @@ void checkpoint_run (bool do_write);
+ #define WARN_XDEV 0x00040000
+ #define WARN_DECOMPRESS_PROGRAM 0x00080000
+ #define WARN_EXISTING_FILE 0x00100000
++#define WARN_XATTR_WRITE 0x00200000
+
+ /* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default
+ in verbose mode */
+diff --git a/src/create.c b/src/create.c
+index f98cbb5..25387a9 100644
+--- a/src/create.c
++++ b/src/create.c
+@@ -1,7 +1,8 @@
+ /* Create a tar archive.
+
+ Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
+- 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
++ 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012
++ Free Software Foundation, Inc.
+
+ Written by John Gilmore, on 1985-08-25.
+
+@@ -936,6 +937,30 @@ start_header (struct tar_stat_info *st)
+ GNAME_TO_CHARS (st->gname, header->header.gname);
+ }
+
++ if (archive_format == POSIX_FORMAT)
++ {
++ if (acls_option > 0)
++ {
++ if (st->acls_a_ptr)
++ xheader_store ("SCHILY.acl.access", st, NULL);
++ if (st->acls_d_ptr)
++ xheader_store ("SCHILY.acl.default", st, NULL);
++ }
++ if ((selinux_context_option > 0) && st->cntx_name)
++ xheader_store ("RHT.security.selinux", st, NULL);
++ if (xattrs_option > 0)
++ {
++ size_t scan_xattr = 0;
++ struct xattr_array *xattr_map = st->xattr_map;
++
++ while (scan_xattr < st->xattr_map_size)
++ {
++ xheader_store (xattr_map[scan_xattr].xkey, st, &scan_xattr);
++ ++scan_xattr;
++ }
++ }
++ }
++
+ return header;
+ }
+
+@@ -1711,6 +1736,10 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
+ bool ok;
+ struct stat final_stat;
+
++ xattrs_acls_get (parentfd, name, st, 0, !is_dir);
++ xattrs_selinux_get (parentfd, name, st, fd);
++ xattrs_xattrs_get (parentfd, name, st, fd);
++
+ if (is_dir)
+ {
+ const char *tag_file_name;
+@@ -1830,6 +1859,9 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
+ if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
+ write_long_link (st);
+
++ xattrs_selinux_get (parentfd, name, st, 0);
++ xattrs_xattrs_get (parentfd, name, st, 0);
++
+ block_ordinal = current_block_ordinal ();
+ st->stat.st_size = 0; /* force 0 size on symlink */
+ header = start_header (st);
+@@ -1848,11 +1880,26 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
+ }
+ #endif
+ else if (S_ISCHR (st->stat.st_mode))
+- type = CHRTYPE;
++ {
++ type = CHRTYPE;
++ xattrs_acls_get (parentfd, name, st, 0, true);
++ xattrs_selinux_get (parentfd, name, st, 0);
++ xattrs_xattrs_get (parentfd, name, st, 0);
++ }
+ else if (S_ISBLK (st->stat.st_mode))
+- type = BLKTYPE;
++ {
++ type = BLKTYPE;
++ xattrs_acls_get (parentfd, name, st, 0, true);
++ xattrs_selinux_get (parentfd, name, st, 0);
++ xattrs_xattrs_get (parentfd, name, st, 0);
++ }
+ else if (S_ISFIFO (st->stat.st_mode))
+- type = FIFOTYPE;
++ {
++ type = FIFOTYPE;
++ xattrs_acls_get (parentfd, name, st, 0, true);
++ xattrs_selinux_get (parentfd, name, st, 0);
++ xattrs_xattrs_get (parentfd, name, st, 0);
++ }
+ else if (S_ISSOCK (st->stat.st_mode))
+ {
+ WARNOPT (WARN_FILE_IGNORED,
+diff --git a/src/extract.c b/src/extract.c
+index 662ea0b..87b383a 100644
+--- a/src/extract.c
++++ b/src/extract.c
+@@ -1,7 +1,8 @@
+ /* Extract files from a tar archive.
+
+ Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
+- 2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
++ 2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012
++ Free Software Foundation, Inc.
+
+ Written by John Gilmore, on 1985-11-19.
+
+@@ -97,6 +98,14 @@ struct delayed_set_stat
+ /* Directory that the name is relative to. */
+ int change_dir;
+
++ /* extended attributes*/
++ char *cntx_name;
++ char *acls_a_ptr;
++ size_t acls_a_len;
++ char *acls_d_ptr;
++ size_t acls_d_len;
++ size_t xattr_map_size;
++ struct xattr_array *xattr_map;
+ /* Length and contents of name. */
+ size_t file_name_len;
+ char file_name[1];
+@@ -134,6 +143,18 @@ struct delayed_link
+ hard-linked together. */
+ struct string_list *sources;
+
++ /* SELinux context */
++ char *cntx_name;
++
++ /* ACLs */
++ char *acls_a_ptr;
++ size_t acls_a_len;
++ char *acls_d_ptr;
++ size_t acls_d_len;
++
++ size_t xattr_map_size;
++ struct xattr_array *xattr_map;
++
+ /* The desired target of the desired link. */
+ char target[1];
+ };
+@@ -360,6 +381,12 @@ set_stat (char const *file_name,
+ st->stat.st_mode & ~ current_umask,
+ 0 < same_permissions_option && ! interdir ? MODE_ALL : MODE_RWX,
+ fd, current_mode, current_mode_mask, typeflag, atflag);
++
++ /* these three calls must be done *after* fd_chown() call because fd_chown
++ causes that linux capabilities becomes cleared. */
++ xattrs_xattrs_set (st, file_name, typeflag, 1);
++ xattrs_acls_set (st, file_name, typeflag);
++ xattrs_selinux_set (st, file_name, typeflag);
+ }
+
+ /* For each entry H in the leading prefix of entries in HEAD that do
+@@ -431,6 +458,36 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
+ data->atflag = atflag;
+ data->after_links = 0;
+ data->change_dir = chdir_current;
++ data->cntx_name = NULL;
++ if (st)
++ assign_string (&data->cntx_name, st->cntx_name);
++ if (st && st->acls_a_ptr)
++ {
++ data->acls_a_ptr = xmemdup (st->acls_a_ptr, st->acls_a_len + 1);
++ data->acls_a_len = st->acls_a_len;
++ }
++ else
++ {
++ data->acls_a_ptr = NULL;
++ data->acls_a_len = 0;
++ }
++ if (st && st->acls_d_ptr)
++ {
++ data->acls_d_ptr = xmemdup (st->acls_d_ptr, st->acls_d_len + 1);
++ data->acls_d_len = st->acls_d_len;
++ }
++ else
++ {
++ data->acls_d_ptr = NULL;
++ data->acls_d_len = 0;
++ }
++ if (st)
++ xheader_xattr_copy (st, &data->xattr_map, &data->xattr_map_size);
++ else
++ {
++ data->xattr_map = NULL;
++ data->xattr_map_size = 0;
++ }
+ strcpy (data->file_name, file_name);
+ delayed_set_stat_head = data;
+ if (must_be_dot_or_slash (file_name))
+@@ -678,6 +735,40 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
+ return RECOVER_NO;
+ }
+
++/* Restore stat extended attributes (xattr) for FILE_NAME, using information
++ given in *ST. Restore before extraction because they may affect file layout
++ (e.g. on Lustre distributed parallel filesystem - setting info about how many
++ servers is this file striped over, stripe size, mirror copies, etc.
++ in advance dramatically improves the following performance of reading and
++ writing a file). If not restoring permissions, invert the INVERT_PERMISSIONS
++ bits from the file's current permissions. TYPEFLAG specifies the type of the
++ file. FILE_CREATED indicates set_xattr has created the file */
++static int
++set_xattr (char const *file_name, struct tar_stat_info const *st,
++ mode_t invert_permissions, char typeflag, int *file_created)
++{
++ int status = 0;
++
++#ifdef HAVE_XATTRS
++ bool interdir_made = false;
++
++ if ((xattrs_option > 0) && st->xattr_map_size)
++ {
++ mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
++
++ do
++ status = mknodat (chdir_fd, file_name, mode ^ invert_permissions, 0);
++ while (status && maybe_recoverable ((char *)file_name, false,
++ &interdir_made));
++
++ xattrs_xattrs_set (st, file_name, typeflag, 0);
++ *file_created = 1;
++ }
++#endif
++
++ return(status);
++}
++
+ /* Fix the statuses of all directories whose statuses need fixing, and
+ which are not ancestors of FILE_NAME. If AFTER_LINKS is
+ nonzero, do this for all such directories; otherwise, stop at the
+@@ -738,12 +829,23 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
+ sb.stat.st_gid = data->gid;
+ sb.atime = data->atime;
+ sb.mtime = data->mtime;
++ sb.cntx_name = data->cntx_name;
++ sb.acls_a_ptr = data->acls_a_ptr;
++ sb.acls_a_len = data->acls_a_len;
++ sb.acls_d_ptr = data->acls_d_ptr;
++ sb.acls_d_len = data->acls_d_len;
++ sb.xattr_map = data->xattr_map;
++ sb.xattr_map_size = data->xattr_map_size;
+ set_stat (data->file_name, &sb,
+ -1, current_mode, current_mode_mask,
+ DIRTYPE, data->interdir, data->atflag);
+ }
+
+ delayed_set_stat_head = data->next;
++ xheader_xattr_free (data->xattr_map, data->xattr_map_size);
++ free (data->cntx_name);
++ free (data->acls_a_ptr);
++ free (data->acls_d_ptr);
+ free (data);
+ }
+ }
+@@ -859,7 +961,8 @@ extract_dir (char *file_name, int typeflag)
+
+ static int
+ open_output_file (char const *file_name, int typeflag, mode_t mode,
+- mode_t *current_mode, mode_t *current_mode_mask)
++ int file_created, mode_t *current_mode,
++ mode_t *current_mode_mask)
+ {
+ int fd;
+ bool overwriting_old_files = old_files_option == OVERWRITE_OLD_FILES;
+@@ -869,6 +972,10 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
+ ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW)
+ : O_EXCL));
+
++ /* File might be created in set_xattr. So clear O_EXCL to avoid open() fail */
++ if (file_created)
++ openflag = openflag & ~O_EXCL;
++
+ if (typeflag == CONTTYPE)
+ {
+ static int conttype_diagnosed;
+@@ -939,6 +1046,8 @@ extract_file (char *file_name, int typeflag)
+ bool interdir_made = false;
+ mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
+ & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
++ mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO)
++ : 0;
+ mode_t current_mode = 0;
+ mode_t current_mode_mask = 0;
+
+@@ -955,8 +1064,18 @@ extract_file (char *file_name, int typeflag)
+ }
+ else
+ {
++ int file_created = 0;
++ if (set_xattr (file_name, ¤t_stat_info, invert_permissions,
++ typeflag, &file_created))
++ {
++ skip_member ();
++ open_error (file_name);
++ return 1;
++ }
++
+ while ((fd = open_output_file (file_name, typeflag, mode,
+- ¤t_mode, ¤t_mode_mask))
++ file_created, ¤t_mode,
++ ¤t_mode_mask))
+ < 0)
+ {
+ int recover = maybe_recoverable (file_name, true, &interdir_made);
+@@ -1096,6 +1215,13 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
+ + strlen (file_name) + 1);
+ p->sources->next = 0;
+ strcpy (p->sources->string, file_name);
++ p->cntx_name = NULL;
++ assign_string (&p->cntx_name, current_stat_info.cntx_name);
++ p->acls_a_ptr = NULL;
++ p->acls_a_len = 0;
++ p->acls_d_ptr = NULL;
++ p->acls_d_len = 0;
++ xheader_xattr_copy (¤t_stat_info, &p->xattr_map, &p->xattr_map_size);
+ strcpy (p->target, current_stat_info.link_name);
+
+ h = delayed_set_stat_head;
+@@ -1530,6 +1656,13 @@ apply_delayed_links (void)
+ st1.stat.st_gid = ds->gid;
+ st1.atime = ds->atime;
+ st1.mtime = ds->mtime;
++ st1.cntx_name = ds->cntx_name;
++ st1.acls_a_ptr = ds->acls_a_ptr;
++ st1.acls_a_len = ds->acls_a_len;
++ st1.acls_d_ptr = ds->acls_d_ptr;
++ st1.acls_d_len = ds->acls_d_len;
++ st1.xattr_map = ds->xattr_map;
++ st1.xattr_map_size = ds->xattr_map_size;
+ set_stat (source, &st1, -1, 0, 0, SYMTYPE,
+ false, AT_SYMLINK_NOFOLLOW);
+ valid_source = source;
+@@ -1544,6 +1677,9 @@ apply_delayed_links (void)
+ sources = next;
+ }
+
++ xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
++ free (ds->cntx_name);
++
+ {
+ struct delayed_link *next = ds->next;
+ free (ds);
+diff --git a/src/list.c b/src/list.c
+index f4e6e0a..dd501a9 100644
+--- a/src/list.c
++++ b/src/list.c
+@@ -1,7 +1,8 @@
+ /* List a tar archive, with support routines for reading a tar archive.
+
+ Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
+- 2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
++ 2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012
++ Free Software Foundation, Inc.
+
+ Written by John Gilmore, on 1985-08-26.
+
+@@ -615,6 +616,8 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
+ assign_string (&stat_info->gname,
+ header->header.gname[0] ? header->header.gname : NULL);
+
++ xheader_xattr_init (stat_info);
++
+ if (format == OLDGNU_FORMAT && incremental_option)
+ {
+ stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
+@@ -1075,7 +1078,7 @@ static void
+ simple_print_header (struct tar_stat_info *st, union block *blk,
+ off_t block_ordinal)
+ {
+- char modes[11];
++ char modes[12];
+ char const *time_stamp;
+ int time_stamp_len;
+ char *temp_name;
+@@ -1167,6 +1170,9 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
+
+ pax_decode_mode (st->stat.st_mode, modes + 1);
+
++ /* extended attributes: GNU `ls -l'-like preview */
++ xattrs_print_char (st, modes + 10);
++
+ /* Time stamp. */
+
+ time_stamp = tartime (st->mtime, full_time_option);
+@@ -1312,6 +1318,7 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
+ }
+ }
+ fflush (stdlis);
++ xattrs_print (st);
+ }
+
+
+diff --git a/src/tar.c b/src/tar.c
+index 7a673e0..e244808 100644
+--- a/src/tar.c
++++ b/src/tar.c
+@@ -1,7 +1,8 @@
+ /* A tar (tape archiver) program.
+
+ Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
+- 2001, 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
++ 2001, 2003, 2004, 2005, 2006, 2007, 2012
++ Free Software Foundation, Inc.
+
+ Written by John Gilmore, starting 1985-08-25.
+
+@@ -255,7 +256,8 @@ tar_set_quoting_style (char *arg)
+
+ enum
+ {
+- ANCHORED_OPTION = CHAR_MAX + 1,
++ ACLS_OPTION = CHAR_MAX + 1,
++ ANCHORED_OPTION,
+ ATIME_PRESERVE_OPTION,
+ BACKUP_OPTION,
+ CHECK_DEVICE_OPTION,
+@@ -288,6 +290,7 @@ enum
+ MODE_OPTION,
+ MTIME_OPTION,
+ NEWER_MTIME_OPTION,
++ NO_ACLS_OPTION,
+ NO_ANCHORED_OPTION,
+ NO_AUTO_COMPRESS_OPTION,
+ NO_CHECK_DEVICE_OPTION,
+@@ -301,9 +304,11 @@ enum
+ NO_SAME_OWNER_OPTION,
+ NO_SAME_PERMISSIONS_OPTION,
+ NO_SEEK_OPTION,
++ NO_SELINUX_CONTEXT_OPTION,
+ NO_UNQUOTE_OPTION,
+ NO_WILDCARDS_MATCH_SLASH_OPTION,
+ NO_WILDCARDS_OPTION,
++ NO_XATTR_OPTION,
+ NULL_OPTION,
+ NUMERIC_OWNER_OPTION,
+ OCCURRENCE_OPTION,
+@@ -325,6 +330,7 @@ enum
+ RMT_COMMAND_OPTION,
+ RSH_COMMAND_OPTION,
+ SAME_OWNER_OPTION,
++ SELINUX_CONTEXT_OPTION,
+ SHOW_DEFAULTS_OPTION,
+ SHOW_OMITTED_DIRS_OPTION,
+ SHOW_TRANSFORMED_NAMES_OPTION,
+@@ -341,7 +347,10 @@ enum
+ VOLNO_FILE_OPTION,
+ WARNING_OPTION,
+ WILDCARDS_MATCH_SLASH_OPTION,
+- WILDCARDS_OPTION
++ WILDCARDS_OPTION,
++ XATTR_OPTION,
++ XATTR_EXCLUDE,
++ XATTR_INCLUDE
+ };
+
+ const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
+@@ -530,6 +539,28 @@ static struct argp_option options[] = {
+ N_("cancel the effect of --delay-directory-restore option"), GRID+1 },
+ #undef GRID
+
++#define GRID 55
++ {NULL, 0, NULL, 0,
++ N_("Handling of extended file attributes:"), GRID },
++
++ {"xattrs", XATTR_OPTION, 0, 0,
++ N_("Enable extended attributes support"), GRID+1 },
++ {"no-xattrs", NO_XATTR_OPTION, 0, 0,
++ N_("Disable extended attributes support"), GRID+1 },
++ {"xattrs-include", XATTR_INCLUDE, N_("MASK"), 0,
++ N_("specify the include pattern for xattr keys"), GRID+1 },
++ {"xattrs-exclude", XATTR_EXCLUDE, N_("MASK"), 0,
++ N_("specify the exclude pattern for xattr keys"), GRID+1 },
++ {"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
++ N_("Enable the SELinux context support"), GRID+1 },
++ {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
++ N_("Disable the SELinux context support"), GRID+1 },
++ {"acls", ACLS_OPTION, 0, 0,
++ N_("Enable the POSIX ACLs support"), GRID+1 },
++ {"no-acls", NO_ACLS_OPTION, 0, 0,
++ N_("Disable the POSIX ACLs support"), GRID+1 },
++#undef GRID
++
+ #define GRID 60
+ {NULL, 0, NULL, 0,
+ N_("Device selection and switching:"), GRID },
+@@ -2091,6 +2122,38 @@ parse_opt (int key, char *arg, struct argp_state *state)
+ same_permissions_option = -1;
+ break;
+
++ case ACLS_OPTION:
++ set_archive_format ("posix");
++ acls_option = 1;
++ break;
++
++ case NO_ACLS_OPTION:
++ acls_option = -1;
++ break;
++
++ case SELINUX_CONTEXT_OPTION:
++ set_archive_format ("posix");
++ selinux_context_option = 1;
++ break;
++
++ case NO_SELINUX_CONTEXT_OPTION:
++ selinux_context_option = -1;
++ break;
++
++ case XATTR_OPTION:
++ set_archive_format ("posix");
++ xattrs_option = 1;
++ break;
++
++ case NO_XATTR_OPTION:
++ xattrs_option = -1;
++ break;
++
++ case XATTR_INCLUDE:
++ case XATTR_EXCLUDE:
++ xattrs_mask_add (arg, (key == XATTR_INCLUDE));
++ break;
++
+ case RECURSION_OPTION:
+ recursion_option = FNM_LEADING_DIR;
+ break;
+@@ -2468,11 +2531,26 @@ decode_options (int argc, char **argv)
+ --gray */
+ if (args.pax_option
+ && archive_format != POSIX_FORMAT
+- && (subcommand_option != EXTRACT_SUBCOMMAND
+- || subcommand_option != DIFF_SUBCOMMAND
+- || subcommand_option != LIST_SUBCOMMAND))
++ && !READ_LIKE_SUBCOMMAND)
+ USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
+
++ /* star creates non-POSIX typed archives with xattr support, so allow the
++ extra headers whenn reading */
++ if ((acls_option > 0)
++ && archive_format != POSIX_FORMAT
++ && !READ_LIKE_SUBCOMMAND)
++ USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
++
++ if ((selinux_context_option > 0)
++ && archive_format != POSIX_FORMAT
++ && !READ_LIKE_SUBCOMMAND)
++ USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
++
++ if ((xattrs_option > 0)
++ && archive_format != POSIX_FORMAT
++ && !READ_LIKE_SUBCOMMAND)
++ USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives")));
++
+ /* If ready to unlink hierarchies, so we are for simpler files. */
+ if (recursive_unlink_option)
+ old_files_option = UNLINK_FIRST_OLD_FILES;
+@@ -2681,6 +2759,7 @@ main (int argc, char **argv)
+ /* Dispose of allocated memory, and return. */
+
+ free (archive_name_array);
++ xattrs_clear_setup ();
+ name_term ();
+
+ if (exit_status == TAREXIT_FAILURE)
+@@ -2725,11 +2804,15 @@ void
+ tar_stat_destroy (struct tar_stat_info *st)
+ {
+ tar_stat_close (st);
++ xheader_xattr_free (st->xattr_map, st->xattr_map_size);
+ free (st->orig_file_name);
+ free (st->file_name);
+ free (st->link_name);
+ free (st->uname);
+ free (st->gname);
++ free (st->cntx_name);
++ free (st->acls_a_ptr);
++ free (st->acls_d_ptr);
+ free (st->sparse_map);
+ free (st->dumpdir);
+ xheader_destroy (&st->xhdr);
+diff --git a/src/tar.h b/src/tar.h
+index ce9850c..b181e58 100644
+--- a/src/tar.h
++++ b/src/tar.h
+@@ -1,7 +1,8 @@
+ /* GNU tar Archive Format description.
+
+ Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+- 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
++ 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2012
++ 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
+@@ -276,6 +277,14 @@ struct xheader
+ uintmax_t string_length;
+ };
+
++/* Information about xattrs for a file. */
++struct xattr_array
++ {
++ char *xkey;
++ char *xval_ptr;
++ size_t xval_len;
++ };
++
+ struct tar_stat_info
+ {
+ char *orig_file_name; /* name of file read from the archive header */
+@@ -287,6 +296,15 @@ struct tar_stat_info
+
+ char *uname; /* user name of owner */
+ char *gname; /* group name of owner */
++
++ char *cntx_name; /* SELinux context for the current archive entry. */
++
++ char *acls_a_ptr; /* Access ACLs for the current archive entry. */
++ size_t acls_a_len; /* Access ACLs for the current archive entry. */
++
++ char *acls_d_ptr; /* Default ACLs for the current archive entry. */
++ size_t acls_d_len; /* Default ACLs for the current archive entry. */
++
+ struct stat stat; /* regular filesystem stat */
+
+ /* STAT doesn't always have access, data modification, and status
+@@ -309,6 +327,9 @@ struct tar_stat_info
+ size_t sparse_map_size; /* Size of the sparse map */
+ struct sp_array *sparse_map;
+
++ size_t xattr_map_size; /* Size of the xattr map */
++ struct xattr_array *xattr_map;
++
+ /* Extended headers */
+ struct xheader xhdr;
+
+diff --git a/src/warning.c b/src/warning.c
+index ee9d684..570b3c1 100644
+--- a/src/warning.c
++++ b/src/warning.c
+@@ -1,6 +1,6 @@
+ /* This file is part of GNU tar.
+
+- Copyright (C) 2009 Free Software Foundation, Inc.
++ Copyright (C) 2009, 2012 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
+@@ -43,6 +43,7 @@ static char const *const warning_args[] = {
+ "xdev",
+ "decompress-program",
+ "existing-file",
++ "xattr-write",
+ NULL
+ };
+
+@@ -69,6 +70,7 @@ static int warning_types[] = {
+ WARN_XDEV,
+ WARN_DECOMPRESS_PROGRAM,
+ WARN_EXISTING_FILE,
++ WARN_XATTR_WRITE
+ };
+
+ ARGMATCH_VERIFY (warning_args, warning_types);
+diff --git a/src/xattrs.c b/src/xattrs.c
+new file mode 100644
+index 0000000..5a4bf72
+--- /dev/null
++++ b/src/xattrs.c
+@@ -0,0 +1,732 @@
++/* Support for extended attributes.
++
++ Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software
++ Foundation, Inc.
++
++ Written by James Antill, on 2006-07-27.
++
++ 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 2, 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, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
++
++#include <config.h>
++#include <system.h>
++
++#include <fnmatch.h>
++#include <quotearg.h>
++
++#include "common.h"
++
++#include "xattr-at.h"
++#include "selinux-at.h"
++
++struct xattrs_mask_map
++{
++ const char **masks;
++ size_t size;
++ size_t used;
++};
++
++/* list of fnmatch patterns */
++static struct
++{
++ /* lists of fnmatch patterns */
++ struct xattrs_mask_map incl;
++ struct xattrs_mask_map excl;
++} xattrs_setup;
++
++/* disable posix acls when problem found in gnulib script m4/acl.m4 */
++#if ! USE_ACL
++# undef HAVE_POSIX_ACLS
++#endif
++
++#ifdef HAVE_POSIX_ACLS
++# include "acl.h"
++# include <sys/acl.h>
++#endif
++
++#ifdef HAVE_POSIX_ACLS
++
++/* acl-at wrappers, TODO: move to gnulib in future? */
++acl_t acl_get_file_at (int dirfd, const char *file, acl_type_t type);
++int acl_set_file_at (int dirfd, const char *file, acl_type_t type, acl_t acl);
++int file_has_acl_at (int dirfd, char const *, struct stat const *);
++
++/* acl_get_file_at */
++#define AT_FUNC_NAME acl_get_file_at
++#define AT_FUNC_RESULT acl_t
++#define AT_FUNC_FAIL (acl_t)NULL
++#define AT_FUNC_F1 acl_get_file
++#define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type
++#define AT_FUNC_POST_FILE_ARGS , type
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_RESULT
++#undef AT_FUNC_FAIL
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* acl_set_file_at */
++#define AT_FUNC_NAME acl_set_file_at
++#define AT_FUNC_F1 acl_set_file
++#define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type, acl_t acl
++#define AT_FUNC_POST_FILE_ARGS , type, acl
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* gnulib file_has_acl_at */
++#define AT_FUNC_NAME file_has_acl_at
++#define AT_FUNC_F1 file_has_acl
++#define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat const *st
++#define AT_FUNC_POST_FILE_ARGS , st
++#include "at-func.c"
++#undef AT_FUNC_NAME
++#undef AT_FUNC_F1
++#undef AT_FUNC_POST_FILE_PARAM_DECLS
++#undef AT_FUNC_POST_FILE_ARGS
++
++/* convert unix permissions into an ACL ... needed due to "default" ACLs */
++static acl_t
++perms2acl (int perms)
++{
++ char val[] = "user::---,group::---,other::---";
++ /* 0123456789 123456789 123456789 123456789 */
++
++ /* user */
++ if (perms & 0400)
++ val[6] = 'r';
++ if (perms & 0200)
++ val[7] = 'w';
++ if (perms & 0100)
++ val[8] = 'x';
++
++ /* group */
++ if (perms & 0040)
++ val[17] = 'r';
++ if (perms & 0020)
++ val[18] = 'w';
++ if (perms & 0010)
++ val[19] = 'x';
++
++ /* other */
++ if (perms & 0004)
++ val[28] = 'r';
++ if (perms & 0002)
++ val[29] = 'w';
++ if (perms & 0001)
++ val[30] = 'x';
++
++ return acl_from_text (val);
++}
++
++static char *
++skip_to_ext_fields (char *ptr)
++{
++ /* skip tag name (user/group/default/mask) */
++ ptr += strcspn (ptr, ":,\n");
++
++ if (*ptr != ':')
++ return ptr;
++ ++ptr;
++
++ ptr += strcspn (ptr, ":,\n"); /* skip user/group name */
++
++ if (*ptr != ':')
++ return ptr;
++ ++ptr;
++
++ ptr += strcspn (ptr, ":,\n"); /* skip perms */
++
++ return ptr;
++}
++
++/* The POSIX draft allows extra fields after the three main ones. Star
++ uses this to add a fourth field for user/group which is the numeric ID.
++ This function removes such extra fields by overwriting them with the
++ characters that follow. */
++static char *
++fixup_extra_acl_fields (char *ptr)
++{
++ char *src = ptr;
++ char *dst = ptr;
++
++ while (*src)
++ {
++ const char *old = src;
++ size_t len = 0;
++
++ src = skip_to_ext_fields (src);
++ len = src - old;
++ if (old != dst)
++ memmove (dst, old, len);
++ dst += len;
++
++ if (*src == ':') /* We have extra fields, skip them all */
++ src += strcspn (src, "\n,");
++
++ if ((*src == '\n') || (*src == ','))
++ *dst++ = *src++; /* also done when dst == src, but that's ok */
++ }
++ if (src != dst)
++ *dst = 0;
++
++ return ptr;
++}
++
++/* "system.posix_acl_access" */
++static void
++xattrs__acls_set (struct tar_stat_info const *st,
++ char const *file_name, int type,
++ char *ptr, size_t len, bool def)
++{
++ acl_t acl;
++
++ if (ptr)
++ {
++ /* assert (strlen (ptr) == len); */
++ ptr = fixup_extra_acl_fields (ptr);
++
++ acl = acl_from_text (ptr);
++ acls_option = 1;
++ }
++ else if (acls_option > 0)
++ acl = perms2acl (st->stat.st_mode);
++ else
++ return; /* don't call acl functions unless we first hit an ACL, or
++ --acls was passed explicitly */
++
++ if (!acl)
++ {
++ call_arg_warn ("acl_from_text", file_name);
++ return;
++ }
++
++ if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
++ /* warn even if filesystem does not support acls */
++ WARNOPT (WARN_XATTR_WRITE,
++ (0, errno,
++ _ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
++ file_name));
++
++ acl_free (acl);
++}
++
++static void
++xattrs__acls_get_a (int parentfd, const char *file_name,
++ struct tar_stat_info *st,
++ char **ret_ptr, size_t * ret_len)
++{
++ char *val = NULL;
++ ssize_t len;
++ acl_t acl;
++
++ if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS)))
++ {
++ if (errno != ENOTSUP)
++ call_arg_warn ("acl_get_file_at", file_name);
++ return;
++ }
++
++ val = acl_to_text (acl, &len);
++ acl_free (acl);
++
++ if (!val)
++ {
++ call_arg_warn ("acl_to_text", file_name);
++ return;
++ }
++
++ *ret_ptr = xstrdup (val);
++ *ret_len = len;
++
++ acl_free (val);
++}
++
++/* "system.posix_acl_default" */
++static void
++xattrs__acls_get_d (int parentfd, char const *file_name,
++ struct tar_stat_info *st,
++ char **ret_ptr, size_t * ret_len)
++{
++ char *val = NULL;
++ ssize_t len;
++ acl_t acl;
++
++ if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT)))
++ {
++ if (errno != ENOTSUP)
++ call_arg_warn ("acl_get_file_at", file_name);
++ return;
++ }
++
++ val = acl_to_text (acl, &len);
++ acl_free (acl);
++
++ if (!val)
++ {
++ call_arg_warn ("acl_to_text", file_name);
++ return;
++ }
++
++ *ret_ptr = xstrdup (val);
++ *ret_len = len;
++
++ acl_free (val);
++}
++#endif /* HAVE_POSIX_ACLS */
++
++static void
++acls_one_line (const char *prefix, char delim,
++ const char *aclstring, size_t len)
++{
++ /* support both long and short text representation of posix acls */
++ struct obstack stk;
++ int pref_len = strlen (prefix);
++ const char *oldstring = aclstring;
++ int pos = 0;
++
++ if (!aclstring || !len)
++ return;
++
++ obstack_init (&stk);
++ while (pos <= len)
++ {
++ int move = strcspn (aclstring, ",\n");
++ if (!move)
++ break;
++
++ if (oldstring != aclstring)
++ obstack_1grow (&stk, delim);
++
++ obstack_grow (&stk, prefix, pref_len);
++ obstack_grow (&stk, aclstring, move);
++
++ aclstring += move + 1;
++ }
++
++ obstack_1grow (&stk, '\0');
++
++ fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
++
++ obstack_free (&stk, NULL);
++}
++
++void
++xattrs_acls_get (int parentfd, char const *file_name,
++ struct tar_stat_info *st, int fd, int xisfile)
++{
++ if (acls_option > 0)
++ {
++#ifndef HAVE_POSIX_ACLS
++ static int done = 0;
++ if (!done)
++ WARN ((0, 0, _("POSIX ACL support is not available")));
++ done = 1;
++#else
++ int err = file_has_acl_at (parentfd, file_name, &st->stat);
++ if (err == 0)
++ return;
++ if (err == -1)
++ {
++ call_arg_warn ("file_has_acl_at", file_name);
++ return;
++ }
++
++ xattrs__acls_get_a (parentfd, file_name, st,
++ &st->acls_a_ptr, &st->acls_a_len);
++ if (!xisfile)
++ xattrs__acls_get_d (parentfd, file_name, st,
++ &st->acls_d_ptr, &st->acls_d_len);
++#endif
++ }
++}
++
++void
++xattrs_acls_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag)
++{
++ if (acls_option > 0 && typeflag != SYMTYPE)
++ {
++#ifndef HAVE_POSIX_ACLS
++ static int done = 0;
++ if (!done)
++ WARN ((0, 0, _("POSIX ACL support is not available")));
++ done = 1;
++#else
++ xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
++ st->acls_a_ptr, st->acls_a_len, false);
++ if (typeflag == DIRTYPE || typeflag == GNUTYPE_DUMPDIR)
++ xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
++ st->acls_d_ptr, st->acls_d_len, true);
++#endif
++ }
++}
++
++static void
++mask_map_realloc (struct xattrs_mask_map *map)
++{
++ if (map->used == map->size)
++ {
++ if (map->size == 0)
++ map->size = 4;
++ map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
++ }
++}
++
++void
++xattrs_mask_add (const char *mask, bool incl)
++{
++ struct xattrs_mask_map *mask_map =
++ incl ? &xattrs_setup.incl : &xattrs_setup.excl;
++ /* ensure there is enough space */
++ mask_map_realloc (mask_map);
++ /* just assign pointers -- we silently expect that pointer "mask" is valid
++ through the whole program (pointer to argv array) */
++ mask_map->masks[mask_map->used++] = mask;
++}
++
++static void
++clear_mask_map (struct xattrs_mask_map *mask_map)
++{
++ if (mask_map->size)
++ free (mask_map->masks);
++}
++
++void
++xattrs_clear_setup ()
++{
++ clear_mask_map (&xattrs_setup.incl);
++ clear_mask_map (&xattrs_setup.excl);
++}
++
++/* get all xattrs from file given by FILE_NAME or FD (when non-zero). This
++ includes all the user.*, security.*, system.*, etc. available domains */
++void
++xattrs_xattrs_get (int parentfd, char const *file_name,
++ struct tar_stat_info *st, int fd)
++{
++ if (xattrs_option > 0)
++ {
++#ifndef HAVE_XATTRS
++ static int done = 0;
++ if (!done)
++ WARN ((0, 0, _("XATTR support is not available")));
++ done = 1;
++#else
++ static size_t xsz = 1024;
++ static char *xatrs = NULL;
++ ssize_t xret = -1;
++
++ if (!xatrs)
++ xatrs = x2nrealloc (xatrs, &xsz, 1);
++
++ while (((fd == 0) ?
++ ((xret =
++ llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
++ ((xret = flistxattr (fd, xatrs, xsz)) == -1))
++ && (errno == ERANGE))
++ {
++ xatrs = x2nrealloc (xatrs, &xsz, 1);
++ }
++
++ if (xret == -1)
++ call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
++ else
++ {
++ const char *attr = xatrs;
++ static size_t asz = 1024;
++ static char *val = NULL;
++
++ if (!val)
++ val = x2nrealloc (val, &asz, 1);
++
++ while (xret > 0)
++ {
++ size_t len = strlen (attr);
++ ssize_t aret = 0;
++
++ /* Archive all xattrs during creation, decide at extraction time
++ * which ones are of interest/use for the target filesystem. */
++ while (((fd == 0)
++ ? ((aret = lgetxattrat (parentfd, file_name, attr,
++ val, asz)) == -1)
++ : ((aret = fgetxattr (fd, attr, val, asz)) == -1))
++ && (errno == ERANGE))
++ {
++ val = x2nrealloc (val, &asz, 1);
++ }
++
++ if (aret != -1)
++ xheader_xattr_add (st, attr, val, aret);
++ else if (errno != ENOATTR)
++ call_arg_warn ((fd == 0) ? "lgetxattrat"
++ : "fgetxattr", file_name);
++
++ attr += len + 1;
++ xret -= len + 1;
++ }
++ }
++#endif
++ }
++}
++
++static void
++xattrs__fd_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag,
++ const char *attr, const char *ptr, size_t len)
++{
++ if (ptr)
++ {
++ const char *sysname = "setxattrat";
++ int ret = -1;
++
++ if (typeflag != SYMTYPE)
++ ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
++ else
++ {
++ sysname = "lsetxattr";
++ ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
++ }
++
++ if (ret == -1)
++ WARNOPT (WARN_XATTR_WRITE,
++ (0, errno,
++ _("%s: Cannot set '%s' extended attribute for file '%s'"),
++ sysname, attr, file_name));
++ }
++}
++
++/* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
++ zero, otherwise the fgetfileconat is used against correct file descriptor */
++void
++xattrs_selinux_get (int parentfd, char const *file_name,
++ struct tar_stat_info *st, int fd)
++{
++ if (selinux_context_option > 0)
++ {
++#if HAVE_SELINUX_SELINUX_H != 1
++ static int done = 0;
++ if (!done)
++ WARN ((0, 0, _("SELinux support is not available")));
++ done = 1;
++#else
++ int result = fd ?
++ fgetfilecon (fd, &st->cntx_name)
++ : lgetfileconat (parentfd, file_name, &st->cntx_name);
++
++ if (result == -1 && errno != ENODATA && errno != ENOTSUP)
++ call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
++#endif
++ }
++}
++
++void
++xattrs_selinux_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag)
++{
++ if (selinux_context_option > 0)
++ {
++#if HAVE_SELINUX_SELINUX_H != 1
++ static int done = 0;
++ if (!done)
++ WARN ((0, 0, _("SELinux support is not available")));
++ done = 1;
++#else
++ const char *sysname = "setfilecon";
++ int ret;
++
++ if (!st->cntx_name)
++ return;
++
++ if (typeflag != SYMTYPE)
++ {
++ ret = setfileconat (chdir_fd, file_name, st->cntx_name);
++ sysname = "setfileconat";
++ }
++ else
++ {
++ ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
++ sysname = "lsetfileconat";
++ }
++
++ if (ret == -1)
++ WARNOPT (WARN_XATTR_WRITE,
++ (0, errno,
++ _("%s: Cannot set SELinux context for file '%s'"),
++ sysname, file_name));
++#endif
++ }
++}
++
++static bool
++xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
++{
++ int i;
++
++ if (!mm->size)
++ return false;
++
++ for (i = 0; i < mm->used; i++)
++ if (fnmatch (mm->masks[i], kw, 0) == 0)
++ return true;
++
++ return false;
++}
++
++#define USER_DOT_PFX "user."
++
++static bool
++xattrs_kw_included (const char *kw, bool archiving)
++{
++ if (xattrs_setup.incl.size)
++ return xattrs_matches_mask (kw, &xattrs_setup.incl);
++ else if (archiving)
++ return true;
++ else
++ return strncmp (kw, USER_DOT_PFX, sizeof (USER_DOT_PFX) - 1) == 0;
++}
++
++static bool
++xattrs_kw_excluded (const char *kw, bool archiving)
++{
++ return xattrs_setup.excl.size ?
++ xattrs_matches_mask (kw, &xattrs_setup.excl) : false;
++}
++
++/* Check whether the xattr with keyword KW should be discarded from list of
++ attributes that are going to be archived/excluded (set ARCHIVING=true for
++ archiving, false for excluding) */
++static bool
++xattrs_masked_out (const char *kw, bool archiving)
++{
++ return xattrs_kw_included (kw, archiving) ?
++ xattrs_kw_excluded (kw, archiving) : true;
++}
++
++void
++xattrs_xattrs_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag, int later_run)
++{
++ if (xattrs_option > 0)
++ {
++#ifndef HAVE_XATTRS
++ static int done = 0;
++ if (!done)
++ WARN ((0, 0, _("XATTR support is not available")));
++ done = 1;
++#else
++ size_t scan = 0;
++
++ if (!st->xattr_map_size)
++ return;
++
++ for (; scan < st->xattr_map_size; ++scan)
++ {
++ char *keyword = st->xattr_map[scan].xkey;
++ keyword += strlen ("SCHILY.xattr.");
++
++ /* TODO: this 'later_run' workaround is temporary solution -> once
++ capabilities should become fully supported by it's API and there
++ should exist something like xattrs_capabilities_set() call.
++ For a regular files: all extended attributes are restored during
++ the first run except 'security.capability' which is restored in
++ 'later_run == 1'. */
++ if (typeflag == REGTYPE
++ && later_run == !!strcmp (keyword, "security.capability"))
++ continue;
++
++ if (xattrs_masked_out (keyword, false /* extracting */ ))
++ /* we don't want to restore this keyword */
++ continue;
++
++ xattrs__fd_set (st, file_name, typeflag, keyword,
++ st->xattr_map[scan].xval_ptr,
++ st->xattr_map[scan].xval_len);
++ }
++#endif
++ }
++}
++
++void
++xattrs_print_char (struct tar_stat_info const *st, char *output)
++{
++ int i;
++
++ if (verbose_option < 2)
++ {
++ *output = 0;
++ return;
++ }
++
++ if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
++ {
++ /* placeholders */
++ *output = ' ';
++ output[1] = 0;
++ }
++
++ if (xattrs_option > 0 && st->xattr_map_size)
++ for (i = 0; i < st->xattr_map_size; ++i)
++ {
++ char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
++ if (!xattrs_masked_out (keyword, false /* like extracting */ ))
++ {
++ *output = '*';
++ break;
++ }
++ }
++
++ if (selinux_context_option > 0 && st->cntx_name)
++ *output = '.';
++
++ if (acls_option && (st->acls_a_len || st->acls_d_len))
++ *output = '+';
++}
++
++void
++xattrs_print (struct tar_stat_info const *st)
++{
++ if (verbose_option < 3)
++ return;
++
++ /* selinux */
++ if (selinux_context_option && st->cntx_name)
++ fprintf (stdlis, " s: %s\n", st->cntx_name);
++
++ /* acls */
++ if (acls_option && (st->acls_a_len || st->acls_d_len))
++ {
++ fprintf (stdlis, " a: ");
++ acls_one_line ("", ',', st->acls_a_ptr, st->acls_a_len);
++ acls_one_line ("default:", ',', st->acls_d_ptr, st->acls_d_len);
++ fprintf (stdlis, "\n");
++ }
++
++ /* xattrs */
++ if (xattrs_option && st->xattr_map_size)
++ {
++ int i;
++
++ for (i = 0; i < st->xattr_map_size; ++i)
++ {
++ char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
++ if (!xattrs_masked_out (keyword, false /* like extracting */ ))
++ fprintf (stdlis, " x: %lu %s\n",
++ (unsigned long) st->xattr_map[i].xval_len, keyword);
++ }
++ }
++}
+diff --git a/src/xattrs.h b/src/xattrs.h
+new file mode 100644
+index 0000000..bfef466
+--- /dev/null
++++ b/src/xattrs.h
+@@ -0,0 +1,51 @@
++/* Support for extended attributes.
++
++ Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software
++ Foundation, Inc.
++
++ Written by James Antill, on 2006-07-27.
++
++ 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, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++*/
++
++#ifndef GUARD_XATTTRS_H
++#define GUARD_XATTTRS_H
++
++/* Add include/exclude fnmatch pattern for xattr key domain. Set INCL parameter
++ to true/false if you want to add include/exclude pattern */
++extern void xattrs_mask_add (const char *mask, bool incl);
++
++/* clear helping structures when tar finishes */
++extern void xattrs_clear_setup ();
++
++extern void xattrs_acls_get (int parentfd, char const *file_name,
++ struct tar_stat_info *st, int fd, int xisfile);
++extern void xattrs_selinux_get (int parentfd, char const *file_name,
++ struct tar_stat_info *st, int fd);
++extern void xattrs_xattrs_get (int parentfd, char const *file_name,
++ struct tar_stat_info *st, int fd);
++
++extern void xattrs_acls_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag);
++extern void xattrs_selinux_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag);
++extern void xattrs_xattrs_set (struct tar_stat_info const *st,
++ char const *file_name, char typeflag,
++ int later_run);
++
++extern void xattrs_print_char (struct tar_stat_info const *st, char *output);
++extern void xattrs_print (struct tar_stat_info const *st);
++
++#endif /* GUARD_XATTTRS_H */
+diff --git a/src/xheader.c b/src/xheader.c
+index 2284e97..be793d4 100644
+--- a/src/xheader.c
++++ b/src/xheader.c
+@@ -1,7 +1,7 @@
+ /* POSIX extended headers for tar.
+
+- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software
+- Foundation, Inc.
++ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012
++ 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
+@@ -460,6 +460,123 @@ xheader_write_global (struct xheader *xhdr)
+ }
+ }
+
++void
++xheader_xattr_init (struct tar_stat_info *st)
++{
++ st->xattr_map = NULL;
++ st->xattr_map_size = 0;
++
++ st->acls_a_ptr = NULL;
++ st->acls_a_len = 0;
++ st->acls_d_ptr = NULL;
++ st->acls_d_len = 0;
++ st->cntx_name = NULL;
++}
++
++void
++xheader_xattr_free (struct xattr_array *xattr_map, size_t xattr_map_size)
++{
++ size_t scan = 0;
++
++ while (scan < xattr_map_size)
++ {
++ free (xattr_map[scan].xkey);
++ free (xattr_map[scan].xval_ptr);
++
++ ++scan;
++ }
++ free (xattr_map);
++}
++
++static void
++xheader_xattr__add (struct xattr_array **xattr_map,
++ size_t *xattr_map_size,
++ const char *key, const char *val, size_t len)
++{
++ size_t pos = (*xattr_map_size)++;
++
++ *xattr_map = xrealloc (*xattr_map,
++ *xattr_map_size * sizeof(struct xattr_array));
++ (*xattr_map)[pos].xkey = xstrdup (key);
++ (*xattr_map)[pos].xval_ptr = xmemdup (val, len + 1);
++ (*xattr_map)[pos].xval_len = len;
++}
++
++/* This is reversal function for xattr_encode_keyword. See comment for
++ xattr_encode_keyword() for more info. */
++static void
++xattr_decode_keyword (char *keyword)
++{
++ char *kpr, *kpl; /* keyword pointer left/right */
++ kpr = kpl = keyword;
++
++ for (;;)
++ {
++ if (*kpr == '%')
++ {
++ if (kpr[1] == '3' && kpr[2] == 'D')
++ {
++ *kpl = '=';
++ kpr += 3;
++ kpl ++;
++ continue;
++ }
++ else if (kpr[1] == '2' && kpr[2] == '5')
++ {
++ *kpl = '%';
++ kpr += 3;
++ kpl ++;
++ continue;
++ }
++ }
++
++ *kpl = *kpr;
++
++ if (*kpr == 0)
++ break;
++
++ kpr++;
++ kpl++;
++ }
++}
++
++void
++xheader_xattr_add (struct tar_stat_info *st,
++ const char *key, const char *val, size_t len)
++{
++ size_t klen = strlen (key);
++ char *xkey = xmalloc (strlen("SCHILY.xattr.") + klen + 1);
++ char *tmp = xkey;
++
++ tmp = stpcpy (tmp, "SCHILY.xattr.");
++ stpcpy (tmp, key);
++
++ xheader_xattr__add (&st->xattr_map, &st->xattr_map_size, xkey, val, len);
++
++ free (xkey);
++}
++
++void
++xheader_xattr_copy (const struct tar_stat_info *st,
++ struct xattr_array **xattr_map, size_t *xattr_map_size)
++{
++ size_t scan = 0;
++
++ *xattr_map = NULL;
++ *xattr_map_size = 0;
++
++ while (scan < st->xattr_map_size)
++ {
++ char *key = st->xattr_map[scan].xkey;
++ char *val = st->xattr_map[scan].xval_ptr;
++ size_t len = st->xattr_map[scan].xval_len;
++
++ xheader_xattr__add(xattr_map, xattr_map_size, key, val, len);
++
++ ++scan;
++ }
++}
++
+
+ /* General Interface */
+
+@@ -473,6 +590,7 @@ struct xhdr_tab
+ struct xheader *, void const *data);
+ void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
+ int flags;
++ bool prefix; /* select handler comparing prefix only */
+ };
+
+ /* This declaration must be extern, because ISO C99 section 6.9.2
+@@ -489,8 +607,17 @@ locate_handler (char const *keyword)
+ struct xhdr_tab const *p;
+
+ for (p = xhdr_tab; p->keyword; p++)
+- if (strcmp (p->keyword, keyword) == 0)
+- return p;
++ if (p->prefix)
++ {
++ if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0)
++ return p;
++ }
++ else
++ {
++ if (strcmp (p->keyword, keyword) == 0)
++ return p;
++ }
++
+ return NULL;
+ }
+
+@@ -500,7 +627,8 @@ xheader_protected_pattern_p (const char *pattern)
+ struct xhdr_tab const *p;
+
+ for (p = xhdr_tab; p->keyword; p++)
+- if ((p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
++ if (!p->prefix && (p->flags & XHDR_PROTECTED)
++ && fnmatch (pattern, p->keyword, 0) == 0)
+ return true;
+ return false;
+ }
+@@ -511,7 +639,8 @@ xheader_protected_keyword_p (const char *keyword)
+ struct xhdr_tab const *p;
+
+ for (p = xhdr_tab; p->keyword; p++)
+- if ((p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
++ if (!p->prefix && (p->flags & XHDR_PROTECTED)
++ && strcmp (p->keyword, keyword) == 0)
+ return true;
+ return false;
+ }
+@@ -721,15 +850,71 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size)
+ while (size > 0);
+ }
+
++/* xattr_encode_keyword() substitutes '=' ~~> '%3D' and '%' ~~> '%25'
++ in extended attribute keywords. This is needed because the '=' character
++ has special purpose in extended attribute header - it splits keyword and
++ value part of header. If there was the '=' occurrence allowed inside
++ keyword, there would be no unambiguous way how to decode this extended
++ attribute.
++
++ (http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html)
++ */
++static char *
++xattr_encode_keyword(const char *keyword)
++{
++ static char *encode_buffer = NULL;
++ static size_t encode_buffer_size = 0;
++ size_t bp; /* keyword/buffer pointers */
++
++ if (!encode_buffer)
++ {
++ encode_buffer_size = 256;
++ encode_buffer = xmalloc (encode_buffer_size);
++ }
++ else
++ *encode_buffer = 0;
++
++ for (bp = 0; *keyword != 0; ++bp, ++keyword)
++ {
++ char c = *keyword;
++
++ if (bp + 2 /* enough for URL encoding also.. */ >= encode_buffer_size)
++ {
++ encode_buffer = x2realloc (encode_buffer, &encode_buffer_size);
++ }
++
++ if (c == '%')
++ {
++ strcpy (encode_buffer + bp, "%25");
++ bp += 2;
++ }
++ else if (c == '=')
++ {
++ strcpy (encode_buffer + bp, "%3D");
++ bp += 2;
++ }
++ else
++ encode_buffer[bp] = c;
++ }
++
++ encode_buffer[bp] = 0;
++
++ return encode_buffer;
++}
++
+ static void
+ xheader_print_n (struct xheader *xhdr, char const *keyword,
+ char const *value, size_t vsize)
+ {
+- size_t len = strlen (keyword) + vsize + 3; /* ' ' + '=' + '\n' */
+ size_t p;
+ size_t n = 0;
+ char nbuf[UINTMAX_STRSIZE_BOUND];
+ char const *np;
++ size_t len, klen;
++
++ keyword = xattr_encode_keyword (keyword);
++ klen = strlen (keyword);
++ len = klen + vsize + 3; /* ' ' + '=' + '\n' */
+
+ do
+ {
+@@ -741,7 +926,7 @@ xheader_print_n (struct xheader *xhdr, char const *keyword,
+
+ x_obstack_grow (xhdr, np, n);
+ x_obstack_1grow (xhdr, ' ');
+- x_obstack_grow (xhdr, keyword, strlen (keyword));
++ x_obstack_grow (xhdr, keyword, klen);
+ x_obstack_1grow (xhdr, '=');
+ x_obstack_grow (xhdr, value, vsize);
+ x_obstack_1grow (xhdr, '\n');
+@@ -1002,8 +1187,6 @@ decode_time (struct timespec *ts, char const *arg, char const *keyword)
+ return true;
+ }
+
+-
+-
+ static void
+ code_num (uintmax_t value, char const *keyword, struct xheader *xhdr)
+ {
+@@ -1470,6 +1653,80 @@ volume_filename_decoder (struct tar_stat_info *st,
+ }
+
+ static void
++xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword,
++ struct xheader *xhdr, void const *data)
++{
++ code_string (st->cntx_name, keyword, xhdr);
++}
++
++static void
++xattr_selinux_decoder (struct tar_stat_info *st,
++ char const *keyword, char const *arg, size_t size)
++{
++ decode_string (&st->cntx_name, arg);
++}
++
++static void
++xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword,
++ struct xheader *xhdr, void const *data)
++{
++ xheader_print_n (xhdr, keyword, st->acls_a_ptr, st->acls_a_len);
++}
++
++static void
++xattr_acls_a_decoder (struct tar_stat_info *st,
++ char const *keyword, char const *arg, size_t size)
++{
++ st->acls_a_ptr = xmemdup (arg, size + 1);
++ st->acls_a_len = size;
++}
++
++static void
++xattr_acls_d_coder (struct tar_stat_info const *st , char const *keyword,
++ struct xheader *xhdr, void const *data)
++{
++ xheader_print_n (xhdr, keyword, st->acls_d_ptr, st->acls_d_len);
++}
++
++static void
++xattr_acls_d_decoder (struct tar_stat_info *st,
++ char const *keyword, char const *arg, size_t size)
++{
++ st->acls_d_ptr = xmemdup (arg, size + 1);
++ st->acls_d_len = size;
++}
++
++static void
++xattr_coder (struct tar_stat_info const *st, char const *keyword,
++ struct xheader *xhdr, void const *data)
++{
++ struct xattr_array *xattr_map = st->xattr_map;
++ const size_t *off = data;
++ xheader_print_n (xhdr, keyword,
++ xattr_map[*off].xval_ptr, xattr_map[*off].xval_len);
++}
++
++static void
++xattr_decoder (struct tar_stat_info *st,
++ char const *keyword, char const *arg, size_t size)
++{
++ char *xstr, *xkey;
++
++ /* copy keyword */
++ size_t klen_raw = strlen (keyword);
++ xkey = alloca (klen_raw + 1);
++ memcpy (xkey, keyword, klen_raw + 1) /* including null-terminating */;
++
++ /* copy value */
++ xstr = alloca (size + 1);
++ memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */;
++
++ xattr_decode_keyword (xkey);
++
++ xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size);
++}
++
++static void
+ sparse_major_coder (struct tar_stat_info const *st, char const *keyword,
+ struct xheader *xhdr, void const *data)
+ {
+@@ -1506,53 +1763,53 @@ sparse_minor_decoder (struct tar_stat_info *st,
+ }
+
+ struct xhdr_tab const xhdr_tab[] = {
+- { "atime", atime_coder, atime_decoder, 0 },
+- { "comment", dummy_coder, dummy_decoder, 0 },
+- { "charset", dummy_coder, dummy_decoder, 0 },
+- { "ctime", ctime_coder, ctime_decoder, 0 },
+- { "gid", gid_coder, gid_decoder, 0 },
+- { "gname", gname_coder, gname_decoder, 0 },
+- { "linkpath", linkpath_coder, linkpath_decoder, 0 },
+- { "mtime", mtime_coder, mtime_decoder, 0 },
+- { "path", path_coder, path_decoder, 0 },
+- { "size", size_coder, size_decoder, 0 },
+- { "uid", uid_coder, uid_decoder, 0 },
+- { "uname", uname_coder, uname_decoder, 0 },
++ { "atime", atime_coder, atime_decoder, 0, false },
++ { "comment", dummy_coder, dummy_decoder, 0, false },
++ { "charset", dummy_coder, dummy_decoder, 0, false },
++ { "ctime", ctime_coder, ctime_decoder, 0, false },
++ { "gid", gid_coder, gid_decoder, 0, false },
++ { "gname", gname_coder, gname_decoder, 0, false },
++ { "linkpath", linkpath_coder, linkpath_decoder, 0, false },
++ { "mtime", mtime_coder, mtime_decoder, 0, false },
++ { "path", path_coder, path_decoder, 0, false },
++ { "size", size_coder, size_decoder, 0, false },
++ { "uid", uid_coder, uid_decoder, 0, false },
++ { "uname", uname_coder, uname_decoder, 0, false },
+
+ /* Sparse file handling */
+ { "GNU.sparse.name", path_coder, path_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ { "GNU.sparse.major", sparse_major_coder, sparse_major_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ { "GNU.sparse.minor", sparse_minor_coder, sparse_minor_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ { "GNU.sparse.realsize", sparse_size_coder, sparse_size_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ { "GNU.sparse.numblocks", sparse_numblocks_coder, sparse_numblocks_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+
+ /* tar 1.14 - 1.15.90 keywords. */
+ { "GNU.sparse.size", sparse_size_coder, sparse_size_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
+ headers, and each of them was meaningful. It confilcted with POSIX specs,
+ which requires that "when extended header records conflict, the last one
+ given in the header shall take precedence." */
+ { "GNU.sparse.offset", sparse_offset_coder, sparse_offset_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ { "GNU.sparse.numbytes", sparse_numbytes_coder, sparse_numbytes_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+ /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
+ { "GNU.sparse.map", NULL /* Unused, see pax_dump_header() */,
+- sparse_map_decoder, 0 },
++ sparse_map_decoder, 0, false },
+
+ { "GNU.dumpdir", dumpdir_coder, dumpdir_decoder,
+- XHDR_PROTECTED },
++ XHDR_PROTECTED, false },
+
+ /* Keeps the tape/volume label. May be present only in the global headers.
+ Equivalent to GNUTYPE_VOLHDR. */
+ { "GNU.volume.label", volume_label_coder, volume_label_decoder,
+- XHDR_PROTECTED | XHDR_GLOBAL },
++ XHDR_PROTECTED | XHDR_GLOBAL, false },
+
+ /* These may be present in a first global header of the archive.
+ They provide the same functionality as GNUTYPE_MULTIVOL header.
+@@ -1561,11 +1818,28 @@ struct xhdr_tab const xhdr_tab[] = {
+ GNU.volume.offset keeps the offset of the start of this volume,
+ otherwise kept in oldgnu_header.offset. */
+ { "GNU.volume.filename", volume_label_coder, volume_filename_decoder,
+- XHDR_PROTECTED | XHDR_GLOBAL },
++ XHDR_PROTECTED | XHDR_GLOBAL, false },
+ { "GNU.volume.size", volume_size_coder, volume_size_decoder,
+- XHDR_PROTECTED | XHDR_GLOBAL },
++ XHDR_PROTECTED | XHDR_GLOBAL, false },
+ { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder,
+- XHDR_PROTECTED | XHDR_GLOBAL },
++ XHDR_PROTECTED | XHDR_GLOBAL, false },
++
++ /* We get the SELinux value from filecon, so add a namespace for SELinux
++ instead of storing it in SCHILY.xattr.* (which would be RAW). */
++ { "RHT.security.selinux",
++ xattr_selinux_coder, xattr_selinux_decoder, 0, false },
++
++ /* ACLs, use the star format... */
++ { "SCHILY.acl.access",
++ xattr_acls_a_coder, xattr_acls_a_decoder, 0, false },
++
++ { "SCHILY.acl.default",
++ xattr_acls_d_coder, xattr_acls_d_decoder, 0, false },
++
++ /* We are storing all extended attributes using this rule even if some of them
++ were stored by some previous rule (duplicates) -- we just have to make sure
++ they are restored *only once* during extraction later on. */
++ { "SCHILY.xattr", xattr_coder, xattr_decoder, 0, true },
+
+- { NULL, NULL, NULL, 0 }
++ { NULL, NULL, NULL, 0, false }
+ };
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 3d78ea2..b0da439 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -171,7 +171,17 @@ TESTSUITE_AT = \
+ star/multi-fail.at\
+ star/ustar-big-2g.at\
+ star/ustar-big-8g.at\
+- star/pax-big-10g.at
++ star/pax-big-10g.at\
++ xattr01.at\
++ xattr02.at\
++ xattr03.at\
++ xattr04.at\
++ xattr05.at\
++ acls01.at\
++ acls02.at\
++ selnx01.at\
++ selacl01.at\
++ capabs_raw01.at
+
+ TESTSUITE = $(srcdir)/testsuite
+
+diff --git a/tests/acls01.at b/tests/acls01.at
+new file mode 100644
+index 0000000..0149f2d
+--- /dev/null
++++ b/tests/acls01.at
+@@ -0,0 +1,53 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2011 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/>.
++#
++# Test description:
++#
++# This is basic test for acl support.
++
++AT_SETUP([acls: basic functionality])
++AT_KEYWORDS([xattrs acls acls01])
++
++AT_TAR_CHECK([
++AT_XATTRS_UTILS_PREREQ
++AT_ACLS_PREREQ
++
++mkdir dir
++genfile --file dir/file
++
++MYNAME=$( id -un )
++
++setfacl -m u:$MYNAME:--x dir/file
++setfacl -m u:$MYNAME:--x dir
++
++getfattr -h -m. -d dir dir/file > before
++
++tar --acls -cf archive.tar dir
++rm -rf dir
++
++tar --acls -xf archive.tar
++
++getfattr -h -m. -d dir dir/file > after
++
++diff before after
++test "$?" = 0
++],
++[0],
++[])
++
++AT_CLEANUP
+diff --git a/tests/acls02.at b/tests/acls02.at
+new file mode 100644
+index 0000000..2ee1c5f
+--- /dev/null
++++ b/tests/acls02.at
+@@ -0,0 +1,59 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2011 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/>.
++#
++# Test description:
++#
++# This is basic test for acl support.
++
++AT_SETUP([acls: work with -C])
++AT_KEYWORDS([xattrs acls acls02])
++
++AT_TAR_CHECK([
++AT_XATTRS_UTILS_PREREQ
++AT_ACLS_PREREQ
++
++mkdir dir
++mkdir dir/subdir
++genfile --file dir/subdir/file
++
++MYNAME=$( id -un )
++
++setfacl -m u:$MYNAME:--x dir/subdir
++setfacl -m u:$MYNAME:--x dir/subdir/file
++
++cd dir
++getfattr -h -m. -d subdir subdir/file > ../before
++cd ..
++
++tar --acls -cf archive.tar -C dir subdir
++rm -rf dir
++
++mkdir dir
++tar --acls -xf archive.tar -C dir
++
++cd dir
++getfattr -h -m. -d subdir subdir/file > ../after
++cd ..
++
++diff before after
++test "$?" = 0
++],
++[0],
++[])
++
++AT_CLEANUP
+diff --git a/tests/capabs_raw01.at b/tests/capabs_raw01.at
+new file mode 100644
+index 0000000..8eea0cf
+--- /dev/null
++++ b/tests/capabs_raw01.at
+@@ -0,0 +1,51 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2012 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/>.
++#
++# Test description: Test if file capabilities are archived/restored correctly
++# using just the default xattr support (capabilities are stored/restored in
++# binary format -> system dependant).
++
++AT_SETUP([capabilities: binary store/restore])
++AT_KEYWORDS([xattrs capabilities capabs_raw01])
++
++AT_TAR_CHECK([
++AT_PRIVILEGED_PREREQ
++AT_XATTRS_PREREQ
++AT_CAPABILITIES_UTILS_PREREQ
++
++mkdir dir
++genfile --file dir/file
++
++setcap "= cap_chown=ei" dir/file
++
++# archive whole directory including binary xattrs
++tar --xattrs -cf archive.tar dir
++
++# clear the directory
++rm -rf dir
++
++# restore _all_ xattrs (not just the user.* domain)
++tar --xattrs --xattrs-include='*' -xf archive.tar
++
++getcap dir/file
++],
++[0],
++[dir/file = cap_chown+ei
++])
++
++AT_CLEANUP
+diff --git a/tests/selacl01.at b/tests/selacl01.at
+new file mode 100644
+index 0000000..90d0c5b
+--- /dev/null
++++ b/tests/selacl01.at
+@@ -0,0 +1,64 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2011 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/>.
++#
++# Test description:
++#
++# This is basic test for support of extended attributes.
++
++AT_SETUP([acls/selinux: special files & fifos])
++AT_KEYWORDS([xattrs selinux acls selacls01])
++
++AT_TAR_CHECK([
++AT_PRIVILEGED_PREREQ
++AT_XATTRS_UTILS_PREREQ
++AT_SELINUX_PREREQ
++AT_ACLS_PREREQ
++
++mkdir dir
++mkfifo dir/fifo
++MAJOR=$( stat /dev/urandom --printf="%t" )
++MINOR=$( stat /dev/urandom --printf="%T" )
++mknod dir/chartype c $MAJOR $MINOR
++
++# setup attributes
++restorecon -R dir
++chcon -h --user=system_u dir/fifo
++chcon -h --user=system_u dir/chartype
++setfacl -m u:$UID:--- dir/fifo
++setfacl -m u:$UID:rwx dir/chartype
++
++getfacl dir/fifo >> before
++getfattr -msecurity.selinux dir/chartype >> before
++
++tar --xattrs --selinux --acls -cf archive.tar dir
++
++mv dir olddir
++
++tar --xattrs --selinux --acls -xf archive.tar
++
++getfacl dir/fifo >> after
++getfattr -msecurity.selinux dir/chartype >> after
++
++diff before after
++echo separator
++],
++[0],
++[separator
++])
++
++AT_CLEANUP
+diff --git a/tests/selnx01.at b/tests/selnx01.at
+new file mode 100644
+index 0000000..79f7267
+--- /dev/null
++++ b/tests/selnx01.at
+@@ -0,0 +1,96 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2012 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/>.
++#
++# Test description:
++#
++# This is basic test for selinux support (store & restore).
++
++AT_SETUP([selinux: basic store/restore])
++AT_KEYWORDS([xattrs selinux selnx01])
++
++AT_TAR_CHECK([
++AT_XATTRS_UTILS_PREREQ
++AT_SELINUX_PREREQ
++
++mkdir dir
++genfile --file dir/file
++ln -s file dir/link
++
++getfattr -h -d -msecurity.selinux dir dir/file dir/link > start
++
++restorecon -R dir
++chcon -h --user=system_u dir
++chcon -h --user=unconfined_u dir/file
++chcon -h --user=system_u dir/link
++
++# archive whole directory including selinux contexts
++tar --selinux -cf archive.tar dir
++
++# clear the directory
++rm -rf dir
++
++# ================================================
++# check if selinux contexts are correctly restored
++
++tar --selinux -xf archive.tar
++
++# archive for later debugging
++cp archive.tar archive_origin.tar
++
++# check if selinux contexts were restored
++getfattr -h -d dir dir/file dir/link -msecurity.selinux | \
++ grep -v -e '^#' -e ^$ | cut -d: -f1
++
++# ===========================================================================
++# check if selinux contexts are not restored when --selinux option is missing
++
++getfattr -h -d -msecurity.selinux dir dir/file dir/link > with_selinux
++rm -rf dir
++tar -xf archive.tar
++getfattr -h -d -msecurity.selinux dir dir/file dir/link > without_selinux
++
++diff with_selinux without_selinux > diff_with_without
++if test "$?" -eq "0"; then
++ echo "selinux contexts probably restored while --selinux is off"
++fi
++
++# =================================================================
++# check if selinux is not archived when --selinux option is missing
++
++tar -cf archive.tar dir
++
++# clear the directory
++rm -rf dir
++
++# restore (with --selinux)
++tar --selinux -xf archive.tar dir
++
++getfattr -h -d -msecurity.selinux dir dir/file dir/link > final
++diff start final > final_diff
++if test "$?" -ne "0"; then
++ echo "bad result"
++fi
++
++],
++[0],
++[security.selinux="system_u
++security.selinux="unconfined_u
++security.selinux="system_u
++])
++
++AT_CLEANUP
+diff --git a/tests/testsuite.at b/tests/testsuite.at
+index e43653e..8d5811d 100644
+--- a/tests/testsuite.at
++++ b/tests/testsuite.at
+@@ -81,13 +81,6 @@ m4_define([AT_GZIP_PREREQ],[
+ cat /dev/null | m4_if([$1],[],gzip,[$1]) - > /dev/null 2>&1 || AT_SKIP_TEST
+ ])
+
+-dnl AT_SIGPIPE_PREREQ - Skip test unless SIGPIPE handling is the default
+-m4_define([AT_SIGPIPE_PREREQ],[
+-case `(cat "$at_myself" 2>&3 | :) 3>&1 >/dev/null` in #(
+-?*) AT_SKIP_TEST;;
+-esac
+-])
+-
+ dnl AT_SORT_PREREQ - Skip test if sort utility outputs unwanted data on stderr
+ m4_define([AT_SORT_PREREQ],[
+ test -z "`sort < /dev/null 2>&1`" || AT_SKIP_TEST
+@@ -103,10 +96,86 @@ rm -f $[]$
+ test $result -eq 0 && AT_SKIP_TEST
+ ])
+
++dnl AT_SIGPIPE_PREREQ - Skip test unless SIGPIPE handling is the default
++m4_define([AT_SIGPIPE_PREREQ],[
++case `(cat "$at_myself" 2>&3 | :) 3>&1 >/dev/null` in #(
++?*) AT_SKIP_TEST;;
++esac
++])
++
++dnl AT_PRIVILEGED_PREREQ - Skip test if not running at root privileges
++m4_define([AT_PRIVILEGED_PREREQ],[
++echo "test" > $[]$
++chmod 0 $[]$
++cat $[]$ > /dev/null 2>&1
++result=$?
++rm -f $[]$
++test $result -eq 0 || AT_SKIP_TEST
++])
++
+ m4_define([AT_TAR_MKHIER],[
+ install-sh -d $1 >/dev/null dnl
+ m4_if([$2],,,&& genfile --file [$1]/[$2]) || AT_SKIP_TEST])
+
++dnl Skip test when utlity does not return expected return value
++m4_define([AT_CHECK_UTIL],[
++ $1 &> /dev/null
++ if test "$?" != $2; then
++ AT_SKIP_TEST
++ fi
++])
++
++m4_define([AT_XATTRS_UTILS_PREREQ],[
++ file=$( mktemp -p . )
++ AT_CHECK_UTIL(setfattr -n user.test -v test $file,0)
++ AT_CHECK_UTIL(getfattr $file,0)
++])
++m4_define([AT_SELINUX_UTILS_PREREQ],[
++ file=$( mktemp -p . )
++ AT_CHECK_UTIL(restorecon $file, 0)
++ AT_CHECK_UTIL(chcon -h --user=unconfined_u $file,0)
++ rm -rf $file
++])
++m4_define([AT_ACLS_UTILS_PREREQ],[
++ file=$( mktemp -p . )
++ AT_CHECK_UTIL(setfacl -m u:$UID:rwx $file,0)
++ AT_CHECK_UTIL(getfacl $file,0)
++ rm -rf $file
++])
++m4_define([AT_CAPABILITIES_UTILS_PREREQ],[
++ file=$( mktemp -p . )
++ AT_CHECK_UTIL(setcap "= cap_chown=ei" $file,0)
++ AT_CHECK_UTIL(getcap $file,0)
++ rm -rf $file
++])
++m4_define([AT_XATTRS_PREREQ],[
++ AT_XATTRS_UTILS_PREREQ
++ file=$( mktemp -p . )
++ setfattr -n user.test -v ahoj $file
++ # check whether tar fails to store xattrs
++ err=$( tar --xattrs -cf /dev/null $file 2>&1 >/dev/null | wc -l )
++ if test "$err" != "0"; then
++ AT_SKIP_TEST
++ fi
++])
++m4_define([AT_SELINUX_PREREQ],[
++ AT_SELINUX_UTILS_PREREQ
++ file=$( mktemp -p . )
++ err=$( tar --selinux -cf /dev/null $file 2>&1 >/dev/null | wc -l )
++ if test "$err" != "0"; then
++ AT_SKIP_TEST
++ fi
++])
++m4_define([AT_ACLS_PREREQ],[
++ AT_ACLS_UTILS_PREREQ
++ file=$( mktemp -p . )
++ setfacl -m u:$UID:rwx $file
++ err=$( tar --acls -cf /dev/null $file 2>&1 >/dev/null | wc -l )
++ if test "$err" != "0"; then
++ AT_SKIP_TEST
++ fi
++])
++
+ m4_include([sparsemvp.at])
+
+ AT_INIT
+@@ -264,6 +334,20 @@ m4_include([remfiles03.at])
+
+ m4_include([sigpipe.at])
+
++m4_include([xattr01.at])
++m4_include([xattr02.at])
++m4_include([xattr03.at])
++m4_include([xattr04.at])
++m4_include([xattr05.at])
++
++m4_include([acls01.at])
++m4_include([acls02.at])
++
++m4_include([selnx01.at])
++m4_include([selacl01.at])
++
++m4_include([capabs_raw01.at])
++
+ m4_include([star/gtarfail.at])
+ m4_include([star/gtarfail2.at])
+
+@@ -273,3 +357,4 @@ m4_include([star/ustar-big-2g.at])
+ m4_include([star/ustar-big-8g.at])
+
+ m4_include([star/pax-big-10g.at])
++
+diff --git a/tests/xattr01.at b/tests/xattr01.at
+new file mode 100644
+index 0000000..fd960d5
+--- /dev/null
++++ b/tests/xattr01.at
+@@ -0,0 +1,47 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2011 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/>.
++#
++# Test description:
++#
++# This is basic test for support of extended attributes.
++
++AT_SETUP([xattrs: basic functionality])
++AT_KEYWORDS([xattrs xattr01])
++
++AT_TAR_CHECK([
++AT_XATTRS_PREREQ
++mkdir dir
++genfile --file dir/file
++
++setfattr -n user.test -v OurDirValue dir
++setfattr -n user.test -v OurFileValue dir/file
++
++tar --xattrs -cf archive.tar dir
++
++rm -rf dir
++tar --xattrs -xf archive.tar
++
++getfattr -h -d dir | grep -v -e '^#' -e ^$
++getfattr -h -d dir/file | grep -v -e '^#' -e ^$
++],
++[0],
++[user.test="OurDirValue"
++user.test="OurFileValue"
++])
++
++AT_CLEANUP
+diff --git a/tests/xattr02.at b/tests/xattr02.at
+new file mode 100644
+index 0000000..3aae3f9
+--- /dev/null
++++ b/tests/xattr02.at
+@@ -0,0 +1,55 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2011 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/>.
++#
++# Test description:
++#
++# Cooperation of the '-C' option and storing/restoring extended attributes.
++
++AT_SETUP([xattrs: change directory with -C option])
++AT_KEYWORDS([xattrs xattr02])
++
++AT_TAR_CHECK([
++AT_XATTRS_PREREQ
++
++mkdir dir
++mkdir dir/subdir
++mkdir dir/subdir/subsubdir
++genfile --file dir/file1
++genfile --file dir/subdir/file2
++
++setfattr -n user.test -v OurFile1Value dir/file1
++setfattr -n user.test -v OurFile2Value dir/subdir/file2
++setfattr -n user.test -v OurDirValue dir/subdir/subsubdir
++
++tar --xattrs -cf archive.tar -C dir file1 -C subdir file2 subsubdir
++
++rm -rf dir
++
++tar --xattrs -xf archive.tar
++
++getfattr -h -d file1 | grep -v -e '^#' -e ^$
++getfattr -h -d file2 | grep -v -e '^#' -e ^$
++getfattr -h -d subsubdir | grep -v -e '^#' -e ^$
++],
++[0],
++[user.test="OurFile1Value"
++user.test="OurFile2Value"
++user.test="OurDirValue"
++])
++
++AT_CLEANUP
+diff --git a/tests/xattr03.at b/tests/xattr03.at
+new file mode 100644
+index 0000000..d834f9f
+--- /dev/null
++++ b/tests/xattr03.at
+@@ -0,0 +1,56 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2012 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/>.
++#
++# Test description:
++#
++# Setup of the trusted.* domain under privileged user.
++
++AT_SETUP([xattrs: trusted.* attributes])
++AT_KEYWORDS([xattrs xattr03])
++
++AT_TAR_CHECK([
++AT_PRIVILEGED_PREREQ
++AT_XATTRS_PREREQ
++
++mkdir dir
++mkdir dir/subdir
++mkdir dir/subdir/subsubdir
++genfile --file dir/file1
++genfile --file dir/subdir/file2
++
++setfattr -n trusted.test -v OurFile1Value dir/file1
++setfattr -n trusted.test -v OurFile2Value dir/subdir/file2
++setfattr -n trusted.test -v OurDirValue dir/subdir/subsubdir
++
++tar --xattrs -cf archive.tar -C dir file1 -C subdir file2 subsubdir
++
++rm -rf dir
++
++tar --xattrs --xattrs-include=trusted* -xf archive.tar
++
++getfattr -mtrusted. -d file1 | grep -v -e '^#' -e ^$
++getfattr -mtrusted. -d file2 | grep -v -e '^#' -e ^$
++getfattr -mtrusted. -d subsubdir | grep -v -e '^#' -e ^$
++],
++[0],
++[trusted.test="OurFile1Value"
++trusted.test="OurFile2Value"
++trusted.test="OurDirValue"
++])
++
++AT_CLEANUP
+diff --git a/tests/xattr04.at b/tests/xattr04.at
+new file mode 100644
+index 0000000..31832af
+--- /dev/null
++++ b/tests/xattr04.at
+@@ -0,0 +1,48 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2012 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/>.
++#
++# Test description: Test for the regression caused by tar update from 1.23 to
++# 1.26, Red Hat xattr patch was not ready for open->openat conversion.
++#
++# Related commit 4bde4f3. See the bug: https://bugzilla.redhat.com/717684
++
++AT_SETUP([xattrs: s/open/openat/ regression])
++AT_KEYWORDS([xattrs xattr04])
++
++AT_TAR_CHECK([
++AT_XATTRS_PREREQ
++
++mkdir dir
++mkdir output
++genfile --file dir/file
++
++setfattr -n user.test -v value dir/file
++
++# archive whole directory including binary xattrs
++tar --xattrs -cf archive.tar -C dir .
++
++tar --xattrs -xf archive.tar -C output
++ret=$?
++getfattr -h -d output/file | grep -v -e '^#' -e ^$
++exit $ret
++],
++[0],
++[user.test="value"
++])
++
++AT_CLEANUP
+diff --git a/tests/xattr05.at b/tests/xattr05.at
+new file mode 100644
+index 0000000..27dc469
+--- /dev/null
++++ b/tests/xattr05.at
+@@ -0,0 +1,49 @@
++# Process this file with autom4te to create testsuite. -*- Autotest -*-
++#
++# Test suite for GNU tar.
++# Copyright (C) 2012 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/>.
++#
++# Test description: Test for archiving/extracting of extended attributes
++# having the '=' character in its keyword.
++#
++# Relevant mailing list thread:
++#
++# http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html
++
++AT_SETUP([xattrs: keywords with '=' and '%'])
++AT_KEYWORDS([xattrs xattr05])
++
++AT_TAR_CHECK([
++AT_XATTRS_PREREQ
++
++mkdir dir
++mkdir output
++genfile --file dir/file
++
++setfattr -n user.=NAME%3D= -v value dir/file
++getfattr -d dir/file | grep -v '# ' > before
++
++# archive whole directory including binary xattrs
++tar --xattrs -cf archive.tar -C dir .
++
++tar --xattrs -xf archive.tar -C output
++getfattr -d output/file | grep -v '# ' > after
++diff before after
++],
++[0],
++[])
++
++AT_CLEANUP
diff --git a/tar.spec b/tar.spec
index 07ff853..552d4c0 100644
--- a/tar.spec
+++ b/tar.spec
@@ -61,6 +61,15 @@ Patch7: tar-1.26-stdio.in.patch
# ~> upstream (7a5a3708c)
Patch8: tar-1.26-add-skip-old-files-option.patch
+# Prepare included gnulib library for SELinux support.
+# -> Related to the next patch.
+Patch9: tar-1.26-selinux-gnulib.patch
+
+# Add support for extended attributes, SELinux and POSIX ACLs.
+# ~> Original implementation #200925
+# ~> http://lists.gnu.org/archive/html/bug-tar/2012-08/msg00012.html
+# ~> upstream (b997c90f9, 696338043, d36f5a3cc, 085cace18, up-to ~> 83701a590)
+Patch10: tar-1.26-xattrs.patch
# run "make check" by default
%bcond_without check
@@ -101,6 +110,8 @@ the rmt package.
%patch6 -p1 -b .update_and_changedir
%patch7 -p1 -b .gets %{?_rawbuild}
%patch8 -p1 -b .skip-old-files
+%patch9 -p1 -b .selinux-gnulib-prep
+%patch10 -p1 -b .xattrs-selinux-acls
autoreconf -v
@@ -170,6 +181,7 @@ fi
- remove patches which we don't need now (xattrs - will be updated, sigpipe -
test should work now, partial revert of *at() conversion was done because of
incompatible xattr patch)
+- add upstream up2date xattr patch
* Fri Feb 01 2013 Pavel Raiskup <praiskup at redhat.com> - 2:1.26-16
- make the info documentation more visible in manpage (#903666)
More information about the scm-commits
mailing list