| https://bugs.gentoo.org/382067 |
| |
| add optional xattr support |
| |
| --- 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]) |
| --- 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 |
| --- 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) |
| --- 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 */ |
| |
| --- 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, |
| --- 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, ¤t_stat_info, invert_permissions, |
| + typeflag, &file_created)) |
| + { |
| + skip_member (); |
| + open_error (file_name); |
| + return 1; |
| + } |
| + |
| while ((fd = open_output_file (file_name, typeflag, mode, |
| + file_created, |
| ¤t_mode, ¤t_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 (¤t_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); |
| --- 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); |
| --- 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); |
| --- 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; |
| |
| --- /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 |
| + } |
| +} |
| --- /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); |
| --- 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 } |
| }; |