Blame SOURCES/tar-1.26-xattrs.patch

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