Blame SOURCES/tar-1.26-xattrs.patch

a4d85a
diff --git a/NEWS b/NEWS
a4d85a
index 12c1dd6..8aeae33 100644
a4d85a
--- a/NEWS
a4d85a
+++ b/NEWS
a4d85a
@@ -1,4 +1,4 @@
a4d85a
-GNU tar NEWS - User visible changes. 2011-03-12
a4d85a
+GNU tar NEWS - User visible changes. 2012-11-19
a4d85a
 Please send GNU tar bug reports to <bug-tar@gnu.org>
a4d85a
 
a4d85a
 
a4d85a
@@ -22,6 +22,17 @@ zero-sized files.
a4d85a
 When invoked with these two options, tar 1.25 would add only the
a4d85a
 top-level directory to the archive, but not its contents.
a4d85a
 
a4d85a
+* Support for POSIX ACLs, extended attributes and SELinux context.
a4d85a
+
a4d85a
+Starting with this version tar is able to store, extract and list
a4d85a
+extended file attributes, POSIX.1e ACLs and SELinux context.  This is
a4d85a
+controlled by the command line options --xattrs, --acls and --selinux,
a4d85a
+correspondingly.  Each of these options has a `--no-' counterpart
a4d85a
+(e.g. --no-xattrs), which disables the corresponding feature.
a4d85a
+Additionally, the options --xattrs-include and --xattrs-exclude allow
a4d85a
+you to selectively control for which files to store (or extract) the
a4d85a
+extended attributes.
a4d85a
+
a4d85a
 
a4d85a
 version 1.25 - Sergey Poznyakoff, 2010-11-07
a4d85a
 
a4d85a
diff --git a/THANKS b/THANKS
a4d85a
index 0364c50..54c96a0 100644
a4d85a
--- a/THANKS
a4d85a
+++ b/THANKS
a4d85a
@@ -296,6 +296,7 @@ Kimmy Posey		kimmyd@bnr.ca
a4d85a
 Koji Kishi		kis@rqa.sony.co.jp
a4d85a
 Konno Hiroharu		konno@pac.co.jp
a4d85a
 Kurt Jaeger		pi@lf.net
a4d85a
+James Antill            jantill@redhat.com
a4d85a
 Larry Creech		lcreech@lonestar.rcclub.org
a4d85a
 Larry Schwimmer		rosebud@cyclone.stanford.edu
a4d85a
 Lasse Collin 		lasse.collin@tukaani.org
a4d85a
@@ -374,6 +375,7 @@ Oswald P. Backus IV	backus@lks.csi.com
a4d85a
 Pascal Meheut		pascal@cnam.cnam.fr
a4d85a
 Patrick Fulconis	fulco@sig.uvsq.fr
a4d85a
 Patrick Timmons		timmons@electech.polymtl.ca
a4d85a
+Pavel Raiskup           praiskup@redhat.com
a4d85a
 Paul Eggert		eggert@twinsun.com
a4d85a
 Paul Kanz		paul@icx.com
a4d85a
 Paul Mitchell		P.Mitchell@surrey.ac.uk
a4d85a
diff --git a/acinclude.m4 b/acinclude.m4
a4d85a
index 10a27e5..30381d3 100644
a4d85a
--- a/acinclude.m4
a4d85a
+++ b/acinclude.m4
a4d85a
@@ -24,3 +24,29 @@ AC_DEFUN([TAR_COMPR_PROGRAM],[
a4d85a
 	     [tar_compr_var=m4_if($2,,$1,$2)])
a4d85a
  AC_DEFINE_UNQUOTED(tar_compr_define, "$tar_compr_var",
a4d85a
                     [Define to the program name of ]$1[ compressor program])])
a4d85a
+
a4d85a
+# Provide <attr/xattr.h>, if necessary
a4d85a
+
a4d85a
+AC_DEFUN([TAR_HEADERS_ATTR_XATTR_H],
a4d85a
+[
a4d85a
+  AC_ARG_WITH([xattrs],
a4d85a
+    AS_HELP_STRING([--without-xattrs], [don't use linux extended attributes]),
a4d85a
+    [], [with_xattrs=maybe]
a4d85a
+  )
a4d85a
+
a4d85a
+  AC_CHECK_HEADERS([attr/xattr.h])
a4d85a
+  AM_CONDITIONAL([TAR_COND_XATTR_H],[test "$ac_cv_header_attr_xattr_h" = yes])
a4d85a
+  if test "$ac_cv_header_attr_xattr_h" = yes; then
a4d85a
+    AC_CHECK_FUNCS(getxattr  fgetxattr  lgetxattr \
a4d85a
+                   setxattr  fsetxattr  lsetxattr \
a4d85a
+                   listxattr flistxattr llistxattr,
a4d85a
+        # only when functions are present
a4d85a
+        AC_DEFINE([HAVE_ATTR_XATTR_H], [1],
a4d85a
+                    [define to 1 if we have <attr/xattr.h> header])
a4d85a
+        if test "$with_xattrs" != no; then
a4d85a
+          AC_DEFINE([HAVE_XATTRS],,[Define when we have working linux xattrs.])
a4d85a
+        fi
a4d85a
+    )
a4d85a
+  fi
a4d85a
+])
a4d85a
+		    
a4d85a
\ No newline at end of file
a4d85a
diff --git a/configure.ac b/configure.ac
a4d85a
index db69cb8..9b3e0c8 100644
a4d85a
--- a/configure.ac
a4d85a
+++ b/configure.ac
a4d85a
@@ -70,6 +70,29 @@ if test $diff_cv_st_fstype_string = yes; then
a4d85a
     [Define if struct stat has a char st_fstype[] member.])
a4d85a
 fi
a4d85a
 
a4d85a
+# even if we use gnulib's acl.h with integrated m4 file later on (used because
a4d85a
+# of very useful file_has_acl() function) we need following checks that restrict
a4d85a
+# tar to use POSIX.1e ACLs only.
a4d85a
+AC_ARG_WITH([posix-acls],
a4d85a
+    AS_HELP_STRING([--without-posix-acls],
a4d85a
+                   [do not use POSIX.1e access control lists]),
a4d85a
+    [with_posix_acls=no])
a4d85a
+if test "x$with_posix_acls" != "xno"; then
a4d85a
+  AC_CHECK_HEADERS(sys/acl.h,, [with_posix_acl=no])
a4d85a
+  AC_SEARCH_LIBS([acl_get_file],  [acl pacl],, [with_posix_acl=no])
a4d85a
+  AC_SEARCH_LIBS([acl_get_fd],    [acl pacl],, [with_posix_acl=no])
a4d85a
+  AC_SEARCH_LIBS([acl_set_file],  [acl pacl],, [with_posix_acl=no])
a4d85a
+  AC_SEARCH_LIBS([acl_set_fd],    [acl pacl],, [with_posix_acl=no])
a4d85a
+  AC_SEARCH_LIBS([acl_to_text],   [acl pacl],, [with_posix_acl=no])
a4d85a
+  AC_SEARCH_LIBS([acl_from_text], [acl pacl],, [with_posix_acl=no])
a4d85a
+  if test "x$with_posix_acls" != xno; then
a4d85a
+    AC_DEFINE(HAVE_POSIX_ACLS,,[Define when we have working POSIX acls])
a4d85a
+  fi
a4d85a
+else
a4d85a
+  # disable acls in gnulib's checks
a4d85a
+  export enable_acl=no
a4d85a
+fi
a4d85a
+
a4d85a
 AC_TYPE_SIGNAL
a4d85a
 AC_TYPE_MODE_T
a4d85a
 AC_TYPE_PID_T
a4d85a
@@ -90,7 +113,10 @@ gl_INIT
a4d85a
 # paxutils modules
a4d85a
 tar_PAXUTILS
a4d85a
 
a4d85a
+TAR_HEADERS_ATTR_XATTR_H
a4d85a
+
a4d85a
 AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
a4d85a
+
a4d85a
 AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
a4d85a
 AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
a4d85a
 AC_CHECK_DECLS([time],,, [#include <time.h>])
a4d85a
diff --git a/lib/Makefile.am b/lib/Makefile.am
a4d85a
index efd6826..d73fac8 100644
a4d85a
--- a/lib/Makefile.am
a4d85a
+++ b/lib/Makefile.am
a4d85a
@@ -28,11 +28,24 @@ BUILT_SOURCES = rmt-command.h
a4d85a
 CLEANFILES = rmt-command.h rmt-command.h-t
a4d85a
 INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu
a4d85a
 
a4d85a
-noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h
a4d85a
+noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h xattr-at.h
a4d85a
 libtar_a_SOURCES = \
a4d85a
   paxerror.c paxexit-status.c paxlib.h paxnames.c \
a4d85a
   prepargs.c prepargs.h \
a4d85a
   rtapelib.c \
a4d85a
   rmt.h \
a4d85a
   stdopen.c stdopen.h \
a4d85a
-  system.h system-ioctl.h
a4d85a
+  system.h system-ioctl.h \
a4d85a
+  xattr-at.c
a4d85a
+
a4d85a
+if !TAR_COND_XATTR_H
a4d85a
+BUILT_SOURCES += attr/xattr.h
a4d85a
+attr/xattr.h: attr-xattr.in.h $(top_builddir)/config.status
a4d85a
+	$(AM_V_at)$(MKDIR_P) attr
a4d85a
+	$(AM_V_GEN)rm -f $@-t $@ && \
a4d85a
+	 cp $(srcdir)/attr-xattr.in.h attr/xattr.h
a4d85a
+
a4d85a
+MOSTLYCLEANFILES = attr/xattr.h attr/xattr.h
a4d85a
+endif
a4d85a
+
a4d85a
+EXTRA_DIST = attr-xattr.in.h
a4d85a
diff --git a/lib/attr-xattr.in.h b/lib/attr-xattr.in.h
a4d85a
new file mode 100644
a4d85a
index 0000000..b5796fe
a4d85a
--- /dev/null
a4d85a
+++ b/lib/attr-xattr.in.h
a4d85a
@@ -0,0 +1,58 @@
a4d85a
+/* Replacement <attr/xattr.h> for platforms that lack it.
a4d85a
+   Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+
a4d85a
+   This program is free software: you can redistribute it and/or modify
a4d85a
+   it under the terms of the GNU General Public License as published by
a4d85a
+   the Free Software Foundation; either version 3 of the License, or
a4d85a
+   (at your option) any later version.
a4d85a
+
a4d85a
+   This program is distributed in the hope that it will be useful,
a4d85a
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+   GNU General Public License for more details.
a4d85a
+
a4d85a
+   You should have received a copy of the GNU General Public License
a4d85a
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
a4d85a
+
a4d85a
+#ifndef TAR_ATTR_XATTR_H
a4d85a
+#define TAR_ATTR_XATTR_H
a4d85a
+#include <errno.h>
a4d85a
+
a4d85a
+/* setting */
a4d85a
+static inline int setxattr (const char *path, const char *name, const void
a4d85a
+                            *value, size_t size, int flags)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+static inline int lsetxattr (const char *path, const char *name, const void
a4d85a
+                             *value, size_t size, int flags)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+static inline int fsetxattr (int filedes, const char *name, const void *value,
a4d85a
+                             size_t size, int flags)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+
a4d85a
+/* getting */
a4d85a
+static inline ssize_t getxattr (const char *path, const char *name, void *value,
a4d85a
+                                size_t size)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+static inline ssize_t lgetxattr (const char *path, const char *name, void
a4d85a
+                                 *value, size_t size)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+static inline ssize_t fgetxattr (int filedes, const char *name, void *value,
a4d85a
+                                 size_t size)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+
a4d85a
+/* listing */
a4d85a
+static inline ssize_t listxattr (const char *path, char *list, size_t size)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+static inline ssize_t llistxattr (const char *path, char *list, size_t size)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+static inline ssize_t flistxattr (int filedes, char *list, size_t size)
a4d85a
+{ errno = ENOTSUP; return -1; }
a4d85a
+
a4d85a
+#endif
a4d85a
+
a4d85a
diff --git a/lib/xattr-at.c b/lib/xattr-at.c
a4d85a
new file mode 100644
a4d85a
index 0000000..746578c
a4d85a
--- /dev/null
a4d85a
+++ b/lib/xattr-at.c
a4d85a
@@ -0,0 +1,110 @@
a4d85a
+/* openat-style fd-relative functions for operating with extended file
a4d85a
+   attributes.
a4d85a
+
a4d85a
+   Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+
a4d85a
+   This program is free software: you can redistribute it and/or modify
a4d85a
+   it under the terms of the GNU General Public License as published by
a4d85a
+   the Free Software Foundation, either version 3 of the License, or
a4d85a
+   (at your option) any later version.
a4d85a
+
a4d85a
+   This program is distributed in the hope that it will be useful,
a4d85a
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+   GNU General Public License for more details.
a4d85a
+
a4d85a
+   You should have received a copy of the GNU General Public License
a4d85a
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
a4d85a
+
a4d85a
+#include <config.h>
a4d85a
+
a4d85a
+#include "xattr-at.h"
a4d85a
+#include "openat.h"
a4d85a
+
a4d85a
+#include <stdlib.h>
a4d85a
+#include <unistd.h>
a4d85a
+#include <errno.h>
a4d85a
+#include <fcntl.h>
a4d85a
+
a4d85a
+#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
a4d85a
+#include "save-cwd.h"
a4d85a
+
a4d85a
+#include "openat-priv.h"
a4d85a
+
a4d85a
+/* setxattrat */
a4d85a
+#define AT_FUNC_NAME setxattrat
a4d85a
+#define AT_FUNC_F1 setxattr
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , const char *name, const void *value \
a4d85a
+                                        , size_t size, int flags
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , name, value, size, flags
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* lsetxattrat */
a4d85a
+#define AT_FUNC_NAME lsetxattrat
a4d85a
+#define AT_FUNC_F1 lsetxattr
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , const char *name, const void *value \
a4d85a
+                                        , size_t size, int flags
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , name, value, size, flags
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* getxattrat */
a4d85a
+#define AT_FUNC_NAME getxattrat
a4d85a
+#define AT_FUNC_RESULT ssize_t
a4d85a
+#define AT_FUNC_F1 getxattr
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , const char *name, void *value \
a4d85a
+                                        , size_t size
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , name, value, size
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_RESULT
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* lgetxattrat */
a4d85a
+#define AT_FUNC_NAME lgetxattrat
a4d85a
+#define AT_FUNC_RESULT ssize_t
a4d85a
+#define AT_FUNC_F1 lgetxattr
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , const char *name, void *value \
a4d85a
+                                        , size_t size
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , name, value, size
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_RESULT
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* listxattrat */
a4d85a
+#define AT_FUNC_NAME listxattrat
a4d85a
+#define AT_FUNC_RESULT ssize_t
a4d85a
+#define AT_FUNC_F1 listxattr
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , char *list , size_t size
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , list , size
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_RESULT
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* llistxattrat */
a4d85a
+#define AT_FUNC_NAME llistxattrat
a4d85a
+#define AT_FUNC_RESULT ssize_t
a4d85a
+#define AT_FUNC_F1 llistxattr
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , char *list , size_t size
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , list , size
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_RESULT
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
diff --git a/lib/xattr-at.h b/lib/xattr-at.h
a4d85a
new file mode 100644
a4d85a
index 0000000..360245c
a4d85a
--- /dev/null
a4d85a
+++ b/lib/xattr-at.h
a4d85a
@@ -0,0 +1,66 @@
a4d85a
+/* Prototypes for openat-style fd-relative functions for operating with
a4d85a
+   extended file attributes.
a4d85a
+
a4d85a
+   Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+
a4d85a
+   This program is free software: you can redistribute it and/or modify
a4d85a
+   it under the terms of the GNU General Public License as published by
a4d85a
+   the Free Software Foundation, either version 3 of the License, or
a4d85a
+   (at your option) any later version.
a4d85a
+
a4d85a
+   This program is distributed in the hope that it will be useful,
a4d85a
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+   GNU General Public License for more details.
a4d85a
+
a4d85a
+   You should have received a copy of the GNU General Public License
a4d85a
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
a4d85a
+
a4d85a
+#ifndef XATTRS_AT_H
a4d85a
+#define XATTRS_AT_H
a4d85a
+
a4d85a
+#include <sys/types.h>
a4d85a
+#include <attr/xattr.h>
a4d85a
+
a4d85a
+/* These are the dir-fd-relative variants of the functions without the
a4d85a
+   "at" suffix.  For example, setxattrat (AT_FDCWD, path, name, value, size,
a4d85a
+   flags &c) is usually equivalent to setxattr (file, name, value, size,
a4d85a
+   flags).  For more info use the setxattr(2), getxattr(2) or listxattr(2)
a4d85a
+   manpages. */
a4d85a
+
a4d85a
+/* dir-fd-relative setxattr.  Operation sets the VALUE of the extended
a4d85a
+   attribute identified by NAME and associated with the given PATH in the
a4d85a
+   filesystem relatively to directory identified by DIR_FD.  See the
a4d85a
+   setxattr(2) manpage for the description of all parameters. */
a4d85a
+int setxattrat (int dir_fd, const char *path, const char *name,
a4d85a
+                const void *value, size_t size, int flags);
a4d85a
+
a4d85a
+/* dir-fd-relative lsetxattr.  This function is just like setxattrat,
a4d85a
+   except when DIR_FD and FILE specify a symlink:  lsetxattrat operates on the
a4d85a
+   symlink, while the setxattrat operates on the referent of the symlink.  */
a4d85a
+int lsetxattrat (int dir_fd, const char *path, const char *name,
a4d85a
+                 const void *value, size_t size, int flags);
a4d85a
+
a4d85a
+/* dir-fd-relative getxattr.  Operation gets the VALUE of the extended
a4d85a
+   attribute idenfified by NAME and associated with the given PATH in the
a4d85a
+   filesystem relatively to directory identified by DIR_FD.  For more info
a4d85a
+   about all parameters see the getxattr(2) manpage. */
a4d85a
+ssize_t getxattrat (int dir_fd, const char *path, const char *name,
a4d85a
+                    void *value, size_t size);
a4d85a
+
a4d85a
+/* dir-fd-relative lgetxattr.  This function is just like getxattrat,
a4d85a
+   except when DIR_FD and FILE specify a symlink:  lgetxattrat operates on the
a4d85a
+   symlink, while the getxattrat operates on the referent of the symlink.  */
a4d85a
+ssize_t lgetxattrat (int dir_fd, const char *path, const char *name,
a4d85a
+                     void *value, size_t size);
a4d85a
+
a4d85a
+/* dir-fd-relative listxattr.  Obtain the list of extended attrubtes names.  For
a4d85a
+   more info see the listxattr(2) manpage. */
a4d85a
+ssize_t listxattrat (int dir_fd, const char *path, char *list, size_t size);
a4d85a
+
a4d85a
+/* dir-fd-relative llistxattr.  This function is just like listxattrat,
a4d85a
+   except when DIR_FD and FILE specify a symlink:  llistxattr operates on the
a4d85a
+   symlink, while the listxattrat operates on the referent of the symlink.  */
a4d85a
+ssize_t llistxattrat (int dir_fd, const char *path, char *list, size_t size);
a4d85a
+
a4d85a
+#endif /* XATTRS_AT_H */
a4d85a
diff --git a/src/Makefile.am b/src/Makefile.am
a4d85a
index de310f4..782df19 100644
a4d85a
--- a/src/Makefile.am
a4d85a
+++ b/src/Makefile.am
a4d85a
@@ -20,7 +20,7 @@
a4d85a
 
a4d85a
 bin_PROGRAMS = tar
a4d85a
 
a4d85a
-noinst_HEADERS = arith.h common.h tar.h
a4d85a
+noinst_HEADERS = arith.h common.h tar.h xattrs.h
a4d85a
 tar_SOURCES = \
a4d85a
  buffer.c\
a4d85a
  checkpoint.c\
a4d85a
@@ -42,10 +42,11 @@ tar_SOURCES = \
a4d85a
  unlink.c\
a4d85a
  update.c\
a4d85a
  utf8.c\
a4d85a
- warning.c
a4d85a
+ warning.c\
a4d85a
+ xattrs.c
a4d85a
 
a4d85a
 INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
a4d85a
 
a4d85a
 LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
a4d85a
 
a4d85a
-tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
a4d85a
+tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) $(LIB_SELINUX)
a4d85a
diff --git a/src/common.h b/src/common.h
a4d85a
index 2409413..16ba401 100644
a4d85a
--- a/src/common.h
a4d85a
+++ b/src/common.h
a4d85a
@@ -1,8 +1,8 @@
a4d85a
 /* Common declarations for the tar program.
a4d85a
 
a4d85a
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
a4d85a
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
a4d85a
-   Inc.
a4d85a
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    This program is free software; you can redistribute it and/or modify it
a4d85a
    under the terms of the GNU General Public License as published by the
a4d85a
@@ -91,6 +91,11 @@ enum subcommand
a4d85a
 
a4d85a
 GLOBAL enum subcommand subcommand_option;
a4d85a
 
a4d85a
+#define READ_LIKE_SUBCOMMAND                    \
a4d85a
+  (subcommand_option == EXTRACT_SUBCOMMAND      \
a4d85a
+   || subcommand_option == DIFF_SUBCOMMAND      \
a4d85a
+   || subcommand_option == LIST_SUBCOMMAND)
a4d85a
+
a4d85a
 /* Selected format for output archive.  */
a4d85a
 GLOBAL enum archive_format archive_format;
a4d85a
 
a4d85a
@@ -254,6 +259,15 @@ GLOBAL int same_owner_option;
a4d85a
 /* If positive, preserve permissions when extracting.  */
a4d85a
 GLOBAL int same_permissions_option;
a4d85a
 
a4d85a
+/* If positive, save the SELinux context.  */
a4d85a
+GLOBAL int selinux_context_option;
a4d85a
+
a4d85a
+/* If positive, save the ACLs.  */
a4d85a
+GLOBAL int acls_option;
a4d85a
+
a4d85a
+/* If positive, save the user and root xattrs.  */
a4d85a
+GLOBAL int xattrs_option;
a4d85a
+
a4d85a
 /* When set, strip the given number of file name components from the file name
a4d85a
    before extracting */
a4d85a
 GLOBAL size_t strip_name_components;
a4d85a
@@ -708,6 +722,9 @@ extern char *output_start;
a4d85a
 
a4d85a
 void update_archive (void);
a4d85a
 
a4d85a
+/* Module attrs.c.  */
a4d85a
+#include "xattrs.h"
a4d85a
+
a4d85a
 /* Module xheader.c.  */
a4d85a
 
a4d85a
 void xheader_decode (struct tar_stat_info *stat);
a4d85a
@@ -728,6 +745,12 @@ bool xheader_string_end (struct xheader *xhdr, char const *keyword);
a4d85a
 bool xheader_keyword_deleted_p (const char *kw);
a4d85a
 char *xheader_format_name (struct tar_stat_info *st, const char *fmt,
a4d85a
 			   size_t n);
a4d85a
+void xheader_xattr_init (struct tar_stat_info *st);
a4d85a
+void xheader_xattr_free (struct xattr_array *vals, size_t sz);
a4d85a
+void xheader_xattr_copy (const struct tar_stat_info *st,
a4d85a
+                         struct xattr_array **vals, size_t *sz);
a4d85a
+void xheader_xattr_add (struct tar_stat_info *st,
a4d85a
+                        const char *key, const char *val, size_t len);
a4d85a
 
a4d85a
 /* Module system.c */
a4d85a
 
a4d85a
@@ -809,6 +832,7 @@ void checkpoint_run (bool do_write);
a4d85a
 #define WARN_XDEV                0x00040000
a4d85a
 #define WARN_DECOMPRESS_PROGRAM  0x00080000
a4d85a
 #define WARN_EXISTING_FILE       0x00100000
a4d85a
+#define WARN_XATTR_WRITE         0x00200000
a4d85a
 
a4d85a
 /* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default
a4d85a
    in verbose mode */
a4d85a
diff --git a/src/create.c b/src/create.c
a4d85a
index f98cbb5..25387a9 100644
a4d85a
--- a/src/create.c
a4d85a
+++ b/src/create.c
a4d85a
@@ -1,7 +1,8 @@
a4d85a
 /* Create a tar archive.
a4d85a
 
a4d85a
    Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
a4d85a
-   2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
a4d85a
+   2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    Written by John Gilmore, on 1985-08-25.
a4d85a
 
a4d85a
@@ -936,6 +937,30 @@ start_header (struct tar_stat_info *st)
a4d85a
       GNAME_TO_CHARS (st->gname, header->header.gname);
a4d85a
     }
a4d85a
 
a4d85a
+  if (archive_format == POSIX_FORMAT)
a4d85a
+    {
a4d85a
+      if (acls_option > 0)
a4d85a
+        {
a4d85a
+          if (st->acls_a_ptr)
a4d85a
+            xheader_store ("SCHILY.acl.access", st, NULL);
a4d85a
+          if (st->acls_d_ptr)
a4d85a
+            xheader_store ("SCHILY.acl.default", st, NULL);
a4d85a
+        }
a4d85a
+      if ((selinux_context_option > 0) && st->cntx_name)
a4d85a
+        xheader_store ("RHT.security.selinux", st, NULL);
a4d85a
+      if (xattrs_option > 0)
a4d85a
+        {
a4d85a
+          size_t scan_xattr = 0;
a4d85a
+          struct xattr_array *xattr_map = st->xattr_map;
a4d85a
+
a4d85a
+          while (scan_xattr < st->xattr_map_size)
a4d85a
+            {
a4d85a
+              xheader_store (xattr_map[scan_xattr].xkey, st, &scan_xattr);
a4d85a
+              ++scan_xattr;
a4d85a
+            }
a4d85a
+        }
a4d85a
+    }
a4d85a
+
a4d85a
   return header;
a4d85a
 }
a4d85a
 
a4d85a
@@ -1711,6 +1736,10 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
a4d85a
       bool ok;
a4d85a
       struct stat final_stat;
a4d85a
 
a4d85a
+      xattrs_acls_get (parentfd, name, st, 0, !is_dir);
a4d85a
+      xattrs_selinux_get (parentfd, name, st, fd);
a4d85a
+      xattrs_xattrs_get (parentfd, name, st, fd);
a4d85a
+
a4d85a
       if (is_dir)
a4d85a
 	{
a4d85a
 	  const char *tag_file_name;
a4d85a
@@ -1830,6 +1859,9 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
a4d85a
       if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
a4d85a
 	write_long_link (st);
a4d85a
 
a4d85a
+      xattrs_selinux_get (parentfd, name, st, 0);
a4d85a
+      xattrs_xattrs_get (parentfd, name, st, 0);
a4d85a
+
a4d85a
       block_ordinal = current_block_ordinal ();
a4d85a
       st->stat.st_size = 0;	/* force 0 size on symlink */
a4d85a
       header = start_header (st);
a4d85a
@@ -1848,11 +1880,26 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
a4d85a
     }
a4d85a
 #endif
a4d85a
   else if (S_ISCHR (st->stat.st_mode))
a4d85a
-    type = CHRTYPE;
a4d85a
+    {
a4d85a
+      type = CHRTYPE;
a4d85a
+      xattrs_acls_get (parentfd, name, st, 0, true);
a4d85a
+      xattrs_selinux_get (parentfd, name, st, 0);
a4d85a
+      xattrs_xattrs_get (parentfd, name, st, 0);
a4d85a
+    }
a4d85a
   else if (S_ISBLK (st->stat.st_mode))
a4d85a
-    type = BLKTYPE;
a4d85a
+    {
a4d85a
+      type = BLKTYPE;
a4d85a
+      xattrs_acls_get (parentfd, name, st, 0, true);
a4d85a
+      xattrs_selinux_get (parentfd, name, st, 0);
a4d85a
+      xattrs_xattrs_get (parentfd, name, st, 0);
a4d85a
+    }
a4d85a
   else if (S_ISFIFO (st->stat.st_mode))
a4d85a
-    type = FIFOTYPE;
a4d85a
+    {
a4d85a
+      type = FIFOTYPE;
a4d85a
+      xattrs_acls_get (parentfd, name, st, 0, true);
a4d85a
+      xattrs_selinux_get (parentfd, name, st, 0);
a4d85a
+      xattrs_xattrs_get (parentfd, name, st, 0);
a4d85a
+    }
a4d85a
   else if (S_ISSOCK (st->stat.st_mode))
a4d85a
     {
a4d85a
       WARNOPT (WARN_FILE_IGNORED,
a4d85a
diff --git a/src/extract.c b/src/extract.c
a4d85a
index 662ea0b..87b383a 100644
a4d85a
--- a/src/extract.c
a4d85a
+++ b/src/extract.c
a4d85a
@@ -1,7 +1,8 @@
a4d85a
 /* Extract files from a tar archive.
a4d85a
 
a4d85a
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
a4d85a
-   2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
a4d85a
+   2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    Written by John Gilmore, on 1985-11-19.
a4d85a
 
a4d85a
@@ -97,6 +98,14 @@ struct delayed_set_stat
a4d85a
     /* Directory that the name is relative to.  */
a4d85a
     int change_dir;
a4d85a
 
a4d85a
+    /* extended attributes*/
a4d85a
+    char *cntx_name;
a4d85a
+    char *acls_a_ptr;
a4d85a
+    size_t acls_a_len;
a4d85a
+    char *acls_d_ptr;
a4d85a
+    size_t acls_d_len;
a4d85a
+    size_t xattr_map_size;
a4d85a
+    struct xattr_array *xattr_map;
a4d85a
     /* Length and contents of name.  */
a4d85a
     size_t file_name_len;
a4d85a
     char file_name[1];
a4d85a
@@ -134,6 +143,18 @@ struct delayed_link
a4d85a
        hard-linked together.  */
a4d85a
     struct string_list *sources;
a4d85a
 
a4d85a
+    /* SELinux context */
a4d85a
+    char *cntx_name;
a4d85a
+
a4d85a
+    /* ACLs */
a4d85a
+    char *acls_a_ptr;
a4d85a
+    size_t acls_a_len;
a4d85a
+    char *acls_d_ptr;
a4d85a
+    size_t acls_d_len;
a4d85a
+
a4d85a
+    size_t xattr_map_size;
a4d85a
+    struct xattr_array *xattr_map;
a4d85a
+
a4d85a
     /* The desired target of the desired link.  */
a4d85a
     char target[1];
a4d85a
   };
a4d85a
@@ -360,6 +381,12 @@ set_stat (char const *file_name,
a4d85a
 	    st->stat.st_mode & ~ current_umask,
a4d85a
 	    0 < same_permissions_option && ! interdir ? MODE_ALL : MODE_RWX,
a4d85a
 	    fd, current_mode, current_mode_mask, typeflag, atflag);
a4d85a
+
a4d85a
+  /* these three calls must be done *after* fd_chown() call because fd_chown
a4d85a
+     causes that linux capabilities becomes cleared. */
a4d85a
+  xattrs_xattrs_set (st, file_name, typeflag, 1);
a4d85a
+  xattrs_acls_set (st, file_name, typeflag);
a4d85a
+  xattrs_selinux_set (st, file_name, typeflag);
a4d85a
 }
a4d85a
 
a4d85a
 /* For each entry H in the leading prefix of entries in HEAD that do
a4d85a
@@ -431,6 +458,36 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
a4d85a
   data->atflag = atflag;
a4d85a
   data->after_links = 0;
a4d85a
   data->change_dir = chdir_current;
a4d85a
+  data->cntx_name = NULL;
a4d85a
+  if (st)
a4d85a
+    assign_string (&data->cntx_name, st->cntx_name);
a4d85a
+  if (st && st->acls_a_ptr)
a4d85a
+    {
a4d85a
+      data->acls_a_ptr = xmemdup (st->acls_a_ptr, st->acls_a_len + 1);
a4d85a
+      data->acls_a_len = st->acls_a_len;
a4d85a
+    }
a4d85a
+  else
a4d85a
+    {
a4d85a
+      data->acls_a_ptr = NULL;
a4d85a
+      data->acls_a_len = 0;
a4d85a
+    }
a4d85a
+  if (st && st->acls_d_ptr)
a4d85a
+    {
a4d85a
+      data->acls_d_ptr = xmemdup (st->acls_d_ptr, st->acls_d_len + 1);
a4d85a
+      data->acls_d_len = st->acls_d_len;
a4d85a
+    }
a4d85a
+  else
a4d85a
+    {
a4d85a
+      data->acls_d_ptr = NULL;
a4d85a
+      data->acls_d_len = 0;
a4d85a
+    }
a4d85a
+  if (st)
a4d85a
+    xheader_xattr_copy (st, &data->xattr_map, &data->xattr_map_size);
a4d85a
+  else
a4d85a
+    {
a4d85a
+      data->xattr_map = NULL;
a4d85a
+      data->xattr_map_size = 0;
a4d85a
+    }
a4d85a
   strcpy (data->file_name, file_name);
a4d85a
   delayed_set_stat_head = data;
a4d85a
   if (must_be_dot_or_slash (file_name))
a4d85a
@@ -678,6 +735,40 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
a4d85a
   return RECOVER_NO;
a4d85a
 }
a4d85a
 
a4d85a
+/* Restore stat extended attributes (xattr) for FILE_NAME, using information
a4d85a
+   given in *ST.  Restore before extraction because they may affect file layout
a4d85a
+   (e.g. on Lustre distributed parallel filesystem - setting info about how many
a4d85a
+   servers is this file striped over, stripe size, mirror copies, etc.
a4d85a
+   in advance dramatically improves the following  performance of reading and
a4d85a
+   writing a file).  If not restoring permissions, invert the INVERT_PERMISSIONS
a4d85a
+   bits from the file's current permissions.  TYPEFLAG specifies the type of the
a4d85a
+   file.  FILE_CREATED indicates set_xattr has created the file */
a4d85a
+static int
a4d85a
+set_xattr (char const *file_name, struct tar_stat_info const *st,
a4d85a
+           mode_t invert_permissions, char typeflag, int *file_created)
a4d85a
+{
a4d85a
+  int status = 0;
a4d85a
+
a4d85a
+#ifdef HAVE_XATTRS
a4d85a
+  bool interdir_made = false;
a4d85a
+
a4d85a
+  if ((xattrs_option > 0) && st->xattr_map_size)
a4d85a
+    {
a4d85a
+      mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
a4d85a
+
a4d85a
+      do
a4d85a
+        status = mknodat (chdir_fd, file_name, mode ^ invert_permissions, 0);
a4d85a
+      while (status && maybe_recoverable ((char *)file_name, false,
a4d85a
+                                          &interdir_made));
a4d85a
+
a4d85a
+      xattrs_xattrs_set (st, file_name, typeflag, 0);
a4d85a
+      *file_created = 1;
a4d85a
+    }
a4d85a
+#endif
a4d85a
+
a4d85a
+  return(status);
a4d85a
+}
a4d85a
+
a4d85a
 /* Fix the statuses of all directories whose statuses need fixing, and
a4d85a
    which are not ancestors of FILE_NAME.  If AFTER_LINKS is
a4d85a
    nonzero, do this for all such directories; otherwise, stop at the
a4d85a
@@ -738,12 +829,23 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
a4d85a
 	  sb.stat.st_gid = data->gid;
a4d85a
 	  sb.atime = data->atime;
a4d85a
 	  sb.mtime = data->mtime;
a4d85a
+	  sb.cntx_name = data->cntx_name;
a4d85a
+	  sb.acls_a_ptr = data->acls_a_ptr;
a4d85a
+	  sb.acls_a_len = data->acls_a_len;
a4d85a
+	  sb.acls_d_ptr = data->acls_d_ptr;
a4d85a
+	  sb.acls_d_len = data->acls_d_len;
a4d85a
+	  sb.xattr_map = data->xattr_map;
a4d85a
+	  sb.xattr_map_size = data->xattr_map_size;
a4d85a
 	  set_stat (data->file_name, &sb,
a4d85a
 		    -1, current_mode, current_mode_mask,
a4d85a
 		    DIRTYPE, data->interdir, data->atflag);
a4d85a
 	}
a4d85a
 
a4d85a
       delayed_set_stat_head = data->next;
a4d85a
+      xheader_xattr_free (data->xattr_map, data->xattr_map_size);
a4d85a
+      free (data->cntx_name);
a4d85a
+      free (data->acls_a_ptr);
a4d85a
+      free (data->acls_d_ptr);
a4d85a
       free (data);
a4d85a
     }
a4d85a
 }
a4d85a
@@ -859,7 +961,8 @@ extract_dir (char *file_name, int typeflag)
a4d85a
 
a4d85a
 static int
a4d85a
 open_output_file (char const *file_name, int typeflag, mode_t mode,
a4d85a
-		  mode_t *current_mode, mode_t *current_mode_mask)
a4d85a
+                  int file_created, mode_t *current_mode,
a4d85a
+                  mode_t *current_mode_mask)
a4d85a
 {
a4d85a
   int fd;
a4d85a
   bool overwriting_old_files = old_files_option == OVERWRITE_OLD_FILES;
a4d85a
@@ -869,6 +972,10 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
a4d85a
 		     ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW)
a4d85a
 		     : O_EXCL));
a4d85a
 
a4d85a
+  /* File might be created in set_xattr. So clear O_EXCL to avoid open() fail */
a4d85a
+  if (file_created)
a4d85a
+    openflag = openflag & ~O_EXCL;
a4d85a
+
a4d85a
   if (typeflag == CONTTYPE)
a4d85a
     {
a4d85a
       static int conttype_diagnosed;
a4d85a
@@ -939,6 +1046,8 @@ extract_file (char *file_name, int typeflag)
a4d85a
   bool interdir_made = false;
a4d85a
   mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
a4d85a
 		 & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
a4d85a
+  mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO)
a4d85a
+                                                    : 0;
a4d85a
   mode_t current_mode = 0;
a4d85a
   mode_t current_mode_mask = 0;
a4d85a
 
a4d85a
@@ -955,8 +1064,18 @@ extract_file (char *file_name, int typeflag)
a4d85a
     }
a4d85a
   else
a4d85a
     {
a4d85a
+      int file_created = 0;
a4d85a
+      if (set_xattr (file_name, &current_stat_info, invert_permissions,
a4d85a
+                     typeflag, &file_created))
a4d85a
+        {
a4d85a
+          skip_member ();
a4d85a
+          open_error (file_name);
a4d85a
+          return 1;
a4d85a
+        }
a4d85a
+
a4d85a
       while ((fd = open_output_file (file_name, typeflag, mode,
a4d85a
-				     &current_mode, &current_mode_mask))
a4d85a
+                                     file_created, &current_mode,
a4d85a
+                                     &current_mode_mask))
a4d85a
 	     < 0)
a4d85a
 	{
a4d85a
 	  int recover = maybe_recoverable (file_name, true, &interdir_made);
a4d85a
@@ -1096,6 +1215,13 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
a4d85a
 			    + strlen (file_name) + 1);
a4d85a
       p->sources->next = 0;
a4d85a
       strcpy (p->sources->string, file_name);
a4d85a
+      p->cntx_name = NULL;
a4d85a
+      assign_string (&p->cntx_name, current_stat_info.cntx_name);
a4d85a
+      p->acls_a_ptr = NULL;
a4d85a
+      p->acls_a_len = 0;
a4d85a
+      p->acls_d_ptr = NULL;
a4d85a
+      p->acls_d_len = 0;
a4d85a
+      xheader_xattr_copy (&current_stat_info, &p->xattr_map, &p->xattr_map_size);
a4d85a
       strcpy (p->target, current_stat_info.link_name);
a4d85a
 
a4d85a
       h = delayed_set_stat_head;
a4d85a
@@ -1530,6 +1656,13 @@ apply_delayed_links (void)
a4d85a
 		  st1.stat.st_gid = ds->gid;
a4d85a
 		  st1.atime = ds->atime;
a4d85a
 		  st1.mtime = ds->mtime;
a4d85a
+                  st1.cntx_name = ds->cntx_name;
a4d85a
+                  st1.acls_a_ptr = ds->acls_a_ptr;
a4d85a
+                  st1.acls_a_len = ds->acls_a_len;
a4d85a
+                  st1.acls_d_ptr = ds->acls_d_ptr;
a4d85a
+                  st1.acls_d_len = ds->acls_d_len;
a4d85a
+                  st1.xattr_map = ds->xattr_map;
a4d85a
+                  st1.xattr_map_size = ds->xattr_map_size;
a4d85a
 		  set_stat (source, &st1, -1, 0, 0, SYMTYPE,
a4d85a
 			    false, AT_SYMLINK_NOFOLLOW);
a4d85a
 		  valid_source = source;
a4d85a
@@ -1544,6 +1677,9 @@ apply_delayed_links (void)
a4d85a
 	  sources = next;
a4d85a
 	}
a4d85a
 
a4d85a
+   xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
a4d85a
+   free (ds->cntx_name);
a4d85a
+
a4d85a
       {
a4d85a
 	struct delayed_link *next = ds->next;
a4d85a
 	free (ds);
a4d85a
diff --git a/src/list.c b/src/list.c
a4d85a
index f4e6e0a..dd501a9 100644
a4d85a
--- a/src/list.c
a4d85a
+++ b/src/list.c
a4d85a
@@ -1,7 +1,8 @@
a4d85a
 /* List a tar archive, with support routines for reading a tar archive.
a4d85a
 
a4d85a
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
a4d85a
-   2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
a4d85a
+   2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    Written by John Gilmore, on 1985-08-26.
a4d85a
 
a4d85a
@@ -615,6 +616,8 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
a4d85a
   assign_string (&stat_info->gname,
a4d85a
 		 header->header.gname[0] ? header->header.gname : NULL);
a4d85a
 
a4d85a
+  xheader_xattr_init (stat_info);
a4d85a
+
a4d85a
   if (format == OLDGNU_FORMAT && incremental_option)
a4d85a
     {
a4d85a
       stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
a4d85a
@@ -1075,7 +1078,7 @@ static void
a4d85a
 simple_print_header (struct tar_stat_info *st, union block *blk,
a4d85a
 		     off_t block_ordinal)
a4d85a
 {
a4d85a
-  char modes[11];
a4d85a
+  char modes[12];
a4d85a
   char const *time_stamp;
a4d85a
   int time_stamp_len;
a4d85a
   char *temp_name;
a4d85a
@@ -1167,6 +1170,9 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
a4d85a
 
a4d85a
       pax_decode_mode (st->stat.st_mode, modes + 1);
a4d85a
 
a4d85a
+      /* extended attributes:  GNU `ls -l'-like preview */
a4d85a
+      xattrs_print_char (st, modes + 10);
a4d85a
+
a4d85a
       /* Time stamp.  */
