bf72587
 configure.ac    |   20 ++-
bf72587
 doc/tar.texi    |   75 +++++++++
bf72587
 src/Makefile.am |    7 +-
bf72587
 src/common.h    |   18 ++
bf72587
 src/create.c    |   54 ++++++-
bf72587
 src/extract.c   |  123 ++++++++++++++
bf72587
 src/list.c      |    7 +
bf72587
 src/tar.c       |   80 +++++++++-
bf72587
 src/tar.h       |   20 +++
bf72587
 src/xattrs.c    |  489 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
bf72587
 src/xattrs.h    |   14 ++
bf72587
 src/xheader.c   |  237 +++++++++++++++++++++++----
bf72587
 12 files changed, 1103 insertions(+), 41 deletions(-)
bf72587
9934f57
diff -urNp tar-1.24-orig/configure.ac tar-1.24/configure.ac
9934f57
--- tar-1.24-orig/configure.ac	2010-10-24 23:35:35.000000000 +0200
9934f57
+++ tar-1.24/configure.ac	2010-10-25 10:24:52.548214037 +0200
a4e0f12
@@ -44,7 +44,7 @@ AC_CHECK_HEADERS_ONCE(fcntl.h linux/fd.h
9934f57
   sys/param.h sys/device.h sys/gentape.h \
Radek Brich fc86677
   sys/inet.h sys/io/trioctl.h \
Radek Brich fc86677
   sys/mtio.h sys/time.h sys/tprintf.h sys/tape.h \
Radek Brich fc86677
-  unistd.h locale.h)
c038029
+  unistd.h locale.h attr/xattr.h sys/acl.h)
Radek Brich fc86677
 
