summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app-arch/tar/files/tar-1.26-full-xattr.patch')
-rw-r--r--app-arch/tar/files/tar-1.26-full-xattr.patch953
1 files changed, 0 insertions, 953 deletions
diff --git a/app-arch/tar/files/tar-1.26-full-xattr.patch b/app-arch/tar/files/tar-1.26-full-xattr.patch
deleted file mode 100644
index 7a5c7f89..00000000
--- a/app-arch/tar/files/tar-1.26-full-xattr.patch
+++ /dev/null
@@ -1,953 +0,0 @@
-diff --git a/configure.ac b/configure.ac
-index db69cb8..2afa463 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -223,6 +223,20 @@ AC_CHECK_TYPE(iconv_t,:,
- #endif
- ])
-
-+AC_ARG_ENABLE(xattr,
-+ AC_HELP_STRING([--enable-xattr],
-+ [enable Extended Attribute support (disabled by default)]),
-+ [xattr_enabled=$enableval],
-+ [xattr_enabled=no])
-+
-+if test "x$xattr_enabled" = xyes; then
-+ AC_CHECK_HEADERS(attr/xattr.h)
-+ AC_CHECK_FUNCS(getxattr fgetxattr lgetxattr \
-+ setxattr fsetxattr lsetxattr \
-+ listxattr flistxattr llistxattr,
-+ AC_DEFINE(HAVE_XATTRS,1,[Define if we have a working extended attributes]),)
-+fi
-+
- # Gettext.
- AM_GNU_GETTEXT([external], [need-formatstring-macros])
- AM_GNU_GETTEXT_VERSION([0.16])
-diff --git a/doc/tar.texi b/doc/tar.texi
-index db8f986..d861d12 100644
---- a/doc/tar.texi
-+++ b/doc/tar.texi
-@@ -3002,6 +3002,10 @@ mechanism.
- Treat all input file or member names literally, do not interpret
- escape sequences. @xref{input name quoting}.
-
-+@opsummary{no-xattrs}
-+@item --no-xattrs
-+Causes @command{tar} not to store and not to extract xattrs. @xref{Attributes}.
-+
- @opsummary{no-wildcards}
- @item --no-wildcards
- Do not use wildcards.
-@@ -3447,6 +3451,10 @@ Enable or disable warning messages identified by @var{keyword}. The
- messages are suppressed if @var{keyword} is prefixed with @samp{no-}.
- @xref{warnings}.
-
-+@opsummary{xattrs}
-+@item --xattrs
-+Causes @command{tar} to store xattrs. @xref{Attributes}.
-+
- @opsummary{wildcards}
- @item --wildcards
- Use wildcards when matching member names with patterns.
-@@ -8659,6 +8667,8 @@ implementation able to read @samp{ustar} archives will be able to read
- most @samp{posix} archives as well, with the only exception that any
- additional information (such as long file names etc.) will in such
- case be extracted as plain text files along with the files it refers to.
-+This is the only format that can store ACLs, SELinux context and extended
-+attributes.
-
- This archive format will be the default format for future versions
- of @GNUTAR{}.
-@@ -9293,6 +9303,20 @@ Same as both @option{--same-permissions} and @option{--same-order}.
-
- This option is deprecated, and will be removed in @GNUTAR{} version 1.23.
-
-+@opindex xattrs
-+@item --xattrs
-+This option causes @command{tar} to store the current extended attributes in
-+the archive.
-+
-+The @option{--xattrs} option has no equivalent short option name.
-+
-+@opindex no-xattrs
-+@item --no-xattrs
-+This option causes @command{tar} not to store the current extended attributes in
-+the archive and not to extract any extended attributes in an archive.
-+
-+The @option{--no-xattrs} option has no equivalent short option name.
-+
- @end table
-
- @node Portability
-diff --git a/src/Makefile.am b/src/Makefile.am
-index de310f4..27c28be 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -20,7 +20,7 @@
-
- bin_PROGRAMS = tar
-
--noinst_HEADERS = arith.h common.h tar.h
-+noinst_HEADERS = arith.h common.h tar.h xattrs.h
- tar_SOURCES = \
- buffer.c\
- checkpoint.c\
-@@ -42,10 +42,11 @@ tar_SOURCES = \
- unlink.c\
- update.c\
- utf8.c\
-- warning.c
-+ warning.c\
-+ xattrs.c
-
- INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
-
- LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
-
--tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
-+tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
-diff --git a/src/common.h b/src/common.h
-index 0b9bd7a..e4ee345 100644
---- a/src/common.h
-+++ b/src/common.h
-@@ -253,6 +253,9 @@ GLOBAL int same_owner_option;
- /* If positive, preserve permissions when extracting. */
- GLOBAL int same_permissions_option;
-
-+/* If positive, save the user and root xattrs. */
-+GLOBAL int xattrs_option;
-+
- /* When set, strip the given number of file name components from the file name
- before extracting */
- GLOBAL size_t strip_name_components;
-@@ -707,6 +710,9 @@ extern char *output_start;
-
- void update_archive (void);
-
-+/* Module attrs.c. */
-+#include "xattrs.h"
-+
- /* Module xheader.c. */
-
- void xheader_decode (struct tar_stat_info *stat);
-@@ -727,6 +733,12 @@ bool xheader_string_end (struct xheader *xhdr, char const *keyword);
- bool xheader_keyword_deleted_p (const char *kw);
- char *xheader_format_name (struct tar_stat_info *st, const char *fmt,
- size_t n);
-+void xheader_xattr_init(struct tar_stat_info *st);
-+void xheader_xattr_free(struct xattr_array *vals, size_t sz);
-+void xheader_xattr_copy(const struct tar_stat_info *st,
-+ struct xattr_array **vals, size_t *sz);
-+void xheader_xattr_add(struct tar_stat_info *st,
-+ const char *key, const char *val, size_t len);
-
- /* Module system.c */
-
-diff --git a/src/create.c b/src/create.c
-index 43b5a4c..7ed5d10 100644
---- a/src/create.c
-+++ b/src/create.c
-@@ -936,6 +936,21 @@ start_header (struct tar_stat_info *st)
- GNAME_TO_CHARS (st->gname, header->header.gname);
- }
-
-+ if (archive_format == POSIX_FORMAT)
-+ {
-+ if (xattrs_option > 0)
-+ {
-+ size_t scan_xattr = 0;
-+ struct xattr_array *xattr_map = st->xattr_map;
-+
-+ while (scan_xattr < st->xattr_map_size)
-+ {
-+ xheader_store (xattr_map[scan_xattr].xkey, st, &scan_xattr);
-+ ++scan_xattr;
-+ }
-+ }
-+ }
-+
- return header;
- }
-
-@@ -1711,6 +1726,11 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
- bool ok;
- struct stat final_stat;
-
-+ if (fd == 0)
-+ xattrs_xattrs_get(st, p, -1);
-+ else
-+ xattrs_xattrs_get(st, p, fd);
-+
- if (is_dir)
- {
- const char *tag_file_name;
-@@ -1829,6 +1849,8 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
- if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
- write_long_link (st);
-
-+ xattrs_xattrs_get(st, p, -1);
-+
- block_ordinal = current_block_ordinal ();
- st->stat.st_size = 0; /* force 0 size on symlink */
- header = start_header (st);
-@@ -1847,11 +1869,20 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
- }
- #endif
- else if (S_ISCHR (st->stat.st_mode))
-- type = CHRTYPE;
-+ {
-+ type = CHRTYPE;
-+ xattrs_xattrs_get(st, p, -1);
-+ }
- else if (S_ISBLK (st->stat.st_mode))
-- type = BLKTYPE;
-+ {
-+ type = BLKTYPE;
-+ xattrs_xattrs_get(st, p, -1);
-+ }
- else if (S_ISFIFO (st->stat.st_mode))
-- type = FIFOTYPE;
-+ {
-+ type = FIFOTYPE;
-+ xattrs_xattrs_get(st, p, -1);
-+ }
- else if (S_ISSOCK (st->stat.st_mode))
- {
- WARNOPT (WARN_FILE_IGNORED,
-diff --git a/src/extract.c b/src/extract.c
-index aaea56e..5c0a9c9 100644
---- a/src/extract.c
-+++ b/src/extract.c
-@@ -97,6 +97,9 @@ struct delayed_set_stat
- /* Directory that the name is relative to. */
- int change_dir;
-
-+ /* extended attributes*/
-+ size_t xattr_map_size; /* Size of the xattr map */
-+ struct xattr_array *xattr_map;
- /* Length and contents of name. */
- size_t file_name_len;
- char file_name[1];
-@@ -134,6 +137,9 @@ struct delayed_link
- hard-linked together. */
- struct string_list *sources;
-
-+ size_t xattr_map_size; /* Size of the xattr map */
-+ struct xattr_array *xattr_map;
-+
- /* The desired target of the desired link. */
- char target[1];
- };
-@@ -335,6 +341,8 @@ set_stat (char const *file_name,
- utime_error (file_name);
- }
-
-+ xattrs_xattrs_set(st, file_name, typeflag);
-+
- if (0 < same_owner_option && ! interdir)
- {
- /* Some systems allow non-root users to give files away. Once this
-@@ -431,6 +439,13 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
- data->atflag = atflag;
- data->after_links = 0;
- data->change_dir = chdir_current;
-+ if (st)
-+ xheader_xattr_copy (st, &data->xattr_map, &data->xattr_map_size);
-+ else
-+ {
-+ data->xattr_map = NULL;
-+ data->xattr_map_size = 0;
-+ }
- strcpy (data->file_name, file_name);
- delayed_set_stat_head = data;
- if (must_be_dot_or_slash (file_name))
-@@ -673,6 +688,31 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
- return RECOVER_NO;
- }
-
-+/* Restore stat extended attributes (xattr) for FILE_NAME, using information
-+ given in *ST. Restore before extraction because they may affect layout.
-+ If not restoring permissions, invert the
-+ INVERT_PERMISSIONS bits from the file's current permissions.
-+ TYPEFLAG specifies the type of the file.
-+ FILE_CREATED indicates set_xattr has created the file */
-+static int
-+set_xattr (char const *file_name, struct tar_stat_info const *st,
-+ mode_t invert_permissions, char typeflag, int *file_created)
-+{
-+ int status = 0;
-+ bool interdir_made = false;
-+
-+ if ((xattrs_option >= 0) && st->xattr_map_size) {
-+ mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
-+
-+ do
-+ status = mknod (file_name, mode ^ invert_permissions, 0);
-+ while (status && maybe_recoverable ((char *)file_name, false, &interdir_made));
-+ xattrs_xattrs_set(st, file_name, typeflag);
-+ *file_created = 1;
-+ }
-+ return(status);
-+}
-+
- /* Fix the statuses of all directories whose statuses need fixing, and
- which are not ancestors of FILE_NAME. If AFTER_LINKS is
- nonzero, do this for all such directories; otherwise, stop at the
-@@ -733,12 +773,15 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
- sb.stat.st_gid = data->gid;
- sb.atime = data->atime;
- sb.mtime = data->mtime;
-+ sb.xattr_map = data->xattr_map;
-+ sb.xattr_map_size = data->xattr_map_size;
- set_stat (data->file_name, &sb,
- -1, current_mode, current_mode_mask,
- DIRTYPE, data->interdir, data->atflag);
- }
-
- delayed_set_stat_head = data->next;
-+ xheader_xattr_free (data->xattr_map, data->xattr_map_size);
- free (data);
- }
- }
-@@ -854,6 +897,7 @@ extract_dir (char *file_name, int typeflag)
-
- static int
- open_output_file (char const *file_name, int typeflag, mode_t mode,
-+ int file_created,
- mode_t *current_mode, mode_t *current_mode_mask)
- {
- int fd;
-@@ -864,6 +908,10 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
- ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW)
- : O_EXCL));
-
-+ /* File might be created in set_xattr. So clear O_EXCL to avoid open() failure */
-+ if (file_created)
-+ openflag = openflag & ~O_EXCL;
-+
- if (typeflag == CONTTYPE)
- {
- static int conttype_diagnosed;
-@@ -934,6 +982,7 @@ extract_file (char *file_name, int typeflag)
- bool interdir_made = false;
- mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
- & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
-+ mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
- mode_t current_mode = 0;
- mode_t current_mode_mask = 0;
-
-@@ -950,7 +999,17 @@ extract_file (char *file_name, int typeflag)
- }
- else
- {
-+ int file_created = 0;
-+ if (set_xattr (file_name, &current_stat_info, invert_permissions,
-+ typeflag, &file_created))
-+ {
-+ skip_member ();
-+ open_error (file_name);
-+ return 1;
-+ }
-+
- while ((fd = open_output_file (file_name, typeflag, mode,
-+ file_created,
- &current_mode, &current_mode_mask))
- < 0)
- {
-@@ -1091,6 +1150,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
- + strlen (file_name) + 1);
- p->sources->next = 0;
- strcpy (p->sources->string, file_name);
-+ xheader_xattr_copy (&current_stat_info, &p->xattr_map, &p->xattr_map_size);
- strcpy (p->target, current_stat_info.link_name);
-
- h = delayed_set_stat_head;
-@@ -1525,6 +1585,8 @@ apply_delayed_links (void)
- st1.stat.st_gid = ds->gid;
- st1.atime = ds->atime;
- st1.mtime = ds->mtime;
-+ st1.xattr_map = ds->xattr_map;
-+ st1.xattr_map_size = ds->xattr_map_size;
- set_stat (source, &st1, -1, 0, 0, SYMTYPE,
- false, AT_SYMLINK_NOFOLLOW);
- valid_source = source;
-@@ -1539,6 +1601,8 @@ apply_delayed_links (void)
- sources = next;
- }
-
-+ xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
-+
- {
- struct delayed_link *next = ds->next;
- free (ds);
-diff --git a/src/list.c b/src/list.c
-index cf2de09..6f52579 100644
---- a/src/list.c
-+++ b/src/list.c
-@@ -604,6 +604,8 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
- assign_string (&stat_info->gname,
- header->header.gname[0] ? header->header.gname : NULL);
-
-+ xheader_xattr_init(stat_info);
-+
- if (format == OLDGNU_FORMAT && incremental_option)
- {
- stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
-diff --git a/src/tar.c b/src/tar.c
-index 928cfdd..75510d8 100644
---- a/src/tar.c
-+++ b/src/tar.c
-@@ -304,6 +304,7 @@ enum
- NO_UNQUOTE_OPTION,
- NO_WILDCARDS_MATCH_SLASH_OPTION,
- NO_WILDCARDS_OPTION,
-+ NO_XATTR_OPTION,
- NULL_OPTION,
- NUMERIC_OWNER_OPTION,
- OCCURRENCE_OPTION,
-@@ -340,7 +341,8 @@ enum
- VOLNO_FILE_OPTION,
- WARNING_OPTION,
- WILDCARDS_MATCH_SLASH_OPTION,
-- WILDCARDS_OPTION
-+ WILDCARDS_OPTION,
-+ XATTR_OPTION
- };
-
- const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
-@@ -516,6 +518,10 @@ static struct argp_option options[] = {
- {"preserve-order", 's', 0, 0,
- N_("sort names to extract to match archive"), GRID+1 },
- {"same-order", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
-+ {"xattrs", XATTR_OPTION, 0, 0,
-+ N_("Save the user/root xattrs to the archive"), GRID+1 },
-+ {"no-xattrs", NO_XATTR_OPTION, 0, 0,
-+ N_("Don't extract the user/root xattrs from the archive"), GRID+1 },
- {"preserve", PRESERVE_OPTION, 0, 0,
- N_("same as both -p and -s"), GRID+1 },
- {"delay-directory-restore", DELAY_DIRECTORY_RESTORE_OPTION, 0, 0,
-@@ -2079,6 +2085,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
- same_permissions_option = -1;
- break;
-
-+ case XATTR_OPTION:
-+ set_archive_format ("posix");
-+ xattrs_option = 1;
-+ break;
-+
-+ case NO_XATTR_OPTION:
-+ xattrs_option = -1;
-+ break;
-+
- case RECURSION_OPTION:
- recursion_option = FNM_LEADING_DIR;
- break;
-@@ -2461,6 +2476,15 @@ decode_options (int argc, char **argv)
- || subcommand_option != LIST_SUBCOMMAND))
- USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
-
-+ /* star create's non-POSIX typed archives with xattr support, so allow the
-+ extra headers */
-+ if ((xattrs_option > 0)
-+ && archive_format != POSIX_FORMAT
-+ && (subcommand_option != EXTRACT_SUBCOMMAND
-+ || subcommand_option != DIFF_SUBCOMMAND
-+ || subcommand_option != LIST_SUBCOMMAND))
-+ USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives")));
-+
- /* If ready to unlink hierarchies, so we are for simpler files. */
- if (recursive_unlink_option)
- old_files_option = UNLINK_FIRST_OLD_FILES;
-@@ -2713,6 +2737,7 @@ void
- tar_stat_destroy (struct tar_stat_info *st)
- {
- tar_stat_close (st);
-+ xheader_xattr_free (st->xattr_map, st->xattr_map_size);
- free (st->orig_file_name);
- free (st->file_name);
- free (st->link_name);
-diff --git a/src/tar.h b/src/tar.h
-index ce9850c..955b18e 100644
---- a/src/tar.h
-+++ b/src/tar.h
-@@ -276,6 +276,14 @@ struct xheader
- uintmax_t string_length;
- };
-
-+/* Information about xattrs for a file. */
-+struct xattr_array
-+ {
-+ char *xkey;
-+ char *xval_ptr;
-+ size_t xval_len;
-+ };
-+
- struct tar_stat_info
- {
- char *orig_file_name; /* name of file read from the archive header */
-@@ -287,6 +295,7 @@ struct tar_stat_info
-
- char *uname; /* user name of owner */
- char *gname; /* group name of owner */
-+
- struct stat stat; /* regular filesystem stat */
-
- /* STAT doesn't always have access, data modification, and status
-@@ -309,6 +318,9 @@ struct tar_stat_info
- size_t sparse_map_size; /* Size of the sparse map */
- struct sp_array *sparse_map;
-
-+ size_t xattr_map_size; /* Size of the xattr map */
-+ struct xattr_array *xattr_map;
-+
- /* Extended headers */
- struct xheader xhdr;
-
-diff --git a/src/xattrs.c b/src/xattrs.c
-new file mode 100644
-index 0000000..6a9950e
---- /dev/null
-+++ b/src/xattrs.c
-@@ -0,0 +1,181 @@
-+/* Create a tar archive.
-+
-+ Copyright (C) 2006 Free Software Foundation, Inc.
-+
-+ Written by James Antill, on 2006-07-27.
-+
-+ This program is free software; you can redistribute it and/or modify it
-+ under the terms of the GNU General Public License as published by the
-+ Free Software Foundation; either version 2, or (at your option) any later
-+ version.
-+
-+ This program is distributed in the hope that it will be useful, but
-+ WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-+ Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License along
-+ with this program; if not, write to the Free Software Foundation, Inc.,
-+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-+
-+#include <system.h>
-+
-+#include <quotearg.h>
-+
-+#include "common.h"
-+
-+
-+#ifndef HAVE_ATTR_XATTR_H
-+# undef HAVE_XATTRS
-+#endif
-+
-+#ifdef HAVE_ATTR_XATTR_H
-+# include <attr/xattr.h>
-+#endif
-+
-+
-+void xattrs_xattrs_get(struct tar_stat_info *st, char const *file_name, int fd)
-+{
-+ if (xattrs_option > 0)
-+ { /* get all xattrs ... this include security.* and system.* if
-+ available. We filter them here, but we have to filter them
-+ in xattrs_xattrs_set() anyway.
-+ */
-+ static ssize_t xsz = 1024;
-+ static char *xatrs = NULL;
-+ ssize_t xret = -1;
-+
-+#ifndef HAVE_XATTRS
-+ static int done = 0;
-+ if ((xattrs_option > 0) && !done)
-+ WARN ((0, 0, _("Xattr support requested, but not available")));
-+ done = 1;
-+#else
-+
-+ if (!xatrs) xatrs = xmalloc (xsz);
-+
-+ while (((fd == -1) ?
-+ ((xret = llistxattr (file_name, xatrs, xsz)) == -1) :
-+ ((xret = flistxattr (fd, xatrs, xsz)) == -1)) &&
-+ (errno == ERANGE))
-+ {
-+ xsz <<= 1;
-+ xatrs = xrealloc (xatrs, xsz);
-+ }
-+
-+ if (xret == -1)
-+ call_arg_warn ((fd == -1) ? "llistxattrs" : "flistxattrs", file_name);
-+ else
-+ {
-+ const char *attr = xatrs;
-+ static ssize_t asz = 1024;
-+ static char *val = NULL;
-+
-+ if (!val) val = xmalloc (asz);
-+
-+ while (xret > 0)
-+ {
-+ size_t len = strlen (attr);
-+ ssize_t aret = 0;
-+
-+ /* Archive all xattrs during creation, decide at extraction time
-+ * which ones are of interest/use for the target filesystem. */
-+ while (((fd == -1) ?
-+ ((aret = lgetxattr (file_name, attr, val, asz)) == -1) :
-+ ((aret = fgetxattr (fd, attr, val, asz)) == -1)) &&
-+ (errno == ERANGE))
-+ {
-+ asz <<= 1;
-+ val = xrealloc (val, asz);
-+ }
-+
-+ if (aret != -1)
-+ xheader_xattr_add (st, attr, val, aret);
-+ else if (errno != ENOATTR)
-+ call_arg_warn ((fd==-1) ? "lgetxattr" : "fgetxattr", file_name);
-+
-+ attr += len + 1;
-+ xret -= len + 1;
-+ }
-+ }
-+#endif
-+ }
-+}
-+
-+static void xattrs__fd_set(struct tar_stat_info const *st,
-+ char const *file_name, char typeflag,
-+ const char *attr,
-+ const char *ptr, size_t len)
-+{
-+#ifdef HAVE_XATTRS
-+ if (ptr)
-+ {
-+ const char *sysname = "setxattr";
-+ int ret = -1;
-+
-+ if (typeflag != SYMTYPE)
-+ ret = setxattr (file_name, attr, ptr, len, 0);
-+ else
-+ {
-+ sysname = "lsetxattr";
-+ ret = lsetxattr (file_name, attr, ptr, len, 0);
-+ }
-+
-+ /* do not print warnings when SELinux is disabled */
-+ if ((ret == -1) && (errno != EPERM) && (errno != ENOTSUP))
-+ call_arg_error(sysname, file_name);
-+ }
-+#endif
-+}
-+
-+static char *skip_to_ext_fields(char *ptr)
-+{
-+ ptr += strcspn(ptr, ":,\n"); /* skip tag name. Ie. user/group/default/mask */
-+
-+ if (*ptr != ':')
-+ return (ptr); /* error? no user/group field */
-+ ++ptr;
-+
-+ ptr += strcspn(ptr, ":,\n"); /* skip user/group name */
-+
-+ if (*ptr != ':')
-+ return (ptr); /* error? no perms field */
-+ ++ptr;
-+
-+ ptr += strcspn(ptr, ":,\n"); /* skip perms */
-+
-+ if (*ptr != ':')
-+ return (ptr); /* no extra fields */
-+
-+ return (ptr);
-+}
-+
-+void xattrs_xattrs_set(struct tar_stat_info const *st,
-+ char const *file_name, char typeflag)
-+{
-+ if ((xattrs_option >= 0) && st->xattr_map_size)
-+ {
-+ size_t scan = 0;
-+
-+#ifndef HAVE_XATTRS
-+ static int done = 0;
-+ if (!done)
-+ WARN ((0, 0, _("Xattr support requested, but not available")));
-+ done = 1;
-+#else
-+ while (scan < st->xattr_map_size)
-+ {
-+ char *keyword = st->xattr_map[scan].xkey;
-+
-+ /* assert (!memcpy (keyword, "SCHILY.xattr.", strlen("SCHILY.xattr."))); */
-+ keyword += strlen("SCHILY.xattr.");
-+
-+ xattrs__fd_set (st, file_name, typeflag, keyword,
-+ st->xattr_map[scan].xval_ptr,
-+ st->xattr_map[scan].xval_len);
-+
-+ ++scan;
-+ }
-+#endif
-+ }
-+}
-diff --git a/src/xattrs.h b/src/xattrs.h
-new file mode 100644
-index 0000000..7ffdce1
---- /dev/null
-+++ b/src/xattrs.h
-@@ -0,0 +1,6 @@
-+
-+extern void xattrs_xattrs_get(struct tar_stat_info *st,
-+ char const *file_name, int fd);
-+
-+extern void xattrs_xattrs_set(struct tar_stat_info const *st,
-+ char const *file_name, char typeflag);
-diff --git a/src/xheader.c b/src/xheader.c
-index 2284e97..557b3e5 100644
---- a/src/xheader.c
-+++ b/src/xheader.c
-@@ -460,6 +460,74 @@ xheader_write_global (struct xheader *xhdr)
- }
- }
-
-+void xheader_xattr_init(struct tar_stat_info *st)
-+{
-+ st->xattr_map = NULL;
-+ st->xattr_map_size = 0;
-+}
-+
-+void xheader_xattr_free(struct xattr_array *xattr_map, size_t xattr_map_size)
-+{
-+ size_t scan = 0;
-+
-+ while (scan < xattr_map_size)
-+ {
-+ free (xattr_map[scan].xkey);
-+ free (xattr_map[scan].xval_ptr);
-+
-+ ++scan;
-+ }
-+ free (xattr_map);
-+}
-+
-+static void xheader_xattr__add(struct xattr_array **xattr_map,
-+ size_t *xattr_map_size,
-+ const char *key, const char *val, size_t len)
-+{
-+ size_t pos = (*xattr_map_size)++;
-+
-+ *xattr_map = xrealloc (*xattr_map,
-+ *xattr_map_size * sizeof(struct xattr_array));
-+ (*xattr_map)[pos].xkey = xstrdup (key);
-+ (*xattr_map)[pos].xval_ptr = xmemdup (val, len + 1);
-+ (*xattr_map)[pos].xval_len = len;
-+}
-+
-+void xheader_xattr_add(struct tar_stat_info *st,
-+ const char *key, const char *val, size_t len)
-+{
-+ size_t klen = strlen (key);
-+ char *xkey = xmalloc (strlen("SCHILY.xattr.") + klen + 1);
-+ char *tmp = xkey;
-+
-+ tmp = stpcpy (tmp, "SCHILY.xattr.");
-+ tmp = stpcpy (tmp, key);
-+
-+ xheader_xattr__add (&st->xattr_map, &st->xattr_map_size, xkey, val, len);
-+
-+ free (xkey);
-+}
-+
-+void xheader_xattr_copy(const struct tar_stat_info *st,
-+ struct xattr_array **xattr_map, size_t *xattr_map_size)
-+{
-+ size_t scan = 0;
-+
-+ *xattr_map = NULL;
-+ *xattr_map_size = 0;
-+
-+ while (scan < st->xattr_map_size)
-+ {
-+ char *key = st->xattr_map[scan].xkey;
-+ char *val = st->xattr_map[scan].xval_ptr;
-+ size_t len = st->xattr_map[scan].xval_len;
-+
-+ xheader_xattr__add(xattr_map, xattr_map_size, key, val, len);
-+
-+ ++scan;
-+ }
-+}
-+
-
- /* General Interface */
-
-@@ -473,6 +541,7 @@ struct xhdr_tab
- struct xheader *, void const *data);
- void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
- int flags;
-+ bool prefix;
- };
-
- /* This declaration must be extern, because ISO C99 section 6.9.2
-@@ -489,8 +558,17 @@ locate_handler (char const *keyword)
- struct xhdr_tab const *p;
-
- for (p = xhdr_tab; p->keyword; p++)
-- if (strcmp (p->keyword, keyword) == 0)
-- return p;
-+ if (p->prefix)
-+ {
-+ if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0)
-+ return p;
-+ }
-+ else
-+ {
-+ if (strcmp (p->keyword, keyword) == 0)
-+ return p;
-+ }
-+
- return NULL;
- }
-
-@@ -500,7 +578,7 @@ xheader_protected_pattern_p (const char *pattern)
- struct xhdr_tab const *p;
-
- for (p = xhdr_tab; p->keyword; p++)
-- if ((p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
-+ if (!p->prefix && (p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0)
- return true;
- return false;
- }
-@@ -511,7 +589,7 @@ xheader_protected_keyword_p (const char *keyword)
- struct xhdr_tab const *p;
-
- for (p = xhdr_tab; p->keyword; p++)
-- if ((p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
-+ if (!p->prefix && (p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0)
- return true;
- return false;
- }
-@@ -1470,6 +1548,27 @@ volume_filename_decoder (struct tar_stat_info *st,
- }
-
- static void
-+xattr_coder (struct tar_stat_info const *st , char const *keyword,
-+ struct xheader *xhdr, void const *data)
-+{
-+ struct xattr_array *xattr_map = st->xattr_map;
-+ const size_t *off = data;
-+ xheader_print_n (xhdr, keyword,
-+ xattr_map[*off].xval_ptr, xattr_map[*off].xval_len);
-+}
-+
-+static void
-+xattr_decoder (struct tar_stat_info *st,
-+ char const *keyword, char const *arg, size_t size)
-+{
-+ char *xstr = NULL;
-+
-+ xstr = xmemdup(arg, size + 1);
-+ xheader_xattr_add(st, keyword + strlen("SCHILY.xattr."), xstr, size);
-+ free(xstr);
-+}
-+
-+static void
- sparse_major_coder (struct tar_stat_info const *st, char const *keyword,
- struct xheader *xhdr, void const *data)
- {
-@@ -1506,53 +1605,53 @@ sparse_minor_decoder (struct tar_stat_info *st,
- }
-
- struct xhdr_tab const xhdr_tab[] = {
-- { "atime", atime_coder, atime_decoder, 0 },
-- { "comment", dummy_coder, dummy_decoder, 0 },
-- { "charset", dummy_coder, dummy_decoder, 0 },
-- { "ctime", ctime_coder, ctime_decoder, 0 },
-- { "gid", gid_coder, gid_decoder, 0 },
-- { "gname", gname_coder, gname_decoder, 0 },
-- { "linkpath", linkpath_coder, linkpath_decoder, 0 },
-- { "mtime", mtime_coder, mtime_decoder, 0 },
-- { "path", path_coder, path_decoder, 0 },
-- { "size", size_coder, size_decoder, 0 },
-- { "uid", uid_coder, uid_decoder, 0 },
-- { "uname", uname_coder, uname_decoder, 0 },
-+ { "atime", atime_coder, atime_decoder, 0, false },
-+ { "comment", dummy_coder, dummy_decoder, 0, false },
-+ { "charset", dummy_coder, dummy_decoder, 0, false },
-+ { "ctime", ctime_coder, ctime_decoder, 0, false },
-+ { "gid", gid_coder, gid_decoder, 0, false },
-+ { "gname", gname_coder, gname_decoder, 0, false },
-+ { "linkpath", linkpath_coder, linkpath_decoder, 0, false },
-+ { "mtime", mtime_coder, mtime_decoder, 0, false },
-+ { "path", path_coder, path_decoder, 0, false },
-+ { "size", size_coder, size_decoder, 0, false },
-+ { "uid", uid_coder, uid_decoder, 0, false },
-+ { "uname", uname_coder, uname_decoder, 0, false },
-
- /* Sparse file handling */
- { "GNU.sparse.name", path_coder, path_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- { "GNU.sparse.major", sparse_major_coder, sparse_major_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- { "GNU.sparse.minor", sparse_minor_coder, sparse_minor_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- { "GNU.sparse.realsize", sparse_size_coder, sparse_size_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- { "GNU.sparse.numblocks", sparse_numblocks_coder, sparse_numblocks_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
-
- /* tar 1.14 - 1.15.90 keywords. */
- { "GNU.sparse.size", sparse_size_coder, sparse_size_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
- headers, and each of them was meaningful. It confilcted with POSIX specs,
- which requires that "when extended header records conflict, the last one
- given in the header shall take precedence." */
- { "GNU.sparse.offset", sparse_offset_coder, sparse_offset_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- { "GNU.sparse.numbytes", sparse_numbytes_coder, sparse_numbytes_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
- /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
- { "GNU.sparse.map", NULL /* Unused, see pax_dump_header() */,
-- sparse_map_decoder, 0 },
-+ sparse_map_decoder, 0, false },
-
- { "GNU.dumpdir", dumpdir_coder, dumpdir_decoder,
-- XHDR_PROTECTED },
-+ XHDR_PROTECTED, false },
-
- /* Keeps the tape/volume label. May be present only in the global headers.
- Equivalent to GNUTYPE_VOLHDR. */
- { "GNU.volume.label", volume_label_coder, volume_label_decoder,
-- XHDR_PROTECTED | XHDR_GLOBAL },
-+ XHDR_PROTECTED | XHDR_GLOBAL, false },
-
- /* These may be present in a first global header of the archive.
- They provide the same functionality as GNUTYPE_MULTIVOL header.
-@@ -1561,11 +1660,14 @@ struct xhdr_tab const xhdr_tab[] = {
- GNU.volume.offset keeps the offset of the start of this volume,
- otherwise kept in oldgnu_header.offset. */
- { "GNU.volume.filename", volume_label_coder, volume_filename_decoder,
-- XHDR_PROTECTED | XHDR_GLOBAL },
-+ XHDR_PROTECTED | XHDR_GLOBAL, false },
- { "GNU.volume.size", volume_size_coder, volume_size_decoder,
-- XHDR_PROTECTED | XHDR_GLOBAL },
-+ XHDR_PROTECTED | XHDR_GLOBAL, false },
- { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder,
-- XHDR_PROTECTED | XHDR_GLOBAL },
-+ XHDR_PROTECTED | XHDR_GLOBAL, false },
-+
-+ /* xattrs use the star format. note we only save some variants... */
-+ { "SCHILY.xattr", xattr_coder, xattr_decoder, 0, true },
-
-- { NULL, NULL, NULL, 0 }
-+ { NULL, NULL, NULL, 0, false }
- };