You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3233 lines
96 KiB

diff --git a/NEWS b/NEWS
index 12c1dd6..8aeae33 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU tar NEWS - User visible changes. 2011-03-12
+GNU tar NEWS - User visible changes. 2012-11-19
Please send GNU tar bug reports to <bug-tar@gnu.org>
@@ -22,6 +22,17 @@ zero-sized files.
When invoked with these two options, tar 1.25 would add only the
top-level directory to the archive, but not its contents.
+* Support for POSIX ACLs, extended attributes and SELinux context.
+
+Starting with this version tar is able to store, extract and list
+extended file attributes, POSIX.1e ACLs and SELinux context. This is
+controlled by the command line options --xattrs, --acls and --selinux,
+correspondingly. Each of these options has a `--no-' counterpart
+(e.g. --no-xattrs), which disables the corresponding feature.
+Additionally, the options --xattrs-include and --xattrs-exclude allow
+you to selectively control for which files to store (or extract) the
+extended attributes.
+
version 1.25 - Sergey Poznyakoff, 2010-11-07
diff --git a/THANKS b/THANKS
index 0364c50..54c96a0 100644
--- a/THANKS
+++ b/THANKS
@@ -296,6 +296,7 @@ Kimmy Posey kimmyd@bnr.ca
Koji Kishi kis@rqa.sony.co.jp
Konno Hiroharu konno@pac.co.jp
Kurt Jaeger pi@lf.net
+James Antill jantill@redhat.com
Larry Creech lcreech@lonestar.rcclub.org
Larry Schwimmer rosebud@cyclone.stanford.edu
Lasse Collin lasse.collin@tukaani.org
@@ -374,6 +375,7 @@ Oswald P. Backus IV backus@lks.csi.com
Pascal Meheut pascal@cnam.cnam.fr
Patrick Fulconis fulco@sig.uvsq.fr
Patrick Timmons timmons@electech.polymtl.ca
+Pavel Raiskup praiskup@redhat.com
Paul Eggert eggert@twinsun.com
Paul Kanz paul@icx.com
Paul Mitchell P.Mitchell@surrey.ac.uk
diff --git a/acinclude.m4 b/acinclude.m4
index 10a27e5..30381d3 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -24,3 +24,29 @@ AC_DEFUN([TAR_COMPR_PROGRAM],[
[tar_compr_var=m4_if($2,,$1,$2)])
AC_DEFINE_UNQUOTED(tar_compr_define, "$tar_compr_var",
[Define to the program name of ]$1[ compressor program])])
+
+# Provide <attr/xattr.h>, if necessary
+
+AC_DEFUN([TAR_HEADERS_ATTR_XATTR_H],
+[
+ AC_ARG_WITH([xattrs],
+ AS_HELP_STRING([--without-xattrs], [don't use linux extended attributes]),
+ [], [with_xattrs=maybe]
+ )
+
+ AC_CHECK_HEADERS([attr/xattr.h])
+ AM_CONDITIONAL([TAR_COND_XATTR_H],[test "$ac_cv_header_attr_xattr_h" = yes])
+ if test "$ac_cv_header_attr_xattr_h" = yes; then
+ AC_CHECK_FUNCS(getxattr fgetxattr lgetxattr \
+ setxattr fsetxattr lsetxattr \
+ listxattr flistxattr llistxattr,
+ # only when functions are present
+ AC_DEFINE([HAVE_ATTR_XATTR_H], [1],
+ [define to 1 if we have <attr/xattr.h> header])
+ if test "$with_xattrs" != no; then
+ AC_DEFINE([HAVE_XATTRS],,[Define when we have working linux xattrs.])
+ fi
+ )
+ fi
+])
+
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
index db69cb8..9b3e0c8 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
@@ -90,7 +113,10 @@ gl_INIT
# paxutils modules
tar_PAXUTILS
+TAR_HEADERS_ATTR_XATTR_H
+
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/lib/Makefile.am b/lib/Makefile.am
index efd6826..d73fac8 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -28,11 +28,24 @@ BUILT_SOURCES = rmt-command.h
CLEANFILES = rmt-command.h rmt-command.h-t
INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu
-noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h
+noinst_HEADERS = system.h system-ioctl.h rmt.h paxlib.h stdopen.h xattr-at.h
libtar_a_SOURCES = \
paxerror.c paxexit-status.c paxlib.h paxnames.c \
prepargs.c prepargs.h \
rtapelib.c \
rmt.h \
stdopen.c stdopen.h \
- system.h system-ioctl.h
+ system.h system-ioctl.h \
+ xattr-at.c
+
+if !TAR_COND_XATTR_H
+BUILT_SOURCES += attr/xattr.h
+attr/xattr.h: attr-xattr.in.h $(top_builddir)/config.status
+ $(AM_V_at)$(MKDIR_P) attr
+ $(AM_V_GEN)rm -f $@-t $@ && \
+ cp $(srcdir)/attr-xattr.in.h attr/xattr.h
+
+MOSTLYCLEANFILES = attr/xattr.h attr/xattr.h
+endif
+
+EXTRA_DIST = attr-xattr.in.h
diff --git a/lib/attr-xattr.in.h b/lib/attr-xattr.in.h
new file mode 100644
index 0000000..b5796fe
--- /dev/null
+++ b/lib/attr-xattr.in.h
@@ -0,0 +1,58 @@
+/* Replacement <attr/xattr.h> for platforms that lack it.
+ 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 of the License, 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/>. */
+
+#ifndef TAR_ATTR_XATTR_H
+#define TAR_ATTR_XATTR_H
+#include <errno.h>
+
+/* setting */
+static inline int setxattr (const char *path, const char *name, const void
+ *value, size_t size, int flags)
+{ errno = ENOTSUP; return -1; }
+
+static inline int lsetxattr (const char *path, const char *name, const void
+ *value, size_t size, int flags)
+{ errno = ENOTSUP; return -1; }
+
+static inline int fsetxattr (int filedes, const char *name, const void *value,
+ size_t size, int flags)
+{ errno = ENOTSUP; return -1; }
+
+
+/* getting */
+static inline ssize_t getxattr (const char *path, const char *name, void *value,
+ size_t size)
+{ errno = ENOTSUP; return -1; }
+static inline ssize_t lgetxattr (const char *path, const char *name, void
+ *value, size_t size)
+{ errno = ENOTSUP; return -1; }
+static inline ssize_t fgetxattr (int filedes, const char *name, void *value,
+ size_t size)
+{ errno = ENOTSUP; return -1; }
+
+
+/* listing */
+static inline ssize_t listxattr (const char *path, char *list, size_t size)
+{ errno = ENOTSUP; return -1; }
+
+static inline ssize_t llistxattr (const char *path, char *list, size_t size)
+{ errno = ENOTSUP; return -1; }
+
+static inline ssize_t flistxattr (int filedes, char *list, size_t size)
+{ errno = ENOTSUP; return -1; }
+
+#endif
+
diff --git a/lib/xattr-at.c b/lib/xattr-at.c
new file mode 100644
index 0000000..746578c
--- /dev/null
+++ b/lib/xattr-at.c
@@ -0,0 +1,110 @@
+/* openat-style fd-relative functions for operating with extended file
+ attributes.
+
+ 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 of the License, 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/>. */
+
+#include <config.h>
+
+#include "xattr-at.h"
+#include "openat.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
+#include "save-cwd.h"
+
+#include "openat-priv.h"
+
+/* setxattrat */
+#define AT_FUNC_NAME setxattrat
+#define AT_FUNC_F1 setxattr
+#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, const void *value \
+ , size_t size, int flags
+#define AT_FUNC_POST_FILE_ARGS , name, value, size, flags
+#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
+
+/* lsetxattrat */
+#define AT_FUNC_NAME lsetxattrat
+#define AT_FUNC_F1 lsetxattr
+#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, const void *value \
+ , size_t size, int flags
+#define AT_FUNC_POST_FILE_ARGS , name, value, size, flags
+#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
+
+/* getxattrat */
+#define AT_FUNC_NAME getxattrat
+#define AT_FUNC_RESULT ssize_t
+#define AT_FUNC_F1 getxattr
+#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, void *value \
+ , size_t size
+#define AT_FUNC_POST_FILE_ARGS , name, value, size
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_RESULT
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+
+/* lgetxattrat */
+#define AT_FUNC_NAME lgetxattrat
+#define AT_FUNC_RESULT ssize_t
+#define AT_FUNC_F1 lgetxattr
+#define AT_FUNC_POST_FILE_PARAM_DECLS , const char *name, void *value \
+ , size_t size
+#define AT_FUNC_POST_FILE_ARGS , name, value, size
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_RESULT
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+
+/* listxattrat */
+#define AT_FUNC_NAME listxattrat
+#define AT_FUNC_RESULT ssize_t
+#define AT_FUNC_F1 listxattr
+#define AT_FUNC_POST_FILE_PARAM_DECLS , char *list , size_t size
+#define AT_FUNC_POST_FILE_ARGS , list , size
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_RESULT
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+
+/* llistxattrat */
+#define AT_FUNC_NAME llistxattrat
+#define AT_FUNC_RESULT ssize_t
+#define AT_FUNC_F1 llistxattr
+#define AT_FUNC_POST_FILE_PARAM_DECLS , char *list , size_t size
+#define AT_FUNC_POST_FILE_ARGS , list , size
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_RESULT
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
diff --git a/lib/xattr-at.h b/lib/xattr-at.h
new file mode 100644
index 0000000..360245c
--- /dev/null
+++ b/lib/xattr-at.h
@@ -0,0 +1,66 @@
+/* Prototypes for openat-style fd-relative functions for operating with
+ extended file attributes.
+
+ 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 of the License, 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/>. */
+
+#ifndef XATTRS_AT_H
+#define XATTRS_AT_H
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+/* These are the dir-fd-relative variants of the functions without the
+ "at" suffix. For example, setxattrat (AT_FDCWD, path, name, value, size,
+ flags &c) is usually equivalent to setxattr (file, name, value, size,
+ flags). For more info use the setxattr(2), getxattr(2) or listxattr(2)
+ manpages. */
+
+/* dir-fd-relative setxattr. Operation sets the VALUE of the extended
+ attribute identified by NAME and associated with the given PATH in the
+ filesystem relatively to directory identified by DIR_FD. See the
+ setxattr(2) manpage for the description of all parameters. */
+int setxattrat (int dir_fd, const char *path, const char *name,
+ const void *value, size_t size, int flags);
+
+/* dir-fd-relative lsetxattr. This function is just like setxattrat,
+ except when DIR_FD and FILE specify a symlink: lsetxattrat operates on the
+ symlink, while the setxattrat operates on the referent of the symlink. */
+int lsetxattrat (int dir_fd, const char *path, const char *name,
+ const void *value, size_t size, int flags);
+
+/* dir-fd-relative getxattr. Operation gets the VALUE of the extended
+ attribute idenfified by NAME and associated with the given PATH in the
+ filesystem relatively to directory identified by DIR_FD. For more info
+ about all parameters see the getxattr(2) manpage. */
+ssize_t getxattrat (int dir_fd, const char *path, const char *name,
+ void *value, size_t size);
+
+/* dir-fd-relative lgetxattr. This function is just like getxattrat,
+ except when DIR_FD and FILE specify a symlink: lgetxattrat operates on the
+ symlink, while the getxattrat operates on the referent of the symlink. */
+ssize_t lgetxattrat (int dir_fd, const char *path, const char *name,
+ void *value, size_t size);
+
+/* dir-fd-relative listxattr. Obtain the list of extended attrubtes names. For
+ more info see the listxattr(2) manpage. */
+ssize_t listxattrat (int dir_fd, const char *path, char *list, size_t size);
+
+/* dir-fd-relative llistxattr. This function is just like listxattrat,
+ except when DIR_FD and FILE specify a symlink: llistxattr operates on the
+ symlink, while the listxattrat operates on the referent of the symlink. */
+ssize_t llistxattrat (int dir_fd, const char *path, char *list, size_t size);
+
+#endif /* XATTRS_AT_H */
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 2409413..16ba401 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;
@@ -254,6 +259,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;
@@ -708,6 +722,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);
@@ -728,6 +745,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 */
@@ -809,6 +832,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 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 662ea0b..87b383a 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))
@@ -678,6 +735,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
@@ -738,12 +829,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);
}
}
@@ -859,7 +961,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;
@@ -869,6 +972,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;
@@ -939,6 +1046,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;
@@ -955,8 +1064,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);
@@ -1096,6 +1215,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;
@@ -1530,6 +1656,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;
@@ -1544,6 +1677,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 7a673e0..e244808 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,
@@ -341,7 +347,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 +539,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 },
@@ -2091,6 +2122,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;
@@ -2468,11 +2531,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;
@@ -2681,6 +2759,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)
@@ -2725,11 +2804,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 ee9d684..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
};
@@ -69,6 +70,7 @@ static int warning_types[] = {
WARN_XDEV,
WARN_DECOMPRESS_PROGRAM,
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..5a4bf72
--- /dev/null
+++ b/src/xattrs.c
@@ -0,0 +1,732 @@
+/* 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 <config.h>
+#include <system.h>
+
+#include <fnmatch.h>
+#include <quotearg.h>
+
+#include "common.h"
+
+#include "xattr-at.h"
+#include "selinux-at.h"
+
+struct xattrs_mask_map
+{
+ const char **masks;
+ size_t size;
+ size_t used;
+};
+
+/* list of fnmatch patterns */
+static struct
+{
+ /* lists of fnmatch patterns */
+ struct xattrs_mask_map incl;
+ struct xattrs_mask_map excl;
+} xattrs_setup;
+
+/* 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
+
+#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)
+{
+ /* skip tag name (user/group/default/mask) */
+ ptr += strcspn (ptr, ":,\n");
+
+ if (*ptr != ':')
+ return ptr;
+ ++ptr;
+
+ ptr += strcspn (ptr, ":,\n"); /* skip user/group name */
+
+ if (*ptr != ':')
+ return ptr;
+ ++ptr;
+
+ ptr += strcspn (ptr, ":,\n"); /* skip perms */
+
+ 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.
+ This function removes such extra fields by overwriting them with the
+ characters that follow. */
+static char *
+fixup_extra_acl_fields (char *ptr)
+{
+ char *src = ptr;
+ char *dst = 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;
+}
+
+/* "system.posix_acl_access" */
+static void
+xattrs__acls_set (struct tar_stat_info const *st,
+ char const *file_name, int type,
+ char *ptr, size_t len, bool def)
+{
+ 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)
+ {
+ 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)
+{
+ char *val = NULL;
+ ssize_t len;
+ acl_t acl;
+
+ if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS)))
+ {
+ if (errno != ENOTSUP)
+ call_arg_warn ("acl_get_file_at", file_name);
+ return;
+ }
+
+ val = acl_to_text (acl, &len);
+ acl_free (acl);
+
+ if (!val)
+ {
+ call_arg_warn ("acl_to_text", file_name);
+ return;
+ }
+
+ *ret_ptr = xstrdup (val);
+ *ret_len = len;
+
+ acl_free (val);
+}
+
+/* "system.posix_acl_default" */
+static void
+xattrs__acls_get_d (int parentfd, char const *file_name,
+ struct tar_stat_info *st,
+ char **ret_ptr, size_t * ret_len)
+{
+ char *val = NULL;
+ ssize_t len;
+ acl_t acl;
+
+ if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT)))
+ {
+ if (errno != ENOTSUP)
+ call_arg_warn ("acl_get_file_at", file_name);
+ return;
+ }
+
+ val = acl_to_text (acl, &len);
+ acl_free (acl);
+
+ if (!val)
+ {
+ 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
+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;
+ int pref_len = strlen (prefix);
+ const char *oldstring = aclstring;
+ int pos = 0;
+
+ if (!aclstring || !len)
+ return;
+
+ obstack_init (&stk);
+ 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');
+
+ fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
+
+ obstack_free (&stk, NULL);
+}
+
+void
+xattrs_acls_get (int parentfd, char const *file_name,
+ struct tar_stat_info *st, int fd, int xisfile)
+{
+ 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
+ int 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
+ }
+}
+
+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
+ }
+}
+
+static void
+mask_map_realloc (struct xattrs_mask_map *map)
+{
+ if (map->used == map->size)
+ {
+ if (map->size == 0)
+ map->size = 4;
+ map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
+ }
+}
+
+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 size_t xsz = 1024;
+ static char *xatrs = NULL;
+ ssize_t xret = -1;
+
+ if (!xatrs)
+ xatrs = x2nrealloc (xatrs, &xsz, 1);
+
+ while (((fd == 0) ?
+ ((xret =
+ llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
+ ((xret = flistxattr (fd, xatrs, xsz)) == -1))
+ && (errno == ERANGE))
+ {
+ xatrs = x2nrealloc (xatrs, &xsz, 1);
+ }
+
+ if (xret == -1)
+ call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
+ else
+ {
+ const char *attr = xatrs;
+ static size_t asz = 1024;
+ static char *val = NULL;
+
+ if (!val)
+ val = x2nrealloc (val, &asz, 1);
+
+ 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))
+ {
+ val = x2nrealloc (val, &asz, 1);
+ }
+
+ 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));
+ }
+}
+
+/* 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;
+}
+
+#define USER_DOT_PFX "user."
+
+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_DOT_PFX, sizeof (USER_DOT_PFX) - 1) == 0;
+}
+
+static bool
+xattrs_kw_excluded (const char *kw, bool archiving)
+{
+ return xattrs_setup.excl.size ?
+ xattrs_matches_mask (kw, &xattrs_setup.excl) : false;
+}
+
+/* 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)
+{
+ return xattrs_kw_included (kw, archiving) ?
+ xattrs_kw_excluded (kw, archiving) : true;
+}
+
+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 */ ))
+ {
+ *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 */ ))
+ 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..bfef466
--- /dev/null
+++ b/src/xattrs.h
@@ -0,0 +1,51 @@
+/* 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 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef GUARD_XATTTRS_H
+#define GUARD_XATTTRS_H
+
+/* 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..be793d4 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,123 @@ 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;
+}
+
+/* This is reversal function for xattr_encode_keyword. See comment for
+ xattr_encode_keyword() for more info. */
+static void
+xattr_decode_keyword (char *keyword)
+{
+ char *kpr, *kpl; /* keyword pointer left/right */
+ kpr = kpl = keyword;
+
+ for (;;)
+ {
+ if (*kpr == '%')
+ {
+ if (kpr[1] == '3' && kpr[2] == 'D')
+ {
+ *kpl = '=';
+ kpr += 3;
+ kpl ++;
+ continue;
+ }
+ else if (kpr[1] == '2' && kpr[2] == '5')
+ {
+ *kpl = '%';
+ kpr += 3;
+ kpl ++;
+ continue;
+ }
+ }
+
+ *kpl = *kpr;
+
+ if (*kpr == 0)
+ break;
+
+ kpr++;
+ kpl++;
+ }
+}
+
+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 +590,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 +607,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 +627,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 +639,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;
}
@@ -721,15 +850,71 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size)
while (size > 0);
}
+/* xattr_encode_keyword() substitutes '=' ~~> '%3D' and '%' ~~> '%25'
+ in extended attribute keywords. This is needed because the '=' character
+ has special purpose in extended attribute header - it splits keyword and
+ value part of header. If there was the '=' occurrence allowed inside
+ keyword, there would be no unambiguous way how to decode this extended
+ attribute.
+
+ (http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html)
+ */
+static char *
+xattr_encode_keyword(const char *keyword)
+{
+ static char *encode_buffer = NULL;
+ static size_t encode_buffer_size = 0;
+ size_t bp; /* keyword/buffer pointers */
+
+ if (!encode_buffer)
+ {
+ encode_buffer_size = 256;
+ encode_buffer = xmalloc (encode_buffer_size);
+ }
+ else
+ *encode_buffer = 0;
+
+ for (bp = 0; *keyword != 0; ++bp, ++keyword)
+ {
+ char c = *keyword;
+
+ if (bp + 2 /* enough for URL encoding also.. */ >= encode_buffer_size)
+ {
+ encode_buffer = x2realloc (encode_buffer, &encode_buffer_size);
+ }
+
+ if (c == '%')
+ {
+ strcpy (encode_buffer + bp, "%25");
+ bp += 2;
+ }
+ else if (c == '=')
+ {
+ strcpy (encode_buffer + bp, "%3D");
+ bp += 2;
+ }
+ else
+ encode_buffer[bp] = c;
+ }
+
+ encode_buffer[bp] = 0;
+
+ return encode_buffer;
+}
+
static void
xheader_print_n (struct xheader *xhdr, char const *keyword,
char const *value, size_t vsize)
{
- size_t len = strlen (keyword) + vsize + 3; /* ' ' + '=' + '\n' */
size_t p;
size_t n = 0;
char nbuf[UINTMAX_STRSIZE_BOUND];
char const *np;
+ size_t len, klen;
+
+ keyword = xattr_encode_keyword (keyword);
+ klen = strlen (keyword);
+ len = klen + vsize + 3; /* ' ' + '=' + '\n' */
do
{
@@ -741,7 +926,7 @@ xheader_print_n (struct xheader *xhdr, char const *keyword,
x_obstack_grow (xhdr, np, n);
x_obstack_1grow (xhdr, ' ');
- x_obstack_grow (xhdr, keyword, strlen (keyword));
+ x_obstack_grow (xhdr, keyword, klen);
x_obstack_1grow (xhdr, '=');
x_obstack_grow (xhdr, value, vsize);
x_obstack_1grow (xhdr, '\n');
@@ -1002,8 +1187,6 @@ decode_time (struct timespec *ts, char const *arg, char const *keyword)
return true;
}
-
-
static void
code_num (uintmax_t value, char const *keyword, struct xheader *xhdr)
{
@@ -1470,6 +1653,80 @@ 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, *xkey;
+
+ /* copy keyword */
+ size_t klen_raw = strlen (keyword);
+ xkey = alloca (klen_raw + 1);
+ memcpy (xkey, keyword, klen_raw + 1) /* including null-terminating */;
+
+ /* copy value */
+ xstr = alloca (size + 1);
+ memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */;
+
+ xattr_decode_keyword (xkey);
+
+ xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size);
+}
+
+static void
sparse_major_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void const *data)
{
@@ -1506,53 +1763,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 +1818,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 3d78ea2..b0da439 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -171,7 +171,17 @@ 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\
+ xattr05.at\
+ acls01.at\
+ acls02.at\
+ selnx01.at\
+ selacl01.at\
+ capabs_raw01.at
TESTSUITE = $(srcdir)/testsuite
diff --git a/tests/acls01.at b/tests/acls01.at
new file mode 100644
index 0000000..0149f2d
--- /dev/null
+++ b/tests/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/acls02.at b/tests/acls02.at
new file mode 100644
index 0000000..2ee1c5f
--- /dev/null
+++ b/tests/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/capabs_raw01.at b/tests/capabs_raw01.at
new file mode 100644
index 0000000..8eea0cf
--- /dev/null
+++ b/tests/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/selacl01.at b/tests/selacl01.at
new file mode 100644
index 0000000..90d0c5b
--- /dev/null
+++ b/tests/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/selnx01.at b/tests/selnx01.at
new file mode 100644
index 0000000..79f7267
--- /dev/null
+++ b/tests/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/testsuite.at b/tests/testsuite.at
index e43653e..8d5811d 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -81,13 +81,6 @@ m4_define([AT_GZIP_PREREQ],[
cat /dev/null | m4_if([$1],[],gzip,[$1]) - > /dev/null 2>&1 || AT_SKIP_TEST
])
-dnl AT_SIGPIPE_PREREQ - Skip test unless SIGPIPE handling is the default
-m4_define([AT_SIGPIPE_PREREQ],[
-case `(cat "$at_myself" 2>&3 | :) 3>&1 >/dev/null` in #(
-?*) AT_SKIP_TEST;;
-esac
-])
-
dnl AT_SORT_PREREQ - Skip test if sort utility outputs unwanted data on stderr
m4_define([AT_SORT_PREREQ],[
test -z "`sort < /dev/null 2>&1`" || AT_SKIP_TEST
@@ -103,10 +96,86 @@ rm -f $[]$
test $result -eq 0 && AT_SKIP_TEST
])
+dnl AT_SIGPIPE_PREREQ - Skip test unless SIGPIPE handling is the default
+m4_define([AT_SIGPIPE_PREREQ],[
+case `(cat "$at_myself" 2>&3 | :) 3>&1 >/dev/null` in #(
+?*) AT_SKIP_TEST;;
+esac
+])
+
+dnl AT_PRIVILEGED_PREREQ - Skip test if not running at root privileges
+m4_define([AT_PRIVILEGED_PREREQ],[
+echo "test" > $[]$
+chmod 0 $[]$
+cat $[]$ > /dev/null 2>&1
+result=$?
+rm -f $[]$
+test $result -eq 0 || 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])
+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
+])
+
m4_include([sparsemvp.at])
AT_INIT
@@ -264,6 +334,20 @@ m4_include([remfiles03.at])
m4_include([sigpipe.at])
+m4_include([xattr01.at])
+m4_include([xattr02.at])
+m4_include([xattr03.at])
+m4_include([xattr04.at])
+m4_include([xattr05.at])
+
+m4_include([acls01.at])
+m4_include([acls02.at])
+
+m4_include([selnx01.at])
+m4_include([selacl01.at])
+
+m4_include([capabs_raw01.at])
+
m4_include([star/gtarfail.at])
m4_include([star/gtarfail2.at])
@@ -273,3 +357,4 @@ m4_include([star/ustar-big-2g.at])
m4_include([star/ustar-big-8g.at])
m4_include([star/pax-big-10g.at])
+
diff --git a/tests/xattr01.at b/tests/xattr01.at
new file mode 100644
index 0000000..fd960d5
--- /dev/null
+++ b/tests/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/xattr02.at b/tests/xattr02.at
new file mode 100644
index 0000000..3aae3f9
--- /dev/null
+++ b/tests/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/xattr03.at b/tests/xattr03.at
new file mode 100644
index 0000000..d834f9f
--- /dev/null
+++ b/tests/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/xattr04.at b/tests/xattr04.at
new file mode 100644
index 0000000..31832af
--- /dev/null
+++ b/tests/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
diff --git a/tests/xattr05.at b/tests/xattr05.at
new file mode 100644
index 0000000..27dc469
--- /dev/null
+++ b/tests/xattr05.at
@@ -0,0 +1,49 @@
+# 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 archiving/extracting of extended attributes
+# having the '=' character in its keyword.
+#
+# Relevant mailing list thread:
+#
+# http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html
+
+AT_SETUP([xattrs: keywords with '=' and '%'])
+AT_KEYWORDS([xattrs xattr05])
+
+AT_TAR_CHECK([
+AT_XATTRS_PREREQ
+
+mkdir dir
+mkdir output
+genfile --file dir/file
+
+setfattr -n user.=NAME%3D= -v value dir/file
+getfattr -d dir/file | grep -v '# ' > before
+
+# archive whole directory including binary xattrs
+tar --xattrs -cf archive.tar -C dir .
+
+tar --xattrs -xf archive.tar -C output
+getfattr -d output/file | grep -v '# ' > after
+diff before after
+],
+[0],
+[])
+
+AT_CLEANUP