Radek Brich fc86677
 AC_CHECK_HEADERS([sys/buf.h], [], [],
Radek Brich fc86677
 [#if HAVE_SYS_PARAM_H
a4e0f12
@@ -91,6 +91,12 @@ gl_INIT
Radek Brich fc86677
 tar_PAXUTILS
Radek Brich fc86677
 
9934f57
 AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
Radek Brich fc86677
+AC_CHECK_FUNCS(getxattr  fgetxattr  lgetxattr \
Radek Brich fc86677
+               setxattr  fsetxattr  lsetxattr \
Radek Brich fc86677
+               listxattr flistxattr llistxattr,
Radek Brich fc86677
+               AC_DEFINE(HAVE_XATTRS,,[Define if we have a working extended attributes]),)
Radek Brich fc86677
+AC_CHECK_LIB(acl, acl_get_fd)
Radek Brich fc86677
+
Radek Brich fc86677
 AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
Radek Brich fc86677
 AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
Radek Brich fc86677
 AC_CHECK_DECLS([time],,, [#include <time.h>])
a4e0f12
@@ -214,6 +220,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_QUOTING_STYLE
Radek Brich fc86677
 # Iconv
Radek Brich fc86677
 AM_ICONV
Radek Brich fc86677
 AC_CHECK_HEADERS(iconv.h)
Radek Brich fc86677
+AC_CHECK_HEADERS(attr/xattr.h)
Radek Brich fc86677
 AC_CHECK_TYPE(iconv_t,:,
Radek Brich fc86677
               AC_DEFINE(iconv_t, int,
Radek Brich fc86677
                         [Conversion descriptor type]),
a4e0f12
@@ -223,6 +230,17 @@ AC_CHECK_TYPE(iconv_t,:,
c038029
 #endif
c038029
 ])
c038029
 
88bee90
+AC_ARG_ENABLE(selinux,
88bee90
+  AC_HELP_STRING([--enable-selinux],
88bee90
+    [enable SELinux support (disabled by default)]),
88bee90
+  [selinux_enabled=$enableval],
88bee90
+  [selinux_enabled=no])
88bee90
+
88bee90
+if test "x$selinux_enabled" = xyes; then
88bee90
+  AC_CHECK_LIB(selinux, getfilecon)
88bee90
+  AC_CHECK_HEADERS(selinux/selinux.h)
c038029
+fi
c038029
+
c038029
 # Gettext.
c038029
 AM_GNU_GETTEXT([external], [need-formatstring-macros])
c038029
 AM_GNU_GETTEXT_VERSION([0.16])
9934f57
diff -urNp tar-1.24-orig/doc/tar.texi tar-1.24/doc/tar.texi
9934f57
--- tar-1.24-orig/doc/tar.texi	2010-10-24 20:07:54.000000000 +0200
9934f57
+++ tar-1.24/doc/tar.texi	2010-10-25 10:24:52.554213688 +0200
9934f57
@@ -2370,6 +2370,10 @@ Normally when creating an archive, @comm
0aec5e9
 @samp{/} from member names.  This option disables that behavior.
0aec5e9
 @xref{absolute}.
0aec5e9
 
0aec5e9
+@opsummary{acl}
0aec5e9
+@item --acls
0aec5e9
+Causes @command{tar} to store ACL's.  @xref{Attributes}.
0aec5e9
+
0aec5e9
 @opsummary{after-date}
0aec5e9
 @item --after-date
0aec5e9
 
bf72587
@@ -2914,6 +2918,10 @@ contents have changed (as opposed to just @option{--newer}, which will
0aec5e9
 also back up files for which any status information has
0aec5e9
 changed).  @xref{after}.
0aec5e9
 
0aec5e9
+@opsummary{no-acl}
0aec5e9
+@item --no-acls
0aec5e9
+Causes @command{tar} not to store and not to extract ACL's.  @xref{Attributes}.
0aec5e9
+
0aec5e9
 @opsummary{no-anchored}
0aec5e9
 @item --no-anchored
0aec5e9
 An exclude pattern can match any subsequence of the name's components.
bf72587
@@ -2997,11 +3005,21 @@ locations.  Usually @command{tar} determines automatically whether
a4e0f12
 the archive can be seeked or not.  Use this option to disable this
a4e0f12
 mechanism.
0aec5e9
 
0aec5e9
+@opsummary{no-selinux}
0aec5e9
+@item --no-selinux
0aec5e9
+Causes @command{tar} not to store and not to extract SELinux security context.
0aec5e9
+@xref{Attributes}.
0aec5e9
+
0aec5e9
 @opsummary{no-unquote}
0aec5e9
 @item --no-unquote
0aec5e9
 Treat all input file or member names literally, do not interpret
0aec5e9
 escape sequences.  @xref{input name quoting}.
0aec5e9
 
0aec5e9
+@opsummary{no-xattrs}
0aec5e9
+@item --no-xattrs
0aec5e9
+Causes @command{tar} not to store and not to extract xattrs. This option also
0aec5e9
+enables @option{--no-selinux} and @option{--no-acls}.  @xref{Attributes}.
0aec5e9
+
0aec5e9
 @opsummary{no-wildcards}
0aec5e9
 @item --no-wildcards
0aec5e9
 Do not use wildcards.
bf72587
@@ -3234,6 +3252,11 @@ in cases when such recognition fails.  It takes effect only if the
a4e0f12
 archive is open for reading (e.g. with @option{--list} or
a4e0f12
 @option{--extract} options).
0aec5e9
 
0aec5e9
+@opsummary{selinux}
0aec5e9
+@item --selinux
0aec5e9
+Causes @command{tar} to store SElinux security context.  @xref{Attributes}.
0aec5e9
+
0aec5e9
+
0aec5e9
 @opsummary{show-defaults}
0aec5e9
 @item --show-defaults
0aec5e9
 
bf72587
@@ -3447,6 +3470,11 @@ Enable or disable warning messages identified by @var{keyword}.  The
a4e0f12
 messages are suppressed if @var{keyword} is prefixed with @samp{no-}.
a4e0f12
 @xref{warnings}.
0aec5e9
 
0aec5e9
+@opsummary{xattrs}
0aec5e9
+@item --xattrs
0aec5e9
+Causes @command{tar} to store xattrs. This option also enables
0aec5e9
+@option{--selinux} and @option{--acls}.  @xref{Attributes}.
0aec5e9
+
0aec5e9
 @opsummary{wildcards}
0aec5e9
 @item --wildcards
0aec5e9
 Use wildcards when matching member names with patterns.
bf72587
@@ -8659,6 +8687,8 @@ implementation able to read @samp{ustar} archives will be able to read
Radek Brich fc86677
 most @samp{posix} archives as well, with the only exception that any
Radek Brich fc86677
 additional information (such as long file names etc.) will in such
Radek Brich fc86677
 case be extracted as plain text files along with the files it refers to.
Radek Brich fc86677
+This is the only format that can store ACLs, SELinux context and extended
Radek Brich fc86677
+attributes.
Radek Brich fc86677
 
Radek Brich fc86677
 This archive format will be the default format for future versions
Radek Brich fc86677
 of @GNUTAR{}.
bf72587
@@ -9293,6 +9323,51 @@ Same as both @option{--same-permissions} and @option{--same-order}.
2de9cec
 
2de9cec
 This option is deprecated, and will be removed in @GNUTAR{} version 1.23.
Radek Brich fc86677
 
Radek Brich fc86677
+@opindex acls
Radek Brich fc86677
+@item --acls
Radek Brich fc86677
+This option causes @command{tar} to store the current ACL in the archive.
Radek Brich fc86677
+
Radek Brich fc86677
+The @option{--acls} option has no equivalent short option name.
Radek Brich fc86677
+
Radek Brich fc86677
+@opindex selinux
Radek Brich fc86677
+@item --selinux
Radek Brich fc86677
+This option causes @command{tar} to store the current SELinux security context
Radek Brich fc86677
+information in the archive.
Radek Brich fc86677
+
Radek Brich fc86677
+The @option{--selinux} option has no equivalent short option name.
Radek Brich fc86677
+
Radek Brich fc86677
+@opindex xattrs
Radek Brich fc86677
+@item --xattrs
Radek Brich fc86677
+This option causes @command{tar} to store the current extended attributes in
Radek Brich fc86677
+the archive. This option also enables @option{--acls} and @option{--selinux} if
Radek Brich fc86677
+they haven't been set already.
Radek Brich fc86677
+
Radek Brich fc86677
+The @option{--xattrs} option has no equivalent short option name.
Radek Brich fc86677
+
Radek Brich fc86677
+@opindex no-acls
Radek Brich fc86677
+@item --no-acls
Radek Brich fc86677
+This option causes @command{tar} not to store the current ACL in the archive
Radek Brich fc86677
+and not to extract any ACL information in an archive.
Radek Brich fc86677
+
0aec5e9
+The @option{--no-acls} option has no equivalent short option name.
Radek Brich fc86677
+
Radek Brich fc86677
+@opindex no-selinux
Radek Brich fc86677
+@item --no-selinux
Radek Brich fc86677
+This option causes @command{tar} not to store the current SELinux security
Radek Brich fc86677
+context information in the archive and not to extract any SELinux information in
Radek Brich fc86677
+an archive.
Radek Brich fc86677
+
0aec5e9
+The @option{--no-selinux} option has no equivalent short option name.
Radek Brich fc86677
+
0aec5e9
+@opindex no-xattrs
0aec5e9
+@item --no-xattrs
Radek Brich fc86677
+This option causes @command{tar} not to store the current extended attributes in
Radek Brich fc86677
+the archive and not to extract any extended attributes in an archive. This
0aec5e9
+option also enables @option{--no-acls} and @option{--no-selinux} if
Radek Brich fc86677
+they haven't been set already.
Radek Brich fc86677
+
0aec5e9
+The @option{--no-xattrs} option has no equivalent short option name.
Radek Brich fc86677
+
Radek Brich fc86677
 @end table
Radek Brich fc86677
 
Radek Brich fc86677
 @node Portability
9934f57
diff -urNp tar-1.24-orig/src/common.h tar-1.24/src/common.h
9934f57
--- tar-1.24-orig/src/common.h	2010-10-24 20:07:54.000000000 +0200
9934f57
+++ tar-1.24/src/common.h	2010-10-25 10:24:52.558475456 +0200
9934f57
@@ -253,6 +253,15 @@ GLOBAL int same_owner_option;
Radek Brich fc86677
 /* If positive, preserve permissions when extracting.  */
Radek Brich fc86677
 GLOBAL int same_permissions_option;
Radek Brich fc86677
 
Radek Brich fc86677
+/* If positive, save the SELinux context.  */
Radek Brich fc86677
+GLOBAL int selinux_context_option;
Radek Brich fc86677
+
Radek Brich fc86677
+/* If positive, save the ACLs.  */
Radek Brich fc86677
+GLOBAL int acls_option;
Radek Brich fc86677
+
Radek Brich fc86677
+/* If positive, save the user and root xattrs.  */
Radek Brich fc86677
+GLOBAL int xattrs_option;
Radek Brich fc86677
+
Radek Brich fc86677
 /* When set, strip the given number of file name components from the file name
Radek Brich fc86677
    before extracting */
Radek Brich fc86677
 GLOBAL size_t strip_name_components;
bf72587
@@ -707,6 +716,9 @@ extern char *output_start;
Radek Brich fc86677
 
Radek Brich fc86677
 void update_archive (void);
Radek Brich fc86677
 
Radek Brich fc86677
+/* Module attrs.c.  */
Radek Brich fc86677
+#include "xattrs.h"
Radek Brich fc86677
+
Radek Brich fc86677
 /* Module xheader.c.  */
Radek Brich fc86677
 
9934f57
 void xheader_decode (struct tar_stat_info *stat);
bf72587
@@ -727,6 +739,12 @@ bool xheader_string_end (struct xheader *xhdr, char const *keyword);
Radek Brich fc86677
 bool xheader_keyword_deleted_p (const char *kw);
Radek Brich fc86677
 char *xheader_format_name (struct tar_stat_info *st, const char *fmt,
Radek Brich fc86677
 			   size_t n);
Radek Brich fc86677
+void xheader_xattr_init(struct tar_stat_info *st);
Radek Brich fc86677
+void xheader_xattr_free(struct xattr_array *vals, size_t sz);
Radek Brich fc86677
+void xheader_xattr_copy(const struct tar_stat_info *st,
Radek Brich fc86677
+                        struct xattr_array **vals, size_t *sz);
Radek Brich fc86677
+void xheader_xattr_add(struct tar_stat_info *st,
Radek Brich fc86677
+                       const char *key, const char *val, size_t len);
Radek Brich fc86677
 
Radek Brich fc86677
 /* Module system.c */
Radek Brich fc86677
 
9934f57
diff -urNp tar-1.24-orig/src/create.c tar-1.24/src/create.c
9934f57
--- tar-1.24-orig/src/create.c	2010-10-24 20:07:54.000000000 +0200
9934f57
+++ tar-1.24/src/create.c	2010-10-25 10:24:52.560213618 +0200
2de9cec
@@ -24,6 +24,7 @@
2de9cec
 #include <quotearg.h>
2de9cec
 
2de9cec
 #include "common.h"
Radek Brich fc86677
+
2de9cec
 #include <hash.h>
2de9cec
 
9934f57
 /* Error number to use when an impostor is discovered.
bf72587
@@ -936,6 +937,30 @@ start_header (struct tar_stat_info *st)
2de9cec
       GNAME_TO_CHARS (st->gname, header->header.gname);
2de9cec
     }
2de9cec
 
2de9cec
+  if (archive_format == POSIX_FORMAT)
2de9cec
+    {
2de9cec
+      if (acls_option > 0)
2de9cec
+        {
2de9cec
+          if (st->acls_a_ptr)
2de9cec
+            xheader_store ("SCHILY.acl.access", st, NULL);
2de9cec
+          if (st->acls_d_ptr)
2de9cec
+            xheader_store ("SCHILY.acl.default", st, NULL);
2de9cec
+        }
2de9cec
+      if ((selinux_context_option > 0) && st->cntx_name)
2de9cec
+        xheader_store ("RHT.security.selinux", st, NULL);
2de9cec
+      if (xattrs_option > 0)
2de9cec
+        {
2de9cec
+          size_t scan_xattr = 0;
2de9cec
+          struct xattr_array *xattr_map = st->xattr_map;
d2ac734
+
2de9cec
+          while (scan_xattr < st->xattr_map_size)
2de9cec
+            {
2de9cec
+              xheader_store (xattr_map[scan_xattr].xkey, st, &scan_xattr);
2de9cec
+              ++scan_xattr;
2de9cec
+            }
2de9cec
+        }
2de9cec
+    }
Radek Brich fc86677
+
2de9cec
   return header;
2de9cec
 }
2de9cec
 
bf72587
@@ -1710,6 +1735,14 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
bf72587
     {
9934f57
       bool ok;
9934f57
       struct stat final_stat;
bf72587
+      int fd_xattr = fd;
bf72587
+      if (!fd_xattr)
bf72587
+	// upstream tar now uses zero fd as "no fd"
bf72587
+	fd_xattr = -1;
Radek Brich fc86677
+
bf72587
+      xattrs_acls_get(st, p, fd_xattr, !is_dir);
bf72587
+      xattrs_selinux_get(st, p, fd_xattr);
bf72587
+      xattrs_xattrs_get(st, p, fd_xattr);
bf72587
 
2de9cec
       if (is_dir)
2de9cec
 	{
bf72587
@@ -1829,6 +1862,9 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
7b35b1d
       if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
7b35b1d
 	write_long_link (st);
7b35b1d
 
7b35b1d
+      xattrs_selinux_get(st, p, -1);
7b35b1d
+      xattrs_xattrs_get(st, p, -1);
7b35b1d
+
7b35b1d
       block_ordinal = current_block_ordinal ();
7b35b1d
       st->stat.st_size = 0;	/* force 0 size on symlink */
7b35b1d
       header = start_header (st);
bf72587
@@ -1847,11 +1883,23 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
0fc563d
     }
0fc563d
 #endif
0fc563d
   else if (S_ISCHR (st->stat.st_mode))
0fc563d
-    type = CHRTYPE;
0fc563d
+    {
0fc563d
+      type = CHRTYPE;
0fc563d
+      xattrs_selinux_get(st, p, -1);
0fc563d
+      xattrs_xattrs_get(st, p, -1);
0fc563d
+    }
0fc563d
   else if (S_ISBLK (st->stat.st_mode))
0fc563d
-    type = BLKTYPE;
0fc563d
+    {
0fc563d
+      type = BLKTYPE;
0fc563d
+      xattrs_selinux_get(st, p, -1);
0fc563d
+      xattrs_xattrs_get(st, p, -1);
0fc563d
+    }
0fc563d
   else if (S_ISFIFO (st->stat.st_mode))
0fc563d
-    type = FIFOTYPE;
0fc563d
+    {
0fc563d
+      type = FIFOTYPE;
0fc563d
+      xattrs_selinux_get(st, p, -1);
0fc563d
+      xattrs_xattrs_get(st, p, -1);
0fc563d
+    }
0fc563d
   else if (S_ISSOCK (st->stat.st_mode))
0fc563d
     {
0fc563d
       WARNOPT (WARN_FILE_IGNORED,
9934f57
diff -urNp tar-1.24-orig/src/extract.c tar-1.24/src/extract.c
9934f57
--- tar-1.24-orig/src/extract.c	2010-10-24 20:07:54.000000000 +0200
9934f57
+++ tar-1.24/src/extract.c	2010-10-25 10:35:10.903214037 +0200
9934f57
@@ -97,6 +97,14 @@ struct delayed_set_stat
9934f57
     /* Directory that the name is relative to.  */
9934f57
     int change_dir;
9934f57
 
9934f57
+    /* extended attributes*/
2de9cec
+    char *cntx_name;
2de9cec
+    char *acls_a_ptr;
2de9cec
+    size_t acls_a_len;
2de9cec
+    char *acls_d_ptr;
2de9cec
+    size_t acls_d_len;
2de9cec
+    size_t xattr_map_size;   /* Size of the xattr map */
2de9cec
+    struct xattr_array *xattr_map;
9934f57
     /* Length and contents of name.  */
9934f57
     size_t file_name_len;
2de9cec
     char file_name[1];
9934f57
@@ -134,6 +142,18 @@ struct delayed_link
2de9cec
        hard-linked together.  */
2de9cec
     struct string_list *sources;
2de9cec
 
2de9cec
+    /* SELinux context */
2de9cec
+    char *cntx_name;
d2ac734
+
2de9cec
+    /* ACLs */
2de9cec
+    char *acls_a_ptr;
2de9cec
+    size_t acls_a_len;
2de9cec
+    char *acls_d_ptr;
2de9cec
+    size_t acls_d_len;
d2ac734
+
2de9cec
+    size_t xattr_map_size;   /* Size of the xattr map */
2de9cec
+    struct xattr_array *xattr_map;
d2ac734
+
2de9cec
     /* The desired target of the desired link.  */
2de9cec
     char target[1];
2de9cec
   };
9934f57
@@ -335,6 +355,10 @@ set_stat (char const *file_name,
9934f57
 	utime_error (file_name);
2de9cec
     }
2de9cec
 
2de9cec
+  xattrs_acls_set(st, file_name, typeflag);
2de9cec
+  xattrs_selinux_set(st, file_name, typeflag);
2de9cec
+  xattrs_xattrs_set(st, file_name, typeflag);
Radek Brich fc86677
+
9934f57
   if (0 < same_owner_option && ! interdir)
2de9cec
     {
9934f57
       /* Some systems allow non-root users to give files away.  Once this
68aae18
@@ -431,6 +455,36 @@ delay_set_stat (char const *file_name, s
9934f57
   data->atflag = atflag;
2de9cec
   data->after_links = 0;
9934f57
   data->change_dir = chdir_current;
2de9cec
+  data->cntx_name = NULL;
68aae18
+  if (st)
68aae18
+    assign_string (&data->cntx_name, st->cntx_name);
68aae18
+  if (st && st->acls_a_ptr)
Radek Brich fc86677
+    {
2de9cec
+      data->acls_a_ptr = xmemdup(st->acls_a_ptr, st->acls_a_len + 1);
2de9cec
+      data->acls_a_len = st->acls_a_len;
Radek Brich fc86677
+    }
2de9cec
+  else
Radek Brich fc86677
+    {
2de9cec
+      data->acls_a_ptr = NULL;
2de9cec
+      data->acls_a_len = 0;
Radek Brich fc86677
+    }
68aae18
+  if (st && st->acls_d_ptr)
Radek Brich fc86677
+    {
2de9cec
+      data->acls_d_ptr = xmemdup(st->acls_d_ptr, st->acls_d_len + 1);
2de9cec
+      data->acls_d_len = st->acls_d_len;
Radek Brich fc86677
+    }
2de9cec
+  else
Radek Brich fc86677
+    {
2de9cec
+      data->acls_d_ptr = NULL;
2de9cec
+      data->acls_d_len = 0;
Radek Brich fc86677
+    }
68aae18
+  if (st)
68aae18
+    xheader_xattr_copy (st, &data->xattr_map, &data->xattr_map_size);
68aae18
+  else
68aae18
+    {
68aae18
+      data->xattr_map = NULL;
68aae18
+      data->xattr_map_size = 0;
68aae18
+    }
2de9cec
   strcpy (data->file_name, file_name);
2de9cec
   delayed_set_stat_head = data;
9934f57
   if (must_be_dot_or_slash (file_name))
bf72587
@@ -673,6 +727,31 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
9934f57
   return RECOVER_NO;
d2ac734
 }
d2ac734
 
d2ac734
+/* Restore stat extended attributes (xattr) for FILE_NAME, using information
d2ac734
+   given in *ST.  Restore before extraction because they may affect layout.
d2ac734
+   If not restoring permissions, invert the
d2ac734
+   INVERT_PERMISSIONS bits from the file's current permissions.
d2ac734
+   TYPEFLAG specifies the type of the file.
d2ac734
+   FILE_CREATED indicates set_xattr has created the file */
d2ac734
+static int
d2ac734
+set_xattr (char const *file_name, struct tar_stat_info const *st,
d2ac734
+	   mode_t invert_permissions, char typeflag, int *file_created)
d2ac734
+{
d2ac734
+  int status = 0;
68aae18
+  bool interdir_made = false;
d2ac734
+
d2ac734
+  if ((xattrs_option >= 0) && st->xattr_map_size) {
d2ac734
+    mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
d2ac734
+
d2ac734
+    do
d2ac734
+      status = mknod (file_name, mode ^ invert_permissions, 0);
9934f57
+    while (status && maybe_recoverable ((char *)file_name, false, &interdir_made));
d2ac734
+    xattrs_xattrs_set(st, file_name, typeflag);
d2ac734
+    *file_created = 1;
d2ac734
+  }
d2ac734
+  return(status);
d2ac734
+}
d2ac734
+
d2ac734
 /* Fix the statuses of all directories whose statuses need fixing, and
d2ac734
    which are not ancestors of FILE_NAME.  If AFTER_LINKS is
d2ac734
    nonzero, do this for all such directories; otherwise, stop at the
bf72587
@@ -733,12 +812,23 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
2de9cec
 	  sb.stat.st_gid = data->gid;
2de9cec
 	  sb.atime = data->atime;
2de9cec
 	  sb.mtime = data->mtime;
9934f57
+	  sb.cntx_name = data->cntx_name;
9934f57
+	  sb.acls_a_ptr = data->acls_a_ptr;
9934f57
+	  sb.acls_a_len = data->acls_a_len;
9934f57
+	  sb.acls_d_ptr = data->acls_d_ptr;
9934f57
+	  sb.acls_d_len = data->acls_d_len;
9934f57
+	  sb.xattr_map = data->xattr_map;
9934f57
+	  sb.xattr_map_size = data->xattr_map_size;
9934f57
 	  set_stat (data->file_name, &sb,
9934f57
 		    -1, current_mode, current_mode_mask,
9934f57
 		    DIRTYPE, data->interdir, data->atflag);
2de9cec
 	}
2de9cec
 
2de9cec
       delayed_set_stat_head = data->next;
2de9cec
+      xheader_xattr_free (data->xattr_map, data->xattr_map_size);
2de9cec
+      free (data->cntx_name);
2de9cec
+      free (data->acls_a_ptr);
2de9cec
+      free (data->acls_d_ptr);
2de9cec
       free (data);
2de9cec
     }
2de9cec
 }
bf72587
@@ -854,6 +944,7 @@ extract_dir (char *file_name, int typeflag)
d2ac734
 
d2ac734
 static int
9934f57
 open_output_file (char const *file_name, int typeflag, mode_t mode,
9934f57
+                  int file_created,
9934f57
 		  mode_t *current_mode, mode_t *current_mode_mask)
d2ac734
 {
d2ac734
   int fd;
bf72587
@@ -864,6 +955,10 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
9934f57
 		     ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW)
d2ac734
 		     : O_EXCL));
d2ac734
 
d2ac734
+  /* File might be created in set_xattr. So clear O_EXCL to avoid open() failure */
d2ac734
+  if (file_created)
d2ac734
+    openflag = openflag & ~O_EXCL;
d2ac734
+
9934f57
   if (typeflag == CONTTYPE)
9934f57
     {
9934f57
       static int conttype_diagnosed;
bf72587
@@ -934,6 +1029,7 @@ extract_file (char *file_name, int typeflag)
9934f57
   bool interdir_made = false;
9934f57
   mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
9934f57
 		 & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
9934f57
+  mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
9934f57
   mode_t current_mode = 0;
9934f57
   mode_t current_mode_mask = 0;
9934f57
 
bf72587
@@ -950,7 +1046,17 @@ extract_file (char *file_name, int typeflag)
9934f57
     }
d2ac734
   else
d2ac734
     {
d2ac734
+      int file_created = 0;
d2ac734
+      if (set_xattr (file_name, &current_stat_info, invert_permissions,
d2ac734
+		     typeflag, &file_created))
d2ac734
+        {
d2ac734
+          skip_member ();
d2ac734
+          open_error (file_name);
d2ac734
+          return 1;
d2ac734
+        }
d2ac734
+
9934f57
       while ((fd = open_output_file (file_name, typeflag, mode,
9934f57
+                                     file_created,
9934f57
 				     &current_mode, &current_mode_mask))
9934f57
 	     < 0)
9934f57
 	{
bf72587
@@ -1091,6 +1197,13 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
2de9cec
 			    + strlen (file_name) + 1);
2de9cec
       p->sources->next = 0;
2de9cec
       strcpy (p->sources->string, file_name);
2de9cec
+      p->cntx_name = NULL;
7b35b1d
+      assign_string (&p->cntx_name, current_stat_info.cntx_name);
2de9cec
+      p->acls_a_ptr = NULL;
2de9cec
+      p->acls_a_len = 0;
2de9cec
+      p->acls_d_ptr = NULL;
2de9cec
+      p->acls_d_len = 0;
7b35b1d
+      xheader_xattr_copy (&current_stat_info, &p->xattr_map, &p->xattr_map_size);
2de9cec
       strcpy (p->target, current_stat_info.link_name);
2de9cec
 
2de9cec
       h = delayed_set_stat_head;
bf72587
@@ -1525,6 +1638,13 @@ apply_delayed_links (void)
2de9cec
 		  st1.stat.st_gid = ds->gid;
9934f57
 		  st1.atime = ds->atime;
9934f57
 		  st1.mtime = ds->mtime;
2de9cec
+                  st1.cntx_name = ds->cntx_name;
2de9cec
+                  st1.acls_a_ptr = ds->acls_a_ptr;
2de9cec
+                  st1.acls_a_len = ds->acls_a_len;
2de9cec
+                  st1.acls_d_ptr = ds->acls_d_ptr;
2de9cec
+                  st1.acls_d_len = ds->acls_d_len;
2de9cec
+                  st1.xattr_map = ds->xattr_map;
2de9cec
+                  st1.xattr_map_size = ds->xattr_map_size;
9934f57
 		  set_stat (source, &st1, -1, 0, 0, SYMTYPE,
9934f57
 			    false, AT_SYMLINK_NOFOLLOW);
2de9cec
 		  valid_source = source;
bf72587
@@ -1539,6 +1659,9 @@ apply_delayed_links (void)
7b35b1d
 	  sources = next;
7b35b1d
 	}
7b35b1d
 
7b35b1d
+   xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
7b35b1d
+   free (ds->cntx_name);
7b35b1d
+
7b35b1d
       {
7b35b1d
 	struct delayed_link *next = ds->next;
7b35b1d
 	free (ds);
9934f57
diff -urNp tar-1.24-orig/src/list.c tar-1.24/src/list.c
9934f57
--- tar-1.24-orig/src/list.c	2010-10-25 09:15:14.216463863 +0200
9934f57
+++ tar-1.24/src/list.c	2010-10-25 10:24:52.563213968 +0200
bf72587
@@ -615,6 +615,13 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
2de9cec
   assign_string (&stat_info->gname,
2de9cec
 		 header->header.gname[0] ? header->header.gname : NULL);
2de9cec
 
2de9cec
+  stat_info->acls_a_ptr = NULL;
2de9cec
+  stat_info->acls_a_len = 0;
2de9cec
+  stat_info->acls_d_ptr = NULL;
2de9cec
+  stat_info->acls_d_len = 0;
2de9cec
+  stat_info->cntx_name = NULL;
2de9cec
+  xheader_xattr_init(stat_info);
d2ac734
+
2de9cec
   if (format == OLDGNU_FORMAT && incremental_option)
2de9cec
     {
2de9cec
       stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
9934f57
diff -urNp tar-1.24-orig/src/Makefile.am tar-1.24/src/Makefile.am
9934f57
--- tar-1.24-orig/src/Makefile.am	2010-10-24 20:07:54.000000000 +0200
9934f57
+++ tar-1.24/src/Makefile.am	2010-10-25 10:24:52.564214456 +0200
a4e0f12
@@ -20,7 +20,7 @@
a4e0f12
 
a4e0f12
 bin_PROGRAMS = tar
a4e0f12
 
a4e0f12
-noinst_HEADERS = arith.h common.h tar.h
a4e0f12
+noinst_HEADERS = arith.h common.h tar.h xattrs.h
a4e0f12
 tar_SOURCES = \
a4e0f12
  buffer.c\
a4e0f12
  checkpoint.c\
a4e0f12
@@ -42,10 +42,11 @@ tar_SOURCES = \
a4e0f12
  unlink.c\
a4e0f12
  update.c\
a4e0f12
  utf8.c\
a4e0f12
- warning.c
a4e0f12
+ warning.c\
a4e0f12
+ xattrs.c
a4e0f12
 
a4e0f12
 INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
a4e0f12
 
a4e0f12
 LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
a4e0f12
 
9934f57
-tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
9934f57
+tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
9934f57
diff -urNp tar-1.24-orig/src/tar.c tar-1.24/src/tar.c
9934f57
--- tar-1.24-orig/src/tar.c	2010-10-24 20:07:55.000000000 +0200
9934f57
+++ tar-1.24/src/tar.c	2010-10-25 10:24:52.565223676 +0200
a4e0f12
@@ -255,7 +255,8 @@ tar_set_quoting_style (char *arg)
2de9cec
 
2de9cec
 enum
2de9cec
 {
2de9cec
-  ANCHORED_OPTION = CHAR_MAX + 1,
2de9cec
+  ACLS_OPTION = CHAR_MAX + 1,
2de9cec
+  ANCHORED_OPTION,
2de9cec
   ATIME_PRESERVE_OPTION,
2de9cec
   BACKUP_OPTION,
2de9cec
   CHECK_DEVICE_OPTION,
9934f57
@@ -288,6 +289,7 @@ enum
2de9cec
   MODE_OPTION,
2de9cec
   MTIME_OPTION,
2de9cec
   NEWER_MTIME_OPTION,
2de9cec
+  NO_ACLS_OPTION,
2de9cec
   NO_ANCHORED_OPTION,
2de9cec
   NO_AUTO_COMPRESS_OPTION,
2de9cec
   NO_CHECK_DEVICE_OPTION,
9934f57
@@ -301,9 +303,11 @@ enum
2de9cec
   NO_SAME_OWNER_OPTION,
2de9cec
   NO_SAME_PERMISSIONS_OPTION,
a4e0f12
   NO_SEEK_OPTION,
2de9cec
+  NO_SELINUX_CONTEXT_OPTION,
2de9cec
   NO_UNQUOTE_OPTION,
2de9cec
   NO_WILDCARDS_MATCH_SLASH_OPTION,
2de9cec
   NO_WILDCARDS_OPTION,
2de9cec
+  NO_XATTR_OPTION,
2de9cec
   NULL_OPTION,
2de9cec
   NUMERIC_OWNER_OPTION,
2de9cec
   OCCURRENCE_OPTION,
9934f57
@@ -325,6 +329,7 @@ enum
2de9cec
   RMT_COMMAND_OPTION,
2de9cec
   RSH_COMMAND_OPTION,
2de9cec
   SAME_OWNER_OPTION,
2de9cec
+  SELINUX_CONTEXT_OPTION,
2de9cec
   SHOW_DEFAULTS_OPTION,
2de9cec
   SHOW_OMITTED_DIRS_OPTION,
2de9cec
   SHOW_TRANSFORMED_NAMES_OPTION,
9934f57
@@ -340,7 +345,8 @@ enum
2de9cec
   VOLNO_FILE_OPTION,
9934f57
   WARNING_OPTION,
2de9cec
   WILDCARDS_MATCH_SLASH_OPTION,
2de9cec
-  WILDCARDS_OPTION
2de9cec
+  WILDCARDS_OPTION,
2de9cec
+  XATTR_OPTION
2de9cec
 };
2de9cec
 
2de9cec
 const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
9934f57
@@ -486,6 +492,10 @@ static struct argp_option options[] = {
2de9cec
   {NULL, 0, NULL, 0,
2de9cec
    N_("Handling of file attributes:"), GRID },
2de9cec
 
d2ac734
+  {"acls", ACLS_OPTION, 0, 0,
2de9cec
+   N_("Save the ACLs to the archive"), GRID+1 },
d2ac734
+  {"no-acls", NO_ACLS_OPTION, 0, 0,
2de9cec
+   N_("Don't extract the ACLs from the archive"), GRID+1 },
2de9cec
   {"owner", OWNER_OPTION, N_("NAME"), 0,
2de9cec
    N_("force NAME as owner for added files"), GRID+1 },
2de9cec
   {"group", GROUP_OPTION, N_("NAME"), 0,
9934f57
@@ -516,6 +526,14 @@ static struct argp_option options[] = {
2de9cec
   {"preserve-order", 's', 0, 0,
2de9cec
    N_("sort names to extract to match archive"), GRID+1 },
2de9cec
   {"same-order", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
d2ac734
+  {"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
2de9cec
+   N_("Save the SELinux context to the archive"), GRID+1 },
d2ac734
+  {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
2de9cec
+   N_("Don't extract the SELinux context from the archive"), GRID+1 },
d2ac734
+  {"xattrs", XATTR_OPTION, 0, 0,
2de9cec
+   N_("Save the user/root xattrs to the archive"), GRID+1 },
d2ac734
+  {"no-xattrs", NO_XATTR_OPTION, 0, 0,
2de9cec
+   N_("Don't extract the user/root xattrs from the archive"), GRID+1 },
2de9cec
   {"preserve", PRESERVE_OPTION, 0, 0,
2de9cec
    N_("same as both -p and -s"), GRID+1 },
2de9cec
   {"delay-directory-restore", DELAY_DIRECTORY_RESTORE_OPTION, 0, 0,
9934f57
@@ -2079,6 +2097,37 @@ parse_opt (int key, char *arg, struct ar
2de9cec
       same_permissions_option = -1;
2de9cec
       break;
2de9cec
 
2de9cec
+    case ACLS_OPTION:
2de9cec
+      set_archive_format ("posix");
2de9cec
+      acls_option = 1;
2de9cec
+      break;
Radek Brich fc86677
+
2de9cec
+    case NO_ACLS_OPTION:
2de9cec
+      acls_option = -1;
2de9cec
+      break;
Radek Brich fc86677
+
2de9cec
+    case SELINUX_CONTEXT_OPTION:
2de9cec
+      set_archive_format ("posix");
2de9cec
+      selinux_context_option = 1;
2de9cec
+      break;
Radek Brich fc86677
+
2de9cec
+    case NO_SELINUX_CONTEXT_OPTION:
2de9cec
+      selinux_context_option = -1;
2de9cec
+      break;
Radek Brich 7254d79
+
2de9cec
+    case XATTR_OPTION:
2de9cec
+      set_archive_format ("posix");
2de9cec
+      if (!acls_option) acls_option = 1;
2de9cec
+      if (!selinux_context_option) selinux_context_option = 1;
2de9cec
+      xattrs_option = 1;
2de9cec
+      break;
Radek Brich fc86677
+
2de9cec
+    case NO_XATTR_OPTION:
2de9cec
+      if (!acls_option) acls_option = -1;
2de9cec
+      if (!selinux_context_option) selinux_context_option = -1;
2de9cec
+      xattrs_option = -1;
2de9cec
+      break;
2de9cec
+
2de9cec
     case RECURSION_OPTION:
2de9cec
       recursion_option = FNM_LEADING_DIR;
2de9cec
       break;
9934f57
@@ -2461,6 +2510,29 @@ decode_options (int argc, char **argv)
2de9cec
 	  || subcommand_option != LIST_SUBCOMMAND))
2de9cec
     USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
2de9cec
 
2de9cec
+  /* star create's non-POSIX typed archives with xattr support, so allow the
2de9cec
+     extra headers */
2de9cec
+  if ((acls_option > 0)
2de9cec
+      && archive_format != POSIX_FORMAT
2de9cec
+      && (subcommand_option != EXTRACT_SUBCOMMAND
2de9cec
+	  || subcommand_option != DIFF_SUBCOMMAND
2de9cec
+	  || subcommand_option != LIST_SUBCOMMAND))
2de9cec
+    USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
2de9cec
+
2de9cec
+  if ((selinux_context_option > 0)
2de9cec
+      && archive_format != POSIX_FORMAT
2de9cec
+      && (subcommand_option != EXTRACT_SUBCOMMAND
2de9cec
+	  || subcommand_option != DIFF_SUBCOMMAND
2de9cec
+	  || subcommand_option != LIST_SUBCOMMAND))
2de9cec
+    USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
2de9cec
+
2de9cec
+  if ((xattrs_option > 0)
2de9cec
+      && archive_format != POSIX_FORMAT
2de9cec
+      && (subcommand_option != EXTRACT_SUBCOMMAND
2de9cec
+	  || subcommand_option != DIFF_SUBCOMMAND
2de9cec
+	  || subcommand_option != LIST_SUBCOMMAND))
2de9cec
+    USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives")));
2de9cec
+
2de9cec
   /* If ready to unlink hierarchies, so we are for simpler files.  */
2de9cec
   if (recursive_unlink_option)
2de9cec
     old_files_option = UNLINK_FIRST_OLD_FILES;
9934f57
@@ -2713,11 +2785,15 @@ void
2de9cec
 tar_stat_destroy (struct tar_stat_info *st)
2de9cec
 {
9934f57
   tar_stat_close (st);
2de9cec
+  xheader_xattr_free (st->xattr_map, st->xattr_map_size);
2de9cec
   free (st->orig_file_name);
2de9cec
   free (st->file_name);
2de9cec
   free (st->link_name);
2de9cec
   free (st->uname);
2de9cec
   free (st->gname);
2de9cec
+  free (st->cntx_name);
2de9cec
+  free (st->acls_a_ptr);
2de9cec
+  free (st->acls_d_ptr);
2de9cec
   free (st->sparse_map);
2de9cec
   free (st->dumpdir);
2de9cec
   xheader_destroy (&st->xhdr);
9934f57
diff -urNp tar-1.24-orig/src/tar.h tar-1.24/src/tar.h
9934f57
--- tar-1.24-orig/src/tar.h	2010-10-24 20:07:46.000000000 +0200
9934f57
+++ tar-1.24/src/tar.h	2010-10-25 10:24:52.567223606 +0200
2de9cec
@@ -276,6 +276,14 @@ struct xheader
2de9cec
   uintmax_t string_length;
2de9cec
 };
2de9cec
 
2de9cec
+/* Information about xattrs for a file.  */
2de9cec
+struct xattr_array
2de9cec
+  {
2de9cec
+    char *xkey;
2de9cec
+    char *xval_ptr;
2de9cec
+    size_t xval_len;
2de9cec
+  };
Radek Brich fc86677
+
2de9cec
 struct tar_stat_info
2de9cec
 {
2de9cec
   char *orig_file_name;     /* name of file read from the archive header */
2de9cec
@@ -287,6 +295,15 @@ struct tar_stat_info
2de9cec
 
2de9cec
   char          *uname;     /* user name of owner */
2de9cec
   char          *gname;     /* group name of owner */
Radek Brich fc86677
+
2de9cec
+  char *cntx_name;          /* SELinux context for the current archive entry. */
Radek Brich fc86677
+
2de9cec
+  char *acls_a_ptr;         /* Access ACLs for the current archive entry. */
2de9cec
+  size_t acls_a_len;        /* Access ACLs for the current archive entry. */
d2ac734
+
2de9cec
+  char *acls_d_ptr;         /* Default ACLs for the current archive entry. */
2de9cec
+  size_t acls_d_len;        /* Default ACLs for the current archive entry. */
2de9cec
+
2de9cec
   struct stat   stat;       /* regular filesystem stat */
2de9cec
 
2de9cec
   /* STAT doesn't always have access, data modification, and status
2de9cec
@@ -309,6 +326,9 @@ struct tar_stat_info
2de9cec
   size_t sparse_map_size;   /* Size of the sparse map */
2de9cec
   struct sp_array *sparse_map;
2de9cec
 
2de9cec
+  size_t xattr_map_size;   /* Size of the xattr map */
2de9cec
+  struct xattr_array *xattr_map;
2de9cec
+
2de9cec
   /* Extended headers */
2de9cec
   struct xheader xhdr;
9934f57
 
9934f57
diff -urNp tar-1.24-orig/src/xattrs.c tar-1.24/src/xattrs.c
9934f57
--- tar-1.24-orig/src/xattrs.c	1970-01-01 01:00:00.000000000 +0100
9934f57
+++ tar-1.24/src/xattrs.c	2010-10-25 10:24:52.568214736 +0200
2c5c29c
@@ -0,0 +1,490 @@
7b35b1d
+/* Create a tar archive.
Radek Brich fc86677
+
7b35b1d
+   Copyright (C) 2006 Free Software Foundation, Inc.
Radek Brich fc86677
+
7b35b1d
+   Written by James Antill, on 2006-07-27.
Radek Brich fc86677
+
7b35b1d
+   This program is free software; you can redistribute it and/or modify it
7b35b1d
+   under the terms of the GNU General Public License as published by the
7b35b1d
+   Free Software Foundation; either version 2, or (at your option) any later
7b35b1d
+   version.
Radek Brich fc86677
+
7b35b1d
+   This program is distributed in the hope that it will be useful, but
7b35b1d
+   WITHOUT ANY WARRANTY; without even the implied warranty of
7b35b1d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
7b35b1d
+   Public License for more details.
Radek Brich fc86677
+
7b35b1d
+   You should have received a copy of the GNU General Public License along
7b35b1d
+   with this program; if not, write to the Free Software Foundation, Inc.,
7b35b1d
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
Radek Brich fc86677
+
7b35b1d
+#include <system.h>
Radek Brich fc86677
+
7b35b1d
+#include <quotearg.h>
Radek Brich fc86677
+
7b35b1d
+#include "common.h"
Radek Brich fc86677
+
2de9cec
+
7b35b1d
+#ifndef HAVE_SELINUX_SELINUX_H
7b35b1d
+# undef HAVE_LIBSELINUX
7b35b1d
+#endif
2de9cec
+
7b35b1d
+#ifndef HAVE_ATTR_XATTR_H
7b35b1d
+# undef HAVE_XATTRS
7b35b1d
+#endif
2de9cec
+
7b35b1d
+#ifndef HAVE_SYS_ACL_H
7b35b1d
+# undef HAVE_LIBACL
7b35b1d
+#endif
Radek Brich fc86677
+
7b35b1d
+#ifdef HAVE_SELINUX_SELINUX_H
7b35b1d
+# include <selinux/selinux.h>
7b35b1d
+#endif
Radek Brich fc86677
+
7b35b1d
+#ifdef HAVE_ATTR_XATTR_H
7b35b1d
+# include <attr/xattr.h>
7b35b1d
+#endif
Radek Brich fc86677
+
7b35b1d
+#ifdef HAVE_SYS_ACL_H
7b35b1d
+# include <sys/acl.h>
7b35b1d
+#endif
Radek Brich fc86677
+
Radek Brich 7254d79
+
7b35b1d
+#if 0 /* unused by xattr's atm. */
7b35b1d
+static void xattrs__fd_get(struct tar_stat_info *st,
7b35b1d
+                           char const *file_name, int fd, const char *attr,
7b35b1d
+                           char **ret_ptr, size_t *ret_len)
2de9cec
+{
7b35b1d
+#ifdef HAVE_XATTRS
7b35b1d
+  static ssize_t asz = 1024;
7b35b1d
+  ssize_t ret = 0;
7b35b1d
+  static char *val = NULL;
d2ac734
+
7b35b1d
+  if (!val) val = xmalloc (asz);
d2ac734
+
7b35b1d
+  while (((ret = fgetxattr (fd, attr, val, asz)) == -1) &&
7b35b1d
+         (errno == ERANGE))
7b35b1d
+    {
7b35b1d
+      asz <<= 1;
7b35b1d
+      val = xrealloc (val, asz);
7b35b1d
+    }
d2ac734
+
7b35b1d
+  if (ret != -1)
7b35b1d
+    {
7b35b1d
+      *ret_ptr = xmemdup (val, ret + 1);
7b35b1d
+      *ret_len = ret;
7b35b1d
+    }
7b35b1d
+  else if (errno != ENOATTR)
7b35b1d
+    call_arg_warn ("fgetxattr", file_name);
7b35b1d
+#endif
2de9cec
+}
7b35b1d
+#endif
Radek Brich fc86677
+
7b35b1d
+static void xattrs__acls_get_a(struct tar_stat_info *st,
7b35b1d
+                               char const *file_name, int fd,
7b35b1d
+                               char **ret_ptr, size_t *ret_len)
7b35b1d
+{ /* "system.posix_acl_access" */
7b35b1d
+#ifdef HAVE_LIBACL
7b35b1d
+  char *val = NULL;
7b35b1d
+  ssize_t len;
7b35b1d
+  acl_t acl;
2de9cec
+
2de9cec
+  if (fd != -1)
2de9cec
+  {
2de9cec
+    if ((acl = acl_get_fd (fd)) == (acl_t)NULL)
2de9cec
+    {
2de9cec
+      if (errno != ENOTSUP)
2de9cec
+        call_arg_warn ("acl_get_fd", file_name);
2de9cec
+      return;
2de9cec
+    }
2de9cec
+  }
2de9cec
+  else if ((acl = acl_get_file (file_name, ACL_TYPE_ACCESS)) == (acl_t)NULL)
2de9cec
+  {
2de9cec
+    if (errno != ENOTSUP)
2de9cec
+      call_arg_warn ("acl_get_file", file_name);
2de9cec
+    return;
2de9cec
+  }
d2ac734
+
d2ac734
+
2de9cec
+  val = acl_to_text(acl, &len;;
2de9cec
+  acl_free (acl);
d2ac734
+
2de9cec
+  if (val == NULL)
2de9cec
+  {
2de9cec
+    call_arg_warn ("acl_to_text", file_name);
2de9cec
+    return;
2de9cec
+  }
d2ac734
+
2de9cec
+  *ret_ptr = xstrdup (val);
2de9cec
+  *ret_len = len;
d2ac734
+
2de9cec
+  acl_free (val);
2de9cec
+#endif
2de9cec
+}
2de9cec
+
2de9cec
+static void xattrs__acls_get_d(struct tar_stat_info *st,
2de9cec
+                               char const *file_name,
2de9cec
+                               char **ret_ptr, size_t *ret_len)
2de9cec
+{ /* "system.posix_acl_default" */
2de9cec
+#ifdef HAVE_LIBACL
2de9cec
+  char *val = NULL;
2de9cec
+  ssize_t len;
2de9cec
+  acl_t acl;
d2ac734
+
2de9cec
+  if ((acl = acl_get_file (file_name, ACL_TYPE_DEFAULT)) == (acl_t)NULL)
2de9cec
+  {
2de9cec
+    if (errno != ENOTSUP)
2de9cec
+      call_arg_warn ("acl_get_file", file_name);
2de9cec
+    return;
2de9cec
+  }
d2ac734
+
2de9cec
+  val = acl_to_text(acl, &len;;
2de9cec
+  acl_free (acl);
d2ac734
+
2de9cec
+  if (val == NULL)
2de9cec
+  {
2de9cec
+    call_arg_warn ("acl_to_text", file_name);
2de9cec
+    return;
2de9cec
+  }
d2ac734
+
2de9cec
+  *ret_ptr = xstrdup (val);
2de9cec
+  *ret_len = len;
d2ac734
+
2de9cec
+  acl_free (val);
2de9cec
+#endif
2de9cec
+}
2de9cec
+
2de9cec
+void xattrs_acls_get(struct tar_stat_info *st, char const *file_name, int fd,
2de9cec
+                     int xisfile)
2de9cec
+{
2de9cec
+  if (acls_option > 0)
2de9cec
+    {
2de9cec
+#ifndef HAVE_LIBACL
2de9cec
+      static int done = 0;
2de9cec
+      if (!done)
2de9cec
+          WARN ((0, 0, _("ACL support requested, but not available")));
2de9cec
+      done = 1;
2de9cec
+#endif
2de9cec
+      xattrs__acls_get_a (st, file_name, fd,
2de9cec
+                          &st->acls_a_ptr, &st->acls_a_len);
2de9cec
+      if (!xisfile)
2de9cec
+        xattrs__acls_get_d (st, file_name,
2de9cec
+                            &st->acls_d_ptr, &st->acls_d_len);
2de9cec
+    }
Radek Brich fc86677
+}
Radek Brich fc86677
+
2de9cec
+void xattrs_selinux_get(struct tar_stat_info *st, char const *file_name, int fd)
Radek Brich fc86677
+{
2de9cec
+  if (selinux_context_option > 0)
2de9cec
+  {
2de9cec
+#ifndef HAVE_LIBSELINUX
2de9cec
+      static int done = 0;
2de9cec
+      if (!done)
2de9cec
+          WARN ((0, 0, _("SELinux support requested, but not available")));
2de9cec
+      done = 1;
2de9cec
+#else
2de9cec
+    if (fd == -1)
Radek Brich fc86677
+    {
2de9cec
+      if ((lgetfilecon (file_name, &st->cntx_name) == -1) && (errno != ENOTSUP) && (errno != ENODATA))
2de9cec
+        call_arg_warn ("lgetfilecon", file_name);
Radek Brich fc86677
+    }
2de9cec
+    else if ((fgetfilecon (fd, &st->cntx_name) == -1) && (errno != ENOTSUP) && (errno != ENODATA))
2de9cec
+      call_arg_warn ("fgetfilecon", file_name);
2de9cec
+#endif
2de9cec
+  }
Radek Brich fc86677
+}
Radek Brich fc86677
+
2de9cec
+void xattrs_xattrs_get(struct tar_stat_info *st, char const *file_name, int fd)
Radek Brich fc86677
+{
2de9cec
+  if (xattrs_option > 0)
2de9cec
+    { /* get all xattrs ... this include security.* and system.* if
2de9cec
+         available. We filter them here, but we have to filter them
2de9cec
+         in xattrs_xattrs_set() anyway.
2de9cec
+      */
2de9cec
+      static ssize_t xsz = 1024;
2de9cec
+      static char *xatrs = NULL;
2de9cec
+      ssize_t xret = -1;
Radek Brich fc86677
+
2de9cec
+#ifndef HAVE_XATTRS
2de9cec
+      static int done = 0;
2de9cec
+      if ((xattrs_option > 0) && !done)
2de9cec
+          WARN ((0, 0, _("Xattr support requested, but not available")));
2de9cec
+      done = 1;
2de9cec
+#else
Radek Brich fc86677
+
2de9cec
+      if (!xatrs) xatrs = xmalloc (xsz);
d2ac734
+
d2ac734
+      while (((fd == -1) ?
7b35b1d
+              ((xret = llistxattr (file_name, xatrs, xsz)) == -1) :
2de9cec
+              ((xret = flistxattr (fd, xatrs, xsz)) == -1)) &&
2de9cec
+             (errno == ERANGE))
2de9cec
+        {
2de9cec
+          xsz <<= 1;
2de9cec
+          xatrs = xrealloc (xatrs, xsz);
2de9cec
+        }
Radek Brich fc86677
+
2de9cec
+      if (xret == -1)
7b35b1d
+        call_arg_warn ((fd == -1) ? "llistxattrs" : "flistxattrs", file_name);
2de9cec
+      else
2de9cec
+        {
2de9cec
+          const char *attr = xatrs;
2de9cec
+          static ssize_t asz = 1024;
2de9cec
+          static char *val = NULL;
d2ac734
+
2de9cec
+          if (!val) val = xmalloc (asz);
d2ac734
+
2de9cec
+          while (xret > 0)
2de9cec
+            {
2de9cec
+              size_t len = strlen (attr);
2de9cec
+              ssize_t aret = 0;
d2ac734
+
d2ac734
+              /* Archive all xattrs during creation, decide at extraction time
d2ac734
+               * which ones are of interest/use for the target filesystem. */
7b35b1d
+              while (((fd == -1) ?
7b35b1d
+                      ((aret = lgetxattr (file_name, attr, val, asz)) == -1) :
2de9cec
+                      ((aret = fgetxattr (fd, attr, val, asz)) == -1)) &&
2de9cec
+                     (errno == ERANGE))
2de9cec
+                {
2de9cec
+                  asz <<= 1;
2de9cec
+                  val = xrealloc (val, asz);
2de9cec
+                }
d2ac734
+
2de9cec
+              if (aret != -1)
2de9cec
+                xheader_xattr_add (st, attr, val, aret);
2de9cec
+              else if (errno != ENOATTR)
7b35b1d
+                call_arg_warn ((fd==-1) ? "lgetxattr" : "fgetxattr", file_name);
d2ac734
+
2de9cec
+              attr += len + 1;
2de9cec
+              xret -= len + 1;
2de9cec
+            }
2de9cec
+        }
2de9cec
+#endif
2de9cec
+    }
Radek Brich fc86677
+}
Radek Brich fc86677
+
2de9cec
+static void xattrs__fd_set(struct tar_stat_info const *st,
2de9cec
+                           char const *file_name, char typeflag,
2de9cec
+                           const char *attr,
2de9cec
+                           const char *ptr, size_t len)
Radek Brich fc86677
+{
2de9cec
+#ifdef HAVE_XATTRS
2de9cec
+  if (ptr)
Radek Brich fc86677
+    {
2de9cec
+      const char *sysname = "setxattr";
2de9cec
+      int ret = -1;
Radek Brich fc86677
+
2de9cec
+      if (typeflag != SYMTYPE)
2de9cec
+        ret = setxattr (file_name, attr, ptr, len, 0);
2de9cec
+      else
2de9cec
+        {
2de9cec
+          sysname = "lsetxattr";
2de9cec
+          ret = lsetxattr (file_name, attr, ptr, len, 0);
2de9cec
+        }
Radek Brich fc86677
+
2de9cec
+      /* do not print warnings when SELinux is disabled */
2de9cec
+      if ((ret == -1) && (errno != EPERM) && (errno != ENOTSUP))
2de9cec
+        call_arg_error(sysname, file_name);
2de9cec
+    }
2de9cec
+#endif
Radek Brich fc86677
+}
Radek Brich fc86677
+
2de9cec
+/* convert unix permissions into an ACL ... needed due to "default" ACLs */
2de9cec
+#ifdef HAVE_LIBACL
2de9cec
+static acl_t perms2acl(int perms)
Radek Brich fc86677
+{
2de9cec
+  char val[] = "user::---,group::---,other::---";
2de9cec
+  /*            0123456789 123456789 123456789 123456789 */
Radek Brich fc86677
+
2de9cec
+   /* user */
2de9cec
+  if (perms & 0400) val[ 6] = 'r';
2de9cec
+  if (perms & 0200) val[ 7] = 'w';
2de9cec
+  if (perms & 0100) val[ 8] = 'x';
Radek Brich fc86677
+
2de9cec
+  /* group */
2de9cec
+  if (perms & 0040) val[17] = 'r';
2de9cec
+  if (perms & 0020) val[18] = 'w';
2de9cec
+  if (perms & 0010) val[19] = 'x';
Radek Brich fc86677
+
2de9cec
+  /* other */
2de9cec
+  if (perms & 0004) val[28] = 'r';
2de9cec
+  if (perms & 0002) val[29] = 'w';
2de9cec
+  if (perms & 0001) val[30] = 'x';
d2ac734
+
2de9cec
+  return (acl_from_text (val));
Radek Brich fc86677
+}
2de9cec
+#endif
Radek Brich fc86677
+
2de9cec
+static char *skip_to_ext_fields(char *ptr)
Radek Brich fc86677
+{
2de9cec
+  ptr += strcspn(ptr, ":,\n"); /* skip tag name. Ie. user/group/default/mask */
Radek Brich fc86677
+
2de9cec
+  if (*ptr != ':')
2de9cec
+    return (ptr); /* error? no user/group field */
2de9cec
+  ++ptr;
d2ac734
+
2de9cec
+  ptr += strcspn(ptr, ":,\n"); /* skip user/group name */
Radek Brich fc86677
+
2de9cec
+  if (*ptr != ':')
2de9cec
+    return (ptr); /* error? no perms field */
2de9cec
+  ++ptr;
d2ac734
+
2de9cec
+  ptr += strcspn(ptr, ":,\n"); /* skip perms */
Radek Brich fc86677
+
2de9cec
+  if (*ptr != ':')
2de9cec
+    return (ptr); /* no extra fields */
Radek Brich fc86677
+
2de9cec
+  return (ptr);
2de9cec
+}
Radek Brich fc86677
+
2de9cec
+/* The POSIX draft allows extra fields after the three main ones. Star
2de9cec
+   uses this to add a fourth field for user/group which is the numeric ID.
2de9cec
+   We just skip all extra fields atm. */
2de9cec
+static const char *fixup_extra_acl_fields(const char *ptr)
2de9cec
+{
2de9cec
+  char *src = (char *)ptr;
2de9cec
+  char *dst = (char *)ptr;
Radek Brich fc86677
+
2de9cec
+  while (*src)
2de9cec
+  {
2de9cec
+    const char *old = src;
2de9cec
+    size_t len = 0;
Radek Brich fc86677
+
2de9cec
+    src = skip_to_ext_fields(src);
2de9cec
+    len = src - old;
2de9cec
+    if (old != dst) memmove(dst, old, len);
2de9cec
+    dst += len;
Radek Brich fc86677
+
2de9cec
+    if (*src == ':') /* We have extra fields, skip them all */
2de9cec
+      src += strcspn(src, "\n,");
d2ac734
+
2de9cec
+    if ((*src == '\n') || (*src == ','))
2de9cec
+      *dst++ = *src++; /* also done when dst == src, but that's ok */
2de9cec
+  }
2de9cec
+  if (src != dst)
2de9cec
+    *dst = 0;
d2ac734
+
2de9cec
+  return ptr;
2de9cec
+}
Radek Brich fc86677
+
2de9cec
+static void xattrs__acls_set(struct tar_stat_info const *st,
2de9cec
+                             char const *file_name, int type,
2de9cec
+                             const char *ptr, size_t len)
2de9cec
+{ /* "system.posix_acl_access" */
2de9cec
+#ifdef HAVE_LIBACL
2de9cec
+  acl_t acl;
2de9cec
+
2de9cec
+  if (ptr)
2de9cec
+    {
2de9cec
+      /* assert (strlen (ptr) == len); */
2de9cec
+      ptr = fixup_extra_acl_fields(ptr);
d2ac734
+
2de9cec
+      acl = acl_from_text (ptr);
2de9cec
+      acls_option = 1;
2de9cec
+    }
2de9cec
+  else if (acls_option > 0)
2de9cec
+    acl = perms2acl (st->stat.st_mode);
2de9cec
+  else
2de9cec
+    return; /* don't call acl functions unless we first hit an ACL, or
2de9cec
+               --acls was passed explicitly */
d2ac734
+
2de9cec
+  if (acl == (acl_t)NULL)
2de9cec
+    {
2de9cec
+      call_arg_warn ("acl_from_text", file_name);
2de9cec
+      return;
2de9cec
+    }
d2ac734
+
2de9cec
+  if (acl_set_file (file_name, type, acl) == -1)
2de9cec
+    {
2de9cec
+      if (errno != ENOTSUP)
2de9cec
+        call_arg_warn ("acl_set_file", file_name);
2de9cec
+    }
2de9cec
+  acl_free (acl);
2de9cec
+#endif
2de9cec
+}
Radek Brich fc86677
+
2de9cec
+void xattrs_acls_set(struct tar_stat_info const *st,
2de9cec
+                     char const *file_name, char typeflag)
2de9cec
+{
2de9cec
+  if ((acls_option >= 0) && (typeflag != SYMTYPE))
2de9cec
+    {
2de9cec
+#ifndef HAVE_LIBACL
2de9cec
+      static int done = 0;
2de9cec
+      if (!done)
2de9cec
+        WARN ((0, 0, _("ACL support requested, but not available")));
2de9cec
+      done = 1;
2de9cec
+#else
2de9cec
+      xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
2de9cec
+                        st->acls_a_ptr, st->acls_a_len);
f6da44a
+      if ((typeflag == DIRTYPE) || (typeflag == GNUTYPE_DUMPDIR))
2de9cec
+        xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
2de9cec
+                          st->acls_d_ptr, st->acls_d_len);
2de9cec
+#endif
2de9cec
+    }
2de9cec
+}
2de9cec
+
2de9cec
+void xattrs_selinux_set(struct tar_stat_info const *st,
2de9cec
+                        char const *file_name, char typeflag)
2de9cec
+{
2de9cec
+  if ((selinux_context_option >= 0) && st->cntx_name)
Radek Brich fc86677
+    {
2de9cec
+      const char *sysname = "setfilecon";
2de9cec
+      int ret = -1;
2de9cec
+
2de9cec
+#ifndef HAVE_LIBSELINUX
2de9cec
+      static int done = 0;
2de9cec
+      if (!done)
2de9cec
+          WARN ((0, 0, _("SELinux support requested, but not available")));
2de9cec
+      done = 1;
2de9cec
+#else
2de9cec
+      if (typeflag != SYMTYPE)
2de9cec
+        ret = setfilecon (file_name, st->cntx_name);
2de9cec
+      else
Radek Brich fc86677
+        {
2de9cec
+          sysname = "lsetfilecon";
2de9cec
+          ret = lsetfilecon (file_name, st->cntx_name);
Radek Brich fc86677
+        }
2de9cec
+
2de9cec
+      if ((ret == -1) && (errno == EPERM))
2de9cec
+        call_arg_warn(sysname, file_name);
2de9cec
+      else if ((ret == -1) && (errno != EOPNOTSUPP))
2de9cec
+        call_arg_error(sysname, file_name);
2de9cec
+#endif
2de9cec
+    }
2de9cec
+}
2de9cec
+
2de9cec
+void xattrs_xattrs_set(struct tar_stat_info const *st,
2de9cec
+                       char const *file_name, char typeflag)
2de9cec
+{
2de9cec
+  if ((xattrs_option >= 0) && st->xattr_map_size)
2de9cec
+    {
2de9cec
+      size_t scan = 0;
2de9cec
+
2de9cec
+#ifndef HAVE_XATTRS
2de9cec
+      static int done = 0;
2de9cec
+      if (!done)
2de9cec
+          WARN ((0, 0, _("Xattr support requested, but not available")));
2de9cec
+      done = 1;
2de9cec
+#else
2de9cec
+      while (scan < st->xattr_map_size)
Radek Brich fc86677
+        {
2de9cec
+          char *keyword = st->xattr_map[scan].xkey;
2de9cec
+
2de9cec
+          /* assert (!memcpy (keyword, "SCHILY.xattr.", strlen("SCHILY.xattr."))); */
2de9cec
+          keyword += strlen("SCHILY.xattr.");
2de9cec
+
2de9cec
+          if (strncmp (keyword, "user.", strlen("user.")) &&
d2ac734
+              strncmp (keyword, "lustre.", strlen("lustre.")) &&
2591b69
+              strncmp (keyword, "trusted.", strlen("trusted.")) &&
2c5c29c
+              strncmp (keyword, "security.NTACL", strlen("security.NTACL")) &&
2c5c29c
+              strncmp (keyword, "security.capability", strlen("security.capability")))
2de9cec
+            continue; /* don't try and set anything but normal xattrs */
d2ac734
+
2de9cec
+          xattrs__fd_set (st, file_name, typeflag, keyword,
2de9cec
+                          st->xattr_map[scan].xval_ptr,
2de9cec
+                          st->xattr_map[scan].xval_len);
d2ac734
+
2de9cec
+          ++scan;
Radek Brich fc86677
+        }
2de9cec
+#endif
Radek Brich fc86677
+    }
2de9cec
+}
9934f57
diff -urNp tar-1.24-orig/src/xattrs.h tar-1.24/src/xattrs.h
9934f57
--- tar-1.24-orig/src/xattrs.h	1970-01-01 01:00:00.000000000 +0100
9934f57
+++ tar-1.24/src/xattrs.h	2010-10-25 10:24:52.569214526 +0200
Radek Brich fc86677
@@ -0,0 +1,14 @@
Radek Brich fc86677
+
Radek Brich fc86677
+extern void xattrs_acls_get(struct tar_stat_info *st,
Radek Brich fc86677
+                            char const *file_name, int fd, int xisfile);
Radek Brich fc86677
+extern void xattrs_selinux_get(struct tar_stat_info *st,
Radek Brich fc86677
+                               char const *file_name, int fd);
Radek Brich fc86677
+extern void xattrs_xattrs_get(struct tar_stat_info *st,
Radek Brich fc86677
+                              char const *file_name, int fd);
Radek Brich fc86677
+
Radek Brich fc86677
+extern void xattrs_acls_set(struct tar_stat_info const *st,
Radek Brich fc86677
+                            char const *file_name, char typeflag);
Radek Brich fc86677
+extern void xattrs_selinux_set(struct tar_stat_info const *st,
Radek Brich fc86677
+                               char const *file_name, char typeflag);
Radek Brich fc86677
+extern void xattrs_xattrs_set(struct tar_stat_info const *st,
Radek Brich fc86677
+                              char const *file_name, char typeflag);
9934f57
diff -urNp tar-1.24-orig/src/xheader.c tar-1.24/src/xheader.c
9934f57
--- tar-1.24-orig/src/xheader.c	2010-10-24 20:07:46.000000000 +0200
9934f57
+++ tar-1.24/src/xheader.c	2010-10-25 10:24:52.570223396 +0200
9934f57
@@ -460,6 +460,74 @@ xheader_write_global (struct xheader *xh
a4e0f12
     }
7b35b1d
 }
7b35b1d
 
7b35b1d
+void xheader_xattr_init(struct tar_stat_info *st)
7b35b1d
+{
7b35b1d
+  st->xattr_map = NULL;
7b35b1d
+  st->xattr_map_size = 0;
7b35b1d
+}
7b35b1d
+
7b35b1d
+void xheader_xattr_free(struct xattr_array *xattr_map, size_t xattr_map_size)
7b35b1d
+{
7b35b1d
+  size_t scan = 0;
7b35b1d
+
7b35b1d
+  while (scan < xattr_map_size)
7b35b1d
+    {
7b35b1d
+      free (xattr_map[scan].xkey);
7b35b1d
+      free (xattr_map[scan].xval_ptr);
7b35b1d
+
7b35b1d
+      ++scan;
7b35b1d
+    }
7b35b1d
+  free (xattr_map);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void xheader_xattr__add(struct xattr_array **xattr_map,
7b35b1d
+                               size_t *xattr_map_size,
7b35b1d
+                               const char *key, const char *val, size_t len)
7b35b1d
+{
7b35b1d
+  size_t pos = (*xattr_map_size)++;
d2ac734
+
7b35b1d
+  *xattr_map = xrealloc (*xattr_map,
7b35b1d
+                         *xattr_map_size * sizeof(struct xattr_array));
7b35b1d
+  (*xattr_map)[pos].xkey = xstrdup (key);
7b35b1d
+  (*xattr_map)[pos].xval_ptr = xmemdup (val, len + 1);
7b35b1d
+  (*xattr_map)[pos].xval_len = len;
7b35b1d
+}
7b35b1d
+
7b35b1d
+void xheader_xattr_add(struct tar_stat_info *st,
7b35b1d
+                       const char *key, const char *val, size_t len)
7b35b1d
+{
7b35b1d
+  size_t klen = strlen (key);
7b35b1d
+  char *xkey = xmalloc (strlen("SCHILY.xattr.") + klen + 1);
7b35b1d
+  char *tmp = xkey;
7b35b1d
+
7b35b1d
+  tmp = stpcpy (tmp, "SCHILY.xattr.");
7b35b1d
+  tmp = stpcpy (tmp, key);
d2ac734
+
7b35b1d
+  xheader_xattr__add (&st->xattr_map, &st->xattr_map_size, xkey, val, len);
7b35b1d
+
7b35b1d
+  free (xkey);
7b35b1d
+}
7b35b1d
+
7b35b1d
+void xheader_xattr_copy(const struct tar_stat_info *st,
7b35b1d
+                        struct xattr_array **xattr_map, size_t *xattr_map_size)
7b35b1d
+{
7b35b1d
+  size_t scan = 0;
7b35b1d
+
7b35b1d
+  *xattr_map = NULL;
7b35b1d
+  *xattr_map_size = 0;
d2ac734
+
7b35b1d
+  while (scan < st->xattr_map_size)
7b35b1d
+    {
7b35b1d
+      char  *key = st->xattr_map[scan].xkey;
7b35b1d
+      char  *val = st->xattr_map[scan].xval_ptr;
7b35b1d
+      size_t len = st->xattr_map[scan].xval_len;
d2ac734
+
7b35b1d
+      xheader_xattr__add(xattr_map, xattr_map_size, key, val, len);
7b35b1d
+
7b35b1d
+      ++scan;
7b35b1d
+    }
7b35b1d
+}
7b35b1d
+
7b35b1d
 
7b35b1d
 /* General Interface */
7b35b1d
 
9934f57
@@ -473,6 +541,7 @@ struct xhdr_tab
7b35b1d
 		 struct xheader *, void const *data);
7b35b1d
   void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
a4e0f12
   int flags;
7b35b1d
+  bool prefix;
7b35b1d
 };
7b35b1d
 
7b35b1d
 /* This declaration must be extern, because ISO C99 section 6.9.2
9934f57
@@ -489,8 +558,17 @@ locate_handler (char const *keyword)
7b35b1d
   struct xhdr_tab const *p;
7b35b1d
 
7b35b1d
   for (p = xhdr_tab; p->keyword; p++)
7b35b1d
-    if (strcmp (p->keyword, keyword) == 0)
7b35b1d
-      return p;
7b35b1d
+    if (p->prefix)
7b35b1d
+      {
7b35b1d
+        if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0)
7b35b1d
+          return p;
7b35b1d
+      }
7b35b1d
+  else
7b35b1d
+      {
7b35b1d
+        if (strcmp (p->keyword, keyword) == 0)
7b35b1d
+          return p;
7b35b1d
+      }
d2ac734
+
7b35b1d
   return NULL;
7b35b1d
 }
7b35b1d
 
9934f57
@@ -500,7 +578,7 @@ xheader_protected_pattern_p (const char 
7b35b1d
   struct xhdr_tab const *p;
7b35b1d
 
7b35b1d
   for (p = xhdr_tab; p->keyword; p++)
a4e0f12
-    if ((p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
a4e0f12
+    if (!p->prefix && (p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
7b35b1d
       return true;
7b35b1d
   return false;
7b35b1d
 }
9934f57
@@ -511,7 +589,7 @@ xheader_protected_keyword_p (const char 
7b35b1d
   struct xhdr_tab const *p;
7b35b1d
 
7b35b1d
   for (p = xhdr_tab; p->keyword; p++)
a4e0f12
-    if ((p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
a4e0f12
+    if (!p->prefix && (p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
7b35b1d
       return true;
7b35b1d
   return false;
7b35b1d
 }
9934f57
@@ -1470,6 +1548,71 @@ volume_filename_decoder (struct tar_stat
7b35b1d
 }
7b35b1d
 
7b35b1d
 static void
7b35b1d
+xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword,
7b35b1d
+                     struct xheader *xhdr, void const *data)
7b35b1d
+{
7b35b1d
+  code_string (st->cntx_name, keyword, xhdr);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_selinux_decoder (struct tar_stat_info *st,
7b35b1d
+                       char const *keyword, char const *arg, size_t size)
7b35b1d
+{
7b35b1d
+  decode_string (&st->cntx_name, arg);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword,
7b35b1d
+                    struct xheader *xhdr, void const *data)
7b35b1d
+{
7b35b1d
+  xheader_print_n (xhdr, keyword, st->acls_a_ptr, st->acls_a_len);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_acls_a_decoder (struct tar_stat_info *st,
7b35b1d
+                      char const *keyword, char const *arg, size_t size)
7b35b1d
+{
7b35b1d
+  st->acls_a_ptr = xmemdup (arg, size + 1);
7b35b1d
+  st->acls_a_len = size;
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_acls_d_coder (struct tar_stat_info const *st , char const *keyword,
7b35b1d
+                    struct xheader *xhdr, void const *data)
7b35b1d
+{
7b35b1d
+  xheader_print_n (xhdr, keyword, st->acls_d_ptr, st->acls_d_len);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_acls_d_decoder (struct tar_stat_info *st,
7b35b1d
+                      char const *keyword, char const *arg, size_t size)
7b35b1d
+{
7b35b1d
+  st->acls_d_ptr = xmemdup (arg, size + 1);
7b35b1d
+  st->acls_d_len = size;
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_coder (struct tar_stat_info const *st , char const *keyword,
7b35b1d
+             struct xheader *xhdr, void const *data)
7b35b1d
+{
7b35b1d
+  struct xattr_array *xattr_map = st->xattr_map;
7b35b1d
+  const size_t *off = data;
7b35b1d
+  xheader_print_n (xhdr, keyword,
7b35b1d
+                   xattr_map[*off].xval_ptr, xattr_map[*off].xval_len);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
+xattr_decoder (struct tar_stat_info *st,
7b35b1d
+               char const *keyword, char const *arg, size_t size)
7b35b1d
+{
7b35b1d
+  char *xstr = NULL;
7b35b1d
+
7b35b1d
+  xstr = xmemdup(arg, size + 1);
7b35b1d
+  xheader_xattr_add(st, keyword + strlen("SCHILY.xattr."), xstr, size);
7b35b1d
+  free(xstr);
7b35b1d
+}
7b35b1d
+
7b35b1d
+static void
7b35b1d
 sparse_major_coder (struct tar_stat_info const *st, char const *keyword,
7b35b1d
 		    struct xheader *xhdr, void const *data)
7b35b1d
 {
9934f57
@@ -1506,53 +1649,53 @@ sparse_minor_decoder (struct tar_stat_in
7b35b1d
 }
7b35b1d
 
7b35b1d
 struct xhdr_tab const xhdr_tab[] = {
a4e0f12
-  { "atime",	atime_coder,	atime_decoder,	  0 },
a4e0f12
-  { "comment",	dummy_coder,	dummy_decoder,	  0 },
a4e0f12
-  { "charset",	dummy_coder,	dummy_decoder,	  0 },
a4e0f12
-  { "ctime",	ctime_coder,	ctime_decoder,	  0 },
a4e0f12
-  { "gid",	gid_coder,	gid_decoder,	  0 },
a4e0f12
-  { "gname",	gname_coder,	gname_decoder,	  0 },
a4e0f12
-  { "linkpath", linkpath_coder, linkpath_decoder, 0 },
a4e0f12
-  { "mtime",	mtime_coder,	mtime_decoder,	  0 },
a4e0f12
-  { "path",	path_coder,	path_decoder,	  0 },
a4e0f12
-  { "size",	size_coder,	size_decoder,	  0 },
a4e0f12
-  { "uid",	uid_coder,	uid_decoder,	  0 },
a4e0f12
-  { "uname",	uname_coder,	uname_decoder,	  0 },
a4e0f12
+  { "atime",   atime_coder,    atime_decoder,    0, false },
a4e0f12
+  { "comment", dummy_coder,    dummy_decoder,    0, false },
a4e0f12
+  { "charset", dummy_coder,    dummy_decoder,    0, false },
a4e0f12
+  { "ctime",   ctime_coder,    ctime_decoder,    0, false },
a4e0f12
+  { "gid",     gid_coder,      gid_decoder,      0, false },
a4e0f12
+  { "gname",   gname_coder,    gname_decoder,    0, false },
a4e0f12
+  { "linkpath", linkpath_coder, linkpath_decoder, 0, false },
a4e0f12
+  { "mtime",   mtime_coder,    mtime_decoder,    0, false },
a4e0f12
+  { "path",    path_coder,     path_decoder,     0, false },
a4e0f12
+  { "size",    size_coder,     size_decoder,     0, false },
a4e0f12
+  { "uid",     uid_coder,      uid_decoder,      0, false },
a4e0f12
+  { "uname",   uname_coder,    uname_decoder,    0, false },
7b35b1d
 
7b35b1d
   /* Sparse file handling */
7b35b1d
   { "GNU.sparse.name",       path_coder, path_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
a4e0f12
   { "GNU.sparse.major",      sparse_major_coder, sparse_major_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
a4e0f12
   { "GNU.sparse.minor",      sparse_minor_coder, sparse_minor_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
a4e0f12
   { "GNU.sparse.realsize",   sparse_size_coder, sparse_size_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
a4e0f12
   { "GNU.sparse.numblocks",  sparse_numblocks_coder, sparse_numblocks_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
7b35b1d
 
7b35b1d
   /* tar 1.14 - 1.15.90 keywords. */
a4e0f12
   { "GNU.sparse.size",       sparse_size_coder, sparse_size_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
7b35b1d
   /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
7b35b1d
      headers, and each of them was meaningful. It confilcted with POSIX specs,
7b35b1d
      which requires that "when extended header records conflict, the last one
7b35b1d
      given in the header shall take precedence." */
7b35b1d
   { "GNU.sparse.offset",     sparse_offset_coder, sparse_offset_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
7b35b1d
   { "GNU.sparse.numbytes",   sparse_numbytes_coder, sparse_numbytes_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
7b35b1d
   /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
7b35b1d
   { "GNU.sparse.map",        NULL /* Unused, see pax_dump_header() */,
a4e0f12
-    sparse_map_decoder, 0 },
a4e0f12
+    sparse_map_decoder, 0, false },
7b35b1d
 
7b35b1d
   { "GNU.dumpdir",           dumpdir_coder, dumpdir_decoder,
a4e0f12
-    XHDR_PROTECTED },
a4e0f12
+    XHDR_PROTECTED, false },
7b35b1d
 
7b35b1d
   /* Keeps the tape/volume label. May be present only in the global headers.
7b35b1d
      Equivalent to GNUTYPE_VOLHDR.  */
a4e0f12
   { "GNU.volume.label", volume_label_coder, volume_label_decoder,
a4e0f12
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4e0f12
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
7b35b1d
 
7b35b1d
   /* These may be present in a first global header of the archive.
7b35b1d
      They provide the same functionality as GNUTYPE_MULTIVOL header.
2c5c29c
@@ -1561,11 +1704,42 @@ struct xhdr_tab const xhdr_tab[] = {
7b35b1d
      GNU.volume.offset keeps the offset of the start of this volume,
7b35b1d
      otherwise kept in oldgnu_header.offset.  */
7b35b1d
   { "GNU.volume.filename", volume_label_coder, volume_filename_decoder,
a4e0f12
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4e0f12
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
a4e0f12
   { "GNU.volume.size", volume_size_coder, volume_size_decoder,
a4e0f12
-    XHDR_PROTECTED | XHDR_GLOBAL },
a4e0f12
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
a4e0f12
   { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder,
a4e0f12
-    XHDR_PROTECTED | XHDR_GLOBAL },
bf72587
-
bf72587
-  { NULL, NULL, NULL, 0 }
a4e0f12
+    XHDR_PROTECTED | XHDR_GLOBAL, false },
7b35b1d
+
7b35b1d
+  /* We get the SELinux value from filecon, so add a namespace for SELinux
7b35b1d
+     instead of storing it in SCHILY.xattr.* (which would be RAW). */
7b35b1d
+  { "RHT.security.selinux",
a4e0f12
+    xattr_selinux_coder, xattr_selinux_decoder, 0, false },
7b35b1d
+
7b35b1d
+  /* ACLs, use the star format... */
7b35b1d
+  { "SCHILY.acl.access",
a4e0f12
+    xattr_acls_a_coder, xattr_acls_a_decoder, 0, false },
7b35b1d
+
7b35b1d
+  { "SCHILY.acl.default",
a4e0f12
+    xattr_acls_d_coder, xattr_acls_d_decoder, 0, false },
7b35b1d
+
7b35b1d
+  /* FIXME: These are compat. for FC-6 ... we shipped a tar using the generic
7b35b1d
+     header names by accident. */
7b35b1d
+  { "SCHILY.xattr.security.selinux",
a4e0f12
+    xattr_selinux_coder, xattr_selinux_decoder, 0, false },
7b35b1d
+  { "SCHILY.xattr.system.posix_acl_access",
a4e0f12
+    xattr_acls_a_coder, xattr_acls_a_decoder, 0, false },
7b35b1d
+  { "SCHILY.xattr.system.posix_acl_default",
a4e0f12
+    xattr_acls_d_coder, xattr_acls_d_decoder, 0, false },
7b35b1d
+
d2ac734
+  /* xattrs use the star format.  note we only save some variants... */
a4e0f12
+  { "SCHILY.xattr.user",    xattr_coder, xattr_decoder, 0, true },
a4e0f12
+  { "SCHILY.xattr.trusted", xattr_coder, xattr_decoder, 0, true },
a4e0f12
+  { "SCHILY.xattr.lustre",  xattr_coder, xattr_decoder, 0, true },
e8e9e33
+  { "SCHILY.xattr.security.NTACL", xattr_coder, xattr_decoder, 0, true },
2c5c29c
+  { "SCHILY.xattr.security.capability", xattr_coder, xattr_decoder, 0, true },
7b35b1d
+
7b35b1d
+  /* ignore everything else in the xattr namespaces... */
a4e0f12
+  { "SCHILY.xattr",         dummy_coder, dummy_decoder, 0, true },
bf72587
+
a4e0f12
+  { NULL, NULL, NULL, 0, false }
7b35b1d
 };