tar/tar-1.26-xattrs.patch

3062 lines
95 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From f661d5c38ec585f364e47a981ebada092936d38a Mon Sep 17 00:00:00 2001
From: Pavel Raiskup <praiskup@redhat.com>
Date: Tue, 14 Aug 2012 17:00:30 +0200
Subject: [PATCH] Adding support for extended attributes (including listing
feature)
---
Makefile.am | 2 +-
configure.ac | 24 ++
doc/tar.texi | 281 +++++++++++++++++-
src/Makefile.am | 7 +-
src/common.h | 29 +-
src/create.c | 55 +++-
src/extract.c | 142 ++++++++-
src/list.c | 11 +-
src/tar.c | 95 +++++-
src/tar.h | 23 +-
src/warning.c | 4 +-
src/xattrs.c | 705 ++++++++++++++++++++++++++++++++++++++++++++
src/xattrs.h | 52 ++++
src/xheader.c | 236 ++++++++++++---
tests/Makefile.am | 11 +-
tests/testsuite.at | 85 +++++-
tests/xattr/acls01.at | 53 ++++
tests/xattr/acls02.at | 59 ++++
tests/xattr/capabs_raw01.at | 51 ++++
tests/xattr/selacl01.at | 64 ++++
tests/xattr/selnx01.at | 96 ++++++
tests/xattr/xattr01.at | 47 +++
tests/xattr/xattr02.at | 55 ++++
tests/xattr/xattr03.at | 56 ++++
tests/xattr/xattr04.at | 48 +++
25 files changed, 2228 insertions(+), 63 deletions(-)
create mode 100644 src/xattrs.c
create mode 100644 src/xattrs.h
create mode 100644 tests/xattr/acls01.at
create mode 100644 tests/xattr/acls02.at
create mode 100644 tests/xattr/capabs_raw01.at
create mode 100644 tests/xattr/selacl01.at
create mode 100644 tests/xattr/selnx01.at
create mode 100644 tests/xattr/xattr01.at
create mode 100644 tests/xattr/xattr02.at
create mode 100644 tests/xattr/xattr03.at
create mode 100644 tests/xattr/xattr04.at
diff --git a/Makefile.am b/Makefile.am
index af332d7..67ba39b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
# Main Makefile for GNU tar.
# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2007,
-# 2009 Free Software Foundation, Inc.
+# 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
diff --git a/configure.ac b/configure.ac
index db69cb8..4aecee3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,6 +70,29 @@ if test $diff_cv_st_fstype_string = yes; then
[Define if struct stat has a char st_fstype[] member.])
fi
+# even if we use gnulib's acl.h with integrated m4 file later on (used because
+# of very useful file_has_acl() function) we need following checks that restrict
+# tar to use POSIX.1e ACLs only.
+AC_ARG_WITH([posix-acls],
+ AS_HELP_STRING([--without-posix-acls],
+ [do not use POSIX.1e access control lists]),
+ [with_posix_acls=no])
+if test "x$with_posix_acls" != "xno"; then
+ AC_CHECK_HEADERS(sys/acl.h,, [with_posix_acl=no])
+ AC_SEARCH_LIBS([acl_get_file], [acl pacl],, [with_posix_acl=no])
+ AC_SEARCH_LIBS([acl_get_fd], [acl pacl],, [with_posix_acl=no])
+ AC_SEARCH_LIBS([acl_set_file], [acl pacl],, [with_posix_acl=no])
+ AC_SEARCH_LIBS([acl_set_fd], [acl pacl],, [with_posix_acl=no])
+ AC_SEARCH_LIBS([acl_to_text], [acl pacl],, [with_posix_acl=no])
+ AC_SEARCH_LIBS([acl_from_text], [acl pacl],, [with_posix_acl=no])
+ if test "x$with_posix_acls" != xno; then
+ AC_DEFINE(HAVE_POSIX_ACLS,,[Define when we have working POSIX acls])
+ fi
+else
+ # disable acls in gnulib's checks
+ export enable_acl=no
+fi
+
AC_TYPE_SIGNAL
AC_TYPE_MODE_T
AC_TYPE_PID_T
@@ -91,6 +114,7 @@ gl_INIT
tar_PAXUTILS
AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
+
AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
AC_CHECK_DECLS([time],,, [#include <time.h>])
diff --git a/doc/tar.texi b/doc/tar.texi
index d70d113..567745b 100644
--- a/doc/tar.texi
+++ b/doc/tar.texi
@@ -37,7 +37,8 @@ This manual is for @acronym{GNU} @command{tar} (version
from archives.
Copyright @copyright{} 1992, 1994, 1995, 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.
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -162,6 +163,7 @@ How to Create Archives
How to List Archives
* list dir::
+* List Extended Attributes::
How to Extract Members from an Archive
@@ -1492,6 +1494,7 @@ for a detailed discussion of globbing patterns and related
@menu
* list dir::
+* List Extended Attributes::
@end menu
@node list dir
@@ -1522,6 +1525,116 @@ drwxrwxrwx myself/user 0 1990-05-31 21:49 practice/
When you use a directory name as a file name argument, @command{tar} acts on
all the files (including sub-directories) in that directory.
+@node List Extended Attributes
+@unnumberedsubsec Listing xattrs, POSIX ACLs and SELinux context
+
+From upstream GNU tar 1.26.9, tar is able to store, extract and list extended
+file attributes. Listing of those attributes is then active only in verbose and
+double-verbose mode.
+
+This section exercises how to list attributes on examples. Lets start with
+simple verbose mode. This output is inspired by GNU @command{ls -l} command
+output.
+
+@itemize @bullet
+@item
+Show only pure extended attributes.
+
+@smallexample
+$ tar --xattrs --list -v archive.tar
+-rw-rwxr-- user/group 0 2012-08-08 15:15 acls.txt
+-rw-rw-r--* user/group 0 2012-08-08 15:15 xattrs.txt
+@end smallexample
+
+Note the asterisk on the third line! It reflects the situation that the file
+'xattrs.txt' has some extended attribute set. The default mode (same as if you
+are extracting extended attributes) shows information only about extended
+attributes from 'user.*' domain. Anyway, feel free to change the sensitivity
+using @option{--xattrs-include} or @option{--xattrs-exclude} options.
+
+@item Show only POSIX ACLs - the character you should look for is '+':
+
+@smallexample
+$ tar --acls --list -v archive.tar
+-rw-rwxr--+ praiskup/praiskup 0 2012-08-08 15:15 acls.txt
+-rw-rw-r-- praiskup/praiskup 0 2012-08-08 15:15 xattrs.txt
+@end smallexample
+
+@item Show only SELinux - the key character is '.':
+
+@smallexample
+$ tar --selinux --list -v archive.tar
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:16 selinux_only.txt
+-rw-rw-r-- praiskup/praiskup 0 2012-08-08 15:15 xattrs.txt
+@end smallexample
+
+@item
+Show info about ACLs, SELinux and general extended attributes together:
+
+@smallexample
+$ tar --selinux --acls --xattrs --list -v archive.tar
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:16 selinux_only.txt
+-rw-rwxr--+ praiskup/praiskup 0 2012-08-08 15:15 acls.txt
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:15 xattrs.txt
+@end smallexample
+
+In this case, the priority of character is '+' > '.' > '*'. You don't see the
+general extended attributes flag ('*' character) on this example because it is
+hidden by '.' (meaning that the file has SELinux context set).
+
+@end itemize
+
+The example of double verbose mode is here. In this output the single verbose
+characters '.', '+' and '*' are also present after the permission string.
+
+@smallexample
+$ tar --xattrs --selinux --acls -tvvf archive.tar
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:16 selinux_only.txt
+ s: unconfined_u:object_r:user_tmp_t:s0
+-rw-rwxr--+ praiskup/praiskup 0 2012-08-08 15:15 acls.txt
+ s: unconfined_u:object_r:user_tmp_t:s0
+ a: user::rw-,user:tester:rwx,group::rw-,mask::rwx,other::r--
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:15 xattrs.txt
+ s: unconfined_u:object_r:user_tmp_t:s0
+ x: 12 user.xattr
+ x: 12 user.we_like_tar
+@end smallexample
+
+This mode extends tar's output with additional lines beginning with
+distinguishing characters - 's' for SELinux context, 'a' for POSIX Access
+Control Lists and 'x' for generic extended attributes.
+
+In this format, POSIX ACLs are written in SHORT TEXT FORM as specified in manual
+page @command{man 5 acl}.
+
+Use the @option{--xattrs-include} again if you want to print other than default
+'user.*' extended attributes domain:
+
+@smallexample
+$ tar --xattrs --xattrs-include='*' --acls --selinux -tvvf archive.tar
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:16 selinux_only.txt
+ s: unconfined_u:object_r:user_tmp_t:s0
+ x: 36 security.selinux
+-rw-rwxr--+ praiskup/praiskup 0 2012-08-08 15:15 acls.txt
+ s: unconfined_u:object_r:user_tmp_t:s0
+ a: user::rw-,user:tester:rwx,group::rw-,mask::rwx,other::r--
+ x: 36 security.selinux
+ x: 44 system.posix_acl_access
+-rw-rw-r--. praiskup/praiskup 0 2012-08-08 15:15 xattrs.txt
+ s: unconfined_u:object_r:user_tmp_t:s0
+ x: 36 security.selinux
+ x: 12 user.xattr
+ x: 12 user.we_like_tar
+@end smallexample
+
+As is in @pxref{Option Summary} section described, tar by default stores all
+extended attributes that are available (not only 'user.*' domain). It means
+that the SELinux context and POSIX ACLs (because they are implemented using the
+generic extended attributes on usual file system) may be stored twice sometimes
+-- firstly in "raw" file system binary format and secondly in more portable way
+-- using appropriate system calls (invoked by @command{tar} options
+@option{--selinux} and @option{--acls}).
+
@node extract
@section How to Extract Members from an Archive
@cindex Extraction
@@ -2369,6 +2482,10 @@ Normally when creating an archive, @command{tar} strips an initial
@samp{/} from member names. This option disables that behavior.
@xref{absolute}.
+@opsummary{acls}
+@item --acls
+Causes @command{tar} to store/restore/list POSIX ACL's. @xref{Attributes}.
+
@opsummary{after-date}
@item --after-date
@@ -2914,6 +3031,11 @@ contents have changed (as opposed to just @option{--newer}, which will
also back up files for which any status information has
changed). @xref{after}.
+@opsummary{no-acls}
+@item --no-acls
+Causes @command{tar} not to store, extract or list POSIX ACL's.
+@xref{Attributes}.
+
@opsummary{no-anchored}
@item --no-anchored
An exclude pattern can match any subsequence of the name's components.
@@ -2997,11 +3119,20 @@ locations. Usually @command{tar} determines automatically whether
the archive can be seeked or not. Use this option to disable this
mechanism.
+@opsummary{no-selinux}
+@item --no-selinux
+Causes @command{tar} not to store, extract or list SELinux security context.
+@xref{Attributes}.
+
@opsummary{no-unquote}
@item --no-unquote
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, extract or list xattrs. @xref{Attributes}.
+
@opsummary{no-wildcards}
@item --no-wildcards
Do not use wildcards.
@@ -3234,6 +3365,11 @@ in cases when such recognition fails. It takes effect only if the
archive is open for reading (e.g. with @option{--list} or
@option{--extract} options).
+@opsummary{selinux}
+@item --selinux
+Causes @command{tar} to store, extract or list SELinux security context.
+@xref{Attributes}.
+
@opsummary{show-defaults}
@item --show-defaults
@@ -3447,6 +3583,11 @@ 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, restore or list extended file attributes. For
+more info see @xref{Attributes}.
+
@opsummary{wildcards}
@item --wildcards
Use wildcards when matching member names with patterns.
@@ -4182,6 +4323,11 @@ tar (child): trying gzip
This means that @command{tar} first tried to decompress
@file{archive.Z} using @command{compress}, and, when that
failed, switched to @command{gzip}.
+@kwindex xattr-write
+@item xattr-write
+@samp{%s: Cannot set '%s' extended attribute for file '%s'}
+@*@samp{%s: Cannot set POSIX ACLs for file '%s'}
+@*@samp{%s: Cannot set SELinux context for file '%s'}
@end table
@subheading Keywords controlling incremental extraction:
@@ -8659,6 +8805,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 +9441,135 @@ 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, restore or list the extended file
+attributes (for information about extended attributes see @command{man(5)
+attr}).
+
+Note that all extended attributes are stored "as-is" (in file system binary
+format) and the resulting archive may be not fully portable. See the
+@option{--selinux} and @option{--acls} options when you want to deal with these
+types of extended attributes in a better way.
+
+The @option{--xattrs} option implies the option @option{--format=posix} when
+tar is in @option{--create} operation mode. It is the only one format which
+has usable headers for storing additional file information like extended
+attributes are.
+
+By default, all extended attributes are stored into the archive. The reason is
+that we want to make the backup process as complete as possible by default. On
+the other hand, during extracting only the 'user.*' domain is extracted by
+default. Anyway, this default behaviour may be easily modified by the
+@option{--xattrs-include} and @option{--xattrs-exclude} options.
+
+When you list an archive in verbose mode
+(@command{tar --xattrs --verbose -tf archive.tar}), tar shows the '*' character
+after the permissions string of concrete file right to tell you that at least
+one extended attribute is stored with corresponding file.
+
+Double verbose mode (@command{tar --xattrs -tvvf archive.tar}) prints the
+extended attribute length (in bytes) and its ASCII key (for printed examples
+@pxref{List Extended Attributes}).
+
+@option{--xattrs} option has no equivalent short option.
+
+Warnings which occur during impossible writing of extended attributes to
+a file system may be suppressed using the @option{--warning=no-xattr-write}
+option.
+
+@opindex no-xattrs
+@item --no-xattrs
+This option causes @command{tar} not to store/extract or list the current
+extended attributes. This option does not affect options @option{--no-selinux}
+or @option{--no-acls}.
+
+The @option{--no-xattrs} option has no equivalent short option name.
+
+@opindex xattrs-include
+@opindex xattrs-exclude
+@item --xattrs-include=MASK
+@itemx --xattrs-exclude=MASK
+
+These options allows the xattr store/restore/list process to be more fine
+grained. The default configuration is that @option{--create} mode handles all
+available extended attributes and the @option{--extract}/@option{--list} mode
+handles only 'user.*' domain. These options may be used for editing of this
+default behaviour.
+
+@itemize @bullet
+@item
+Lets say we want to store all attributes except some "public restricted" domain
+(e.g. 'user.restricted.*' domain. The correct way how to do it is:
+
+@command{tar --xattrs --xattrs-include='*' --xattrs-exclude='user.restricted.*'
+-cf archive.tar FILES}
+@item
+And, when we want to extract only some specific domain from an archive - we can
+use:
+
+@command{tar --xattrs --xattrs-include='security.capability' -xf archive.tar
+FILES}
+@end itemize
+
+Multiple passed include/exclude patterns are combined together. The attribute
+is covered then only if (1) at least one of all include patterns matches its
+keyword and (2) no exclude pattern matches its keyword.
+
+When only include pattern is set - exclude pattern is left in default mode (and
+vice versa).
+
+@opindex selinux
+@item --selinux
+This option causes @command{tar} to store/extract/list the SELinux context
+information into/from an archive. Command @command{tar} is able to show info
+whether the SELinux context is present in archived file using the verbose
+listing mode (@command{tar --selinux -tvf archive.tar}). It shows the '.'
+character after permission string in that case. Double-verbose listing mode
+(@command{tar -tvvf archive.tar}) then prints the full SELinux context to
+standard output, @pxref{List Extended Attributes} for printed example.
+
+This option implies the @option{--format=posix} when @command{tar} works in
+@option{--create} operation mode.
+
+Warnings complaining that SELinux context may not be written to a file system
+may be suppressed by the @option{--warning=no-xattr-write} option.
+
+The @option{--selinux} option has no equivalent short option name.
+
+@opindex no-selinux
+@item --no-selinux
+This option causes @command{tar} not to store the current SELinux security
+context information in the archive and not to extract any SELinux information in
+an archive.
+
+The @option{--no-selinux} option has no equivalent short option name.
+
+@opindex acls
+@item --acls
+This option causes @command{tar} to store the current POSIX access control lists
+into the archive or restore POSIX ACLs from an archive. It also allows
+@command{tar} to show whether archived file contains ACLs when the verbose mode
+is active (@option{tar --acls -tvf} shows the symbol '+' after the permission
+characters in that case). Double-verbose mode allows @command{tar} to list
+contained POSIX ACLs (@command{tar --acls -tvvf archive.tar}), for printed
+examples @pxref{List Extended Attributes}.
+
+This option implies the @option{--format=posix} when @command{tar} works in
+@option{--create} operation mode.
+
+Warnings complaining that POSIX ACLs may not be written to a file system may be
+suppressed by the @option{--warning=no-xattr-write} option.
+
+The @option{--acls} option has no equivalent short form.
+
+@opindex no-acls
+@item --no-acls
+This option causes @command{tar} not to store the current POSIX ACL into the
+archive and not to extract any POSIX ACL information from an archive.
+
+The @option{--no-acls} option has no equivalent short option name.
+
@end table
@node Portability
@@ -9441,7 +9718,7 @@ tar: ./one: Cannot hard link to `./jeden': No such file or directory
tar: Error exit delayed from previous errors
@end smallexample
-The reason for this behavior is that @command{tar} cannot seek back in
+The reason for this behaviour is that @command{tar} cannot seek back in
the archive to the previous member (in this case, @file{one}), to
extract it@footnote{There are plans to fix this in future releases.}.
If you wish to avoid such problems at the cost of a bigger archive,
diff --git a/src/Makefile.am b/src/Makefile.am
index de310f4..782df19 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) $(LIB_SELINUX)
diff --git a/src/common.h b/src/common.h
index 0b9bd7a..4cf1459 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;
@@ -253,6 +258,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;
@@ -707,6 +721,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 +744,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 */
@@ -808,6 +831,8 @@ void checkpoint_run (bool do_write);
#define WARN_XDEV 0x00040000
#define WARN_DECOMPRESS_PROGRAM 0x00080000
+#define WARN_XATTR_WRITE 0x00200000
+
/* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default
in verbose mode */
#define WARN_VERBOSE_WARNINGS (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY|\
diff --git a/src/create.c b/src/create.c
index f98cbb5..25387a9 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.
@@ -936,6 +937,30 @@ start_header (struct tar_stat_info *st)
GNAME_TO_CHARS (st->gname, header->header.gname);
}
+ if (archive_format == POSIX_FORMAT)
+ {
+ if (acls_option > 0)
+ {
+ if (st->acls_a_ptr)
+ xheader_store ("SCHILY.acl.access", st, NULL);
+ if (st->acls_d_ptr)
+ xheader_store ("SCHILY.acl.default", st, NULL);
+ }
+ if ((selinux_context_option > 0) && st->cntx_name)
+ xheader_store ("RHT.security.selinux", st, NULL);
+ 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 +1736,10 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
bool ok;
struct stat final_stat;
+ xattrs_acls_get (parentfd, name, st, 0, !is_dir);
+ xattrs_selinux_get (parentfd, name, st, fd);
+ xattrs_xattrs_get (parentfd, name, st, fd);
+
if (is_dir)
{
const char *tag_file_name;
@@ -1830,6 +1859,9 @@ 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_selinux_get (parentfd, name, st, 0);
+ 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);
@@ -1848,11 +1880,26 @@ 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_acls_get (parentfd, name, st, 0, true);
+ xattrs_selinux_get (parentfd, name, st, 0);
+ xattrs_xattrs_get (parentfd, name, st, 0);
+ }
else if (S_ISBLK (st->stat.st_mode))
- type = BLKTYPE;
+ {
+ type = BLKTYPE;
+ xattrs_acls_get (parentfd, name, st, 0, true);
+ xattrs_selinux_get (parentfd, name, st, 0);
+ xattrs_xattrs_get (parentfd, name, st, 0);
+ }
else if (S_ISFIFO (st->stat.st_mode))
- type = FIFOTYPE;
+ {
+ type = FIFOTYPE;
+ xattrs_acls_get (parentfd, name, st, 0, true);
+ xattrs_selinux_get (parentfd, name, st, 0);
+ 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 aaea56e..8a7a6ad 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.
@@ -97,6 +98,14 @@ struct delayed_set_stat
/* Directory that the name is relative to. */
int change_dir;
+ /* extended attributes*/
+ char *cntx_name;
+ char *acls_a_ptr;
+ size_t acls_a_len;
+ char *acls_d_ptr;
+ size_t acls_d_len;
+ size_t xattr_map_size;
+ struct xattr_array *xattr_map;
/* Length and contents of name. */
size_t file_name_len;
char file_name[1];
@@ -134,6 +143,18 @@ struct delayed_link
hard-linked together. */
struct string_list *sources;
+ /* SELinux context */
+ char *cntx_name;
+
+ /* ACLs */
+ char *acls_a_ptr;
+ size_t acls_a_len;
+ char *acls_d_ptr;
+ size_t acls_d_len;
+
+ size_t xattr_map_size;
+ struct xattr_array *xattr_map;
+
/* The desired target of the desired link. */
char target[1];
};
@@ -360,6 +381,12 @@ 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);
+ xattrs_acls_set (st, file_name, typeflag);
+ xattrs_selinux_set (st, file_name, typeflag);
}
/* For each entry H in the leading prefix of entries in HEAD that do
@@ -431,6 +458,36 @@ 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;
+ data->cntx_name = NULL;
+ if (st)
+ assign_string (&data->cntx_name, st->cntx_name);
+ if (st && st->acls_a_ptr)
+ {
+ data->acls_a_ptr = xmemdup (st->acls_a_ptr, st->acls_a_len + 1);
+ data->acls_a_len = st->acls_a_len;
+ }
+ else
+ {
+ data->acls_a_ptr = NULL;
+ data->acls_a_len = 0;
+ }
+ if (st && st->acls_d_ptr)
+ {
+ data->acls_d_ptr = xmemdup (st->acls_d_ptr, st->acls_d_len + 1);
+ data->acls_d_len = st->acls_d_len;
+ }
+ else
+ {
+ data->acls_d_ptr = NULL;
+ data->acls_d_len = 0;
+ }
+ 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 +730,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
@@ -733,12 +824,23 @@ 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.cntx_name = data->cntx_name;
+ sb.acls_a_ptr = data->acls_a_ptr;
+ sb.acls_a_len = data->acls_a_len;
+ sb.acls_d_ptr = data->acls_d_ptr;
+ sb.acls_d_len = data->acls_d_len;
+ 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->cntx_name);
+ free (data->acls_a_ptr);
+ free (data->acls_d_ptr);
free (data);
}
}
@@ -854,7 +956,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;
@@ -864,6 +967,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;
@@ -934,6 +1041,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;
@@ -950,8 +1059,18 @@ 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,
- &current_mode, &current_mode_mask))
+ file_created, &current_mode,
+ &current_mode_mask))
< 0)
{
int recover = maybe_recoverable (file_name, true, &interdir_made);
@@ -1091,6 +1210,13 @@ 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);
+ p->cntx_name = NULL;
+ assign_string (&p->cntx_name, current_stat_info.cntx_name);
+ p->acls_a_ptr = NULL;
+ p->acls_a_len = 0;
+ p->acls_d_ptr = NULL;
+ p->acls_d_len = 0;
+ 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 +1651,13 @@ apply_delayed_links (void)
st1.stat.st_gid = ds->gid;
st1.atime = ds->atime;
st1.mtime = ds->mtime;
+ st1.cntx_name = ds->cntx_name;
+ st1.acls_a_ptr = ds->acls_a_ptr;
+ st1.acls_a_len = ds->acls_a_len;
+ st1.acls_d_ptr = ds->acls_d_ptr;
+ st1.acls_d_len = ds->acls_d_len;
+ 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 +1672,9 @@ apply_delayed_links (void)
sources = next;
}
+ xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
+ free (ds->cntx_name);
+
{
struct delayed_link *next = ds->next;
free (ds);
diff --git a/src/list.c b/src/list.c
index f4e6e0a..dd501a9 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.
@@ -615,6 +616,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);
@@ -1075,7 +1078,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;
@@ -1167,6 +1170,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);
@@ -1312,6 +1318,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 7b62996..0ed1717 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.
@@ -255,7 +256,8 @@ tar_set_quoting_style (char *arg)
enum
{
- ANCHORED_OPTION = CHAR_MAX + 1,
+ ACLS_OPTION = CHAR_MAX + 1,
+ ANCHORED_OPTION,
ATIME_PRESERVE_OPTION,
BACKUP_OPTION,
CHECK_DEVICE_OPTION,
@@ -288,6 +290,7 @@ enum
MODE_OPTION,
MTIME_OPTION,
NEWER_MTIME_OPTION,
+ NO_ACLS_OPTION,
NO_ANCHORED_OPTION,
NO_AUTO_COMPRESS_OPTION,
NO_CHECK_DEVICE_OPTION,
@@ -301,9 +304,11 @@ enum
NO_SAME_OWNER_OPTION,
NO_SAME_PERMISSIONS_OPTION,
NO_SEEK_OPTION,
+ NO_SELINUX_CONTEXT_OPTION,
NO_UNQUOTE_OPTION,
NO_WILDCARDS_MATCH_SLASH_OPTION,
NO_WILDCARDS_OPTION,
+ NO_XATTR_OPTION,
NULL_OPTION,
NUMERIC_OWNER_OPTION,
OCCURRENCE_OPTION,
@@ -325,6 +330,7 @@ enum
RMT_COMMAND_OPTION,
RSH_COMMAND_OPTION,
SAME_OWNER_OPTION,
+ SELINUX_CONTEXT_OPTION,
SHOW_DEFAULTS_OPTION,
SHOW_OMITTED_DIRS_OPTION,
SHOW_TRANSFORMED_NAMES_OPTION,
@@ -340,7 +346,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;
@@ -525,6 +534,28 @@ 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 },
+ {"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
+ N_("Enable the SELinux context support"), GRID+1 },
+ {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
+ N_("Disable the SELinux context support"), GRID+1 },
+ {"acls", ACLS_OPTION, 0, 0,
+ N_("Enable the POSIX ACLs support"), GRID+1 },
+ {"no-acls", NO_ACLS_OPTION, 0, 0,
+ N_("Disable the POSIX ACLs support"), GRID+1 },
+#undef GRID
+
#define GRID 60
{NULL, 0, NULL, 0,
N_("Device selection and switching:"), GRID },
@@ -2082,6 +2113,38 @@ parse_opt (int key, char *arg, struct argp_state *state)
same_permissions_option = -1;
break;
+ case ACLS_OPTION:
+ set_archive_format ("posix");
+ acls_option = 1;
+ break;
+
+ case NO_ACLS_OPTION:
+ acls_option = -1;
+ break;
+
+ case SELINUX_CONTEXT_OPTION:
+ set_archive_format ("posix");
+ selinux_context_option = 1;
+ break;
+
+ case NO_SELINUX_CONTEXT_OPTION:
+ selinux_context_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;
@@ -2459,11 +2522,26 @@ 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 whenn reading */
+ if ((acls_option > 0)
+ && archive_format != POSIX_FORMAT
+ && !READ_LIKE_SUBCOMMAND)
+ USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
+
+ if ((selinux_context_option > 0)
+ && archive_format != POSIX_FORMAT
+ && !READ_LIKE_SUBCOMMAND)
+ USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
+
+ 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;
@@ -2672,6 +2750,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)
@@ -2716,11 +2795,15 @@ 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);
free (st->uname);
free (st->gname);
+ free (st->cntx_name);
+ free (st->acls_a_ptr);
+ free (st->acls_d_ptr);
free (st->sparse_map);
free (st->dumpdir);
xheader_destroy (&st->xhdr);
diff --git a/src/tar.h b/src/tar.h
index ce9850c..b181e58 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 */
@@ -287,6 +296,15 @@ struct tar_stat_info
char *uname; /* user name of owner */
char *gname; /* group name of owner */
+
+ char *cntx_name; /* SELinux context for the current archive entry. */
+
+ char *acls_a_ptr; /* Access ACLs for the current archive entry. */
+ size_t acls_a_len; /* Access ACLs for the current archive entry. */
+
+ char *acls_d_ptr; /* Default ACLs for the current archive entry. */
+ size_t acls_d_len; /* Default ACLs for the current archive entry. */
+
struct stat stat; /* regular filesystem stat */
/* STAT doesn't always have access, data modification, and status
@@ -309,6 +327,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 5d1bcab..b0b9884 100644
--- a/src/warning.c
+++ b/src/warning.c
@@ -42,6 +42,7 @@ static char const *const warning_args[] = {
"unknown-keyword",
"xdev",
"decompress-program",
+ "xattr-write",
NULL
};
@@ -66,7 +67,8 @@ static int warning_types[] = {
WARN_UNKNOWN_CAST,
WARN_UNKNOWN_KEYWORD,
WARN_XDEV,
- WARN_DECOMPRESS_PROGRAM
+ WARN_DECOMPRESS_PROGRAM,
+ 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..6e26ace
--- /dev/null
+++ b/src/xattrs.c
@@ -0,0 +1,705 @@
+/* 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 <system.h>
+
+#include <fnmatch.h>
+#include <quotearg.h>
+
+#include "common.h"
+
+#include "selinux-at.h"
+#include "xattr-at.h"
+
+/* disable posix acls when problem found in gnulib script m4/acl.m4 */
+#if ! USE_ACL
+# undef HAVE_POSIX_ACLS
+#endif
+
+#ifdef HAVE_POSIX_ACLS
+# include "acl.h"
+# include <sys/acl.h>
+#endif
+
+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;
+
+#ifdef HAVE_POSIX_ACLS
+
+/* acl-at wrappers, TODO: move to gnulib in future? */
+acl_t acl_get_file_at (int dirfd, const char *file, acl_type_t type);
+int acl_set_file_at (int dirfd, const char *file, acl_type_t type, acl_t acl);
+int file_has_acl_at (int dirfd, char const *, struct stat const *);
+
+/* acl_get_file_at */
+#define AT_FUNC_NAME acl_get_file_at
+#define AT_FUNC_RESULT acl_t
+#define AT_FUNC_FAIL (acl_t)NULL
+#define AT_FUNC_F1 acl_get_file
+#define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type
+#define AT_FUNC_POST_FILE_ARGS , type
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_RESULT
+#undef AT_FUNC_FAIL
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+
+/* acl_set_file_at */
+#define AT_FUNC_NAME acl_set_file_at
+#define AT_FUNC_F1 acl_set_file
+#define AT_FUNC_POST_FILE_PARAM_DECLS , acl_type_t type, acl_t acl
+#define AT_FUNC_POST_FILE_ARGS , type, acl
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+
+/* gnulib file_has_acl_at */
+#define AT_FUNC_NAME file_has_acl_at
+#define AT_FUNC_F1 file_has_acl
+#define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat const *st
+#define AT_FUNC_POST_FILE_ARGS , st
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+
+/* convert unix permissions into an ACL ... needed due to "default" ACLs */
+static acl_t perms2acl (int perms)
+{
+ char val[] = "user::---,group::---,other::---";
+ /* 0123456789 123456789 123456789 123456789 */
+
+ /* user */
+ if (perms & 0400) val[ 6] = 'r';
+ if (perms & 0200) val[ 7] = 'w';
+ if (perms & 0100) val[ 8] = 'x';
+
+ /* group */
+ if (perms & 0040) val[17] = 'r';
+ if (perms & 0020) val[18] = 'w';
+ if (perms & 0010) val[19] = 'x';
+
+ /* other */
+ if (perms & 0004) val[28] = 'r';
+ if (perms & 0002) val[29] = 'w';
+ if (perms & 0001) val[30] = 'x';
+
+ return (acl_from_text (val));
+}
+
+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);
+}
+
+/* The POSIX draft allows extra fields after the three main ones. Star
+ uses this to add a fourth field for user/group which is the numeric ID.
+ We just skip all extra fields atm. */
+static const char *fixup_extra_acl_fields (const char *ptr)
+{
+ char *src = (char *)ptr;
+ char *dst = (char *)ptr;
+
+ while (*src)
+ {
+ const char *old = src;
+ size_t len = 0;
+
+ src = skip_to_ext_fields (src);
+ len = src - old;
+ if (old != dst) memmove (dst, old, len);
+ dst += len;
+
+ if (*src == ':') /* We have extra fields, skip them all */
+ src += strcspn (src, "\n,");
+
+ if ((*src == '\n') || (*src == ','))
+ *dst++ = *src++; /* also done when dst == src, but that's ok */
+ }
+ if (src != dst)
+ *dst = 0;
+
+ return ptr;
+}
+
+static void xattrs__acls_set (struct tar_stat_info const *st,
+ char const *file_name, int type,
+ const char *ptr, size_t len, bool def)
+{ /* "system.posix_acl_access" */
+ acl_t acl;
+
+ if (ptr)
+ {
+ /* assert (strlen (ptr) == len); */
+ ptr = fixup_extra_acl_fields (ptr);
+
+ acl = acl_from_text (ptr);
+ acls_option = 1;
+ }
+ else if (acls_option > 0)
+ acl = perms2acl (st->stat.st_mode);
+ else
+ return; /* don't call acl functions unless we first hit an ACL, or
+ --acls was passed explicitly */
+
+ if (acl == (acl_t)NULL)
+ {
+ call_arg_warn ("acl_from_text", file_name);
+ return;
+ }
+
+ if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
+ /* warn even if filesystem does not support acls */
+ WARNOPT (WARN_XATTR_WRITE, (0, errno,
+ _("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"), file_name));
+
+ acl_free (acl);
+}
+
+static void xattrs__acls_get_a (int parentfd, const char *file_name,
+ struct tar_stat_info *st,
+ char **ret_ptr, size_t *ret_len)
+{ /* "system.posix_acl_access" */
+ char *val = NULL;
+ ssize_t len;
+ acl_t acl;
+
+ if ((acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS))
+ == (acl_t)NULL)
+ {
+ if (errno != ENOTSUP)
+ call_arg_warn ("acl_get_file_at", file_name);
+ return;
+ }
+
+ val = acl_to_text (acl, &len);
+ acl_free (acl);
+
+ if (val == NULL)
+ {
+ call_arg_warn ("acl_to_text", file_name);
+ return;
+ }
+
+ *ret_ptr = xstrdup (val);
+ *ret_len = len;
+
+ acl_free (val);
+}
+
+static void xattrs__acls_get_d (int parentfd, char const *file_name,
+ struct tar_stat_info *st,
+ char **ret_ptr, size_t *ret_len)
+{ /* "system.posix_acl_default" */
+ char *val = NULL;
+ ssize_t len;
+ acl_t acl;
+
+ if ((acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT))
+ == (acl_t)NULL)
+ {
+ if (errno != ENOTSUP)
+ call_arg_warn ("acl_get_file_at", file_name);
+ return;
+ }
+
+ val = acl_to_text (acl, &len);
+ acl_free (acl);
+
+ if (val == NULL)
+ {
+ call_arg_warn ("acl_to_text", file_name);
+ return;
+ }
+
+ *ret_ptr = xstrdup (val);
+ *ret_len = len;
+
+ acl_free (val);
+}
+#endif /* HAVE_POSIX_ACLS */
+
+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);
+}
+
+void xattrs_acls_get (int parentfd, char const *file_name,
+ struct tar_stat_info *st, int fd, int xisfile)
+{
+ int err;
+ if (acls_option > 0)
+ {
+#ifndef HAVE_POSIX_ACLS
+ static int done = 0;
+ if (!done)
+ WARN ((0, 0, _("POSIX ACL support is not available")));
+ done = 1;
+#else
+ err = file_has_acl_at (parentfd, file_name, &st->stat);
+ if (err == 0)
+ return;
+ if (err == -1)
+ {
+ call_arg_warn ("file_has_acl_at", file_name);
+ return;
+ }
+
+ xattrs__acls_get_a (parentfd, file_name, st,
+ &st->acls_a_ptr, &st->acls_a_len);
+ if (!xisfile)
+ xattrs__acls_get_d (parentfd, file_name, st,
+ &st->acls_d_ptr, &st->acls_d_len);
+#endif
+ }
+}
+
+/* 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));
+ }
+}
+
+void xattrs_acls_set (struct tar_stat_info const *st,
+ char const *file_name, char typeflag)
+{
+ if ((acls_option > 0) && (typeflag != SYMTYPE))
+ {
+#ifndef HAVE_POSIX_ACLS
+ static int done = 0;
+ if (!done)
+ WARN ((0, 0, _("POSIX ACL support is not available")));
+ done = 1;
+#else
+ xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
+ st->acls_a_ptr, st->acls_a_len, false);
+ if ((typeflag == DIRTYPE) || (typeflag == GNUTYPE_DUMPDIR))
+ xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
+ st->acls_d_ptr, st->acls_d_len, true);
+#endif
+ }
+}
+
+/* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
+ zero, otherwise the fgetfileconat is used against correct file descriptor */
+void xattrs_selinux_get (int parentfd, char const *file_name,
+ struct tar_stat_info *st, int fd)
+{
+ if (selinux_context_option > 0)
+ {
+#if HAVE_SELINUX_SELINUX_H != 1
+ static int done = 0;
+ if (!done)
+ WARN ((0, 0, _("SELinux support is not available")));
+ done = 1;
+#else
+ int result = (fd ? fgetfilecon (fd, &st->cntx_name)
+ : lgetfileconat (parentfd, file_name, &st->cntx_name));
+
+ if (result == -1 && errno != ENODATA && errno != ENOTSUP)
+ call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
+#endif
+ }
+}
+
+void xattrs_selinux_set (struct tar_stat_info const *st,
+ char const *file_name, char typeflag)
+{
+ if (selinux_context_option > 0)
+ {
+#if HAVE_SELINUX_SELINUX_H != 1
+ static int done = 0;
+ if (!done)
+ WARN ((0, 0, _("SELinux support is not available")));
+ done = 1;
+#else
+ const char *sysname = "setfilecon";
+ int ret;
+
+ if (!st->cntx_name)
+ return;
+
+ if (typeflag != SYMTYPE)
+ {
+ ret = setfileconat (chdir_fd, file_name, st->cntx_name);
+ sysname = "setfileconat";
+ }
+ else
+ {
+ ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
+ sysname = "lsetfileconat";
+ }
+
+ if (ret == -1)
+ WARNOPT (WARN_XATTR_WRITE, (0, errno,
+ _("%s: Cannot set SELinux context for file '%s'"), sysname,
+ file_name));
+#endif
+ }
+}
+
+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);
+}
+
+static void acls_one_line (const char *prefix, char delim,
+ const char *aclstring, size_t len)
+{
+ /* support both long and short text representation of posix acls */
+ struct obstack stk;
+ obstack_init (&stk);
+ int pref_len = strlen (prefix);
+ const char *oldstring = aclstring;
+
+ if (!aclstring || !len)
+ return;
+
+ int pos = 0;
+ while (pos <= len)
+ {
+ int move = strcspn (aclstring, ",\n");
+ if (!move)
+ break;
+
+ if (oldstring != aclstring)
+ obstack_1grow (&stk, delim);
+
+ obstack_grow (&stk, prefix, pref_len);
+ obstack_grow (&stk, aclstring, move);
+
+ aclstring += move + 1;
+ }
+
+ obstack_1grow (&stk, '\0');
+ const char *toprint = obstack_finish (&stk);
+
+ fprintf (stdlis, "%s", toprint);
+
+ obstack_free (&stk, NULL);
+}
+
+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 || selinux_context_option > 0 || acls_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;
+ }
+
+ if (selinux_context_option > 0 && st->cntx_name)
+ *output = '.';
+
+ if (acls_option && (st->acls_a_len || st->acls_d_len))
+ *output = '+';
+}
+
+void xattrs_print (struct tar_stat_info const *st)
+{
+ if (verbose_option < 3)
+ return;
+
+ /* selinux */
+ if (selinux_context_option && st->cntx_name)
+ fprintf (stdlis, " s: %s\n", st->cntx_name);
+
+ /* acls */
+ if (acls_option && (st->acls_a_len || st->acls_d_len))
+ {
+ fprintf (stdlis, " a: ");
+ acls_one_line ("", ',', st->acls_a_ptr, st->acls_a_len);
+ acls_one_line ("default:", ',', st->acls_d_ptr, st->acls_d_len);
+ fprintf (stdlis, "\n");
+ }
+
+ /* 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", 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 2284e97..666297d 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
@@ -448,7 +448,7 @@ xheader_write_global (struct xheader *xhdr)
xheader_init (xhdr);
for (kp = keyword_global_override_list; kp; kp = kp->next)
- code_string (kp->value, kp->pattern, xhdr);
+ code_string (kp->value, kp->pattern, xhdr);
}
if (xhdr->stk)
{
@@ -460,6 +460,80 @@ xheader_write_global (struct xheader *xhdr)
}
}
+void xheader_xattr_init (struct tar_stat_info *st)
+{
+ st->xattr_map = NULL;
+ st->xattr_map_size = 0;
+
+ st->acls_a_ptr = NULL;
+ st->acls_a_len = 0;
+ st->acls_d_ptr = NULL;
+ st->acls_d_len = 0;
+ st->cntx_name = NULL;
+}
+
+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 +547,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 +564,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 +584,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 +596,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;
}
@@ -1470,6 +1556,71 @@ volume_filename_decoder (struct tar_stat_info *st,
}
static void
+xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword,
+ struct xheader *xhdr, void const *data)
+{
+ code_string (st->cntx_name, keyword, xhdr);
+}
+
+static void
+xattr_selinux_decoder (struct tar_stat_info *st,
+ char const *keyword, char const *arg, size_t size)
+{
+ decode_string (&st->cntx_name, arg);
+}
+
+static void
+xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword,
+ struct xheader *xhdr, void const *data)
+{
+ xheader_print_n (xhdr, keyword, st->acls_a_ptr, st->acls_a_len);
+}
+
+static void
+xattr_acls_a_decoder (struct tar_stat_info *st,
+ char const *keyword, char const *arg, size_t size)
+{
+ st->acls_a_ptr = xmemdup (arg, size + 1);
+ st->acls_a_len = size;
+}
+
+static void
+xattr_acls_d_coder (struct tar_stat_info const *st , char const *keyword,
+ struct xheader *xhdr, void const *data)
+{
+ xheader_print_n (xhdr, keyword, st->acls_d_ptr, st->acls_d_len);
+}
+
+static void
+xattr_acls_d_decoder (struct tar_stat_info *st,
+ char const *keyword, char const *arg, size_t size)
+{
+ st->acls_d_ptr = xmemdup (arg, size + 1);
+ st->acls_d_len = size;
+}
+
+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 +1657,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 +1712,28 @@ 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 get the SELinux value from filecon, so add a namespace for SELinux
+ instead of storing it in SCHILY.xattr.* (which would be RAW). */
+ { "RHT.security.selinux",
+ xattr_selinux_coder, xattr_selinux_decoder, 0, false },
+
+ /* ACLs, use the star format... */
+ { "SCHILY.acl.access",
+ xattr_acls_a_coder, xattr_acls_a_decoder, 0, false },
+
+ { "SCHILY.acl.default",
+ xattr_acls_d_coder, xattr_acls_d_decoder, 0, 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 119f1f3..b3c24dc 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -169,7 +169,16 @@ 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\
+ xattr/xattr01.at\
+ xattr/xattr02.at\
+ xattr/xattr03.at\
+ xattr/xattr04.at\
+ xattr/acls01.at\
+ xattr/acls02.at\
+ xattr/selnx01.at\
+ xattr/selacl01.at\
+ xattr/capabs_raw01.at
TESTSUITE = $(srcdir)/testsuite
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 13f7506..3f02a52 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -93,16 +93,81 @@ m4_define([AT_SORT_PREREQ],[
test -z "`sort < /dev/null 2>&1`" || AT_SKIP_TEST
])
-dnl AT_UNPRIVILEGED_PREREQ - Skip test if running at root privileges
-m4_define([AT_UNPRIVILEGED_PREREQ],[
+m4_define([AT_IS_PRIVILEGED],[
echo "test" > $[]$
chmod 0 $[]$
cat $[]$ > /dev/null 2>&1
result=$?
rm -f $[]$
-test $result -eq 0 && AT_SKIP_TEST
+test $result -eq 0])
+
+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_SELINUX_UTILS_PREREQ],[
+ file=$( mktemp -p . )
+ AT_CHECK_UTIL(restorecon $file, 0)
+ AT_CHECK_UTIL(chcon -h --user=unconfined_u $file,0)
+ rm -rf $file
+])
+m4_define([AT_ACLS_UTILS_PREREQ],[
+ file=$( mktemp -p . )
+ AT_CHECK_UTIL(setfacl -m u:$UID:rwx $file,0)
+ AT_CHECK_UTIL(getfacl $file,0)
+ rm -rf $file
+])
+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_define([AT_SELINUX_PREREQ],[
+ AT_SELINUX_UTILS_PREREQ
+ file=$( mktemp -p . )
+ err=$( tar --selinux -cf /dev/null $file 2>&1 >/dev/null | wc -l )
+ if test "$err" != "0"; then
+ AT_SKIP_TEST
+ fi
+])
+m4_define([AT_ACLS_PREREQ],[
+ AT_ACLS_UTILS_PREREQ
+ file=$( mktemp -p . )
+ setfacl -m u:$UID:rwx $file
+ err=$( tar --acls -cf /dev/null $file 2>&1 >/dev/null | wc -l )
+ if test "$err" != "0"; then
+ AT_SKIP_TEST
+ fi
])
+dnl AT_UNPRIVILEGED_PREREQ - Skip test if running at root privileges
+m4_append([AT_UNPRIVILEGED_PREREQ],[AT_IS_PRIVILEGED])
+m4_append([AT_UNPRIVILEGED_PREREQ],[&& AT_SKIP_TEST])
+
+dnl AT_UNPRIVILEGED_PREREQ - Skip test if running at non-root privileges
+m4_append([AT_PRIVILEGED_PREREQ],[ AT_IS_PRIVILEGED ])
+m4_append([AT_PRIVILEGED_PREREQ],[ || AT_SKIP_TEST])
+
m4_define([AT_TAR_MKHIER],[
install-sh -d $1 >/dev/null dnl
m4_if([$2],,,&& genfile --file [$1]/[$2]) || AT_SKIP_TEST])
@@ -270,3 +335,17 @@ m4_include([star/ustar-big-2g.at])
m4_include([star/ustar-big-8g.at])
m4_include([star/pax-big-10g.at])
+
+m4_include([xattr/xattr01.at])
+m4_include([xattr/xattr02.at])
+m4_include([xattr/xattr03.at])
+m4_include([xattr/xattr04.at])
+
+m4_include([xattr/acls01.at])
+m4_include([xattr/acls02.at])
+
+m4_include([xattr/selnx01.at])
+
+m4_include([xattr/selacl01.at])
+
+m4_include([xattr/capabs_raw01.at])
diff --git a/tests/xattr/acls01.at b/tests/xattr/acls01.at
new file mode 100644
index 0000000..0149f2d
--- /dev/null
+++ b/tests/xattr/acls01.at
@@ -0,0 +1,53 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 2011 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# This is basic test for acl support.
+
+AT_SETUP([acls: basic functionality])
+AT_KEYWORDS([xattrs acls acls01])
+
+AT_TAR_CHECK([
+AT_XATTRS_UTILS_PREREQ
+AT_ACLS_PREREQ
+
+mkdir dir
+genfile --file dir/file
+
+MYNAME=$( id -un )
+
+setfacl -m u:$MYNAME:--x dir/file
+setfacl -m u:$MYNAME:--x dir
+
+getfattr -h -m. -d dir dir/file > before
+
+tar --acls -cf archive.tar dir
+rm -rf dir
+
+tar --acls -xf archive.tar
+
+getfattr -h -m. -d dir dir/file > after
+
+diff before after
+test "$?" = 0
+],
+[0],
+[])
+
+AT_CLEANUP
diff --git a/tests/xattr/acls02.at b/tests/xattr/acls02.at
new file mode 100644
index 0000000..2ee1c5f
--- /dev/null
+++ b/tests/xattr/acls02.at
@@ -0,0 +1,59 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 2011 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# This is basic test for acl support.
+
+AT_SETUP([acls: work with -C])
+AT_KEYWORDS([xattrs acls acls02])
+
+AT_TAR_CHECK([
+AT_XATTRS_UTILS_PREREQ
+AT_ACLS_PREREQ
+
+mkdir dir
+mkdir dir/subdir
+genfile --file dir/subdir/file
+
+MYNAME=$( id -un )
+
+setfacl -m u:$MYNAME:--x dir/subdir
+setfacl -m u:$MYNAME:--x dir/subdir/file
+
+cd dir
+getfattr -h -m. -d subdir subdir/file > ../before
+cd ..
+
+tar --acls -cf archive.tar -C dir subdir
+rm -rf dir
+
+mkdir dir
+tar --acls -xf archive.tar -C dir
+
+cd dir
+getfattr -h -m. -d subdir subdir/file > ../after
+cd ..
+
+diff before after
+test "$?" = 0
+],
+[0],
+[])
+
+AT_CLEANUP
diff --git a/tests/xattr/capabs_raw01.at b/tests/xattr/capabs_raw01.at
new file mode 100644
index 0000000..8eea0cf
--- /dev/null
+++ b/tests/xattr/capabs_raw01.at
@@ -0,0 +1,51 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description: Test if file capabilities are archived/restored correctly
+# using just the default xattr support (capabilities are stored/restored in
+# binary format -> system dependant).
+
+AT_SETUP([capabilities: binary store/restore])
+AT_KEYWORDS([xattrs capabilities capabs_raw01])
+
+AT_TAR_CHECK([
+AT_PRIVILEGED_PREREQ
+AT_XATTRS_PREREQ
+AT_CAPABILITIES_UTILS_PREREQ
+
+mkdir dir
+genfile --file dir/file
+
+setcap "= cap_chown=ei" dir/file
+
+# archive whole directory including binary xattrs
+tar --xattrs -cf archive.tar dir
+
+# clear the directory
+rm -rf dir
+
+# restore _all_ xattrs (not just the user.* domain)
+tar --xattrs --xattrs-include='*' -xf archive.tar
+
+getcap dir/file
+],
+[0],
+[dir/file = cap_chown+ei
+])
+
+AT_CLEANUP
diff --git a/tests/xattr/selacl01.at b/tests/xattr/selacl01.at
new file mode 100644
index 0000000..90d0c5b
--- /dev/null
+++ b/tests/xattr/selacl01.at
@@ -0,0 +1,64 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 2011 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# This is basic test for support of extended attributes.
+
+AT_SETUP([acls/selinux: special files & fifos])
+AT_KEYWORDS([xattrs selinux acls selacls01])
+
+AT_TAR_CHECK([
+AT_PRIVILEGED_PREREQ
+AT_XATTRS_UTILS_PREREQ
+AT_SELINUX_PREREQ
+AT_ACLS_PREREQ
+
+mkdir dir
+mkfifo dir/fifo
+MAJOR=$( stat /dev/urandom --printf="%t" )
+MINOR=$( stat /dev/urandom --printf="%T" )
+mknod dir/chartype c $MAJOR $MINOR
+
+# setup attributes
+restorecon -R dir
+chcon -h --user=system_u dir/fifo
+chcon -h --user=system_u dir/chartype
+setfacl -m u:$UID:--- dir/fifo
+setfacl -m u:$UID:rwx dir/chartype
+
+getfacl dir/fifo >> before
+getfattr -msecurity.selinux dir/chartype >> before
+
+tar --xattrs --selinux --acls -cf archive.tar dir
+
+mv dir olddir
+
+tar --xattrs --selinux --acls -xf archive.tar
+
+getfacl dir/fifo >> after
+getfattr -msecurity.selinux dir/chartype >> after
+
+diff before after
+echo separator
+],
+[0],
+[separator
+])
+
+AT_CLEANUP
diff --git a/tests/xattr/selnx01.at b/tests/xattr/selnx01.at
new file mode 100644
index 0000000..79f7267
--- /dev/null
+++ b/tests/xattr/selnx01.at
@@ -0,0 +1,96 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# This is basic test for selinux support (store & restore).
+
+AT_SETUP([selinux: basic store/restore])
+AT_KEYWORDS([xattrs selinux selnx01])
+
+AT_TAR_CHECK([
+AT_XATTRS_UTILS_PREREQ
+AT_SELINUX_PREREQ
+
+mkdir dir
+genfile --file dir/file
+ln -s file dir/link
+
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > start
+
+restorecon -R dir
+chcon -h --user=system_u dir
+chcon -h --user=unconfined_u dir/file
+chcon -h --user=system_u dir/link
+
+# archive whole directory including selinux contexts
+tar --selinux -cf archive.tar dir
+
+# clear the directory
+rm -rf dir
+
+# ================================================
+# check if selinux contexts are correctly restored
+
+tar --selinux -xf archive.tar
+
+# archive for later debugging
+cp archive.tar archive_origin.tar
+
+# check if selinux contexts were restored
+getfattr -h -d dir dir/file dir/link -msecurity.selinux | \
+ grep -v -e '^#' -e ^$ | cut -d: -f1
+
+# ===========================================================================
+# check if selinux contexts are not restored when --selinux option is missing
+
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > with_selinux
+rm -rf dir
+tar -xf archive.tar
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > without_selinux
+
+diff with_selinux without_selinux > diff_with_without
+if test "$?" -eq "0"; then
+ echo "selinux contexts probably restored while --selinux is off"
+fi
+
+# =================================================================
+# check if selinux is not archived when --selinux option is missing
+
+tar -cf archive.tar dir
+
+# clear the directory
+rm -rf dir
+
+# restore (with --selinux)
+tar --selinux -xf archive.tar dir
+
+getfattr -h -d -msecurity.selinux dir dir/file dir/link > final
+diff start final > final_diff
+if test "$?" -ne "0"; then
+ echo "bad result"
+fi
+
+],
+[0],
+[security.selinux="system_u
+security.selinux="unconfined_u
+security.selinux="system_u
+])
+
+AT_CLEANUP
diff --git a/tests/xattr/xattr01.at b/tests/xattr/xattr01.at
new file mode 100644
index 0000000..fd960d5
--- /dev/null
+++ b/tests/xattr/xattr01.at
@@ -0,0 +1,47 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 2011 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# This is basic test for support of extended attributes.
+
+AT_SETUP([xattrs: basic functionality])
+AT_KEYWORDS([xattrs xattr01])
+
+AT_TAR_CHECK([
+AT_XATTRS_PREREQ
+mkdir dir
+genfile --file dir/file
+
+setfattr -n user.test -v OurDirValue dir
+setfattr -n user.test -v OurFileValue dir/file
+
+tar --xattrs -cf archive.tar dir
+
+rm -rf dir
+tar --xattrs -xf archive.tar
+
+getfattr -h -d dir | grep -v -e '^#' -e ^$
+getfattr -h -d dir/file | grep -v -e '^#' -e ^$
+],
+[0],
+[user.test="OurDirValue"
+user.test="OurFileValue"
+])
+
+AT_CLEANUP
diff --git a/tests/xattr/xattr02.at b/tests/xattr/xattr02.at
new file mode 100644
index 0000000..3aae3f9
--- /dev/null
+++ b/tests/xattr/xattr02.at
@@ -0,0 +1,55 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 2011 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# Cooperation of the '-C' option and storing/restoring extended attributes.
+
+AT_SETUP([xattrs: change directory with -C option])
+AT_KEYWORDS([xattrs xattr02])
+
+AT_TAR_CHECK([
+AT_XATTRS_PREREQ
+
+mkdir dir
+mkdir dir/subdir
+mkdir dir/subdir/subsubdir
+genfile --file dir/file1
+genfile --file dir/subdir/file2
+
+setfattr -n user.test -v OurFile1Value dir/file1
+setfattr -n user.test -v OurFile2Value dir/subdir/file2
+setfattr -n user.test -v OurDirValue dir/subdir/subsubdir
+
+tar --xattrs -cf archive.tar -C dir file1 -C subdir file2 subsubdir
+
+rm -rf dir
+
+tar --xattrs -xf archive.tar
+
+getfattr -h -d file1 | grep -v -e '^#' -e ^$
+getfattr -h -d file2 | grep -v -e '^#' -e ^$
+getfattr -h -d subsubdir | grep -v -e '^#' -e ^$
+],
+[0],
+[user.test="OurFile1Value"
+user.test="OurFile2Value"
+user.test="OurDirValue"
+])
+
+AT_CLEANUP
diff --git a/tests/xattr/xattr03.at b/tests/xattr/xattr03.at
new file mode 100644
index 0000000..d834f9f
--- /dev/null
+++ b/tests/xattr/xattr03.at
@@ -0,0 +1,56 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description:
+#
+# Setup of the trusted.* domain under privileged user.
+
+AT_SETUP([xattrs: trusted.* attributes])
+AT_KEYWORDS([xattrs xattr03])
+
+AT_TAR_CHECK([
+AT_PRIVILEGED_PREREQ
+AT_XATTRS_PREREQ
+
+mkdir dir
+mkdir dir/subdir
+mkdir dir/subdir/subsubdir
+genfile --file dir/file1
+genfile --file dir/subdir/file2
+
+setfattr -n trusted.test -v OurFile1Value dir/file1
+setfattr -n trusted.test -v OurFile2Value dir/subdir/file2
+setfattr -n trusted.test -v OurDirValue dir/subdir/subsubdir
+
+tar --xattrs -cf archive.tar -C dir file1 -C subdir file2 subsubdir
+
+rm -rf dir
+
+tar --xattrs --xattrs-include=trusted* -xf archive.tar
+
+getfattr -mtrusted. -d file1 | grep -v -e '^#' -e ^$
+getfattr -mtrusted. -d file2 | grep -v -e '^#' -e ^$
+getfattr -mtrusted. -d subsubdir | grep -v -e '^#' -e ^$
+],
+[0],
+[trusted.test="OurFile1Value"
+trusted.test="OurFile2Value"
+trusted.test="OurDirValue"
+])
+
+AT_CLEANUP
diff --git a/tests/xattr/xattr04.at b/tests/xattr/xattr04.at
new file mode 100644
index 0000000..31832af
--- /dev/null
+++ b/tests/xattr/xattr04.at
@@ -0,0 +1,48 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright (C) 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 Free Software Foundation; either version 3, 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, see <http://www.gnu.org/licenses/>.
+#
+# Test description: Test for the regression caused by tar update from 1.23 to
+# 1.26, Red Hat xattr patch was not ready for open->openat conversion.
+#
+# Related commit 4bde4f3. See the bug: https://bugzilla.redhat.com/717684
+
+AT_SETUP([xattrs: s/open/openat/ regression])
+AT_KEYWORDS([xattrs xattr04])
+
+AT_TAR_CHECK([
+AT_XATTRS_PREREQ
+
+mkdir dir
+mkdir output
+genfile --file dir/file
+
+setfattr -n user.test -v value dir/file
+
+# archive whole directory including binary xattrs
+tar --xattrs -cf archive.tar -C dir .
+
+tar --xattrs -xf archive.tar -C output
+ret=$?
+getfattr -h -d output/file | grep -v -e '^#' -e ^$
+exit $ret
+],
+[0],
+[user.test="value"
+])
+
+AT_CLEANUP
--
1.7.11.2