a4d85a
 
a4d85a
       time_stamp = tartime (st->mtime, full_time_option);
a4d85a
@@ -1312,6 +1318,7 @@ simple_print_header (struct tar_stat_info *st, union block *blk,
a4d85a
 	}
a4d85a
     }
a4d85a
   fflush (stdlis);
a4d85a
+  xattrs_print (st);
a4d85a
 }
a4d85a
 
a4d85a
 
a4d85a
diff --git a/src/tar.c b/src/tar.c
a4d85a
index 7a673e0..e244808 100644
a4d85a
--- a/src/tar.c
a4d85a
+++ b/src/tar.c
a4d85a
@@ -1,7 +1,8 @@
a4d85a
 /* A tar (tape archiver) program.
a4d85a
 
a4d85a
    Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
a4d85a
-   2001, 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
a4d85a
+   2001, 2003, 2004, 2005, 2006, 2007, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    Written by John Gilmore, starting 1985-08-25.
a4d85a
 
a4d85a
@@ -255,7 +256,8 @@ tar_set_quoting_style (char *arg)
a4d85a
 
a4d85a
 enum
a4d85a
 {
a4d85a
-  ANCHORED_OPTION = CHAR_MAX + 1,
a4d85a
+  ACLS_OPTION = CHAR_MAX + 1,
a4d85a
+  ANCHORED_OPTION,
a4d85a
   ATIME_PRESERVE_OPTION,
a4d85a
   BACKUP_OPTION,
a4d85a
   CHECK_DEVICE_OPTION,
a4d85a
@@ -288,6 +290,7 @@ enum
a4d85a
   MODE_OPTION,
a4d85a
   MTIME_OPTION,
a4d85a
   NEWER_MTIME_OPTION,
a4d85a
+  NO_ACLS_OPTION,
a4d85a
   NO_ANCHORED_OPTION,
a4d85a
   NO_AUTO_COMPRESS_OPTION,
a4d85a
   NO_CHECK_DEVICE_OPTION,
a4d85a
@@ -301,9 +304,11 @@ enum
a4d85a
   NO_SAME_OWNER_OPTION,
a4d85a
   NO_SAME_PERMISSIONS_OPTION,
a4d85a
   NO_SEEK_OPTION,
a4d85a
+  NO_SELINUX_CONTEXT_OPTION,
a4d85a
   NO_UNQUOTE_OPTION,
a4d85a
   NO_WILDCARDS_MATCH_SLASH_OPTION,
a4d85a
   NO_WILDCARDS_OPTION,
a4d85a
+  NO_XATTR_OPTION,
a4d85a
   NULL_OPTION,
a4d85a
   NUMERIC_OWNER_OPTION,
a4d85a
   OCCURRENCE_OPTION,
a4d85a
@@ -325,6 +330,7 @@ enum
a4d85a
   RMT_COMMAND_OPTION,
a4d85a
   RSH_COMMAND_OPTION,
a4d85a
   SAME_OWNER_OPTION,
a4d85a
+  SELINUX_CONTEXT_OPTION,
a4d85a
   SHOW_DEFAULTS_OPTION,
a4d85a
   SHOW_OMITTED_DIRS_OPTION,
a4d85a
   SHOW_TRANSFORMED_NAMES_OPTION,
a4d85a
@@ -341,7 +347,10 @@ enum
a4d85a
   VOLNO_FILE_OPTION,
a4d85a
   WARNING_OPTION,
a4d85a
   WILDCARDS_MATCH_SLASH_OPTION,
a4d85a
-  WILDCARDS_OPTION
a4d85a
+  WILDCARDS_OPTION,
a4d85a
+  XATTR_OPTION,
a4d85a
+  XATTR_EXCLUDE,
a4d85a
+  XATTR_INCLUDE
a4d85a
 };
a4d85a
 
a4d85a
 const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
a4d85a
@@ -530,6 +539,28 @@ static struct argp_option options[] = {
a4d85a
    N_("cancel the effect of --delay-directory-restore option"), GRID+1 },
a4d85a
 #undef GRID
a4d85a
 
a4d85a
+#define GRID 55
a4d85a
+  {NULL, 0, NULL, 0,
a4d85a
+   N_("Handling of extended file attributes:"), GRID },
a4d85a
+
a4d85a
+  {"xattrs", XATTR_OPTION, 0, 0,
a4d85a
+   N_("Enable extended attributes support"), GRID+1 },
a4d85a
+  {"no-xattrs", NO_XATTR_OPTION, 0, 0,
a4d85a
+   N_("Disable extended attributes support"), GRID+1 },
a4d85a
+  {"xattrs-include", XATTR_INCLUDE, N_("MASK"), 0,
a4d85a
+   N_("specify the include pattern for xattr keys"), GRID+1 },
a4d85a
+  {"xattrs-exclude", XATTR_EXCLUDE, N_("MASK"), 0,
a4d85a
+   N_("specify the exclude pattern for xattr keys"), GRID+1 },
a4d85a
+  {"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
a4d85a
+   N_("Enable the SELinux context support"), GRID+1 },
a4d85a
+  {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
a4d85a
+   N_("Disable the SELinux context support"), GRID+1 },
a4d85a
+  {"acls", ACLS_OPTION, 0, 0,
a4d85a
+   N_("Enable the POSIX ACLs support"), GRID+1 },
a4d85a
+  {"no-acls", NO_ACLS_OPTION, 0, 0,
a4d85a
+   N_("Disable the POSIX ACLs support"), GRID+1 },
a4d85a
+#undef GRID
a4d85a
+
a4d85a
 #define GRID 60
a4d85a
   {NULL, 0, NULL, 0,
a4d85a
    N_("Device selection and switching:"), GRID },
a4d85a
@@ -2091,6 +2122,38 @@ parse_opt (int key, char *arg, struct argp_state *state)
a4d85a
       same_permissions_option = -1;
a4d85a
       break;
a4d85a
 
a4d85a
+    case ACLS_OPTION:
a4d85a
+      set_archive_format ("posix");
a4d85a
+      acls_option = 1;
a4d85a
+      break;
a4d85a
+
a4d85a
+    case NO_ACLS_OPTION:
a4d85a
+      acls_option = -1;
a4d85a
+      break;
a4d85a
+
a4d85a
+    case SELINUX_CONTEXT_OPTION:
a4d85a
+      set_archive_format ("posix");
a4d85a
+      selinux_context_option = 1;
a4d85a
+      break;
a4d85a
+
a4d85a
+    case NO_SELINUX_CONTEXT_OPTION:
a4d85a
+      selinux_context_option = -1;
a4d85a
+      break;
a4d85a
+
a4d85a
+    case XATTR_OPTION:
a4d85a
+      set_archive_format ("posix");
a4d85a
+      xattrs_option = 1;
a4d85a
+      break;
a4d85a
+
a4d85a
+    case NO_XATTR_OPTION:
a4d85a
+      xattrs_option = -1;
a4d85a
+      break;
a4d85a
+
a4d85a
+    case XATTR_INCLUDE:
a4d85a
+    case XATTR_EXCLUDE:
a4d85a
+      xattrs_mask_add (arg, (key == XATTR_INCLUDE));
a4d85a
+      break;
a4d85a
+
a4d85a
     case RECURSION_OPTION:
a4d85a
       recursion_option = FNM_LEADING_DIR;
a4d85a
       break;
a4d85a
@@ -2468,11 +2531,26 @@ decode_options (int argc, char **argv)
a4d85a
      --gray */
