From: Pavel Raiskup Date: Sun, 18 Nov 2012 17:59:18 +0000 (+0200) Subject: Add basic suuport for extended attributes. X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=696338043e52f440853e1143c52b81b41cd59723;p=chaz%2Ftar Add basic suuport for extended attributes. * src/Makefile.am: Add xattrs.[ch] * src/xattrs.c: New file. * src/xattrs.h: New file. * src/common.h (READ_LIKE_SUBCOMMAND): New define. (selinux_context_option, acls_option, xattrs_option): New globals. (xheader_xattr_init, xheader_xattr_free) (xheader_xattr_copy, xheader_xattr_add): New protos. (WARN_XATTR_WRITE): New mask. * src/create.c (start_header): Handle xattrs pairs if in POSIX format. (dump_file0): Handle extended attributes. * src/extract.c (delayed_set_stat,delayed_link) : New members. (set_xattr): New static function. (open_output_file): Accept an additional argument indicating whether the file has already been created. (set_stat,delay_set_stat) (apply_nonancestor_delayed_set_stat) (extract_file): Handle extended attributes. * src/list.c (decode_header, simple_print_header): Display extended attributes. * src/tar.c: New options --xattrs, --no-xattrs, --xattrs-include, --xattrs-exclude (tar_stat_destroy): Free the xattr_map storage. * src/tar.h (xattr_array): New struct. (tar_stat_info) : New members. * src/warning.c: New warning control keyword "xattr-write". * src/xheader.c (xheader_xattr_init) (xheader_xattr_free, xheader_xattr_add) (xheader_xattr_copy): New functions. (struct xhdr_tab) : New member. (locate_handler): Permit selecting the keyword based on its prefix. (xheader_protected_pattern_p) (xheader_protected_keyword_p): Likewise. (xattr_coder, xattr_decoder): New functions. (xhdr_tab): Reflect the changes to struct xhdr_tab. New keyword "SCHILY.xattr". * tests/Makefile.am: Add new tests. * tests/testsuite.at: Likewise. (AT_CHECK_UTIL, AT_XATTRS_UTILS_PREREQ) (AT_CAPABILITIES_UTILS_PREREQ, AT_XATTRS_PREREQ): New defuns. * tests/xattr01.at: New test. * tests/xattr02.at: New test. * tests/xattr03.at: New test. * tests/xattr04.at: New test. * tests/capabs_raw01.at: New test. --- 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 c51ab11..a78e50f 100644 --- a/src/common.h +++ b/src/common.h @@ -1,8 +1,8 @@ /* Common declarations for the tar program. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, - 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, - Inc. + 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 + Free Software Foundation, Inc. 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 @@ -91,6 +91,11 @@ enum subcommand GLOBAL enum subcommand subcommand_option; +#define READ_LIKE_SUBCOMMAND \ + (subcommand_option == EXTRACT_SUBCOMMAND \ + || subcommand_option == DIFF_SUBCOMMAND \ + || subcommand_option == LIST_SUBCOMMAND) + /* Selected format for output archive. */ GLOBAL enum archive_format archive_format; @@ -256,6 +261,15 @@ GLOBAL int same_owner_option; /* If positive, preserve permissions when extracting. */ GLOBAL int same_permissions_option; +/* If positive, save the SELinux context. */ +GLOBAL int selinux_context_option; + +/* If positive, save the ACLs. */ +GLOBAL int acls_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; @@ -714,6 +728,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); @@ -734,6 +751,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 */ @@ -815,6 +838,7 @@ void checkpoint_run (bool do_write); #define WARN_XDEV 0x00040000 #define WARN_DECOMPRESS_PROGRAM 0x00080000 #define WARN_EXISTING_FILE 0x00100000 +#define WARN_XATTR_WRITE 0x00200000 /* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default in verbose mode */ diff --git a/src/create.c b/src/create.c index 745796c..034639b 100644 --- a/src/create.c +++ b/src/create.c @@ -1,7 +1,8 @@ /* Create a tar archive. Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, - 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 + Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. @@ -943,6 +944,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; } @@ -1718,6 +1734,8 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p) bool ok; struct stat final_stat; + xattrs_xattrs_get (parentfd, name, st, fd); + if (is_dir) { const char *tag_file_name; @@ -1836,6 +1854,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 (parentfd, name, st, 0); + block_ordinal = current_block_ordinal (); st->stat.st_size = 0; /* force 0 size on symlink */ header = start_header (st); @@ -1854,11 +1874,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 (parentfd, name, st, 0); + } else if (S_ISBLK (st->stat.st_mode)) - type = BLKTYPE; + { + type = BLKTYPE; + xattrs_xattrs_get (parentfd, name, st, 0); + } else if (S_ISFIFO (st->stat.st_mode)) - type = FIFOTYPE; + { + type = FIFOTYPE; + xattrs_xattrs_get (parentfd, name, st, 0); + } else if (S_ISSOCK (st->stat.st_mode)) { WARNOPT (WARN_FILE_IGNORED, diff --git a/src/extract.c b/src/extract.c index e35c8f6..2d8e175 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,7 +1,8 @@ /* Extract files from a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, - 2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012 + Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. @@ -98,6 +99,9 @@ struct delayed_set_stat /* Directory that the name is relative to. */ int change_dir; + /* extended attributes*/ + size_t xattr_map_size; + struct xattr_array *xattr_map; /* Length and contents of name. */ size_t file_name_len; char file_name[1]; @@ -138,6 +142,9 @@ struct delayed_link hard-linked together. */ struct string_list *sources; + size_t xattr_map_size; + struct xattr_array *xattr_map; + /* The desired target of the desired link. */ char target[1]; }; @@ -364,6 +371,10 @@ set_stat (char const *file_name, st->stat.st_mode & ~ current_umask, 0 < same_permissions_option && ! interdir ? MODE_ALL : MODE_RWX, fd, current_mode, current_mode_mask, typeflag, atflag); + + /* these three calls must be done *after* fd_chown() call because fd_chown + causes that linux capabilities becomes cleared. */ + xattrs_xattrs_set (st, file_name, typeflag, 1); } /* For each entry H in the leading prefix of entries in HEAD that do @@ -435,6 +446,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)) @@ -682,6 +700,40 @@ 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 file layout + (e.g. on Lustre distributed parallel filesystem - setting info about how many + servers is this file striped over, stripe size, mirror copies, etc. + in advance dramatically improves the following performance of reading and + writing a file). 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; + +#ifdef HAVE_XATTRS + 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 = mknodat (chdir_fd, file_name, mode ^ invert_permissions, 0); + while (status && maybe_recoverable ((char *)file_name, false, + &interdir_made)); + + xattrs_xattrs_set (st, file_name, typeflag, 0); + *file_created = 1; + } +#endif + + 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 @@ -742,12 +794,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); } } @@ -863,7 +918,8 @@ extract_dir (char *file_name, int typeflag) static int open_output_file (char const *file_name, int typeflag, mode_t mode, - mode_t *current_mode, mode_t *current_mode_mask) + int file_created, mode_t *current_mode, + mode_t *current_mode_mask) { int fd; bool overwriting_old_files = old_files_option == OVERWRITE_OLD_FILES; @@ -873,6 +929,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() fail */ + if (file_created) + openflag = openflag & ~O_EXCL; + if (typeflag == CONTTYPE) { static int conttype_diagnosed; @@ -944,6 +1004,8 @@ 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; @@ -960,8 +1022,18 @@ 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, - ¤t_mode, ¤t_mode_mask)) + file_created, ¤t_mode, + ¤t_mode_mask)) < 0) { int recover = maybe_recoverable (file_name, true, &interdir_made); @@ -1101,6 +1173,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; @@ -1536,6 +1609,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; @@ -1550,6 +1625,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 4f85fb5..f2605ad 100644 --- a/src/list.c +++ b/src/list.c @@ -1,7 +1,8 @@ /* List a tar archive, with support routines for reading a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, - 2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012 + Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-26. @@ -604,6 +605,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); @@ -1064,7 +1067,7 @@ static void simple_print_header (struct tar_stat_info *st, union block *blk, off_t block_ordinal) { - char modes[11]; + char modes[12]; char const *time_stamp; int time_stamp_len; char *temp_name; @@ -1156,6 +1159,9 @@ simple_print_header (struct tar_stat_info *st, union block *blk, pax_decode_mode (st->stat.st_mode, modes + 1); + /* extended attributes: GNU `ls -l'-like preview */ + xattrs_print_char (st, modes + 10); + /* Time stamp. */ time_stamp = tartime (st->mtime, full_time_option); @@ -1301,6 +1307,7 @@ simple_print_header (struct tar_stat_info *st, union block *blk, } } fflush (stdlis); + xattrs_print (st); } diff --git a/src/tar.c b/src/tar.c index 8c4f656..c976820 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1,7 +1,8 @@ /* A tar (tape archiver) program. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, - 2001, 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005, 2006, 2007, 2012 + Free Software Foundation, Inc. Written by John Gilmore, starting 1985-08-25. @@ -304,6 +305,7 @@ enum NO_UNQUOTE_OPTION, NO_WILDCARDS_MATCH_SLASH_OPTION, NO_WILDCARDS_OPTION, + NO_XATTR_OPTION, NULL_OPTION, NUMERIC_OWNER_OPTION, OCCURRENCE_OPTION, @@ -341,7 +343,10 @@ enum VOLNO_FILE_OPTION, WARNING_OPTION, WILDCARDS_MATCH_SLASH_OPTION, - WILDCARDS_OPTION + WILDCARDS_OPTION, + XATTR_OPTION, + XATTR_EXCLUDE, + XATTR_INCLUDE }; const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION; @@ -530,6 +535,20 @@ static struct argp_option options[] = { N_("cancel the effect of --delay-directory-restore option"), GRID+1 }, #undef GRID +#define GRID 55 + {NULL, 0, NULL, 0, + N_("Handling of extended file attributes:"), GRID }, + + {"xattrs", XATTR_OPTION, 0, 0, + N_("Enable extended attributes support"), GRID+1 }, + {"no-xattrs", NO_XATTR_OPTION, 0, 0, + N_("Disable extended attributes support"), GRID+1 }, + {"xattrs-include", XATTR_INCLUDE, N_("MASK"), 0, + N_("specify the include pattern for xattr keys"), GRID+1 }, + {"xattrs-exclude", XATTR_EXCLUDE, N_("MASK"), 0, + N_("specify the exclude pattern for xattr keys"), GRID+1 }, +#undef GRID + #define GRID 60 {NULL, 0, NULL, 0, N_("Device selection and switching:"), GRID }, @@ -2150,6 +2169,20 @@ 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 XATTR_INCLUDE: + case XATTR_EXCLUDE: + xattrs_mask_add (arg, (key == XATTR_INCLUDE)); + break; + case RECURSION_OPTION: recursion_option = FNM_LEADING_DIR; break; @@ -2527,11 +2560,16 @@ decode_options (int argc, char **argv) --gray */ if (args.pax_option && archive_format != POSIX_FORMAT - && (subcommand_option != EXTRACT_SUBCOMMAND - || subcommand_option != DIFF_SUBCOMMAND - || subcommand_option != LIST_SUBCOMMAND)) + && !READ_LIKE_SUBCOMMAND) USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives"))); + /* star creates non-POSIX typed archives with xattr support, so allow the + extra headers when reading */ + if ((xattrs_option > 0) + && archive_format != POSIX_FORMAT + && !READ_LIKE_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; @@ -2740,6 +2778,7 @@ main (int argc, char **argv) /* Dispose of allocated memory, and return. */ free (archive_name_array); + xattrs_clear_setup (); name_term (); if (exit_status == TAREXIT_FAILURE) @@ -2784,6 +2823,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 0b090af..7a80e87 100644 --- a/src/tar.h +++ b/src/tar.h @@ -1,7 +1,8 @@ /* GNU tar Archive Format description. Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, - 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2012 + Free Software Foundation, Inc. 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 @@ -276,6 +277,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 */ @@ -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/warning.c b/src/warning.c index 102364d..570b3c1 100644 --- a/src/warning.c +++ b/src/warning.c @@ -1,6 +1,6 @@ /* This file is part of GNU tar. - Copyright (C) 2009 Free Software Foundation, Inc. + Copyright (C) 2009, 2012 Free Software Foundation, Inc. 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 @@ -43,6 +43,7 @@ static char const *const warning_args[] = { "xdev", "decompress-program", "existing-file", + "xattr-write", NULL }; @@ -68,7 +69,8 @@ static int warning_types[] = { WARN_UNKNOWN_KEYWORD, WARN_XDEV, WARN_DECOMPRESS_PROGRAM, - WARN_EXISTING_FILE + WARN_EXISTING_FILE, + WARN_XATTR_WRITE }; ARGMATCH_VERIFY (warning_args, warning_types); diff --git a/src/xattrs.c b/src/xattrs.c new file mode 100644 index 0000000..e813d53 --- /dev/null +++ b/src/xattrs.c @@ -0,0 +1,315 @@ +/* Support for extended attributes. + + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 + +#include +#include + +#include "common.h" + +#include "xattr-at.h" + +struct xattrs_mask_map +{ + const char **masks; + int size; + int used; +}; + +/* list of fnmatch patterns */ +static struct +{ + /* lists of fnmatch patterns */ + struct xattrs_mask_map incl; + struct xattrs_mask_map excl; +} xattrs_setup; + +static void mask_map_realloc (struct xattrs_mask_map *map) +{ + if (map->size == 0) + { + map->size = 4; + map->masks = xmalloc (16 * sizeof (char *)); + return; + } + + if (map->size <= map->used) + { + map->size *= 2; + map->masks = xrealloc (map->masks, map->size * sizeof (char *)); + return; + } +} + +void xattrs_mask_add (const char *mask, bool incl) +{ + struct xattrs_mask_map *mask_map = incl ? &xattrs_setup.incl + : &xattrs_setup.excl; + /* ensure there is enough space */ + mask_map_realloc (mask_map); + /* just assign pointers -- we silently expect that pointer "mask" is valid + through the whole program (pointer to argv array) */ + mask_map->masks[mask_map->used++] = mask; +} + +static void clear_mask_map (struct xattrs_mask_map *mask_map) +{ + if (mask_map->size) + free (mask_map->masks); +} + +void xattrs_clear_setup () +{ + clear_mask_map (&xattrs_setup.incl); + clear_mask_map (&xattrs_setup.excl); +} + +/* get all xattrs from file given by FILE_NAME or FD (when non-zero). This + includes all the user.*, security.*, system.*, etc. available domains */ +void xattrs_xattrs_get (int parentfd, char const *file_name, + struct tar_stat_info *st, int fd) +{ + if (xattrs_option > 0) + { +#ifndef HAVE_XATTRS + static int done = 0; + if (!done) + WARN ((0, 0, _("XATTR support is not available"))); + done = 1; +#else + static ssize_t xsz = 1024; + static char *xatrs = NULL; + ssize_t xret = -1; + + if (!xatrs) xatrs = xmalloc (xsz); + + while (((fd == 0) ? + ((xret = llistxattrat (parentfd, 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 == 0) ? "llistxattrat" : "flistxattr", 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 == 0) + ? ((aret = lgetxattrat (parentfd, 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 == 0) ? "lgetxattrat" + : "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) +{ + if (ptr) + { + const char *sysname = "setxattrat"; + int ret = -1; + + if (typeflag != SYMTYPE) + ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0); + else + { + sysname = "lsetxattr"; + ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0); + } + + if (ret == -1) + WARNOPT (WARN_XATTR_WRITE, (0, errno, + _("%s: Cannot set '%s' extended attribute for file '%s'"), + sysname, attr, file_name)); + } +} + +static bool xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm) +{ + int i; + + if (!mm->size) + return false; + + for (i = 0; i < mm->used; i++) + if (fnmatch (mm->masks[i], kw, 0) == 0) + return true; + + return false; +} + +static bool xattrs_kw_included (const char *kw, bool archiving) +{ + if (xattrs_setup.incl.size) + return xattrs_matches_mask (kw, &xattrs_setup.incl); + else + { + if (archiving) + return true; + else + return strncmp (kw, "user.", strlen ("user.")) == 0; + } +} + +static bool xattrs_kw_excluded (const char *kw, bool archiving) +{ + if (!xattrs_setup.excl.size) + return false; + + return xattrs_matches_mask (kw, &xattrs_setup.excl); +} + +/* Check whether the xattr with keyword KW should be discarded from list of + attributes that are going to be archived/excluded (set ARCHIVING=true for + archiving, false for excluding) */ +static bool xattrs_masked_out (const char *kw, bool archiving) +{ + if (!xattrs_kw_included (kw, archiving)) + return true; + + return xattrs_kw_excluded (kw, archiving); +} + +void xattrs_xattrs_set (struct tar_stat_info const *st, + char const *file_name, char typeflag, + int later_run) +{ + if (xattrs_option > 0) + { +#ifndef HAVE_XATTRS + static int done = 0; + if (!done) + WARN ((0, 0, _("XATTR support is not available"))); + done = 1; +#else + size_t scan = 0; + + if (!st->xattr_map_size) + return; + + for (; scan < st->xattr_map_size; ++scan) + { + char *keyword = st->xattr_map[scan].xkey; + keyword += strlen ("SCHILY.xattr."); + + /* TODO: this 'later_run' workaround is temporary solution -> once + capabilities should become fully supported by it's API and there + should exist something like xattrs_capabilities_set() call. + For a regular files: all extended attributes are restored during + the first run except 'security.capability' which is restored in + 'later_run == 1'. */ + if (typeflag == REGTYPE + && later_run == !!strcmp (keyword, "security.capability")) + continue; + + if (xattrs_masked_out (keyword, false /* extracting */ )) + /* we don't want to restore this keyword */ + continue; + + xattrs__fd_set (st, file_name, typeflag, keyword, + st->xattr_map[scan].xval_ptr, + st->xattr_map[scan].xval_len); + } +#endif + } +} + +void xattrs_print_char (struct tar_stat_info const *st, char *output) +{ + int i; + if (verbose_option < 2) + { + *output = 0; + return; + } + + if (xattrs_option > 0) + { + /* placeholders */ + *output = ' '; + *(output + 1) = 0; + } + + if (xattrs_option > 0 && st->xattr_map_size) + for (i = 0; i < st->xattr_map_size; ++i) + { + char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr."); + if (xattrs_masked_out (keyword, false /* like extracting */ )) + continue; + *output = '*'; + break; + } +} + +void xattrs_print (struct tar_stat_info const *st) +{ + if (verbose_option < 3) + return; + + /* xattrs */ + if (xattrs_option && st->xattr_map_size) + { + int i; + for (i = 0; i < st->xattr_map_size; ++i) + { + char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr."); + if (xattrs_masked_out (keyword, false /* like extracting */ )) + continue; + fprintf (stdlis, " x: %lu %s\n", + (unsigned long) st->xattr_map[i].xval_len, keyword); + } + } +} diff --git a/src/xattrs.h b/src/xattrs.h new file mode 100644 index 0000000..0c08cd7 --- /dev/null +++ b/src/xattrs.h @@ -0,0 +1,52 @@ +#ifndef GUARD_XATTTRS_H +#define GUARD_XATTTRS_H + +/* Support for extended attributes. + + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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. +*/ + + +/* Add include/exclude fnmatch pattern for xattr key domain. Set INCL parameter + to true/false if you want to add include/exclude pattern */ +extern void xattrs_mask_add (const char *mask, bool incl); + +/* clear helping structures when tar finishes */ +extern void xattrs_clear_setup (); + +extern void xattrs_acls_get (int parentfd, char const *file_name, + struct tar_stat_info *st, int fd, int xisfile); +extern void xattrs_selinux_get (int parentfd, char const *file_name, + struct tar_stat_info *st, int fd); +extern void xattrs_xattrs_get (int parentfd, char const *file_name, + struct tar_stat_info *st, int fd); + +extern void xattrs_acls_set (struct tar_stat_info const *st, + char const *file_name, char typeflag); +extern void xattrs_selinux_set (struct tar_stat_info const *st, + char const *file_name, char typeflag); +extern void xattrs_xattrs_set (struct tar_stat_info const *st, + char const *file_name, char typeflag, + int later_run); + +extern void xattrs_print_char (struct tar_stat_info const *st, char *output); +extern void xattrs_print (struct tar_stat_info const *st); + +#endif /* GUARD_XATTTRS_H */ diff --git a/src/xheader.c b/src/xheader.c index 0946493..061d448 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -1,7 +1,7 @@ /* POSIX extended headers for tar. - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software - Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 + Free Software Foundation, Inc. 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 @@ -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."); + 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; /* select handler comparing prefix only */ }; /* 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,8 @@ 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 +590,8 @@ 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; } @@ -1468,6 +1548,26 @@ volume_filename_decoder (struct tar_stat_info *st, { decode_string (&continued_file_name, arg); } +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, @@ -1506,53 +1606,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 +1661,16 @@ 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 }, + + /* We are storing all extended attributes using this rule even if some of them + were stored by some previous rule (duplicates) -- we just have to make sure + they are restored *only once* during extraction later on. */ + { "SCHILY.xattr", xattr_coder, xattr_decoder, 0, true }, - { NULL, NULL, NULL, 0 } + { NULL, NULL, NULL, 0, false } }; diff --git a/tests/Makefile.am b/tests/Makefile.am index 47afea1..3d870f1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,7 +1,7 @@ # Makefile for GNU tar regression tests. # Copyright (C) 1996, 1997, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2009 Free Software Foundation, Inc. +# 2006, 2007, 2009, 2012 Free Software Foundation, Inc. # François Pinard , 1988. # Sergey Poznyakoff , 2004. @@ -173,7 +173,12 @@ TESTSUITE_AT = \ star/multi-fail.at\ star/ustar-big-2g.at\ star/ustar-big-8g.at\ - star/pax-big-10g.at + star/pax-big-10g.at\ + xattr01.at\ + xattr02.at\ + xattr03.at\ + xattr04.at\ + capabs_raw01.at TESTSUITE = $(srcdir)/testsuite diff --git a/tests/testsuite.at b/tests/testsuite.at index d5c6023..7221ddb 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -1,7 +1,7 @@ # Process this file with autom4te to create testsuite. -*- Autotest -*- # Test suite for GNU tar. -# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Free Software +# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2012 Free Software # Foundation, Inc. # This program is free software; you can redistribute it and/or modify @@ -117,6 +117,36 @@ m4_define([AT_TAR_MKHIER],[ install-sh -d $1 >/dev/null dnl m4_if([$2],,,&& genfile --file [$1]/[$2]) || AT_SKIP_TEST]) +dnl Skip test when utlity does not return expected return value +m4_define([AT_CHECK_UTIL],[ + $1 &> /dev/null + if test "$?" != $2; then + AT_SKIP_TEST + fi +]) + +m4_define([AT_XATTRS_UTILS_PREREQ],[ + file=$( mktemp -p . ) + AT_CHECK_UTIL(setfattr -n user.test -v test $file,0) + AT_CHECK_UTIL(getfattr $file,0) +]) +m4_define([AT_CAPABILITIES_UTILS_PREREQ],[ + file=$( mktemp -p . ) + AT_CHECK_UTIL(setcap "= cap_chown=ei" $file,0) + AT_CHECK_UTIL(getcap $file,0) + rm -rf $file +]) +m4_define([AT_XATTRS_PREREQ],[ + AT_XATTRS_UTILS_PREREQ + file=$( mktemp -p . ) + setfattr -n user.test -v ahoj $file + # check whether tar fails to store xattrs + err=$( tar --xattrs -cf /dev/null $file 2>&1 >/dev/null | wc -l ) + if test "$err" != "0"; then + AT_SKIP_TEST + fi +]) + m4_include([sparsemvp.at]) AT_INIT @@ -284,3 +314,10 @@ m4_include([star/ustar-big-2g.at]) m4_include([star/ustar-big-8g.at]) m4_include([star/pax-big-10g.at]) + +m4_include([xattr01.at]) +m4_include([xattr02.at]) +m4_include([xattr03.at]) +m4_include([xattr04.at]) + +m4_include([capabs_raw01.at])