a4d85a
   if (args.pax_option
a4d85a
       && archive_format != POSIX_FORMAT
a4d85a
-      && (subcommand_option != EXTRACT_SUBCOMMAND
a4d85a
-	  || subcommand_option != DIFF_SUBCOMMAND
a4d85a
-	  || subcommand_option != LIST_SUBCOMMAND))
a4d85a
+      && !READ_LIKE_SUBCOMMAND)
a4d85a
     USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
a4d85a
 
a4d85a
+  /* star creates non-POSIX typed archives with xattr support, so allow the
a4d85a
+     extra headers whenn reading */
a4d85a
+  if ((acls_option > 0)
a4d85a
+      && archive_format != POSIX_FORMAT
a4d85a
+      && !READ_LIKE_SUBCOMMAND)
a4d85a
+    USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
a4d85a
+
a4d85a
+  if ((selinux_context_option > 0)
a4d85a
+      && archive_format != POSIX_FORMAT
a4d85a
+      && !READ_LIKE_SUBCOMMAND)
a4d85a
+    USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
a4d85a
+
a4d85a
+  if ((xattrs_option > 0)
a4d85a
+      && archive_format != POSIX_FORMAT
a4d85a
+      && !READ_LIKE_SUBCOMMAND)
a4d85a
+    USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives")));
a4d85a
+
a4d85a
   /* If ready to unlink hierarchies, so we are for simpler files.  */
a4d85a
   if (recursive_unlink_option)
a4d85a
     old_files_option = UNLINK_FIRST_OLD_FILES;
a4d85a
@@ -2681,6 +2759,7 @@ main (int argc, char **argv)
a4d85a
   /* Dispose of allocated memory, and return.  */
a4d85a
 
a4d85a
   free (archive_name_array);
a4d85a
+  xattrs_clear_setup ();
a4d85a
   name_term ();
a4d85a
 
a4d85a
   if (exit_status == TAREXIT_FAILURE)
a4d85a
@@ -2725,11 +2804,15 @@ void
a4d85a
 tar_stat_destroy (struct tar_stat_info *st)
a4d85a
 {
a4d85a
   tar_stat_close (st);
a4d85a
+  xheader_xattr_free (st->xattr_map, st->xattr_map_size);
a4d85a
   free (st->orig_file_name);
a4d85a
   free (st->file_name);
a4d85a
   free (st->link_name);
a4d85a
   free (st->uname);
a4d85a
   free (st->gname);
a4d85a
+  free (st->cntx_name);
a4d85a
+  free (st->acls_a_ptr);
a4d85a
+  free (st->acls_d_ptr);
a4d85a
   free (st->sparse_map);
a4d85a
   free (st->dumpdir);
a4d85a
   xheader_destroy (&st->xhdr);
a4d85a
diff --git a/src/tar.h b/src/tar.h
a4d85a
index ce9850c..b181e58 100644
a4d85a
--- a/src/tar.h
a4d85a
+++ b/src/tar.h
a4d85a
@@ -1,7 +1,8 @@
a4d85a
 /* GNU tar Archive Format description.
a4d85a
 
a4d85a
    Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
a4d85a
-   2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
a4d85a
+   2000, 2001, 2003, 2004, 2005, 2006, 2007, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    This program is free software; you can redistribute it and/or modify it
a4d85a
    under the terms of the GNU General Public License as published by the
a4d85a
@@ -276,6 +277,14 @@ struct xheader
a4d85a
   uintmax_t string_length;
a4d85a
 };
a4d85a
 
a4d85a
+/* Information about xattrs for a file.  */
a4d85a
+struct xattr_array
a4d85a
+  {
a4d85a
+    char *xkey;
a4d85a
+    char *xval_ptr;
a4d85a
+    size_t xval_len;
a4d85a
+  };
a4d85a
+
a4d85a
 struct tar_stat_info
a4d85a
 {
a4d85a
   char *orig_file_name;     /* name of file read from the archive header */
a4d85a
@@ -287,6 +296,15 @@ struct tar_stat_info
a4d85a
 
a4d85a
   char          *uname;     /* user name of owner */
a4d85a
   char          *gname;     /* group name of owner */
a4d85a
+
a4d85a
+  char *cntx_name;          /* SELinux context for the current archive entry. */
a4d85a
+
a4d85a
+  char *acls_a_ptr;         /* Access ACLs for the current archive entry. */
a4d85a
+  size_t acls_a_len;        /* Access ACLs for the current archive entry. */
a4d85a
+
a4d85a
+  char *acls_d_ptr;         /* Default ACLs for the current archive entry. */
a4d85a
+  size_t acls_d_len;        /* Default ACLs for the current archive entry. */
a4d85a
+
a4d85a
   struct stat   stat;       /* regular filesystem stat */
a4d85a
 
a4d85a
   /* STAT doesn't always have access, data modification, and status
a4d85a
@@ -309,6 +327,9 @@ struct tar_stat_info
a4d85a
   size_t sparse_map_size;   /* Size of the sparse map */
a4d85a
   struct sp_array *sparse_map;
a4d85a
 
a4d85a
+  size_t xattr_map_size;   /* Size of the xattr map */
a4d85a
+  struct xattr_array *xattr_map;
a4d85a
+
a4d85a
   /* Extended headers */
a4d85a
   struct xheader xhdr;
a4d85a
 
a4d85a
diff --git a/src/warning.c b/src/warning.c
a4d85a
index ee9d684..570b3c1 100644
a4d85a
--- a/src/warning.c
a4d85a
+++ b/src/warning.c
a4d85a
@@ -1,6 +1,6 @@
a4d85a
 /* This file is part of GNU tar.
a4d85a
 
a4d85a
-   Copyright (C) 2009 Free Software Foundation, Inc.
a4d85a
+   Copyright (C) 2009, 2012 Free Software Foundation, Inc.
a4d85a
 
a4d85a
    This program is free software; you can redistribute it and/or modify it
a4d85a
    under the terms of the GNU General Public License as published by the
a4d85a
@@ -43,6 +43,7 @@ static char const *const warning_args[] = {
a4d85a
   "xdev",
a4d85a
   "decompress-program",
a4d85a
   "existing-file",
a4d85a
+  "xattr-write",
a4d85a
   NULL
a4d85a
 };
a4d85a
 
a4d85a
@@ -69,6 +70,7 @@ static int warning_types[] = {
a4d85a
   WARN_XDEV,
a4d85a
   WARN_DECOMPRESS_PROGRAM,
a4d85a
   WARN_EXISTING_FILE,
a4d85a
+  WARN_XATTR_WRITE
a4d85a
 };
a4d85a
 
a4d85a
 ARGMATCH_VERIFY (warning_args, warning_types);
a4d85a
diff --git a/src/xattrs.c b/src/xattrs.c
a4d85a
new file mode 100644
a4d85a
index 0000000..5a4bf72
a4d85a
--- /dev/null
a4d85a
+++ b/src/xattrs.c
a4d85a
@@ -0,0 +1,732 @@
a4d85a
+/* Support for extended attributes.
a4d85a
+
a4d85a
+   Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software
a4d85a
+   Foundation, Inc.
a4d85a
+
a4d85a
+   Written by James Antill, on 2006-07-27.
a4d85a
+
a4d85a
+   This program is free software; you can redistribute it and/or modify it
a4d85a
+   under the terms of the GNU General Public License as published by the
a4d85a
+   Free Software Foundation; either version 2, or (at your option) any later
a4d85a
+   version.
a4d85a
+
a4d85a
+   This program is distributed in the hope that it will be useful, but
a4d85a
+   WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
a4d85a
+   Public License for more details.
a4d85a
+
a4d85a
+   You should have received a copy of the GNU General Public License along
a4d85a
+   with this program; if not, write to the Free Software Foundation, Inc.,
a4d85a
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
a4d85a
+
a4d85a
+#include <config.h>
a4d85a
+#include <system.h>
a4d85a
+
a4d85a
+#include <fnmatch.h>
a4d85a
+#include <quotearg.h>
a4d85a
+
a4d85a
+#include "common.h"
a4d85a
+
a4d85a
+#include "xattr-at.h"
a4d85a
+#include "selinux-at.h"
a4d85a
+
a4d85a
+struct xattrs_mask_map
a4d85a
+{
a4d85a
+  const char **masks;
a4d85a
+  size_t size;
a4d85a
+  size_t used;
a4d85a
+};
a4d85a
+
a4d85a
+/* list of fnmatch patterns */
a4d85a
+static struct
a4d85a
+{
a4d85a
+  /* lists of fnmatch patterns */
a4d85a
+  struct xattrs_mask_map incl;
a4d85a
+  struct xattrs_mask_map excl;
a4d85a
+} xattrs_setup;
a4d85a
+
a4d85a
+/* disable posix acls when problem found in gnulib script m4/acl.m4 */
a4d85a
+#if ! USE_ACL
a4d85a
+# undef HAVE_POSIX_ACLS
a4d85a
+#endif
a4d85a
+
a4d85a
+#ifdef HAVE_POSIX_ACLS
a4d85a
+# include "acl.h"
a4d85a
+# include <sys/acl.h>
a4d85a
+#endif
a4d85a
+
a4d85a
+#ifdef HAVE_POSIX_ACLS
a4d85a
+
a4d85a
+/* acl-at wrappers, TODO: move to gnulib in future? */
a4d85a
+acl_t acl_get_file_at (int dirfd, const char *file, acl_type_t type);
a4d85a
+int acl_set_file_at (int dirfd, const char *file, acl_type_t type, acl_t acl);
a4d85a
+int file_has_acl_at (int dirfd, char const *, struct stat const *);
a4d85a
+
a4d85a
+/* acl_get_file_at */
a4d85a
+#define AT_FUNC_NAME acl_get_file_at
a4d85a
+#define AT_FUNC_RESULT acl_t
a4d85a
+#define AT_FUNC_FAIL (acl_t)NULL
a4d85a
+#define AT_FUNC_F1 acl_get_file
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , acl_type_t type
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , type
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_RESULT
a4d85a
+#undef AT_FUNC_FAIL
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* acl_set_file_at */
a4d85a
+#define AT_FUNC_NAME acl_set_file_at
a4d85a
+#define AT_FUNC_F1 acl_set_file
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , acl_type_t type, acl_t acl
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , type, acl
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* gnulib file_has_acl_at */
a4d85a
+#define AT_FUNC_NAME file_has_acl_at
a4d85a
+#define AT_FUNC_F1 file_has_acl
a4d85a
+#define AT_FUNC_POST_FILE_PARAM_DECLS   , struct stat const *st
a4d85a
+#define AT_FUNC_POST_FILE_ARGS          , st
a4d85a
+#include "at-func.c"
a4d85a
+#undef AT_FUNC_NAME
a4d85a
+#undef AT_FUNC_F1
a4d85a
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
a4d85a
+#undef AT_FUNC_POST_FILE_ARGS
a4d85a
+
a4d85a
+/* convert unix permissions into an ACL ... needed due to "default" ACLs */
a4d85a
+static acl_t
a4d85a
+perms2acl (int perms)
a4d85a
+{
a4d85a
+  char val[] = "user::---,group::---,other::---";
a4d85a
+  /*            0123456789 123456789 123456789 123456789 */
a4d85a
+
a4d85a
+  /* user */
a4d85a
+  if (perms & 0400)
a4d85a
+    val[6] = 'r';
a4d85a
+  if (perms & 0200)
a4d85a
+    val[7] = 'w';
a4d85a
+  if (perms & 0100)
a4d85a
+    val[8] = 'x';
a4d85a
+
a4d85a
+  /* group */
a4d85a
+  if (perms & 0040)
a4d85a
+    val[17] = 'r';
a4d85a
+  if (perms & 0020)
a4d85a
+    val[18] = 'w';
a4d85a
+  if (perms & 0010)
a4d85a
+    val[19] = 'x';
a4d85a
+
a4d85a
+  /* other */
a4d85a
+  if (perms & 0004)
a4d85a
+    val[28] = 'r';
a4d85a
+  if (perms & 0002)
a4d85a
+    val[29] = 'w';
a4d85a
+  if (perms & 0001)
a4d85a
+    val[30] = 'x';
a4d85a
+
a4d85a
+  return acl_from_text (val);
a4d85a
+}
a4d85a
+
a4d85a
+static char *
a4d85a
+skip_to_ext_fields (char *ptr)
a4d85a
+{
a4d85a
+  /* skip tag name (user/group/default/mask) */
a4d85a
+  ptr += strcspn (ptr, ":,\n"); 
a4d85a
+
a4d85a
+  if (*ptr != ':')
a4d85a
+    return ptr;
a4d85a
+  ++ptr;
a4d85a
+
a4d85a
+  ptr += strcspn (ptr, ":,\n"); /* skip user/group name */
a4d85a
+
a4d85a
+  if (*ptr != ':')
a4d85a
+    return ptr;
a4d85a
+  ++ptr;
a4d85a
+
a4d85a
+  ptr += strcspn (ptr, ":,\n"); /* skip perms */
a4d85a
+
a4d85a
+  return ptr;
a4d85a
+}
a4d85a
+
a4d85a
+/* The POSIX draft allows extra fields after the three main ones. Star
a4d85a
+   uses this to add a fourth field for user/group which is the numeric ID.
a4d85a
+   This function removes such extra fields by overwriting them with the
a4d85a
+   characters that follow. */
a4d85a
+static char *
a4d85a
+fixup_extra_acl_fields (char *ptr)
a4d85a
+{
a4d85a
+  char *src = ptr;
a4d85a
+  char *dst = ptr;
a4d85a
+
a4d85a
+  while (*src)
a4d85a
+    {
a4d85a
+      const char *old = src;
a4d85a
+      size_t len = 0;
a4d85a
+
a4d85a
+      src = skip_to_ext_fields (src);
a4d85a
+      len = src - old;
a4d85a
+      if (old != dst)
a4d85a
+        memmove (dst, old, len);
a4d85a
+      dst += len;
a4d85a
+
a4d85a
+      if (*src == ':')          /* We have extra fields, skip them all */
a4d85a
+        src += strcspn (src, "\n,");
a4d85a
+
a4d85a
+      if ((*src == '\n') || (*src == ','))
a4d85a
+        *dst++ = *src++;        /* also done when dst == src, but that's ok */
a4d85a
+    }
a4d85a
+  if (src != dst)
a4d85a
+    *dst = 0;
a4d85a
+
a4d85a
+  return ptr;
a4d85a
+}
a4d85a
+
a4d85a
+/* "system.posix_acl_access" */
a4d85a
+static void
a4d85a
+xattrs__acls_set (struct tar_stat_info const *st,
a4d85a
+                  char const *file_name, int type,
a4d85a
+                  char *ptr, size_t len, bool def)
a4d85a
+{  
a4d85a
+  acl_t acl;
a4d85a
+
a4d85a
+  if (ptr)
a4d85a
+    {
a4d85a
+      /* assert (strlen (ptr) == len); */
a4d85a
+      ptr = fixup_extra_acl_fields (ptr);
a4d85a
+
a4d85a
+      acl = acl_from_text (ptr);
a4d85a
+      acls_option = 1;
a4d85a
+    }
a4d85a
+  else if (acls_option > 0)
a4d85a
+    acl = perms2acl (st->stat.st_mode);
a4d85a
+  else
a4d85a
+    return;  /* don't call acl functions unless we first hit an ACL, or
a4d85a
+		--acls was passed explicitly */
a4d85a
+
a4d85a
+  if (!acl)
a4d85a
+    {
a4d85a
+      call_arg_warn ("acl_from_text", file_name);
a4d85a
+      return;
a4d85a
+    }
a4d85a
+
a4d85a
+  if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
a4d85a
+    /* warn even if filesystem does not support acls */
a4d85a
+    WARNOPT (WARN_XATTR_WRITE,
a4d85a
+	     (0, errno,
a4d85a
+	      _ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
a4d85a
+	      file_name));
a4d85a
+
a4d85a
+  acl_free (acl);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattrs__acls_get_a (int parentfd, const char *file_name,
a4d85a
+                    struct tar_stat_info *st,
a4d85a
+                    char **ret_ptr, size_t * ret_len)
a4d85a
+{             
a4d85a
+  char *val = NULL;
a4d85a
+  ssize_t len;
a4d85a
+  acl_t acl;
a4d85a
+
a4d85a
+  if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS)))
a4d85a
+    {
a4d85a
+      if (errno != ENOTSUP)
a4d85a
+        call_arg_warn ("acl_get_file_at", file_name);
a4d85a
+      return;
a4d85a
+    }
a4d85a
+
a4d85a
+  val = acl_to_text (acl, &len;;
a4d85a
+  acl_free (acl);
a4d85a
+
a4d85a
+  if (!val)
a4d85a
+    {
a4d85a
+      call_arg_warn ("acl_to_text", file_name);
a4d85a
+      return;
a4d85a
+    }
a4d85a
+
a4d85a
+  *ret_ptr = xstrdup (val);
a4d85a
+  *ret_len = len;
a4d85a
+
a4d85a
+  acl_free (val);
a4d85a
+}
a4d85a
+
a4d85a
+/* "system.posix_acl_default" */
a4d85a
+static void
a4d85a
+xattrs__acls_get_d (int parentfd, char const *file_name,
a4d85a
+                    struct tar_stat_info *st,
a4d85a
+                    char **ret_ptr, size_t * ret_len)
a4d85a
+{         
a4d85a
+  char *val = NULL;
a4d85a
+  ssize_t len;
a4d85a
+  acl_t acl;
a4d85a
+
a4d85a
+  if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT)))
a4d85a
+    {
a4d85a
+      if (errno != ENOTSUP)
a4d85a
+        call_arg_warn ("acl_get_file_at", file_name);
a4d85a
+      return;
a4d85a
+    }
a4d85a
+
a4d85a
+  val = acl_to_text (acl, &len;;
a4d85a
+  acl_free (acl);
a4d85a
+
a4d85a
+  if (!val)
a4d85a
+    {
a4d85a
+      call_arg_warn ("acl_to_text", file_name);
a4d85a
+      return;
a4d85a
+    }
a4d85a
+
a4d85a
+  *ret_ptr = xstrdup (val);
a4d85a
+  *ret_len = len;
a4d85a
+
a4d85a
+  acl_free (val);
a4d85a
+}
a4d85a
+#endif /* HAVE_POSIX_ACLS */
a4d85a
+
a4d85a
+static void
a4d85a
+acls_one_line (const char *prefix, char delim,
a4d85a
+               const char *aclstring, size_t len)
a4d85a
+{
a4d85a
+  /* support both long and short text representation of posix acls */
a4d85a
+  struct obstack stk;
a4d85a
+  int pref_len = strlen (prefix);
a4d85a
+  const char *oldstring = aclstring;
a4d85a
+  int pos = 0;
a4d85a
+
a4d85a
+  if (!aclstring || !len)
a4d85a
+    return;
a4d85a
+
a4d85a
+  obstack_init (&stk;;
a4d85a
+  while (pos <= len)
a4d85a
+    {
a4d85a
+      int move = strcspn (aclstring, ",\n");
a4d85a
+      if (!move)
a4d85a
+        break;
a4d85a
+
a4d85a
+      if (oldstring != aclstring)
a4d85a
+        obstack_1grow (&stk, delim);
a4d85a
+
a4d85a
+      obstack_grow (&stk, prefix, pref_len);
a4d85a
+      obstack_grow (&stk, aclstring, move);
a4d85a
+
a4d85a
+      aclstring += move + 1;
a4d85a
+    }
a4d85a
+
a4d85a
+  obstack_1grow (&stk, '\0');
a4d85a
+
a4d85a
+  fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
a4d85a
+
a4d85a
+  obstack_free (&stk, NULL);
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_acls_get (int parentfd, char const *file_name,
a4d85a
+                 struct tar_stat_info *st, int fd, int xisfile)
a4d85a
+{
a4d85a
+  if (acls_option > 0)
a4d85a
+    {
a4d85a
+#ifndef HAVE_POSIX_ACLS
a4d85a
+      static int done = 0;
a4d85a
+      if (!done)
a4d85a
+        WARN ((0, 0, _("POSIX ACL support is not available")));
a4d85a
+      done = 1;
a4d85a
+#else
a4d85a
+      int err = file_has_acl_at (parentfd, file_name, &st->stat);
a4d85a
+      if (err == 0)
a4d85a
+        return;
a4d85a
+      if (err == -1)
a4d85a
+        {
a4d85a
+          call_arg_warn ("file_has_acl_at", file_name);
a4d85a
+          return;
a4d85a
+        }
a4d85a
+
a4d85a
+      xattrs__acls_get_a (parentfd, file_name, st,
a4d85a
+                          &st->acls_a_ptr, &st->acls_a_len);
a4d85a
+      if (!xisfile)
a4d85a
+        xattrs__acls_get_d (parentfd, file_name, st,
a4d85a
+                            &st->acls_d_ptr, &st->acls_d_len);
a4d85a
+#endif
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_acls_set (struct tar_stat_info const *st,
a4d85a
+                 char const *file_name, char typeflag)
a4d85a
+{
a4d85a
+  if (acls_option > 0 && typeflag != SYMTYPE)
a4d85a
+    {
a4d85a
+#ifndef HAVE_POSIX_ACLS
a4d85a
+      static int done = 0;
a4d85a
+      if (!done)
a4d85a
+        WARN ((0, 0, _("POSIX ACL support is not available")));
a4d85a
+      done = 1;
a4d85a
+#else
a4d85a
+      xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
a4d85a
+                        st->acls_a_ptr, st->acls_a_len, false);
a4d85a
+      if (typeflag == DIRTYPE || typeflag == GNUTYPE_DUMPDIR)
a4d85a
+        xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
a4d85a
+                          st->acls_d_ptr, st->acls_d_len, true);
a4d85a
+#endif
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+mask_map_realloc (struct xattrs_mask_map *map)
a4d85a
+{
a4d85a
+  if (map->used == map->size)
a4d85a
+    {
a4d85a
+      if (map->size == 0)
a4d85a
+	map->size = 4;
a4d85a
+      map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_mask_add (const char *mask, bool incl)
a4d85a
+{
a4d85a
+  struct xattrs_mask_map *mask_map =
a4d85a
+    incl ? &xattrs_setup.incl : &xattrs_setup.excl;
a4d85a
+  /* ensure there is enough space */
a4d85a
+  mask_map_realloc (mask_map);
a4d85a
+  /* just assign pointers -- we silently expect that pointer "mask" is valid
a4d85a
+     through the whole program (pointer to argv array) */
a4d85a
+  mask_map->masks[mask_map->used++] = mask;
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+clear_mask_map (struct xattrs_mask_map *mask_map)
a4d85a
+{
a4d85a
+  if (mask_map->size)
a4d85a
+    free (mask_map->masks);
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_clear_setup ()
a4d85a
+{
a4d85a
+  clear_mask_map (&xattrs_setup.incl);
a4d85a
+  clear_mask_map (&xattrs_setup.excl);
a4d85a
+}
a4d85a
+
a4d85a
+/* get all xattrs from file given by FILE_NAME or FD (when non-zero).  This
a4d85a
+   includes all the user.*, security.*, system.*, etc. available domains */
a4d85a
+void
a4d85a
+xattrs_xattrs_get (int parentfd, char const *file_name,
a4d85a
+                   struct tar_stat_info *st, int fd)
a4d85a
+{
a4d85a
+  if (xattrs_option > 0)
a4d85a
+    {
a4d85a
+#ifndef HAVE_XATTRS
a4d85a
+      static int done = 0;
a4d85a
+      if (!done)
a4d85a
+        WARN ((0, 0, _("XATTR support is not available")));
a4d85a
+      done = 1;
a4d85a
+#else
a4d85a
+      static size_t xsz = 1024;
a4d85a
+      static char *xatrs = NULL;
a4d85a
+      ssize_t xret = -1;
a4d85a
+
a4d85a
+      if (!xatrs)
a4d85a
+	xatrs = x2nrealloc (xatrs, &xsz, 1);
a4d85a
+
a4d85a
+      while (((fd == 0) ?
a4d85a
+              ((xret =
a4d85a
+                llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
a4d85a
+	      ((xret = flistxattr (fd, xatrs, xsz)) == -1))
a4d85a
+             && (errno == ERANGE))
a4d85a
+        {
a4d85a
+	  xatrs = x2nrealloc (xatrs, &xsz, 1);
a4d85a
+        }
a4d85a
+
a4d85a
+      if (xret == -1)
a4d85a
+        call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
a4d85a
+      else
a4d85a
+        {
a4d85a
+          const char *attr = xatrs;
a4d85a
+          static size_t asz = 1024;
a4d85a
+          static char *val = NULL;
a4d85a
+
a4d85a
+          if (!val)
a4d85a
+            val = x2nrealloc (val, &asz, 1);
a4d85a
+
a4d85a
+          while (xret > 0)
a4d85a
+            {
a4d85a
+              size_t len = strlen (attr);
a4d85a
+              ssize_t aret = 0;
a4d85a
+
a4d85a
+              /* Archive all xattrs during creation, decide at extraction time
a4d85a
+               * which ones are of interest/use for the target filesystem. */
a4d85a
+              while (((fd == 0)
a4d85a
+                      ? ((aret = lgetxattrat (parentfd, file_name, attr,
a4d85a
+                                              val, asz)) == -1)
a4d85a
+                      : ((aret = fgetxattr (fd, attr, val, asz)) == -1))
a4d85a
+                     && (errno == ERANGE))
a4d85a
+                {
a4d85a
+		  val = x2nrealloc (val, &asz, 1);
a4d85a
+                }
a4d85a
+
a4d85a
+              if (aret != -1)
a4d85a
+                xheader_xattr_add (st, attr, val, aret);
a4d85a
+              else if (errno != ENOATTR)
a4d85a
+                call_arg_warn ((fd == 0) ? "lgetxattrat"
a4d85a
+                               : "fgetxattr", file_name);
a4d85a
+
a4d85a
+              attr += len + 1;
a4d85a
+              xret -= len + 1;
a4d85a
+            }
a4d85a
+        }
a4d85a
+#endif
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattrs__fd_set (struct tar_stat_info const *st,
a4d85a
+                char const *file_name, char typeflag,
a4d85a
+                const char *attr, const char *ptr, size_t len)
a4d85a
+{
a4d85a
+  if (ptr)
a4d85a
+    {
a4d85a
+      const char *sysname = "setxattrat";
a4d85a
+      int ret = -1;
a4d85a
+
a4d85a
+      if (typeflag != SYMTYPE)
a4d85a
+        ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
a4d85a
+      else
a4d85a
+        {
a4d85a
+          sysname = "lsetxattr";
a4d85a
+          ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
a4d85a
+        }
a4d85a
+
a4d85a
+      if (ret == -1)
a4d85a
+        WARNOPT (WARN_XATTR_WRITE,
a4d85a
+		 (0, errno,
a4d85a
+		  _("%s: Cannot set '%s' extended attribute for file '%s'"),
a4d85a
+		  sysname, attr, file_name));
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+/* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
a4d85a
+   zero, otherwise the fgetfileconat is used against correct file descriptor */
a4d85a
+void
a4d85a
+xattrs_selinux_get (int parentfd, char const *file_name,
a4d85a
+                    struct tar_stat_info *st, int fd)
a4d85a
+{
a4d85a
+  if (selinux_context_option > 0)
a4d85a
+    {
a4d85a
+#if HAVE_SELINUX_SELINUX_H != 1
a4d85a
+      static int done = 0;
a4d85a
+      if (!done)
a4d85a
+        WARN ((0, 0, _("SELinux support is not available")));
a4d85a
+      done = 1;
a4d85a
+#else
a4d85a
+      int result = fd ?
a4d85a
+	            fgetfilecon (fd, &st->cntx_name)
a4d85a
+                    : lgetfileconat (parentfd, file_name, &st->cntx_name);
a4d85a
+
a4d85a
+      if (result == -1 && errno != ENODATA && errno != ENOTSUP)
a4d85a
+        call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
a4d85a
+#endif
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_selinux_set (struct tar_stat_info const *st,
a4d85a
+                    char const *file_name, char typeflag)
a4d85a
+{
a4d85a
+  if (selinux_context_option > 0)
a4d85a
+    {
a4d85a
+#if HAVE_SELINUX_SELINUX_H != 1
a4d85a
+      static int done = 0;
a4d85a
+      if (!done)
a4d85a
+        WARN ((0, 0, _("SELinux support is not available")));
a4d85a
+      done = 1;
a4d85a
+#else
a4d85a
+      const char *sysname = "setfilecon";
a4d85a
+      int ret;
a4d85a
+
a4d85a
+      if (!st->cntx_name)
a4d85a
+        return;
a4d85a
+
a4d85a
+      if (typeflag != SYMTYPE)
a4d85a
+        {
a4d85a
+          ret = setfileconat (chdir_fd, file_name, st->cntx_name);
a4d85a
+          sysname = "setfileconat";
a4d85a
+        }
a4d85a
+      else
a4d85a
+        {
a4d85a
+          ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
a4d85a
+          sysname = "lsetfileconat";
a4d85a
+        }
a4d85a
+
a4d85a
+      if (ret == -1)
a4d85a
+        WARNOPT (WARN_XATTR_WRITE,
a4d85a
+		 (0, errno,
a4d85a
+		  _("%s: Cannot set SELinux context for file '%s'"),
a4d85a
+		  sysname, file_name));
a4d85a
+#endif
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+static bool
a4d85a
+xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
a4d85a
+{
a4d85a
+  int i;
a4d85a
+
a4d85a
+  if (!mm->size)
a4d85a
+    return false;
a4d85a
+
a4d85a
+  for (i = 0; i < mm->used; i++)
a4d85a
+    if (fnmatch (mm->masks[i], kw, 0) == 0)
a4d85a
+      return true;
a4d85a
+
a4d85a
+  return false;
a4d85a
+}
a4d85a
+
a4d85a
+#define USER_DOT_PFX "user."
a4d85a
+
a4d85a
+static bool
a4d85a
+xattrs_kw_included (const char *kw, bool archiving)
a4d85a
+{
a4d85a
+  if (xattrs_setup.incl.size)
a4d85a
+    return xattrs_matches_mask (kw, &xattrs_setup.incl);
a4d85a
+  else if (archiving)
a4d85a
+    return true;
a4d85a
+  else
a4d85a
+    return strncmp (kw, USER_DOT_PFX, sizeof (USER_DOT_PFX) - 1) == 0;
a4d85a
+}
a4d85a
+
a4d85a
+static bool
a4d85a
+xattrs_kw_excluded (const char *kw, bool archiving)
a4d85a
+{
a4d85a
+  return xattrs_setup.excl.size ?
a4d85a
+    xattrs_matches_mask (kw, &xattrs_setup.excl) : false;
a4d85a
+}
a4d85a
+
a4d85a
+/* Check whether the xattr with keyword KW should be discarded from list of
a4d85a
+   attributes that are going to be archived/excluded (set ARCHIVING=true for
a4d85a
+   archiving, false for excluding) */
a4d85a
+static bool
a4d85a
+xattrs_masked_out (const char *kw, bool archiving)
a4d85a
+{
a4d85a
+  return xattrs_kw_included (kw, archiving) ?
a4d85a
+    xattrs_kw_excluded (kw, archiving) : true; 
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_xattrs_set (struct tar_stat_info const *st,
a4d85a
+                   char const *file_name, char typeflag, int later_run)
a4d85a
+{
a4d85a
+  if (xattrs_option > 0)
a4d85a
+    {
a4d85a
+#ifndef HAVE_XATTRS
a4d85a
+      static int done = 0;
a4d85a
+      if (!done)
a4d85a
+        WARN ((0, 0, _("XATTR support is not available")));
a4d85a
+      done = 1;
a4d85a
+#else
a4d85a
+      size_t scan = 0;
a4d85a
+
a4d85a
+      if (!st->xattr_map_size)
a4d85a
+        return;
a4d85a
+
a4d85a
+      for (; scan < st->xattr_map_size; ++scan)
a4d85a
+        {
a4d85a
+          char *keyword = st->xattr_map[scan].xkey;
a4d85a
+          keyword += strlen ("SCHILY.xattr.");
a4d85a
+
a4d85a
+          /* TODO: this 'later_run' workaround is temporary solution -> once
a4d85a
+             capabilities should become fully supported by it's API and there
a4d85a
+             should exist something like xattrs_capabilities_set() call.
a4d85a
+             For a regular files: all extended attributes are restored during
a4d85a
+             the first run except 'security.capability' which is restored in
a4d85a
+             'later_run == 1'.  */
a4d85a
+          if (typeflag == REGTYPE
a4d85a
+              && later_run == !!strcmp (keyword, "security.capability"))
a4d85a
+            continue;
a4d85a
+
a4d85a
+          if (xattrs_masked_out (keyword, false /* extracting */ ))
a4d85a
+            /* we don't want to restore this keyword */
a4d85a
+            continue;
a4d85a
+
a4d85a
+          xattrs__fd_set (st, file_name, typeflag, keyword,
a4d85a
+                          st->xattr_map[scan].xval_ptr,
a4d85a
+                          st->xattr_map[scan].xval_len);
a4d85a
+        }
a4d85a
+#endif
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_print_char (struct tar_stat_info const *st, char *output)
a4d85a
+{
a4d85a
+  int i;
a4d85a
+
a4d85a
+  if (verbose_option < 2)
a4d85a
+    {
a4d85a
+      *output = 0;
a4d85a
+      return;
a4d85a
+    }
a4d85a
+
a4d85a
+  if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
a4d85a
+    {
a4d85a
+      /* placeholders */
a4d85a
+      *output = ' ';
a4d85a
+      output[1] = 0;
a4d85a
+    }
a4d85a
+
a4d85a
+  if (xattrs_option > 0 && st->xattr_map_size)
a4d85a
+    for (i = 0; i < st->xattr_map_size; ++i)
a4d85a
+      {
a4d85a
+        char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
a4d85a
+        if (!xattrs_masked_out (keyword, false /* like extracting */ ))
a4d85a
+	  {
a4d85a
+	    *output = '*';
a4d85a
+	    break;
a4d85a
+	  }
a4d85a
+      }
a4d85a
+
a4d85a
+  if (selinux_context_option > 0 && st->cntx_name)
a4d85a
+    *output = '.';
a4d85a
+
a4d85a
+  if (acls_option && (st->acls_a_len || st->acls_d_len))
a4d85a
+    *output = '+';
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xattrs_print (struct tar_stat_info const *st)
a4d85a
+{
a4d85a
+  if (verbose_option < 3)
a4d85a
+    return;
a4d85a
+
a4d85a
+  /* selinux */
a4d85a
+  if (selinux_context_option && st->cntx_name)
a4d85a
+    fprintf (stdlis, "  s: %s\n", st->cntx_name);
a4d85a
+
a4d85a
+  /* acls */
a4d85a
+  if (acls_option && (st->acls_a_len || st->acls_d_len))
a4d85a
+    {
a4d85a
+      fprintf (stdlis, "  a: ");
a4d85a
+      acls_one_line ("", ',', st->acls_a_ptr, st->acls_a_len);
a4d85a
+      acls_one_line ("default:", ',', st->acls_d_ptr, st->acls_d_len);
a4d85a
+      fprintf (stdlis, "\n");
a4d85a
+    }
a4d85a
+
a4d85a
+  /* xattrs */
a4d85a
+  if (xattrs_option && st->xattr_map_size)
a4d85a
+    {
a4d85a
+      int i;
a4d85a
+      
a4d85a
+      for (i = 0; i < st->xattr_map_size; ++i)
a4d85a
+        {
a4d85a
+          char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
a4d85a
+          if (!xattrs_masked_out (keyword, false /* like extracting */ ))
a4d85a
+	    fprintf (stdlis, "  x: %lu %s\n",
a4d85a
+		     (unsigned long) st->xattr_map[i].xval_len, keyword);
a4d85a
+        }
a4d85a
+    }
a4d85a
+}
a4d85a
diff --git a/src/xattrs.h b/src/xattrs.h
a4d85a
new file mode 100644
a4d85a
index 0000000..bfef466
a4d85a
--- /dev/null
a4d85a
+++ b/src/xattrs.h
a4d85a
@@ -0,0 +1,51 @@
a4d85a
+/* Support for extended attributes.
a4d85a
+
a4d85a
+   Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software
a4d85a
+   Foundation, Inc.
a4d85a
+
a4d85a
+   Written by James Antill, on 2006-07-27.
a4d85a
+
a4d85a
+   This program is free software; you can redistribute it and/or modify it
a4d85a
+   under the terms of the GNU General Public License as published by the
a4d85a
+   Free Software Foundation; either version 3, or (at your option) any later
a4d85a
+   version.
a4d85a
+
a4d85a
+   This program is distributed in the hope that it will be useful, but
a4d85a
+   WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
a4d85a
+   Public License for more details.
a4d85a
+
a4d85a
+   You should have received a copy of the GNU General Public License along
a4d85a
+   with this program; if not, write to the Free Software Foundation, Inc.,
a4d85a
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
a4d85a
+*/
a4d85a
+
a4d85a
+#ifndef GUARD_XATTTRS_H
a4d85a
+#define GUARD_XATTTRS_H
a4d85a
+
a4d85a
+/* Add include/exclude fnmatch pattern for xattr key domain.  Set INCL parameter
a4d85a
+   to true/false if you want to add include/exclude pattern */
a4d85a
+extern void xattrs_mask_add (const char *mask, bool incl);
a4d85a
+
a4d85a
+/* clear helping structures when tar finishes */
a4d85a
+extern void xattrs_clear_setup ();
a4d85a
+
a4d85a
+extern void xattrs_acls_get (int parentfd, char const *file_name,
a4d85a
+                             struct tar_stat_info *st, int fd, int xisfile);
a4d85a
+extern void xattrs_selinux_get (int parentfd, char const *file_name,
a4d85a
+                                struct tar_stat_info *st, int fd);
a4d85a
+extern void xattrs_xattrs_get (int parentfd, char const *file_name,
a4d85a
+                               struct tar_stat_info *st, int fd);
a4d85a
+
a4d85a
+extern void xattrs_acls_set (struct tar_stat_info const *st,
a4d85a
+                             char const *file_name, char typeflag);
a4d85a
+extern void xattrs_selinux_set (struct tar_stat_info const *st,
a4d85a
+                                char const *file_name, char typeflag);
a4d85a
+extern void xattrs_xattrs_set (struct tar_stat_info const *st,
a4d85a
+                               char const *file_name, char typeflag,
a4d85a
+                               int later_run);
a4d85a
+
a4d85a
+extern void xattrs_print_char (struct tar_stat_info const *st, char *output);
a4d85a
+extern void xattrs_print (struct tar_stat_info const *st);
a4d85a
+
a4d85a
+#endif /* GUARD_XATTTRS_H */
a4d85a
diff --git a/src/xheader.c b/src/xheader.c
a4d85a
index 2284e97..be793d4 100644
a4d85a
--- a/src/xheader.c
a4d85a
+++ b/src/xheader.c
a4d85a
@@ -1,7 +1,7 @@
a4d85a
 /* POSIX extended headers for tar.
a4d85a
 
a4d85a
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software
a4d85a
-   Foundation, Inc.
a4d85a
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012
a4d85a
+   Free Software Foundation, Inc.
a4d85a
 
a4d85a
    This program is free software; you can redistribute it and/or modify it
a4d85a
    under the terms of the GNU General Public License as published by the
a4d85a
@@ -460,6 +460,123 @@ xheader_write_global (struct xheader *xhdr)
a4d85a
     }
a4d85a
 }
a4d85a
 
a4d85a
+void
a4d85a
+xheader_xattr_init (struct tar_stat_info *st)
a4d85a
+{
a4d85a
+  st->xattr_map = NULL;
a4d85a
+  st->xattr_map_size = 0;
a4d85a
+
a4d85a
+  st->acls_a_ptr = NULL;
a4d85a
+  st->acls_a_len = 0;
a4d85a
+  st->acls_d_ptr = NULL;
a4d85a
+  st->acls_d_len = 0;
a4d85a
+  st->cntx_name = NULL;
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xheader_xattr_free (struct xattr_array *xattr_map, size_t xattr_map_size)
a4d85a
+{
a4d85a
+  size_t scan = 0;
a4d85a
+
a4d85a
+  while (scan < xattr_map_size)
a4d85a
+    {
a4d85a
+      free (xattr_map[scan].xkey);
a4d85a
+      free (xattr_map[scan].xval_ptr);
a4d85a
+
a4d85a
+      ++scan;
a4d85a
+    }
a4d85a
+  free (xattr_map);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xheader_xattr__add (struct xattr_array **xattr_map,
a4d85a
+		    size_t *xattr_map_size,
a4d85a
+		    const char *key, const char *val, size_t len)
a4d85a
+{
a4d85a
+  size_t pos = (*xattr_map_size)++;
a4d85a
+
a4d85a
+  *xattr_map = xrealloc (*xattr_map,
a4d85a
+                         *xattr_map_size * sizeof(struct xattr_array));
a4d85a
+  (*xattr_map)[pos].xkey = xstrdup (key);
a4d85a
+  (*xattr_map)[pos].xval_ptr = xmemdup (val, len + 1);
a4d85a
+  (*xattr_map)[pos].xval_len = len;
a4d85a
+}
a4d85a
+
a4d85a
+/* This is reversal function for xattr_encode_keyword.  See comment for
a4d85a
+   xattr_encode_keyword() for more info. */
a4d85a
+static void
a4d85a
+xattr_decode_keyword (char *keyword)
a4d85a
+{
a4d85a
+  char *kpr, *kpl; /* keyword pointer left/right */
a4d85a
+  kpr = kpl = keyword;
a4d85a
+
a4d85a
+  for (;;)
a4d85a
+    {
a4d85a
+      if (*kpr == '%')
a4d85a
+        {
a4d85a
+          if (kpr[1] == '3' && kpr[2] == 'D')
a4d85a
+            {
a4d85a
+              *kpl = '=';
a4d85a
+              kpr += 3;
a4d85a
+              kpl ++;
a4d85a
+              continue;
a4d85a
+            }
a4d85a
+          else if (kpr[1] == '2' && kpr[2] == '5')
a4d85a
+            {
a4d85a
+              *kpl = '%';
a4d85a
+              kpr += 3;
a4d85a
+              kpl ++;
a4d85a
+              continue;
a4d85a
+            }
a4d85a
+        }
a4d85a
+
a4d85a
+      *kpl = *kpr;
a4d85a
+
a4d85a
+      if (*kpr == 0)
a4d85a
+        break;
a4d85a
+
a4d85a
+      kpr++;
a4d85a
+      kpl++;
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xheader_xattr_add (struct tar_stat_info *st,
a4d85a
+		   const char *key, const char *val, size_t len)
a4d85a
+{
a4d85a
+  size_t klen = strlen (key);
a4d85a
+  char *xkey = xmalloc (strlen("SCHILY.xattr.") + klen + 1);
a4d85a
+  char *tmp = xkey;
a4d85a
+
a4d85a
+  tmp = stpcpy (tmp, "SCHILY.xattr.");
a4d85a
+  stpcpy (tmp, key);
a4d85a
+
a4d85a
+  xheader_xattr__add (&st->xattr_map, &st->xattr_map_size, xkey, val, len);
a4d85a
+
a4d85a
+  free (xkey);
a4d85a
+}
a4d85a
+
a4d85a
+void
a4d85a
+xheader_xattr_copy (const struct tar_stat_info *st,
a4d85a
+		    struct xattr_array **xattr_map, size_t *xattr_map_size)
a4d85a
+{
a4d85a
+  size_t scan = 0;
a4d85a
+
a4d85a
+  *xattr_map = NULL;
a4d85a
+  *xattr_map_size = 0;
a4d85a
+
a4d85a
+  while (scan < st->xattr_map_size)
a4d85a
+    {
a4d85a
+      char  *key = st->xattr_map[scan].xkey;
a4d85a
+      char  *val = st->xattr_map[scan].xval_ptr;
a4d85a
+      size_t len = st->xattr_map[scan].xval_len;
a4d85a
+
a4d85a
+      xheader_xattr__add(xattr_map, xattr_map_size, key, val, len);
a4d85a
+
a4d85a
+      ++scan;
a4d85a
+    }
a4d85a
+}
a4d85a
+
a4d85a
 
a4d85a
 /* General Interface */
a4d85a
 
a4d85a
@@ -473,6 +590,7 @@ struct xhdr_tab
a4d85a
 		 struct xheader *, void const *data);
a4d85a
   void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
a4d85a
   int flags;
a4d85a
+  bool prefix; /* select handler comparing prefix only */
a4d85a
 };
a4d85a
 
a4d85a
 /* This declaration must be extern, because ISO C99 section 6.9.2
a4d85a
@@ -489,8 +607,17 @@ locate_handler (char const *keyword)
a4d85a
   struct xhdr_tab const *p;
a4d85a
 
a4d85a
   for (p = xhdr_tab; p->keyword; p++)
a4d85a
-    if (strcmp (p->keyword, keyword) == 0)
a4d85a
-      return p;
a4d85a
+    if (p->prefix)
a4d85a
+      {
a4d85a
+        if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0)
a4d85a
+          return p;
a4d85a
+      }
a4d85a
+    else
a4d85a
+      {
a4d85a
+        if (strcmp (p->keyword, keyword) == 0)
a4d85a
+          return p;
a4d85a
+      }
a4d85a
+
a4d85a
   return NULL;
a4d85a
 }
a4d85a
 
a4d85a
@@ -500,7 +627,8 @@ xheader_protected_pattern_p (const char *pattern)
a4d85a
   struct xhdr_tab const *p;
a4d85a
 
a4d85a
   for (p = xhdr_tab; p->keyword; p++)
a4d85a
-    if ((p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
a4d85a
+    if (!p->prefix && (p->flags & XHDR_PROTECTED)
a4d85a
+        && fnmatch (pattern, p->keyword, 0) == 0)
a4d85a
       return true;
a4d85a
   return false;
a4d85a
 }
a4d85a
@@ -511,7 +639,8 @@ xheader_protected_keyword_p (const char *keyword)
a4d85a
   struct xhdr_tab const *p;
a4d85a
 
a4d85a
   for (p = xhdr_tab; p->keyword; p++)
a4d85a
-    if ((p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
a4d85a
+    if (!p->prefix && (p->flags & XHDR_PROTECTED)
a4d85a
+        && strcmp (p->keyword, keyword) == 0)
a4d85a
       return true;
a4d85a
   return false;
a4d85a
 }
a4d85a
@@ -721,15 +850,71 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size)
a4d85a
   while (size > 0);
a4d85a
 }
a4d85a
 
a4d85a
+/* xattr_encode_keyword() substitutes '=' ~~> '%3D' and '%' ~~> '%25'
a4d85a
+   in extended attribute keywords.  This is needed because the '=' character
a4d85a
+   has special purpose in extended attribute header - it splits keyword and
a4d85a
+   value part of header.  If there was the '=' occurrence allowed inside
a4d85a
+   keyword, there would be no unambiguous way how to decode this extended
a4d85a
+   attribute.
a4d85a
+
a4d85a
+   (http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html)
a4d85a
+ */
a4d85a
+static char *
a4d85a
+xattr_encode_keyword(const char *keyword)
a4d85a
+{
a4d85a
+  static char *encode_buffer = NULL;
a4d85a
+  static size_t encode_buffer_size = 0;
a4d85a
+  size_t bp; /* keyword/buffer pointers */
a4d85a
+
a4d85a
+  if (!encode_buffer)
a4d85a
+    {
a4d85a
+      encode_buffer_size = 256;
a4d85a
+      encode_buffer = xmalloc (encode_buffer_size);
a4d85a
+    }
a4d85a
+  else
a4d85a
+    *encode_buffer = 0;
a4d85a
+
a4d85a
+  for (bp = 0; *keyword != 0; ++bp, ++keyword)
a4d85a
+    {
a4d85a
+      char c = *keyword;
a4d85a
+
a4d85a
+      if (bp + 2 /* enough for URL encoding also.. */ >= encode_buffer_size)
a4d85a
+        {
a4d85a
+          encode_buffer = x2realloc (encode_buffer, &encode_buffer_size);
a4d85a
+        }
a4d85a
+
a4d85a
+      if (c == '%')
a4d85a
+        {
a4d85a
+          strcpy (encode_buffer + bp, "%25");
a4d85a
+          bp += 2;
a4d85a
+        }
a4d85a
+      else if (c == '=')
a4d85a
+        {
a4d85a
+          strcpy (encode_buffer + bp, "%3D");
a4d85a
+          bp += 2;
a4d85a
+        }
a4d85a
+      else
a4d85a
+        encode_buffer[bp] = c;
a4d85a
+    }
a4d85a
+
a4d85a
+  encode_buffer[bp] = 0;
a4d85a
+
a4d85a
+  return encode_buffer;
a4d85a
+}
a4d85a
+
a4d85a
 static void
a4d85a
 xheader_print_n (struct xheader *xhdr, char const *keyword,
a4d85a
 		 char const *value, size_t vsize)
a4d85a
 {
a4d85a
-  size_t len = strlen (keyword) + vsize + 3; /* ' ' + '=' + '\n' */
a4d85a
   size_t p;
a4d85a
   size_t n = 0;
a4d85a
   char nbuf[UINTMAX_STRSIZE_BOUND];
a4d85a
   char const *np;
a4d85a
+  size_t len, klen;
a4d85a
+
a4d85a
+  keyword = xattr_encode_keyword (keyword);
a4d85a
+  klen = strlen (keyword);
a4d85a
+  len = klen + vsize + 3; /* ' ' + '=' + '\n' */
a4d85a
 
a4d85a
   do
a4d85a
     {
a4d85a
@@ -741,7 +926,7 @@ xheader_print_n (struct xheader *xhdr, char const *keyword,
a4d85a
 
a4d85a
   x_obstack_grow (xhdr, np, n);
a4d85a
   x_obstack_1grow (xhdr, ' ');
a4d85a
-  x_obstack_grow (xhdr, keyword, strlen (keyword));
a4d85a
+  x_obstack_grow (xhdr, keyword, klen);
a4d85a
   x_obstack_1grow (xhdr, '=');
a4d85a
   x_obstack_grow (xhdr, value, vsize);
a4d85a
   x_obstack_1grow (xhdr, '\n');
a4d85a
@@ -1002,8 +1187,6 @@ decode_time (struct timespec *ts, char const *arg, char const *keyword)
a4d85a
   return true;
a4d85a
 }
a4d85a
 
a4d85a
-
a4d85a
-
a4d85a
 static void
a4d85a
 code_num (uintmax_t value, char const *keyword, struct xheader *xhdr)
a4d85a
 {
a4d85a
@@ -1470,6 +1653,80 @@ volume_filename_decoder (struct tar_stat_info *st,
a4d85a
 }
a4d85a
 
a4d85a
 static void
a4d85a
+xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword,
a4d85a
+                     struct xheader *xhdr, void const *data)
a4d85a
+{
a4d85a
+  code_string (st->cntx_name, keyword, xhdr);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_selinux_decoder (struct tar_stat_info *st,
a4d85a
+                       char const *keyword, char const *arg, size_t size)
a4d85a
+{
a4d85a
+  decode_string (&st->cntx_name, arg);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword,
a4d85a
+                    struct xheader *xhdr, void const *data)
a4d85a
+{
a4d85a
+  xheader_print_n (xhdr, keyword, st->acls_a_ptr, st->acls_a_len);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_acls_a_decoder (struct tar_stat_info *st,
a4d85a
+                      char const *keyword, char const *arg, size_t size)
a4d85a
+{
a4d85a
+  st->acls_a_ptr = xmemdup (arg, size + 1);
a4d85a
+  st->acls_a_len = size;
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_acls_d_coder (struct tar_stat_info const *st , char const *keyword,
a4d85a
+                    struct xheader *xhdr, void const *data)
a4d85a
+{
a4d85a
+  xheader_print_n (xhdr, keyword, st->acls_d_ptr, st->acls_d_len);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_acls_d_decoder (struct tar_stat_info *st,
a4d85a
+                      char const *keyword, char const *arg, size_t size)
a4d85a
+{
a4d85a
+  st->acls_d_ptr = xmemdup (arg, size + 1);
a4d85a
+  st->acls_d_len = size;
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_coder (struct tar_stat_info const *st, char const *keyword,
a4d85a
+             struct xheader *xhdr, void const *data)
a4d85a
+{
a4d85a
+  struct xattr_array *xattr_map = st->xattr_map;
a4d85a
+  const size_t *off = data;
a4d85a
+  xheader_print_n (xhdr, keyword,
a4d85a
+                   xattr_map[*off].xval_ptr, xattr_map[*off].xval_len);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
+xattr_decoder (struct tar_stat_info *st,
a4d85a
+               char const *keyword, char const *arg, size_t size)
a4d85a
+{
a4d85a
+  char *xstr, *xkey;
a4d85a
+
a4d85a
+  /* copy keyword */
a4d85a
+  size_t klen_raw = strlen (keyword);
a4d85a
+  xkey = alloca (klen_raw + 1);
a4d85a
+  memcpy (xkey, keyword, klen_raw + 1) /* including null-terminating */;
a4d85a
+
a4d85a
+  /* copy value */
a4d85a
+  xstr = alloca (size + 1);
a4d85a
+  memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */;
a4d85a
+
a4d85a
+  xattr_decode_keyword (xkey);
a4d85a
+
a4d85a
+  xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size);
a4d85a
+}
a4d85a
+
a4d85a
+static void
a4d85a
 sparse_major_coder (struct tar_stat_info const *st, char const *keyword,
a4d85a
 		    struct xheader *xhdr, void const *data)
a4d85a
 {
a4d85a
@@ -1506,53 +1763,53 @@ sparse_minor_decoder (struct tar_stat_info *st,
a4d85a
 }
a4d85a
 
a4d85a
 struct xhdr_tab const xhdr_tab[] = {
a4d85a
-  { "atime",	atime_coder,	atime_decoder,	  0 },
a4d85a
-  { "comment",	dummy_coder,	dummy_decoder,	  0 },
a4d85a
-  { "charset",	dummy_coder,	dummy_decoder,	  0 },
a4d85a
-  { "ctime",	ctime_coder,	ctime_decoder,	  0 },
a4d85a
-  { "gid",	gid_coder,	gid_decoder,	  0 },
a4d85a
-  { "gname",	gname_coder,	gname_decoder,	  0 },
a4d85a
-  { "linkpath", linkpath_coder, linkpath_decoder, 0 },
a4d85a
-  { "mtime",	mtime_coder,	mtime_decoder,	  0 },
a4d85a
-  { "path",	path_coder,	path_decoder,	  0 },
a4d85a
-  { "size",	size_coder,	size_decoder,	  0 },
a4d85a
-  { "uid",	uid_coder,	uid_decoder,	  0 },
a4d85a
-  { "uname",	uname_coder,	uname_decoder,	  0 },
a4d85a
+  { "atime",    atime_coder,    atime_decoder,    0, false },
a4d85a
+  { "comment",  dummy_coder,    dummy_decoder,    0, false },
a4d85a
+  { "charset",  dummy_coder,    dummy_decoder,    0, false },
a4d85a
+  { "ctime",    ctime_coder,    ctime_decoder,    0, false },
a4d85a
+  { "gid",      gid_coder,      gid_decoder,      0, false },
a4d85a
+  { "gname",    gname_coder,    gname_decoder,    0, false },
a4d85a
+  { "linkpath", linkpath_coder, linkpath_decoder, 0, false },
a4d85a
+  { "mtime",    mtime_coder,    mtime_decoder,    0, false },
a4d85a
+  { "path",     path_coder,     path_decoder,     0, false },
a4d85a
+  { "size",     size_coder,     size_decoder,     0, false },
a4d85a
+  { "uid",      uid_coder,      uid_decoder,      0, false },
a4d85a
+  { "uname",    uname_coder,    uname_decoder,    0, false },
a4d85a
 
a4d85a
   /* Sparse file handling */
a4d85a
   { "GNU.sparse.name",       path_coder, path_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   { "GNU.sparse.major",      sparse_major_coder, sparse_major_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   { "GNU.sparse.minor",      sparse_minor_coder, sparse_minor_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   { "GNU.sparse.realsize",   sparse_size_coder, sparse_size_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   { "GNU.sparse.numblocks",  sparse_numblocks_coder, sparse_numblocks_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
 
a4d85a
   /* tar 1.14 - 1.15.90 keywords. */
a4d85a
   { "GNU.sparse.size",       sparse_size_coder, sparse_size_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
a4d85a
      headers, and each of them was meaningful. It confilcted with POSIX specs,
a4d85a
      which requires that "when extended header records conflict, the last one
a4d85a
      given in the header shall take precedence." */
a4d85a
   { "GNU.sparse.offset",     sparse_offset_coder, sparse_offset_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   { "GNU.sparse.numbytes",   sparse_numbytes_coder, sparse_numbytes_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
   /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
a4d85a
   { "GNU.sparse.map",        NULL /* Unused, see pax_dump_header() */,
a4d85a
-    sparse_map_decoder, 0 },
a4d85a
+    sparse_map_decoder, 0, false },
a4d85a
 
a4d85a
   { "GNU.dumpdir",           dumpdir_coder, dumpdir_decoder,
a4d85a
-    XHDR_PROTECTED },
a4d85a
+    XHDR_PROTECTED, false },
a4d85a
 
a4d85a
   /* Keeps the tape/volume label. May be present only in the global headers.
a4d85a
      Equivalent to GNUTYPE_VOLHDR.  */
a4d85a
   { "GNU.volume.label", volume_label_coder, volume_label_decoder,
a4d85a
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4d85a
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
a4d85a
 
a4d85a
   /* These may be present in a first global header of the archive.
a4d85a
      They provide the same functionality as GNUTYPE_MULTIVOL header.
a4d85a
@@ -1561,11 +1818,28 @@ struct xhdr_tab const xhdr_tab[] = {
a4d85a
      GNU.volume.offset keeps the offset of the start of this volume,
a4d85a
      otherwise kept in oldgnu_header.offset.  */
a4d85a
   { "GNU.volume.filename", volume_label_coder, volume_filename_decoder,
a4d85a
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4d85a
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
a4d85a
   { "GNU.volume.size", volume_size_coder, volume_size_decoder,
a4d85a
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4d85a
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
a4d85a
   { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder,
a4d85a
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4d85a
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
a4d85a
+
a4d85a
+  /* We get the SELinux value from filecon, so add a namespace for SELinux
a4d85a
+     instead of storing it in SCHILY.xattr.* (which would be RAW). */
a4d85a
+  { "RHT.security.selinux",
a4d85a
+    xattr_selinux_coder, xattr_selinux_decoder, 0, false },
a4d85a
+
a4d85a
+  /* ACLs, use the star format... */
a4d85a
+  { "SCHILY.acl.access",
a4d85a
+    xattr_acls_a_coder, xattr_acls_a_decoder, 0, false },
a4d85a
+
a4d85a
+  { "SCHILY.acl.default",
a4d85a
+    xattr_acls_d_coder, xattr_acls_d_decoder, 0, false },
a4d85a
+
a4d85a
+  /* We are storing all extended attributes using this rule even if some of them
a4d85a
+     were stored by some previous rule (duplicates) -- we just have to make sure
a4d85a
+     they are restored *only once* during extraction later on. */
a4d85a
+  { "SCHILY.xattr", xattr_coder, xattr_decoder, 0, true },
a4d85a
 
a4d85a
-  { NULL, NULL, NULL, 0 }
a4d85a
+  { NULL, NULL, NULL, 0, false }
a4d85a
 };
a4d85a
diff --git a/tests/Makefile.am b/tests/Makefile.am
a4d85a
index 3d78ea2..b0da439 100644
a4d85a
--- a/tests/Makefile.am
a4d85a
+++ b/tests/Makefile.am
a4d85a
@@ -171,7 +171,17 @@ TESTSUITE_AT = \
a4d85a
  star/multi-fail.at\
a4d85a
  star/ustar-big-2g.at\
a4d85a
  star/ustar-big-8g.at\
a4d85a
- star/pax-big-10g.at
a4d85a
+ star/pax-big-10g.at\
a4d85a
+ xattr01.at\
a4d85a
+ xattr02.at\
a4d85a
+ xattr03.at\
a4d85a
+ xattr04.at\
a4d85a
+ xattr05.at\
a4d85a
+ acls01.at\
a4d85a
+ acls02.at\
a4d85a
+ selnx01.at\
a4d85a
+ selacl01.at\
a4d85a
+ capabs_raw01.at
a4d85a
 
a4d85a
 TESTSUITE = $(srcdir)/testsuite
a4d85a
 
a4d85a
diff --git a/tests/acls01.at b/tests/acls01.at
a4d85a
new file mode 100644
a4d85a
index 0000000..0149f2d
a4d85a
--- /dev/null
a4d85a
+++ b/tests/acls01.at
a4d85a
@@ -0,0 +1,53 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2011 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# This is basic test for acl support.
a4d85a
+
a4d85a
+AT_SETUP([acls: basic functionality])
a4d85a
+AT_KEYWORDS([xattrs acls acls01])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_UTILS_PREREQ
a4d85a
+AT_ACLS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+genfile --file dir/file
a4d85a
+
a4d85a
+MYNAME=$( id -un )
a4d85a
+
a4d85a
+setfacl -m u:$MYNAME:--x dir/file
a4d85a
+setfacl -m u:$MYNAME:--x dir
a4d85a
+
a4d85a
+getfattr -h -m. -d dir dir/file > before
a4d85a
+
a4d85a
+tar --acls -cf archive.tar dir
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+tar --acls -xf archive.tar
a4d85a
+
a4d85a
+getfattr -h -m. -d dir dir/file > after
a4d85a
+
a4d85a
+diff before after
a4d85a
+test "$?" = 0
a4d85a
+],
a4d85a
+[0],
a4d85a
+[])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/acls02.at b/tests/acls02.at
a4d85a
new file mode 100644
a4d85a
index 0000000..2ee1c5f
a4d85a
--- /dev/null
a4d85a
+++ b/tests/acls02.at
a4d85a
@@ -0,0 +1,59 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2011 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# This is basic test for acl support.
a4d85a
+
a4d85a
+AT_SETUP([acls: work with -C])
a4d85a
+AT_KEYWORDS([xattrs acls acls02])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_UTILS_PREREQ
a4d85a
+AT_ACLS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+mkdir dir/subdir
a4d85a
+genfile --file dir/subdir/file
a4d85a
+
a4d85a
+MYNAME=$( id -un )
a4d85a
+
a4d85a
+setfacl -m u:$MYNAME:--x dir/subdir
a4d85a
+setfacl -m u:$MYNAME:--x dir/subdir/file
a4d85a
+
a4d85a
+cd dir
a4d85a
+getfattr -h -m. -d subdir subdir/file > ../before
a4d85a
+cd ..
a4d85a
+
a4d85a
+tar --acls -cf archive.tar -C dir subdir
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+tar --acls -xf archive.tar -C dir
a4d85a
+
a4d85a
+cd dir
a4d85a
+getfattr -h -m. -d subdir subdir/file > ../after
a4d85a
+cd ..
a4d85a
+
a4d85a
+diff before after
a4d85a
+test "$?" = 0
a4d85a
+],
a4d85a
+[0],
a4d85a
+[])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/capabs_raw01.at b/tests/capabs_raw01.at
a4d85a
new file mode 100644
a4d85a
index 0000000..8eea0cf
a4d85a
--- /dev/null
a4d85a
+++ b/tests/capabs_raw01.at
a4d85a
@@ -0,0 +1,51 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:  Test if file capabilities are archived/restored correctly
a4d85a
+# using just the default xattr support (capabilities are stored/restored in
a4d85a
+# binary format -> system dependant).
a4d85a
+
a4d85a
+AT_SETUP([capabilities: binary store/restore])
a4d85a
+AT_KEYWORDS([xattrs capabilities capabs_raw01])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_PRIVILEGED_PREREQ
a4d85a
+AT_XATTRS_PREREQ
a4d85a
+AT_CAPABILITIES_UTILS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+genfile --file dir/file
a4d85a
+
a4d85a
+setcap "= cap_chown=ei" dir/file
a4d85a
+
a4d85a
+# archive whole directory including binary xattrs
a4d85a
+tar --xattrs -cf archive.tar dir
a4d85a
+
a4d85a
+# clear the directory
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+# restore _all_ xattrs (not just the user.* domain)
a4d85a
+tar --xattrs --xattrs-include='*' -xf archive.tar
a4d85a
+
a4d85a
+getcap dir/file
a4d85a
+],
a4d85a
+[0],
a4d85a
+[dir/file = cap_chown+ei
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/selacl01.at b/tests/selacl01.at
a4d85a
new file mode 100644
a4d85a
index 0000000..90d0c5b
a4d85a
--- /dev/null
a4d85a
+++ b/tests/selacl01.at
a4d85a
@@ -0,0 +1,64 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2011 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# This is basic test for support of extended attributes.
a4d85a
+
a4d85a
+AT_SETUP([acls/selinux: special files & fifos])
a4d85a
+AT_KEYWORDS([xattrs selinux acls selacls01])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_PRIVILEGED_PREREQ
a4d85a
+AT_XATTRS_UTILS_PREREQ
a4d85a
+AT_SELINUX_PREREQ
a4d85a
+AT_ACLS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+mkfifo dir/fifo
a4d85a
+MAJOR=$( stat /dev/urandom --printf="%t" )
a4d85a
+MINOR=$( stat /dev/urandom --printf="%T" )
a4d85a
+mknod dir/chartype c $MAJOR $MINOR
a4d85a
+
a4d85a
+# setup attributes
a4d85a
+restorecon -R dir
a4d85a
+chcon -h --user=system_u dir/fifo
a4d85a
+chcon -h --user=system_u dir/chartype
a4d85a
+setfacl -m u:$UID:--- dir/fifo
a4d85a
+setfacl -m u:$UID:rwx dir/chartype
a4d85a
+
a4d85a
+getfacl dir/fifo >> before
a4d85a
+getfattr -msecurity.selinux dir/chartype >> before
a4d85a
+
a4d85a
+tar --xattrs --selinux --acls -cf archive.tar dir
a4d85a
+
a4d85a
+mv dir olddir
a4d85a
+
a4d85a
+tar --xattrs --selinux --acls -xf archive.tar
a4d85a
+
a4d85a
+getfacl dir/fifo >> after
a4d85a
+getfattr -msecurity.selinux dir/chartype >> after
a4d85a
+
a4d85a
+diff before after
a4d85a
+echo separator
a4d85a
+],
a4d85a
+[0],
a4d85a
+[separator
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/selnx01.at b/tests/selnx01.at
a4d85a
new file mode 100644
a4d85a
index 0000000..79f7267
a4d85a
--- /dev/null
a4d85a
+++ b/tests/selnx01.at
a4d85a
@@ -0,0 +1,96 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# This is basic test for selinux support (store & restore).
a4d85a
+
a4d85a
+AT_SETUP([selinux: basic store/restore])
a4d85a
+AT_KEYWORDS([xattrs selinux selnx01])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_UTILS_PREREQ
a4d85a
+AT_SELINUX_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+genfile --file dir/file
a4d85a
+ln -s file dir/link
a4d85a
+
a4d85a
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > start
a4d85a
+
a4d85a
+restorecon -R dir
a4d85a
+chcon -h --user=system_u     dir
a4d85a
+chcon -h --user=unconfined_u dir/file
a4d85a
+chcon -h --user=system_u     dir/link
a4d85a
+
a4d85a
+# archive whole directory including selinux contexts
a4d85a
+tar --selinux -cf archive.tar dir
a4d85a
+
a4d85a
+# clear the directory
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+# ================================================
a4d85a
+# check if selinux contexts are correctly restored
a4d85a
+
a4d85a
+tar --selinux -xf archive.tar
a4d85a
+
a4d85a
+# archive for later debugging
a4d85a
+cp archive.tar archive_origin.tar
a4d85a
+
a4d85a
+# check if selinux contexts were restored
a4d85a
+getfattr -h -d dir dir/file dir/link -msecurity.selinux | \
a4d85a
+    grep -v -e '^#' -e ^$ | cut -d: -f1
a4d85a
+
a4d85a
+# ===========================================================================
a4d85a
+# check if selinux contexts are not restored when --selinux option is missing
a4d85a
+
a4d85a
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > with_selinux
a4d85a
+rm -rf dir
a4d85a
+tar -xf archive.tar
a4d85a
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > without_selinux
a4d85a
+
a4d85a
+diff with_selinux without_selinux > diff_with_without
a4d85a
+if test "$?" -eq "0"; then
a4d85a
+    echo "selinux contexts probably restored while --selinux is off"
a4d85a
+fi
a4d85a
+
a4d85a
+# =================================================================
a4d85a
+# check if selinux is not archived when --selinux option is missing
a4d85a
+
a4d85a
+tar -cf archive.tar dir
a4d85a
+
a4d85a
+# clear the directory
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+# restore (with --selinux)
a4d85a
+tar --selinux -xf archive.tar dir
a4d85a
+
a4d85a
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > final
a4d85a
+diff start final > final_diff
a4d85a
+if test "$?" -ne "0"; then
a4d85a
+    echo "bad result"
a4d85a
+fi
a4d85a
+
a4d85a
+],
a4d85a
+[0],
a4d85a
+[security.selinux="system_u
a4d85a
+security.selinux="unconfined_u
a4d85a
+security.selinux="system_u
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/testsuite.at b/tests/testsuite.at
a4d85a
index e43653e..8d5811d 100644
a4d85a
--- a/tests/testsuite.at
a4d85a
+++ b/tests/testsuite.at
a4d85a
@@ -81,13 +81,6 @@ m4_define([AT_GZIP_PREREQ],[
a4d85a
 cat /dev/null | m4_if([$1],[],gzip,[$1]) - > /dev/null 2>&1 || AT_SKIP_TEST
a4d85a
 ])
a4d85a
 
a4d85a
-dnl AT_SIGPIPE_PREREQ - Skip test unless SIGPIPE handling is the default
a4d85a
-m4_define([AT_SIGPIPE_PREREQ],[
a4d85a
-case `(cat "$at_myself" 2>&3 | :) 3>&1 >/dev/null` in #(
a4d85a
-?*) AT_SKIP_TEST;;
a4d85a
-esac
a4d85a
-])
a4d85a
-
a4d85a
 dnl AT_SORT_PREREQ - Skip test if sort utility outputs unwanted data on stderr
a4d85a
 m4_define([AT_SORT_PREREQ],[
a4d85a
 test -z "`sort < /dev/null 2>&1`" || AT_SKIP_TEST
a4d85a
@@ -103,10 +96,86 @@ rm -f $[]$
a4d85a
 test $result -eq 0 && AT_SKIP_TEST
a4d85a
 ])
a4d85a
 
a4d85a
+dnl AT_SIGPIPE_PREREQ - Skip test unless SIGPIPE handling is the default
a4d85a
+m4_define([AT_SIGPIPE_PREREQ],[
a4d85a
+case `(cat "$at_myself" 2>&3 | :) 3>&1 >/dev/null` in #(
a4d85a
+?*) AT_SKIP_TEST;;
a4d85a
+esac
a4d85a
+])
a4d85a
+
a4d85a
+dnl AT_PRIVILEGED_PREREQ - Skip test if not running at root privileges
a4d85a
+m4_define([AT_PRIVILEGED_PREREQ],[
a4d85a
+echo "test" > $[]$
a4d85a
+chmod 0 $[]$
a4d85a
+cat $[]$ > /dev/null 2>&1
a4d85a
+result=$?
a4d85a
+rm -f $[]$
a4d85a
+test $result -eq 0 || AT_SKIP_TEST
a4d85a
+])
a4d85a
+
a4d85a
 m4_define([AT_TAR_MKHIER],[
a4d85a
 install-sh -d $1 >/dev/null dnl
a4d85a
 m4_if([$2],,,&& genfile --file [$1]/[$2]) || AT_SKIP_TEST])
a4d85a
 
a4d85a
+dnl Skip test when utlity does not return expected return value
a4d85a
+m4_define([AT_CHECK_UTIL],[
a4d85a
+  $1 &> /dev/null
a4d85a
+  if test "$?" != $2; then
a4d85a
+    AT_SKIP_TEST
a4d85a
+  fi
a4d85a
+])
a4d85a
+
a4d85a
+m4_define([AT_XATTRS_UTILS_PREREQ],[
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  AT_CHECK_UTIL(setfattr -n user.test -v test $file,0)
a4d85a
+  AT_CHECK_UTIL(getfattr $file,0)
a4d85a
+])
a4d85a
+m4_define([AT_SELINUX_UTILS_PREREQ],[
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  AT_CHECK_UTIL(restorecon $file, 0)
a4d85a
+  AT_CHECK_UTIL(chcon -h --user=unconfined_u $file,0)
a4d85a
+  rm -rf $file
a4d85a
+])
a4d85a
+m4_define([AT_ACLS_UTILS_PREREQ],[
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  AT_CHECK_UTIL(setfacl -m u:$UID:rwx $file,0)
a4d85a
+  AT_CHECK_UTIL(getfacl $file,0)
a4d85a
+  rm -rf $file
a4d85a
+])
a4d85a
+m4_define([AT_CAPABILITIES_UTILS_PREREQ],[
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  AT_CHECK_UTIL(setcap "= cap_chown=ei" $file,0)
a4d85a
+  AT_CHECK_UTIL(getcap $file,0)
a4d85a
+  rm -rf $file
a4d85a
+])
a4d85a
+m4_define([AT_XATTRS_PREREQ],[
a4d85a
+  AT_XATTRS_UTILS_PREREQ
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  setfattr -n user.test -v ahoj $file
a4d85a
+  # check whether tar fails to store xattrs
a4d85a
+  err=$( tar --xattrs -cf /dev/null $file 2>&1 >/dev/null | wc -l )
a4d85a
+  if test "$err" != "0"; then
a4d85a
+    AT_SKIP_TEST
a4d85a
+  fi
a4d85a
+])
a4d85a
+m4_define([AT_SELINUX_PREREQ],[
a4d85a
+  AT_SELINUX_UTILS_PREREQ
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  err=$( tar --selinux -cf /dev/null $file 2>&1 >/dev/null | wc -l )
a4d85a
+  if test "$err" != "0"; then
a4d85a
+    AT_SKIP_TEST
a4d85a
+  fi
a4d85a
+])
a4d85a
+m4_define([AT_ACLS_PREREQ],[
a4d85a
+  AT_ACLS_UTILS_PREREQ
a4d85a
+  file=$( mktemp -p . )
a4d85a
+  setfacl -m u:$UID:rwx $file
a4d85a
+  err=$( tar --acls -cf /dev/null $file 2>&1 >/dev/null | wc -l )
a4d85a
+  if test "$err" != "0"; then
a4d85a
+    AT_SKIP_TEST
a4d85a
+  fi
a4d85a
+])
a4d85a
+
a4d85a
 m4_include([sparsemvp.at])
a4d85a
 
a4d85a
 AT_INIT
a4d85a
@@ -264,6 +334,20 @@ m4_include([remfiles03.at])
a4d85a
 
a4d85a
 m4_include([sigpipe.at])
a4d85a
 
a4d85a
+m4_include([xattr01.at])
a4d85a
+m4_include([xattr02.at])
a4d85a
+m4_include([xattr03.at])
a4d85a
+m4_include([xattr04.at])
a4d85a
+m4_include([xattr05.at])
a4d85a
+
a4d85a
+m4_include([acls01.at])
a4d85a
+m4_include([acls02.at])
a4d85a
+
a4d85a
+m4_include([selnx01.at])
a4d85a
+m4_include([selacl01.at])
a4d85a
+
a4d85a
+m4_include([capabs_raw01.at])
a4d85a
+
a4d85a
 m4_include([star/gtarfail.at])
a4d85a
 m4_include([star/gtarfail2.at])
a4d85a
 
a4d85a
@@ -273,3 +357,4 @@ m4_include([star/ustar-big-2g.at])
a4d85a
 m4_include([star/ustar-big-8g.at])
a4d85a
 
a4d85a
 m4_include([star/pax-big-10g.at])
a4d85a
+
a4d85a
diff --git a/tests/xattr01.at b/tests/xattr01.at
a4d85a
new file mode 100644
a4d85a
index 0000000..fd960d5
a4d85a
--- /dev/null
a4d85a
+++ b/tests/xattr01.at
a4d85a
@@ -0,0 +1,47 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2011 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# This is basic test for support of extended attributes.
a4d85a
+
a4d85a
+AT_SETUP([xattrs: basic functionality])
a4d85a
+AT_KEYWORDS([xattrs xattr01])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_PREREQ
a4d85a
+mkdir dir
a4d85a
+genfile --file dir/file
a4d85a
+
a4d85a
+setfattr -n user.test -v OurDirValue dir
a4d85a
+setfattr -n user.test -v OurFileValue dir/file
a4d85a
+
a4d85a
+tar --xattrs -cf archive.tar dir
a4d85a
+
a4d85a
+rm -rf dir
a4d85a
+tar --xattrs -xf archive.tar
a4d85a
+
a4d85a
+getfattr -h -d dir         | grep -v -e '^#' -e ^$
a4d85a
+getfattr -h -d dir/file    | grep -v -e '^#' -e ^$
a4d85a
+],
a4d85a
+[0],
a4d85a
+[user.test="OurDirValue"
a4d85a
+user.test="OurFileValue"
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/xattr02.at b/tests/xattr02.at
a4d85a
new file mode 100644
a4d85a
index 0000000..3aae3f9
a4d85a
--- /dev/null
a4d85a
+++ b/tests/xattr02.at
a4d85a
@@ -0,0 +1,55 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2011 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# Cooperation of the '-C' option and storing/restoring extended attributes.
a4d85a
+
a4d85a
+AT_SETUP([xattrs: change directory with -C option])
a4d85a
+AT_KEYWORDS([xattrs xattr02])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+mkdir dir/subdir
a4d85a
+mkdir dir/subdir/subsubdir
a4d85a
+genfile --file dir/file1
a4d85a
+genfile --file dir/subdir/file2
a4d85a
+
a4d85a
+setfattr -n user.test -v OurFile1Value dir/file1
a4d85a
+setfattr -n user.test -v OurFile2Value dir/subdir/file2
a4d85a
+setfattr -n user.test -v OurDirValue   dir/subdir/subsubdir
a4d85a
+
a4d85a
+tar --xattrs -cf archive.tar -C dir file1 -C subdir file2 subsubdir
a4d85a
+
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+tar --xattrs -xf archive.tar
a4d85a
+
a4d85a
+getfattr -h -d file1        | grep -v -e '^#' -e ^$
a4d85a
+getfattr -h -d file2        | grep -v -e '^#' -e ^$
a4d85a
+getfattr -h -d subsubdir    | grep -v -e '^#' -e ^$
a4d85a
+],
a4d85a
+[0],
a4d85a
+[user.test="OurFile1Value"
a4d85a
+user.test="OurFile2Value"
a4d85a
+user.test="OurDirValue"
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/xattr03.at b/tests/xattr03.at
a4d85a
new file mode 100644
a4d85a
index 0000000..d834f9f
a4d85a
--- /dev/null
a4d85a
+++ b/tests/xattr03.at
a4d85a
@@ -0,0 +1,56 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:
a4d85a
+#
a4d85a
+# Setup of the trusted.* domain under privileged user.
a4d85a
+
a4d85a
+AT_SETUP([xattrs: trusted.* attributes])
a4d85a
+AT_KEYWORDS([xattrs xattr03])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_PRIVILEGED_PREREQ
a4d85a
+AT_XATTRS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+mkdir dir/subdir
a4d85a
+mkdir dir/subdir/subsubdir
a4d85a
+genfile --file dir/file1
a4d85a
+genfile --file dir/subdir/file2
a4d85a
+
a4d85a
+setfattr -n trusted.test -v OurFile1Value dir/file1
a4d85a
+setfattr -n trusted.test -v OurFile2Value dir/subdir/file2
a4d85a
+setfattr -n trusted.test -v OurDirValue   dir/subdir/subsubdir
a4d85a
+
a4d85a
+tar --xattrs -cf archive.tar -C dir file1 -C subdir file2 subsubdir
a4d85a
+
a4d85a
+rm -rf dir
a4d85a
+
a4d85a
+tar --xattrs --xattrs-include=trusted* -xf archive.tar
a4d85a
+
a4d85a
+getfattr -mtrusted. -d file1        | grep -v -e '^#' -e ^$
a4d85a
+getfattr -mtrusted. -d file2        | grep -v -e '^#' -e ^$
a4d85a
+getfattr -mtrusted. -d subsubdir    | grep -v -e '^#' -e ^$
a4d85a
+],
a4d85a
+[0],
a4d85a
+[trusted.test="OurFile1Value"
a4d85a
+trusted.test="OurFile2Value"
a4d85a
+trusted.test="OurDirValue"
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/xattr04.at b/tests/xattr04.at
a4d85a
new file mode 100644
a4d85a
index 0000000..31832af
a4d85a
--- /dev/null
a4d85a
+++ b/tests/xattr04.at
a4d85a
@@ -0,0 +1,48 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:  Test for the regression caused by tar update from 1.23 to
a4d85a
+# 1.26, Red Hat xattr patch was not ready for open->openat conversion.
a4d85a
+#
a4d85a
+# Related commit 4bde4f3.  See the bug: https://bugzilla.redhat.com/717684
a4d85a
+
a4d85a
+AT_SETUP([xattrs: s/open/openat/ regression])
a4d85a
+AT_KEYWORDS([xattrs xattr04])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+mkdir output
a4d85a
+genfile --file dir/file
a4d85a
+
a4d85a
+setfattr -n user.test -v value dir/file
a4d85a
+
a4d85a
+# archive whole directory including binary xattrs
a4d85a
+tar --xattrs -cf archive.tar -C dir .
a4d85a
+
a4d85a
+tar --xattrs -xf archive.tar -C output
a4d85a
+ret=$?
a4d85a
+getfattr -h -d output/file | grep -v -e '^#' -e ^$
a4d85a
+exit $ret
a4d85a
+],
a4d85a
+[0],
a4d85a
+[user.test="value"
a4d85a
+])
a4d85a
+
a4d85a
+AT_CLEANUP
a4d85a
diff --git a/tests/xattr05.at b/tests/xattr05.at
a4d85a
new file mode 100644
a4d85a
index 0000000..27dc469
a4d85a
--- /dev/null
a4d85a
+++ b/tests/xattr05.at
a4d85a
@@ -0,0 +1,49 @@
a4d85a
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
a4d85a
+#
a4d85a
+# Test suite for GNU tar.
a4d85a
+# Copyright (C) 2012 Free Software Foundation, Inc.
a4d85a
+#
a4d85a
+# This program is free software; you can redistribute it and/or modify
a4d85a
+# it under the terms of the GNU General Public License as published by
a4d85a
+# the Free Software Foundation; either version 3, or (at your option)
a4d85a
+# any later version.
a4d85a
+#
a4d85a
+# This program is distributed in the hope that it will be useful,
a4d85a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d85a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d85a
+# GNU General Public License for more details.
a4d85a
+#
a4d85a
+# You should have received a copy of the GNU General Public License
a4d85a
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a4d85a
+#
a4d85a
+# Test description:  Test for archiving/extracting of extended attributes
a4d85a
+# having the '=' character in its keyword.
a4d85a
+#
a4d85a
+# Relevant mailing list thread:
a4d85a
+#
a4d85a
+# http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html
a4d85a
+
a4d85a
+AT_SETUP([xattrs: keywords with '=' and '%'])
a4d85a
+AT_KEYWORDS([xattrs xattr05])
a4d85a
+
a4d85a
+AT_TAR_CHECK([
a4d85a
+AT_XATTRS_PREREQ
a4d85a
+
a4d85a
+mkdir dir
a4d85a
+mkdir output
a4d85a
+genfile --file dir/file
a4d85a
+
a4d85a
+setfattr -n user.=NAME%3D= -v value dir/file
a4d85a
+getfattr -d dir/file | grep -v '# ' > before
a4d85a
+
a4d85a
+# archive whole directory including binary xattrs
a4d85a
+tar --xattrs -cf archive.tar -C dir .
a4d85a
+
a4d85a
+tar --xattrs -xf archive.tar -C output
a4d85a
+getfattr -d output/file | grep -v '# ' > after
a4d85a
+diff before after
a4d85a
+],
a4d85a
+[0],
a4d85a
+[])
a4d85a
+
a4d85a
+AT_CLEANUP