diff --git a/tar-1.24-xattrs.patch b/tar-1.24-xattrs.patch deleted file mode 100644 index cb147b6..0000000 --- a/tar-1.24-xattrs.patch +++ /dev/null @@ -1,1640 +0,0 @@ - configure.ac | 20 ++- - doc/tar.texi | 75 +++++++++ - src/Makefile.am | 7 +- - src/common.h | 18 ++ - src/create.c | 54 ++++++- - src/extract.c | 123 ++++++++++++++ - src/list.c | 7 + - src/tar.c | 80 +++++++++- - src/tar.h | 20 +++ - src/xattrs.c | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ - src/xattrs.h | 14 ++ - src/xheader.c | 237 +++++++++++++++++++++++---- - 12 files changed, 1103 insertions(+), 41 deletions(-) - -diff -urNp tar-1.24-orig/configure.ac tar-1.24/configure.ac ---- tar-1.24-orig/configure.ac 2010-10-24 23:35:35.000000000 +0200 -+++ tar-1.24/configure.ac 2010-10-25 10:24:52.548214037 +0200 -@@ -44,7 +44,7 @@ AC_CHECK_HEADERS_ONCE(fcntl.h linux/fd.h - sys/param.h sys/device.h sys/gentape.h \ - sys/inet.h sys/io/trioctl.h \ - sys/mtio.h sys/time.h sys/tprintf.h sys/tape.h \ -- unistd.h locale.h) -+ unistd.h locale.h attr/xattr.h sys/acl.h) - - AC_CHECK_HEADERS([sys/buf.h], [], [], - [#if HAVE_SYS_PARAM_H -@@ -91,6 +91,12 @@ gl_INIT - tar_PAXUTILS - - AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink]) -+AC_CHECK_FUNCS(getxattr fgetxattr lgetxattr \ -+ setxattr fsetxattr lsetxattr \ -+ listxattr flistxattr llistxattr, -+ AC_DEFINE(HAVE_XATTRS,,[Define if we have a working extended attributes]),) -+AC_CHECK_LIB(acl, acl_get_fd) -+ - AC_CHECK_DECLS([getgrgid],,, [#include ]) - AC_CHECK_DECLS([getpwuid],,, [#include ]) - AC_CHECK_DECLS([time],,, [#include ]) -@@ -214,6 +220,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_QUOTING_STYLE - # Iconv - AM_ICONV - AC_CHECK_HEADERS(iconv.h) -+AC_CHECK_HEADERS(attr/xattr.h) - AC_CHECK_TYPE(iconv_t,:, - AC_DEFINE(iconv_t, int, - [Conversion descriptor type]), -@@ -223,6 +230,17 @@ AC_CHECK_TYPE(iconv_t,:, - #endif - ]) - -+AC_ARG_ENABLE(selinux, -+ AC_HELP_STRING([--enable-selinux], -+ [enable SELinux support (disabled by default)]), -+ [selinux_enabled=$enableval], -+ [selinux_enabled=no]) -+ -+if test "x$selinux_enabled" = xyes; then -+ AC_CHECK_LIB(selinux, getfilecon) -+ AC_CHECK_HEADERS(selinux/selinux.h) -+fi -+ - # Gettext. - AM_GNU_GETTEXT([external], [need-formatstring-macros]) - AM_GNU_GETTEXT_VERSION([0.16]) -diff -urNp tar-1.24-orig/doc/tar.texi tar-1.24/doc/tar.texi ---- tar-1.24-orig/doc/tar.texi 2010-10-24 20:07:54.000000000 +0200 -+++ tar-1.24/doc/tar.texi 2010-10-25 10:24:52.554213688 +0200 -@@ -2370,6 +2370,10 @@ Normally when creating an archive, @comm - @samp{/} from member names. This option disables that behavior. - @xref{absolute}. - -+@opsummary{acl} -+@item --acls -+Causes @command{tar} to store ACL's. @xref{Attributes}. -+ - @opsummary{after-date} - @item --after-date - -@@ -2914,6 +2918,10 @@ 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-acl} -+@item --no-acls -+Causes @command{tar} not to store and not to extract ACL's. @xref{Attributes}. -+ - @opsummary{no-anchored} - @item --no-anchored - An exclude pattern can match any subsequence of the name's components. -@@ -2997,11 +3005,21 @@ 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 and not to extract 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 and not to extract xattrs. This option also -+enables @option{--no-selinux} and @option{--no-acls}. @xref{Attributes}. -+ - @opsummary{no-wildcards} - @item --no-wildcards - Do not use wildcards. -@@ -3234,6 +3252,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 SElinux security context. @xref{Attributes}. -+ -+ - @opsummary{show-defaults} - @item --show-defaults - -@@ -3447,6 +3470,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 xattrs. This option also enables -+@option{--selinux} and @option{--acls}. @xref{Attributes}. -+ - @opsummary{wildcards} - @item --wildcards - Use wildcards when matching member names with patterns. -@@ -8659,6 +8687,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 +9323,51 @@ Same as both @option{--same-permissions} and @option{--same-order}. - - This option is deprecated, and will be removed in @GNUTAR{} version 1.23. - -+@opindex acls -+@item --acls -+This option causes @command{tar} to store the current ACL in the archive. -+ -+The @option{--acls} option has no equivalent short option name. -+ -+@opindex selinux -+@item --selinux -+This option causes @command{tar} to store the current SELinux security context -+information in the archive. -+ -+The @option{--selinux} option has no equivalent short option name. -+ -+@opindex xattrs -+@item --xattrs -+This option causes @command{tar} to store the current extended attributes in -+the archive. This option also enables @option{--acls} and @option{--selinux} if -+they haven't been set already. -+ -+The @option{--xattrs} option has no equivalent short option name. -+ -+@opindex no-acls -+@item --no-acls -+This option causes @command{tar} not to store the current ACL in the archive -+and not to extract any ACL information in an archive. -+ -+The @option{--no-acls} 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 no-xattrs -+@item --no-xattrs -+This option causes @command{tar} not to store the current extended attributes in -+the archive and not to extract any extended attributes in an archive. This -+option also enables @option{--no-acls} and @option{--no-selinux} if -+they haven't been set already. -+ -+The @option{--no-xattrs} option has no equivalent short option name. -+ - @end table - - @node Portability -diff -urNp tar-1.24-orig/src/common.h tar-1.24/src/common.h ---- tar-1.24-orig/src/common.h 2010-10-24 20:07:54.000000000 +0200 -+++ tar-1.24/src/common.h 2010-10-25 10:24:52.558475456 +0200 -@@ -253,6 +253,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 +716,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 +739,12 @@ bool xheader_string_end (struct xheader *xhdr, char const *keyword); - bool xheader_keyword_deleted_p (const char *kw); - char *xheader_format_name (struct tar_stat_info *st, const char *fmt, - size_t n); -+void xheader_xattr_init(struct tar_stat_info *st); -+void xheader_xattr_free(struct xattr_array *vals, size_t sz); -+void xheader_xattr_copy(const struct tar_stat_info *st, -+ struct xattr_array **vals, size_t *sz); -+void xheader_xattr_add(struct tar_stat_info *st, -+ const char *key, const char *val, size_t len); - - /* Module system.c */ - -diff -urNp tar-1.24-orig/src/create.c tar-1.24/src/create.c ---- tar-1.24-orig/src/create.c 2010-10-24 20:07:54.000000000 +0200 -+++ tar-1.24/src/create.c 2010-10-25 10:24:52.560213618 +0200 -@@ -24,6 +24,7 @@ - #include - - #include "common.h" -+ - #include - - /* Error number to use when an impostor is discovered. -@@ -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; - } - -@@ -1710,6 +1735,14 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p) - { - bool ok; - struct stat final_stat; -+ int fd_xattr = fd; -+ if (!fd_xattr) -+ // upstream tar now uses zero fd as "no fd" -+ fd_xattr = -1; -+ -+ xattrs_acls_get(st, p, fd_xattr, !is_dir); -+ xattrs_selinux_get(st, p, fd_xattr); -+ xattrs_xattrs_get(st, p, fd_xattr); - - if (is_dir) - { -@@ -1829,6 +1862,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(st, p, -1); -+ xattrs_xattrs_get(st, p, -1); -+ - block_ordinal = current_block_ordinal (); - st->stat.st_size = 0; /* force 0 size on symlink */ - header = start_header (st); -@@ -1847,11 +1883,23 @@ 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_selinux_get(st, p, -1); -+ xattrs_xattrs_get(st, p, -1); -+ } - else if (S_ISBLK (st->stat.st_mode)) -- type = BLKTYPE; -+ { -+ type = BLKTYPE; -+ xattrs_selinux_get(st, p, -1); -+ xattrs_xattrs_get(st, p, -1); -+ } - else if (S_ISFIFO (st->stat.st_mode)) -- type = FIFOTYPE; -+ { -+ type = FIFOTYPE; -+ xattrs_selinux_get(st, p, -1); -+ xattrs_xattrs_get(st, p, -1); -+ } - else if (S_ISSOCK (st->stat.st_mode)) - { - WARNOPT (WARN_FILE_IGNORED, -diff -urNp tar-1.24-orig/src/extract.c tar-1.24/src/extract.c ---- tar-1.24-orig/src/extract.c 2010-10-24 20:07:54.000000000 +0200 -+++ tar-1.24/src/extract.c 2010-10-25 10:35:10.903214037 +0200 -@@ -97,6 +97,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; /* Size of the xattr map */ -+ struct xattr_array *xattr_map; - /* Length and contents of name. */ - size_t file_name_len; - char file_name[1]; -@@ -134,6 +142,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; /* Size of the xattr map */ -+ struct xattr_array *xattr_map; -+ - /* The desired target of the desired link. */ - char target[1]; - }; -@@ -360,6 +380,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_acls_set(st, file_name, typeflag); -+ xattrs_selinux_set(st, file_name, typeflag); -+ xattrs_xattrs_set(st, file_name, typeflag); - } - - /* For each entry H in the leading prefix of entries in HEAD that do -@@ -431,6 +455,36 @@ delay_set_stat (char const *file_name, s - 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 +727,31 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) - return RECOVER_NO; - } - -+/* Restore stat extended attributes (xattr) for FILE_NAME, using information -+ given in *ST. Restore before extraction because they may affect layout. -+ If not restoring permissions, invert the -+ INVERT_PERMISSIONS bits from the file's current permissions. -+ TYPEFLAG specifies the type of the file. -+ FILE_CREATED indicates set_xattr has created the file */ -+static int -+set_xattr (char const *file_name, struct tar_stat_info const *st, -+ mode_t invert_permissions, char typeflag, int *file_created) -+{ -+ int status = 0; -+ bool interdir_made = false; -+ -+ if ((xattrs_option >= 0) && st->xattr_map_size) { -+ mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; -+ -+ do -+ status = mknod (file_name, mode ^ invert_permissions, 0); -+ while (status && maybe_recoverable ((char *)file_name, false, &interdir_made)); -+ xattrs_xattrs_set(st, file_name, typeflag); -+ *file_created = 1; -+ } -+ return(status); -+} -+ - /* Fix the statuses of all directories whose statuses need fixing, and - which are not ancestors of FILE_NAME. If AFTER_LINKS is - nonzero, do this for all such directories; otherwise, stop at the -@@ -733,12 +812,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,6 +944,7 @@ extract_dir (char *file_name, int typeflag) - - static int - open_output_file (char const *file_name, int typeflag, mode_t mode, -+ int file_created, - mode_t *current_mode, mode_t *current_mode_mask) - { - int fd; -@@ -864,6 +955,10 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, - ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW) - : O_EXCL)); - -+ /* File might be created in set_xattr. So clear O_EXCL to avoid open() failure */ -+ if (file_created) -+ openflag = openflag & ~O_EXCL; -+ - if (typeflag == CONTTYPE) - { - static int conttype_diagnosed; -@@ -934,6 +1029,7 @@ extract_file (char *file_name, int typeflag) - bool interdir_made = false; - mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX - & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0)); -+ mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; - mode_t current_mode = 0; - mode_t current_mode_mask = 0; - -@@ -950,7 +1046,17 @@ extract_file (char *file_name, int typeflag) - } - else - { -+ int file_created = 0; -+ if (set_xattr (file_name, ¤t_stat_info, invert_permissions, -+ typeflag, &file_created)) -+ { -+ skip_member (); -+ open_error (file_name); -+ return 1; -+ } -+ - while ((fd = open_output_file (file_name, typeflag, mode, -+ file_created, - ¤t_mode, ¤t_mode_mask)) - < 0) - { -@@ -1091,6 +1197,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 (¤t_stat_info, &p->xattr_map, &p->xattr_map_size); - strcpy (p->target, current_stat_info.link_name); - - h = delayed_set_stat_head; -@@ -1525,6 +1638,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 +1659,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 -urNp tar-1.24-orig/src/list.c tar-1.24/src/list.c ---- tar-1.24-orig/src/list.c 2010-10-25 09:15:14.216463863 +0200 -+++ tar-1.24/src/list.c 2010-10-25 10:24:52.563213968 +0200 -@@ -615,6 +615,13 @@ decode_header (union block *header, struct tar_stat_info *stat_info, - assign_string (&stat_info->gname, - header->header.gname[0] ? header->header.gname : NULL); - -+ stat_info->acls_a_ptr = NULL; -+ stat_info->acls_a_len = 0; -+ stat_info->acls_d_ptr = NULL; -+ stat_info->acls_d_len = 0; -+ stat_info->cntx_name = NULL; -+ xheader_xattr_init(stat_info); -+ - if (format == OLDGNU_FORMAT && incremental_option) - { - stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime); -diff -urNp tar-1.24-orig/src/Makefile.am tar-1.24/src/Makefile.am ---- tar-1.24-orig/src/Makefile.am 2010-10-24 20:07:54.000000000 +0200 -+++ tar-1.24/src/Makefile.am 2010-10-25 10:24:52.564214456 +0200 -@@ -20,7 +20,7 @@ - - bin_PROGRAMS = tar - --noinst_HEADERS = arith.h common.h tar.h -+noinst_HEADERS = arith.h common.h tar.h xattrs.h - tar_SOURCES = \ - buffer.c\ - checkpoint.c\ -@@ -42,10 +42,11 @@ tar_SOURCES = \ - unlink.c\ - update.c\ - utf8.c\ -- warning.c -+ warning.c\ -+ xattrs.c - - INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib - - LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV) - --tar_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) -+tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) -diff -urNp tar-1.24-orig/src/tar.c tar-1.24/src/tar.c ---- tar-1.24-orig/src/tar.c 2010-10-24 20:07:55.000000000 +0200 -+++ tar-1.24/src/tar.c 2010-10-25 10:24:52.565223676 +0200 -@@ -255,7 +255,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 +289,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 +303,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 +329,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 +345,8 @@ enum - VOLNO_FILE_OPTION, - WARNING_OPTION, - WILDCARDS_MATCH_SLASH_OPTION, -- WILDCARDS_OPTION -+ WILDCARDS_OPTION, -+ XATTR_OPTION - }; - - const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION; -@@ -486,6 +492,10 @@ static struct argp_option options[] = { - {NULL, 0, NULL, 0, - N_("Handling of file attributes:"), GRID }, - -+ {"acls", ACLS_OPTION, 0, 0, -+ N_("Save the ACLs to the archive"), GRID+1 }, -+ {"no-acls", NO_ACLS_OPTION, 0, 0, -+ N_("Don't extract the ACLs from the archive"), GRID+1 }, - {"owner", OWNER_OPTION, N_("NAME"), 0, - N_("force NAME as owner for added files"), GRID+1 }, - {"group", GROUP_OPTION, N_("NAME"), 0, -@@ -516,6 +526,14 @@ static struct argp_option options[] = { - {"preserve-order", 's', 0, 0, - N_("sort names to extract to match archive"), GRID+1 }, - {"same-order", 0, 0, OPTION_ALIAS, NULL, GRID+1 }, -+ {"selinux", SELINUX_CONTEXT_OPTION, 0, 0, -+ N_("Save the SELinux context to the archive"), GRID+1 }, -+ {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0, -+ N_("Don't extract the SELinux context from the archive"), GRID+1 }, -+ {"xattrs", XATTR_OPTION, 0, 0, -+ N_("Save the user/root xattrs to the archive"), GRID+1 }, -+ {"no-xattrs", NO_XATTR_OPTION, 0, 0, -+ N_("Don't extract the user/root xattrs from the archive"), GRID+1 }, - {"preserve", PRESERVE_OPTION, 0, 0, - N_("same as both -p and -s"), GRID+1 }, - {"delay-directory-restore", DELAY_DIRECTORY_RESTORE_OPTION, 0, 0, -@@ -2079,6 +2097,37 @@ parse_opt (int key, char *arg, struct ar - 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"); -+ if (!acls_option) acls_option = 1; -+ if (!selinux_context_option) selinux_context_option = 1; -+ xattrs_option = 1; -+ break; -+ -+ case NO_XATTR_OPTION: -+ if (!acls_option) acls_option = -1; -+ if (!selinux_context_option) selinux_context_option = -1; -+ xattrs_option = -1; -+ break; -+ - case RECURSION_OPTION: - recursion_option = FNM_LEADING_DIR; - break; -@@ -2461,6 +2510,29 @@ decode_options (int argc, char **argv) - || subcommand_option != LIST_SUBCOMMAND)) - USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives"))); - -+ /* star create's non-POSIX typed archives with xattr support, so allow the -+ extra headers */ -+ if ((acls_option > 0) -+ && archive_format != POSIX_FORMAT -+ && (subcommand_option != EXTRACT_SUBCOMMAND -+ || subcommand_option != DIFF_SUBCOMMAND -+ || subcommand_option != LIST_SUBCOMMAND)) -+ USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives"))); -+ -+ if ((selinux_context_option > 0) -+ && archive_format != POSIX_FORMAT -+ && (subcommand_option != EXTRACT_SUBCOMMAND -+ || subcommand_option != DIFF_SUBCOMMAND -+ || subcommand_option != LIST_SUBCOMMAND)) -+ USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives"))); -+ -+ if ((xattrs_option > 0) -+ && archive_format != POSIX_FORMAT -+ && (subcommand_option != EXTRACT_SUBCOMMAND -+ || subcommand_option != DIFF_SUBCOMMAND -+ || subcommand_option != LIST_SUBCOMMAND)) -+ USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives"))); -+ - /* If ready to unlink hierarchies, so we are for simpler files. */ - if (recursive_unlink_option) - old_files_option = UNLINK_FIRST_OLD_FILES; -@@ -2713,11 +2785,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 -urNp tar-1.24-orig/src/tar.h tar-1.24/src/tar.h ---- tar-1.24-orig/src/tar.h 2010-10-24 20:07:46.000000000 +0200 -+++ tar-1.24/src/tar.h 2010-10-25 10:24:52.567223606 +0200 -@@ -276,6 +276,14 @@ struct xheader - uintmax_t string_length; - }; - -+/* Information about xattrs for a file. */ -+struct xattr_array -+ { -+ char *xkey; -+ char *xval_ptr; -+ size_t xval_len; -+ }; -+ - struct tar_stat_info - { - char *orig_file_name; /* name of file read from the archive header */ -@@ -287,6 +295,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 +326,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 -urNp tar-1.24-orig/src/xattrs.c tar-1.24/src/xattrs.c ---- tar-1.24-orig/src/xattrs.c 1970-01-01 01:00:00.000000000 +0100 -+++ tar-1.24/src/xattrs.c 2010-10-25 10:24:52.568214736 +0200 -@@ -0,0 +1,490 @@ -+/* Create a tar archive. -+ -+ Copyright (C) 2006 Free Software Foundation, Inc. -+ -+ Written by James Antill, on 2006-07-27. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 2, or (at your option) any later -+ version. -+ -+ This program is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -+ Public License for more details. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -+ -+#include -+ -+#include -+ -+#include "common.h" -+ -+ -+#ifndef HAVE_SELINUX_SELINUX_H -+# undef HAVE_LIBSELINUX -+#endif -+ -+#ifndef HAVE_ATTR_XATTR_H -+# undef HAVE_XATTRS -+#endif -+ -+#ifndef HAVE_SYS_ACL_H -+# undef HAVE_LIBACL -+#endif -+ -+#ifdef HAVE_SELINUX_SELINUX_H -+# include -+#endif -+ -+#ifdef HAVE_ATTR_XATTR_H -+# include -+#endif -+ -+#ifdef HAVE_SYS_ACL_H -+# include -+#endif -+ -+ -+#if 0 /* unused by xattr's atm. */ -+static void xattrs__fd_get(struct tar_stat_info *st, -+ char const *file_name, int fd, const char *attr, -+ char **ret_ptr, size_t *ret_len) -+{ -+#ifdef HAVE_XATTRS -+ static ssize_t asz = 1024; -+ ssize_t ret = 0; -+ static char *val = NULL; -+ -+ if (!val) val = xmalloc (asz); -+ -+ while (((ret = fgetxattr (fd, attr, val, asz)) == -1) && -+ (errno == ERANGE)) -+ { -+ asz <<= 1; -+ val = xrealloc (val, asz); -+ } -+ -+ if (ret != -1) -+ { -+ *ret_ptr = xmemdup (val, ret + 1); -+ *ret_len = ret; -+ } -+ else if (errno != ENOATTR) -+ call_arg_warn ("fgetxattr", file_name); -+#endif -+} -+#endif -+ -+static void xattrs__acls_get_a(struct tar_stat_info *st, -+ char const *file_name, int fd, -+ char **ret_ptr, size_t *ret_len) -+{ /* "system.posix_acl_access" */ -+#ifdef HAVE_LIBACL -+ char *val = NULL; -+ ssize_t len; -+ acl_t acl; -+ -+ if (fd != -1) -+ { -+ if ((acl = acl_get_fd (fd)) == (acl_t)NULL) -+ { -+ if (errno != ENOTSUP) -+ call_arg_warn ("acl_get_fd", file_name); -+ return; -+ } -+ } -+ else if ((acl = acl_get_file (file_name, ACL_TYPE_ACCESS)) == (acl_t)NULL) -+ { -+ if (errno != ENOTSUP) -+ call_arg_warn ("acl_get_file", 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 -+} -+ -+static void xattrs__acls_get_d(struct tar_stat_info *st, -+ char const *file_name, -+ char **ret_ptr, size_t *ret_len) -+{ /* "system.posix_acl_default" */ -+#ifdef HAVE_LIBACL -+ char *val = NULL; -+ ssize_t len; -+ acl_t acl; -+ -+ if ((acl = acl_get_file (file_name, ACL_TYPE_DEFAULT)) == (acl_t)NULL) -+ { -+ if (errno != ENOTSUP) -+ call_arg_warn ("acl_get_file", 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 -+} -+ -+void xattrs_acls_get(struct tar_stat_info *st, char const *file_name, int fd, -+ int xisfile) -+{ -+ if (acls_option > 0) -+ { -+#ifndef HAVE_LIBACL -+ static int done = 0; -+ if (!done) -+ WARN ((0, 0, _("ACL support requested, but not available"))); -+ done = 1; -+#endif -+ xattrs__acls_get_a (st, file_name, fd, -+ &st->acls_a_ptr, &st->acls_a_len); -+ if (!xisfile) -+ xattrs__acls_get_d (st, file_name, -+ &st->acls_d_ptr, &st->acls_d_len); -+ } -+} -+ -+void xattrs_selinux_get(struct tar_stat_info *st, char const *file_name, int fd) -+{ -+ if (selinux_context_option > 0) -+ { -+#ifndef HAVE_LIBSELINUX -+ static int done = 0; -+ if (!done) -+ WARN ((0, 0, _("SELinux support requested, but not available"))); -+ done = 1; -+#else -+ if (fd == -1) -+ { -+ if ((lgetfilecon (file_name, &st->cntx_name) == -1) && (errno != ENOTSUP) && (errno != ENODATA)) -+ call_arg_warn ("lgetfilecon", file_name); -+ } -+ else if ((fgetfilecon (fd, &st->cntx_name) == -1) && (errno != ENOTSUP) && (errno != ENODATA)) -+ call_arg_warn ("fgetfilecon", file_name); -+#endif -+ } -+} -+ -+void xattrs_xattrs_get(struct tar_stat_info *st, char const *file_name, int fd) -+{ -+ if (xattrs_option > 0) -+ { /* get all xattrs ... this include security.* and system.* if -+ available. We filter them here, but we have to filter them -+ in xattrs_xattrs_set() anyway. -+ */ -+ static ssize_t xsz = 1024; -+ static char *xatrs = NULL; -+ ssize_t xret = -1; -+ -+#ifndef HAVE_XATTRS -+ static int done = 0; -+ if ((xattrs_option > 0) && !done) -+ WARN ((0, 0, _("Xattr support requested, but not available"))); -+ done = 1; -+#else -+ -+ if (!xatrs) xatrs = xmalloc (xsz); -+ -+ while (((fd == -1) ? -+ ((xret = llistxattr (file_name, xatrs, xsz)) == -1) : -+ ((xret = flistxattr (fd, xatrs, xsz)) == -1)) && -+ (errno == ERANGE)) -+ { -+ xsz <<= 1; -+ xatrs = xrealloc (xatrs, xsz); -+ } -+ -+ if (xret == -1) -+ call_arg_warn ((fd == -1) ? "llistxattrs" : "flistxattrs", file_name); -+ else -+ { -+ const char *attr = xatrs; -+ static ssize_t asz = 1024; -+ static char *val = NULL; -+ -+ if (!val) val = xmalloc (asz); -+ -+ while (xret > 0) -+ { -+ size_t len = strlen (attr); -+ ssize_t aret = 0; -+ -+ /* Archive all xattrs during creation, decide at extraction time -+ * which ones are of interest/use for the target filesystem. */ -+ while (((fd == -1) ? -+ ((aret = lgetxattr (file_name, attr, val, asz)) == -1) : -+ ((aret = fgetxattr (fd, attr, val, asz)) == -1)) && -+ (errno == ERANGE)) -+ { -+ asz <<= 1; -+ val = xrealloc (val, asz); -+ } -+ -+ if (aret != -1) -+ xheader_xattr_add (st, attr, val, aret); -+ else if (errno != ENOATTR) -+ call_arg_warn ((fd==-1) ? "lgetxattr" : "fgetxattr", file_name); -+ -+ attr += len + 1; -+ xret -= len + 1; -+ } -+ } -+#endif -+ } -+} -+ -+static void xattrs__fd_set(struct tar_stat_info const *st, -+ char const *file_name, char typeflag, -+ const char *attr, -+ const char *ptr, size_t len) -+{ -+#ifdef HAVE_XATTRS -+ if (ptr) -+ { -+ const char *sysname = "setxattr"; -+ int ret = -1; -+ -+ if (typeflag != SYMTYPE) -+ ret = setxattr (file_name, attr, ptr, len, 0); -+ else -+ { -+ sysname = "lsetxattr"; -+ ret = lsetxattr (file_name, attr, ptr, len, 0); -+ } -+ -+ /* do not print warnings when SELinux is disabled */ -+ if ((ret == -1) && (errno != EPERM) && (errno != ENOTSUP)) -+ call_arg_error(sysname, file_name); -+ } -+#endif -+} -+ -+/* convert unix permissions into an ACL ... needed due to "default" ACLs */ -+#ifdef HAVE_LIBACL -+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)); -+} -+#endif -+ -+static char *skip_to_ext_fields(char *ptr) -+{ -+ ptr += strcspn(ptr, ":,\n"); /* skip tag name. Ie. user/group/default/mask */ -+ -+ if (*ptr != ':') -+ return (ptr); /* error? no user/group field */ -+ ++ptr; -+ -+ ptr += strcspn(ptr, ":,\n"); /* skip user/group name */ -+ -+ if (*ptr != ':') -+ return (ptr); /* error? no perms field */ -+ ++ptr; -+ -+ ptr += strcspn(ptr, ":,\n"); /* skip perms */ -+ -+ if (*ptr != ':') -+ return (ptr); /* no extra fields */ -+ -+ return (ptr); -+} -+ -+/* 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) -+{ /* "system.posix_acl_access" */ -+#ifdef HAVE_LIBACL -+ 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 (file_name, type, acl) == -1) -+ { -+ if (errno != ENOTSUP) -+ call_arg_warn ("acl_set_file", file_name); -+ } -+ acl_free (acl); -+#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_LIBACL -+ static int done = 0; -+ if (!done) -+ WARN ((0, 0, _("ACL support requested, but not available"))); -+ done = 1; -+#else -+ xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS, -+ st->acls_a_ptr, st->acls_a_len); -+ if ((typeflag == DIRTYPE) || (typeflag == GNUTYPE_DUMPDIR)) -+ xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT, -+ st->acls_d_ptr, st->acls_d_len); -+#endif -+ } -+} -+ -+void xattrs_selinux_set(struct tar_stat_info const *st, -+ char const *file_name, char typeflag) -+{ -+ if ((selinux_context_option >= 0) && st->cntx_name) -+ { -+ const char *sysname = "setfilecon"; -+ int ret = -1; -+ -+#ifndef HAVE_LIBSELINUX -+ static int done = 0; -+ if (!done) -+ WARN ((0, 0, _("SELinux support requested, but not available"))); -+ done = 1; -+#else -+ if (typeflag != SYMTYPE) -+ ret = setfilecon (file_name, st->cntx_name); -+ else -+ { -+ sysname = "lsetfilecon"; -+ ret = lsetfilecon (file_name, st->cntx_name); -+ } -+ -+ if ((ret == -1) && (errno == EPERM)) -+ call_arg_warn(sysname, file_name); -+ else if ((ret == -1) && (errno != EOPNOTSUPP)) -+ call_arg_error(sysname, file_name); -+#endif -+ } -+} -+ -+void xattrs_xattrs_set(struct tar_stat_info const *st, -+ char const *file_name, char typeflag) -+{ -+ if ((xattrs_option >= 0) && st->xattr_map_size) -+ { -+ size_t scan = 0; -+ -+#ifndef HAVE_XATTRS -+ static int done = 0; -+ if (!done) -+ WARN ((0, 0, _("Xattr support requested, but not available"))); -+ done = 1; -+#else -+ while (scan < st->xattr_map_size) -+ { -+ char *keyword = st->xattr_map[scan].xkey; -+ -+ /* assert (!memcpy (keyword, "SCHILY.xattr.", strlen("SCHILY.xattr."))); */ -+ keyword += strlen("SCHILY.xattr."); -+ -+ if (strncmp (keyword, "user.", strlen("user.")) && -+ strncmp (keyword, "lustre.", strlen("lustre.")) && -+ strncmp (keyword, "trusted.", strlen("trusted.")) && -+ strncmp (keyword, "security.NTACL", strlen("security.NTACL")) && -+ strncmp (keyword, "security.capability", strlen("security.capability"))) -+ continue; /* don't try and set anything but normal xattrs */ -+ -+ xattrs__fd_set (st, file_name, typeflag, keyword, -+ st->xattr_map[scan].xval_ptr, -+ st->xattr_map[scan].xval_len); -+ -+ ++scan; -+ } -+#endif -+ } -+} -diff -urNp tar-1.24-orig/src/xattrs.h tar-1.24/src/xattrs.h ---- tar-1.24-orig/src/xattrs.h 1970-01-01 01:00:00.000000000 +0100 -+++ tar-1.24/src/xattrs.h 2010-10-25 10:24:52.569214526 +0200 -@@ -0,0 +1,14 @@ -+ -+extern void xattrs_acls_get(struct tar_stat_info *st, -+ char const *file_name, int fd, int xisfile); -+extern void xattrs_selinux_get(struct tar_stat_info *st, -+ char const *file_name, int fd); -+extern void xattrs_xattrs_get(struct tar_stat_info *st, -+ char const *file_name, 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); -diff -urNp tar-1.24-orig/src/xheader.c tar-1.24/src/xheader.c ---- tar-1.24-orig/src/xheader.c 2010-10-24 20:07:46.000000000 +0200 -+++ tar-1.24/src/xheader.c 2010-10-25 10:24:52.570223396 +0200 -@@ -460,6 +460,74 @@ xheader_write_global (struct xheader *xh - } - } - -+void xheader_xattr_init(struct tar_stat_info *st) -+{ -+ st->xattr_map = NULL; -+ st->xattr_map_size = 0; -+} -+ -+void xheader_xattr_free(struct xattr_array *xattr_map, size_t xattr_map_size) -+{ -+ size_t scan = 0; -+ -+ while (scan < xattr_map_size) -+ { -+ free (xattr_map[scan].xkey); -+ free (xattr_map[scan].xval_ptr); -+ -+ ++scan; -+ } -+ free (xattr_map); -+} -+ -+static void xheader_xattr__add(struct xattr_array **xattr_map, -+ size_t *xattr_map_size, -+ const char *key, const char *val, size_t len) -+{ -+ size_t pos = (*xattr_map_size)++; -+ -+ *xattr_map = xrealloc (*xattr_map, -+ *xattr_map_size * sizeof(struct xattr_array)); -+ (*xattr_map)[pos].xkey = xstrdup (key); -+ (*xattr_map)[pos].xval_ptr = xmemdup (val, len + 1); -+ (*xattr_map)[pos].xval_len = len; -+} -+ -+void xheader_xattr_add(struct tar_stat_info *st, -+ const char *key, const char *val, size_t len) -+{ -+ size_t klen = strlen (key); -+ char *xkey = xmalloc (strlen("SCHILY.xattr.") + klen + 1); -+ char *tmp = xkey; -+ -+ tmp = stpcpy (tmp, "SCHILY.xattr."); -+ tmp = stpcpy (tmp, key); -+ -+ xheader_xattr__add (&st->xattr_map, &st->xattr_map_size, xkey, val, len); -+ -+ free (xkey); -+} -+ -+void xheader_xattr_copy(const struct tar_stat_info *st, -+ struct xattr_array **xattr_map, size_t *xattr_map_size) -+{ -+ size_t scan = 0; -+ -+ *xattr_map = NULL; -+ *xattr_map_size = 0; -+ -+ while (scan < st->xattr_map_size) -+ { -+ char *key = st->xattr_map[scan].xkey; -+ char *val = st->xattr_map[scan].xval_ptr; -+ size_t len = st->xattr_map[scan].xval_len; -+ -+ xheader_xattr__add(xattr_map, xattr_map_size, key, val, len); -+ -+ ++scan; -+ } -+} -+ - - /* General Interface */ - -@@ -473,6 +541,7 @@ struct xhdr_tab - struct xheader *, void const *data); - void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t); - int flags; -+ bool prefix; - }; - - /* This declaration must be extern, because ISO C99 section 6.9.2 -@@ -489,8 +558,17 @@ locate_handler (char const *keyword) - struct xhdr_tab const *p; - - for (p = xhdr_tab; p->keyword; p++) -- if (strcmp (p->keyword, keyword) == 0) -- return p; -+ if (p->prefix) -+ { -+ if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0) -+ return p; -+ } -+ else -+ { -+ if (strcmp (p->keyword, keyword) == 0) -+ return p; -+ } -+ - return NULL; - } - -@@ -500,7 +578,7 @@ xheader_protected_pattern_p (const char - struct xhdr_tab const *p; - - for (p = xhdr_tab; p->keyword; p++) -- if ((p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0) -+ if (!p->prefix && (p->flags & XHDR_PROTECTED) && fnmatch (pattern, p->keyword, 0) == 0) - return true; - return false; - } -@@ -511,7 +589,7 @@ xheader_protected_keyword_p (const char - struct xhdr_tab const *p; - - for (p = xhdr_tab; p->keyword; p++) -- if ((p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0) -+ if (!p->prefix && (p->flags & XHDR_PROTECTED) && strcmp (p->keyword, keyword) == 0) - return true; - return false; - } -@@ -1470,6 +1548,71 @@ volume_filename_decoder (struct tar_stat - } - - 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 +1649,53 @@ sparse_minor_decoder (struct tar_stat_in - } - - 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 +1704,42 @@ 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 }, -- -- { NULL, NULL, NULL, 0 } -+ 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 }, -+ -+ /* FIXME: These are compat. for FC-6 ... we shipped a tar using the generic -+ header names by accident. */ -+ { "SCHILY.xattr.security.selinux", -+ xattr_selinux_coder, xattr_selinux_decoder, 0, false }, -+ { "SCHILY.xattr.system.posix_acl_access", -+ xattr_acls_a_coder, xattr_acls_a_decoder, 0, false }, -+ { "SCHILY.xattr.system.posix_acl_default", -+ xattr_acls_d_coder, xattr_acls_d_decoder, 0, false }, -+ -+ /* xattrs use the star format. note we only save some variants... */ -+ { "SCHILY.xattr.user", xattr_coder, xattr_decoder, 0, true }, -+ { "SCHILY.xattr.trusted", xattr_coder, xattr_decoder, 0, true }, -+ { "SCHILY.xattr.lustre", xattr_coder, xattr_decoder, 0, true }, -+ { "SCHILY.xattr.security.NTACL", xattr_coder, xattr_decoder, 0, true }, -+ { "SCHILY.xattr.security.capability", xattr_coder, xattr_decoder, 0, true }, -+ -+ /* ignore everything else in the xattr namespaces... */ -+ { "SCHILY.xattr", dummy_coder, dummy_decoder, 0, true }, -+ -+ { NULL, NULL, NULL, 0, false } - }; diff --git a/tar-1.26-xattrs-gnulib-prepare.patch b/tar-1.26-xattrs-gnulib-prepare.patch new file mode 100644 index 0000000..c29ef42 --- /dev/null +++ b/tar-1.26-xattrs-gnulib-prepare.patch @@ -0,0 +1,3892 @@ +From 1975192b75aefe1d75360e47496725f2f7c589a8 Mon Sep 17 00:00:00 2001 +From: Pavel Raiskup +Date: Tue, 14 Aug 2012 15:42:15 +0200 +Subject: [PATCH 1/2] Prepare gnulib for xattrs patch + +--- + gnu/Makefile.am | 98 +++++- + gnu/acl-internal.h | 267 ++++++++++++++ + gnu/acl.h | 30 ++ + gnu/acl_entries.c | 77 ++++ + gnu/attr-xattr.in.h | 67 ++++ + gnu/copy-acl.c | 620 ++++++++++++++++++++++++++++++++ + gnu/file-has-acl.c | 920 ++++++++++++++++++++++++++++++++++++++++++++++++ + gnu/getfilecon.c | 88 +++++ + gnu/se-context.in.h | 30 ++ + gnu/se-selinux.in.h | 99 ++++++ + gnu/selinux-at.c | 74 ++++ + gnu/selinux-at.h | 54 +++ + gnu/set-mode-acl.c | 699 ++++++++++++++++++++++++++++++++++++ + gnu/xattr-at.c | 112 ++++++ + gnu/xattr-at.h | 68 ++++ + m4/acl.m4 | 165 +++++++++ + m4/attr-xattr-h.m4 | 28 ++ + m4/gnulib-comp.m4 | 33 +- + m4/selinux-context-h.m4 | 22 ++ + m4/selinux-selinux-h.m4 | 69 ++++ + 20 files changed, 3613 insertions(+), 7 deletions(-) + create mode 100644 gnu/acl-internal.h + create mode 100644 gnu/acl.h + create mode 100644 gnu/acl_entries.c + create mode 100644 gnu/attr-xattr.in.h + create mode 100644 gnu/copy-acl.c + create mode 100644 gnu/file-has-acl.c + create mode 100644 gnu/getfilecon.c + create mode 100644 gnu/se-context.in.h + create mode 100644 gnu/se-selinux.in.h + create mode 100644 gnu/selinux-at.c + create mode 100644 gnu/selinux-at.h + create mode 100644 gnu/set-mode-acl.c + create mode 100644 gnu/xattr-at.c + create mode 100644 gnu/xattr-at.h + create mode 100644 m4/acl.m4 + create mode 100644 m4/attr-xattr-h.m4 + create mode 100644 m4/selinux-context-h.m4 + create mode 100644 m4/selinux-selinux-h.m4 + +diff --git a/gnu/Makefile.am b/gnu/Makefile.am +index 06eb0d9..e5913db 100644 +--- a/gnu/Makefile.am ++++ b/gnu/Makefile.am +@@ -1,6 +1,3 @@ +-# -*- buffer-read-only: t -*- vi: set ro: +-# DO NOT EDIT! GENERATED AUTOMATICALLY! +-## DO NOT EDIT! GENERATED AUTOMATICALLY! + ## Process this file with automake to produce Makefile.in. + # Copyright (C) 2002-2011 Free Software Foundation, Inc. + # +@@ -38,8 +35,17 @@ libgnu_a_LIBADD = $(gl_LIBOBJS) + libgnu_a_DEPENDENCIES = $(gl_LIBOBJS) + EXTRA_libgnu_a_SOURCES = + +-## begin gnulib module alloca ++## begin gnulib module acl ++ ++libgnu_a_SOURCES += set-mode-acl.c copy-acl.c file-has-acl.c ++ ++EXTRA_DIST += acl-internal.h acl.h acl_entries.c + ++EXTRA_libgnu_a_SOURCES += acl_entries.c ++ ++## end gnulib module acl ++ ++## begin gnulib module alloca + + EXTRA_DIST += alloca.c + +@@ -1386,6 +1392,60 @@ EXTRA_libgnu_a_SOURCES += savedir.c + + ## end gnulib module savedir + ++## begin gnulib module selinux-at ++ ++ ++EXTRA_DIST += at-func.c selinux-at.c selinux-at.h ++ ++EXTRA_libgnu_a_SOURCES += at-func.c selinux-at.c ++ ++## end gnulib module selinux-at ++ ++## begin gnulib module selinux-h ++ ++libgnu_a_SOURCES += se-context.in.h se-selinux.in.h ++ ++BUILT_SOURCES += selinux/selinux.h ++selinux/selinux.h: se-selinux.in.h $(top_builddir)/config.status $(UNUSED_PARAMETER_H) ++ $(AM_V_at)$(MKDIR_P) selinux ++ $(AM_V_GEN)rm -f $@-t $@ && \ ++ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ ++ sed -e 's|@''GUARD_PREFIX''@|GL|g' \ ++ -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ ++ -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ ++ -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ ++ -e 's|@''NEXT_SELINUX_SELINUX_H''@|$(NEXT_SELINUX_SELINUX_H)|g' \ ++ -e '/definition of _GL_UNUSED_PARAMETER/r $(UNUSED_PARAMETER_H)' \ ++ < $(srcdir)/se-selinux.in.h; \ ++ } > $@-t && \ ++ chmod a-x $@-t && \ ++ mv $@-t $@ ++MOSTLYCLEANFILES += selinux/selinux.h selinux/selinux.h-t ++ ++BUILT_SOURCES += $(SELINUX_CONTEXT_H) ++if GL_GENERATE_SELINUX_CONTEXT_H ++selinux/context.h: se-context.in.h $(top_builddir)/config.status $(UNUSED_PARAMETER_H) ++ $(AM_V_at)$(MKDIR_P) selinux ++ $(AM_V_GEN)rm -f $@-t $@ && \ ++ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ ++ sed -e '/definition of _GL_UNUSED_PARAMETER/r $(UNUSED_PARAMETER_H)' \ ++ < $(srcdir)/se-context.in.h; \ ++ } > $@-t && \ ++ chmod a-x $@-t && \ ++ mv $@-t $@ ++else ++selinux/context.h: $(top_builddir)/config.status ++ rm -f $@ ++endif ++MOSTLYCLEANFILES += selinux/context.h selinux/context.h-t ++MOSTLYCLEANDIRS += selinux ++ ++EXTRA_DIST += getfilecon.c ++ ++EXTRA_libgnu_a_SOURCES += getfilecon.c ++ ++## end gnulib module selinux-h ++ + ## begin gnulib module setenv + + +@@ -2695,6 +2755,36 @@ libgnu_a_SOURCES += xalloc-die.c + + ## end gnulib module xalloc-die + ++## begin gnulib module xattr-at ++ ++ ++EXTRA_DIST += at-func.c xattr-at.c xattr-at.h ++ ++EXTRA_libgnu_a_SOURCES += at-func.c xattr-at.c ++ ++## end gnulib module xattr-at ++ ++## begin gnulib module xattr-h ++ ++libgnu_a_SOURCES += attr-xattr.in.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 $@ && \ ++ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ ++ sed -e 's|@''GUARD_PREFIX''@|GL|g' \ ++ -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ ++ -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ ++ -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ ++ -e '/definition of _GL_UNUSED_PARAMETER/r $(UNUSED_PARAMETER_H)' \ ++ < $(srcdir)/attr-xattr.in.h; \ ++ } > $@-t && \ ++ chmod a-x $@-t && \ ++ mv $@-t $@ ++MOSTLYCLEANFILES += attr/xattr.h attr/xattr.h-t ++ ++## end gnulib module xattr-h ++ + ## begin gnulib module xgetcwd + + +diff --git a/gnu/acl-internal.h b/gnu/acl-internal.h +new file mode 100644 +index 0000000..07309e0 +--- /dev/null ++++ b/gnu/acl-internal.h +@@ -0,0 +1,267 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* Internal implementation of access control lists. ++ ++ Copyright (C) 2002-2003, 2005-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 . ++ ++ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ ++ ++#include "acl.h" ++ ++#include ++#include ++ ++/* All systems define the ACL related API in . */ ++#if HAVE_SYS_ACL_H ++# include ++#endif ++#if defined HAVE_FACL && ! defined GETACLCNT && defined ACL_CNT ++# define GETACLCNT ACL_CNT ++#endif ++ ++/* On Linux, additional ACL related API is available in . */ ++#ifdef HAVE_ACL_LIBACL_H ++# include ++#endif ++ ++/* On HP-UX >= 11.11, additional ACL API is available in . */ ++#if HAVE_ACLV_H ++# include ++# include ++/* HP-UX 11.11 lacks these declarations. */ ++extern int acl (char *, int, int, struct acl *); ++extern int aclsort (int, int, struct acl *); ++#endif ++ ++#include "error.h" ++#include "quote.h" ++ ++#include ++#ifndef ENOSYS ++# define ENOSYS (-1) ++#endif ++#ifndef ENOTSUP ++# define ENOTSUP (-1) ++#endif ++ ++#include ++#ifndef MIN ++# define MIN(a,b) ((a) < (b) ? (a) : (b)) ++#endif ++ ++#ifndef SIZE_MAX ++# define SIZE_MAX ((size_t) -1) ++#endif ++ ++#ifndef HAVE_FCHMOD ++# define HAVE_FCHMOD false ++# define fchmod(fd, mode) (-1) ++#endif ++ ++/* Recognize some common errors such as from an NFS mount that does ++ not support ACLs, even when local drives do. */ ++#if defined __APPLE__ && defined __MACH__ /* Mac OS X */ ++# define ACL_NOT_WELL_SUPPORTED(Err) \ ++ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == ENOENT) ++#elif defined EOPNOTSUPP /* Tru64 NFS */ ++# define ACL_NOT_WELL_SUPPORTED(Err) \ ++ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == EOPNOTSUPP) ++#else ++# define ACL_NOT_WELL_SUPPORTED(Err) \ ++ ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY) ++#endif ++ ++#if USE_ACL ++ ++# if HAVE_ACL_GET_FILE ++/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ ++/* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ ++ ++# ifndef MIN_ACL_ENTRIES ++# define MIN_ACL_ENTRIES 4 ++# endif ++ ++/* POSIX 1003.1e (draft 17) */ ++# ifdef HAVE_ACL_GET_FD ++/* Most platforms have a 1-argument acl_get_fd, only OSF/1 has a 2-argument ++ macro(!). */ ++# if HAVE_ACL_FREE_TEXT /* OSF/1 */ ++static inline acl_t ++rpl_acl_get_fd (int fd) ++{ ++ return acl_get_fd (fd, ACL_TYPE_ACCESS); ++} ++# undef acl_get_fd ++# define acl_get_fd rpl_acl_get_fd ++# endif ++# else ++# define HAVE_ACL_GET_FD false ++# undef acl_get_fd ++# define acl_get_fd(fd) (NULL) ++# endif ++ ++/* POSIX 1003.1e (draft 17) */ ++# ifdef HAVE_ACL_SET_FD ++/* Most platforms have a 2-argument acl_set_fd, only OSF/1 has a 3-argument ++ macro(!). */ ++# if HAVE_ACL_FREE_TEXT /* OSF/1 */ ++static inline int ++rpl_acl_set_fd (int fd, acl_t acl) ++{ ++ return acl_set_fd (fd, ACL_TYPE_ACCESS, acl); ++} ++# undef acl_set_fd ++# define acl_set_fd rpl_acl_set_fd ++# endif ++# else ++# define HAVE_ACL_SET_FD false ++# undef acl_set_fd ++# define acl_set_fd(fd, acl) (-1) ++# endif ++ ++/* POSIX 1003.1e (draft 13) */ ++# if ! HAVE_ACL_FREE_TEXT ++# define acl_free_text(buf) acl_free (buf) ++# endif ++ ++/* Linux-specific */ ++# ifndef HAVE_ACL_EXTENDED_FILE ++# define HAVE_ACL_EXTENDED_FILE false ++# define acl_extended_file(name) (-1) ++# endif ++ ++/* Linux-specific */ ++# ifndef HAVE_ACL_FROM_MODE ++# define HAVE_ACL_FROM_MODE false ++# define acl_from_mode(mode) (NULL) ++# endif ++ ++/* Set to 1 if a file's mode is implicit by the ACL. ++ Set to 0 if a file's mode is stored independently from the ACL. */ ++# if (HAVE_ACL_COPY_EXT_NATIVE && HAVE_ACL_CREATE_ENTRY_NP) || defined __sgi /* Mac OS X, IRIX */ ++# define MODE_INSIDE_ACL 0 ++# else ++# define MODE_INSIDE_ACL 1 ++# endif ++ ++/* Return the number of entries in ACL. ++ Return -1 and set errno upon failure to determine it. */ ++/* Define a replacement for acl_entries if needed. (Only Linux has it.) */ ++# if !HAVE_ACL_ENTRIES ++# define acl_entries rpl_acl_entries ++extern int acl_entries (acl_t); ++# endif ++ ++# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ ++/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. ++ Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial. */ ++extern int acl_extended_nontrivial (acl_t); ++# else ++/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS. ++ Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. ++ Return -1 and set errno upon failure to determine it. */ ++extern int acl_access_nontrivial (acl_t); ++# endif ++ ++# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ ++ ++/* Set to 1 if a file's mode is implicit by the ACL. ++ Set to 0 if a file's mode is stored independently from the ACL. */ ++# if defined __CYGWIN__ /* Cygwin */ ++# define MODE_INSIDE_ACL 0 ++# else /* Solaris */ ++# define MODE_INSIDE_ACL 1 ++# endif ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++extern int acl_nontrivial (int count, aclent_t *entries); ++ ++# ifdef ACE_GETACL /* Solaris 10 */ ++ ++/* Test an ACL retrieved with ACE_GETACL. ++ Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++extern int acl_ace_nontrivial (int count, ace_t *entries); ++ ++/* Definitions for when the built executable is executed on Solaris 10 ++ (newer version) or Solaris 11. */ ++/* For a_type. */ ++# define OLD_ALLOW 0 ++# define OLD_DENY 1 ++# define NEW_ACE_ACCESS_ALLOWED_ACE_TYPE 0 /* replaces ALLOW */ ++# define NEW_ACE_ACCESS_DENIED_ACE_TYPE 1 /* replaces DENY */ ++/* For a_flags. */ ++# define OLD_ACE_OWNER 0x0100 ++# define OLD_ACE_GROUP 0x0200 ++# define OLD_ACE_OTHER 0x0400 ++# define NEW_ACE_OWNER 0x1000 ++# define NEW_ACE_GROUP 0x2000 ++# define NEW_ACE_IDENTIFIER_GROUP 0x0040 ++# define NEW_ACE_EVERYONE 0x4000 ++/* For a_access_mask. */ ++# define NEW_ACE_READ_DATA 0x001 /* corresponds to 'r' */ ++# define NEW_ACE_WRITE_DATA 0x002 /* corresponds to 'w' */ ++# define NEW_ACE_APPEND_DATA 0x004 ++# define NEW_ACE_READ_NAMED_ATTRS 0x008 ++# define NEW_ACE_WRITE_NAMED_ATTRS 0x010 ++# define NEW_ACE_EXECUTE 0x020 ++# define NEW_ACE_DELETE_CHILD 0x040 ++# define NEW_ACE_READ_ATTRIBUTES 0x080 ++# define NEW_ACE_WRITE_ATTRIBUTES 0x100 ++# define NEW_ACE_DELETE 0x10000 ++# define NEW_ACE_READ_ACL 0x20000 ++# define NEW_ACE_WRITE_ACL 0x40000 ++# define NEW_ACE_WRITE_OWNER 0x80000 ++# define NEW_ACE_SYNCHRONIZE 0x100000 ++ ++# endif ++ ++# elif HAVE_GETACL /* HP-UX */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++extern int acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb); ++ ++# if HAVE_ACLV_H /* HP-UX >= 11.11 */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++extern int aclv_nontrivial (int count, struct acl *entries); ++ ++# endif ++ ++# elif HAVE_ACLX_GET && 0 /* AIX */ ++ ++/* TODO */ ++ ++# elif HAVE_STATACL /* older AIX */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++extern int acl_nontrivial (struct acl *a); ++ ++# elif HAVE_ACLSORT /* NonStop Kernel */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++extern int acl_nontrivial (int count, struct acl *entries); ++ ++# endif ++ ++#endif +diff --git a/gnu/acl.h b/gnu/acl.h +new file mode 100644 +index 0000000..d808a90 +--- /dev/null ++++ b/gnu/acl.h +@@ -0,0 +1,30 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* acl.c - access control lists ++ ++ Copyright (C) 2002, 2008-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 . ++ ++ Written by Paul Eggert. */ ++ ++#include ++#include ++ ++int file_has_acl (char const *, struct stat const *); ++int qset_acl (char const *, int, mode_t); ++int set_acl (char const *, int, mode_t); ++int qcopy_acl (char const *, int, char const *, int, mode_t); ++int copy_acl (char const *, int, char const *, int, mode_t); ++int chmod_or_fchmod (char const *, int, mode_t); +diff --git a/gnu/acl_entries.c b/gnu/acl_entries.c +new file mode 100644 +index 0000000..11adc22 +--- /dev/null ++++ b/gnu/acl_entries.c +@@ -0,0 +1,77 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* Return the number of entries in an ACL. ++ ++ Copyright (C) 2002-2003, 2005-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 . ++ ++ Written by Paul Eggert and Andreas Gruenbacher. */ ++ ++#include ++ ++#include "acl-internal.h" ++ ++/* This file assumes POSIX-draft like ACLs ++ (Linux, FreeBSD, Mac OS X, IRIX, Tru64). */ ++ ++/* Return the number of entries in ACL. ++ Return -1 and set errno upon failure to determine it. */ ++ ++int ++acl_entries (acl_t acl) ++{ ++ int count = 0; ++ ++ if (acl != NULL) ++ { ++#if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD, Mac OS X */ ++# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ ++ /* acl_get_entry returns 0 when it successfully fetches an entry, ++ and -1/EINVAL at the end. */ ++ acl_entry_t ace; ++ int got_one; ++ ++ for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace); ++ got_one >= 0; ++ got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace)) ++ count++; ++# else /* Linux, FreeBSD */ ++ /* acl_get_entry returns 1 when it successfully fetches an entry, ++ and 0 at the end. */ ++ acl_entry_t ace; ++ int got_one; ++ ++ for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace); ++ got_one > 0; ++ got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace)) ++ count++; ++ if (got_one < 0) ++ return -1; ++# endif ++#else /* IRIX, Tru64 */ ++# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */ ++ /* Don't use acl_get_entry: it is undocumented. */ ++ count = acl->acl_cnt; ++# endif ++# if HAVE_ACL_FREE_TEXT /* Tru64 */ ++ /* Don't use acl_get_entry: it takes only one argument and does not ++ work. */ ++ count = acl->acl_num; ++# endif ++#endif ++ } ++ ++ return count; ++} +diff --git a/gnu/attr-xattr.in.h b/gnu/attr-xattr.in.h +new file mode 100644 +index 0000000..4349c69 +--- /dev/null ++++ b/gnu/attr-xattr.in.h +@@ -0,0 +1,67 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++#ifndef _@GUARD_PREFIX@_ATTR_XATTR_H ++#define _@GUARD_PREFIX@_ATTR_XATTR_H ++ ++/* Replacement 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 . */ ++ ++#if HAVE_ATTR_XATTR_H ++ ++#include ++#include_next ++ ++#else ++# include ++ ++/* 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 /* if HAVE_ATTR_XATTR_H */ ++#endif /* _@GUARD_PREFIX@_ATTR_XATTR_H */ +diff --git a/gnu/copy-acl.c b/gnu/copy-acl.c +new file mode 100644 +index 0000000..a4d82f7 +--- /dev/null ++++ b/gnu/copy-acl.c +@@ -0,0 +1,620 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* copy-acl.c - copy access control list from one file to another file ++ ++ Copyright (C) 2002-2003, 2005-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 . ++ ++ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ ++ ++#include ++ ++#include "acl.h" ++ ++#include "acl-internal.h" ++ ++#include "gettext.h" ++#define _(msgid) gettext (msgid) ++ ++ ++/* Copy access control lists from one file to another. If SOURCE_DESC is ++ a valid file descriptor, use file descriptor operations, else use ++ filename based operations on SRC_NAME. Likewise for DEST_DESC and ++ DST_NAME. ++ If access control lists are not available, fchmod the target file to ++ MODE. Also sets the non-permission bits of the destination file ++ (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. ++ Return 0 if successful. ++ Return -2 and set errno for an error relating to the source file. ++ Return -1 and set errno for an error relating to the destination file. */ ++ ++int ++qcopy_acl (const char *src_name, int source_desc, const char *dst_name, ++ int dest_desc, mode_t mode) ++{ ++#if USE_ACL && HAVE_ACL_GET_FILE ++ /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ ++ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ ++# if !HAVE_ACL_TYPE_EXTENDED ++ /* Linux, FreeBSD, IRIX, Tru64 */ ++ ++ acl_t acl; ++ int ret; ++ ++ if (HAVE_ACL_GET_FD && source_desc != -1) ++ acl = acl_get_fd (source_desc); ++ else ++ acl = acl_get_file (src_name, ACL_TYPE_ACCESS); ++ if (acl == NULL) ++ { ++ if (ACL_NOT_WELL_SUPPORTED (errno)) ++ return qset_acl (dst_name, dest_desc, mode); ++ else ++ return -2; ++ } ++ ++ if (HAVE_ACL_SET_FD && dest_desc != -1) ++ ret = acl_set_fd (dest_desc, acl); ++ else ++ ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl); ++ if (ret != 0) ++ { ++ int saved_errno = errno; ++ ++ if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl)) ++ { ++ acl_free (acl); ++ return chmod_or_fchmod (dst_name, dest_desc, mode); ++ } ++ else ++ { ++ acl_free (acl); ++ chmod_or_fchmod (dst_name, dest_desc, mode); ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ else ++ acl_free (acl); ++ ++ if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) ++ { ++ /* We did not call chmod so far, and either the mode and the ACL are ++ separate or special bits are to be set which don't fit into ACLs. */ ++ ++ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) ++ return -1; ++ } ++ ++ if (S_ISDIR (mode)) ++ { ++ acl = acl_get_file (src_name, ACL_TYPE_DEFAULT); ++ if (acl == NULL) ++ return -2; ++ ++ if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl)) ++ { ++ int saved_errno = errno; ++ ++ acl_free (acl); ++ errno = saved_errno; ++ return -1; ++ } ++ else ++ acl_free (acl); ++ } ++ return 0; ++ ++# else /* HAVE_ACL_TYPE_EXTENDED */ ++ /* Mac OS X */ ++ ++ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) ++ always return NULL / EINVAL. You have to use ++ acl_get_file (name, ACL_TYPE_EXTENDED) ++ or acl_get_fd (open (name, ...)) ++ to retrieve an ACL. ++ On the other hand, ++ acl_set_file (name, ACL_TYPE_ACCESS, acl) ++ and acl_set_file (name, ACL_TYPE_DEFAULT, acl) ++ have the same effect as ++ acl_set_file (name, ACL_TYPE_EXTENDED, acl): ++ Each of these calls sets the file's ACL. */ ++ ++ acl_t acl; ++ int ret; ++ ++ if (HAVE_ACL_GET_FD && source_desc != -1) ++ acl = acl_get_fd (source_desc); ++ else ++ acl = acl_get_file (src_name, ACL_TYPE_EXTENDED); ++ if (acl == NULL) ++ { ++ if (ACL_NOT_WELL_SUPPORTED (errno)) ++ return qset_acl (dst_name, dest_desc, mode); ++ else ++ return -2; ++ } ++ ++ if (HAVE_ACL_SET_FD && dest_desc != -1) ++ ret = acl_set_fd (dest_desc, acl); ++ else ++ ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl); ++ if (ret != 0) ++ { ++ int saved_errno = errno; ++ ++ if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl)) ++ { ++ acl_free (acl); ++ return chmod_or_fchmod (dst_name, dest_desc, mode); ++ } ++ else ++ { ++ acl_free (acl); ++ chmod_or_fchmod (dst_name, dest_desc, mode); ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ else ++ acl_free (acl); ++ ++ /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ ++ return chmod_or_fchmod (dst_name, dest_desc, mode); ++ ++# endif ++ ++#elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ ++ ++ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions ++ of Unixware. The acl() call returns the access and default ACL both ++ at once. */ ++# ifdef ACE_GETACL ++ int ace_count; ++ ace_t *ace_entries; ++# endif ++ int count; ++ aclent_t *entries; ++ int did_chmod; ++ int saved_errno; ++ int ret; ++ ++# ifdef ACE_GETACL ++ /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 ++ file systems (whereas the other ones are used in UFS file systems). ++ There is an API ++ pathconf (name, _PC_ACL_ENABLED) ++ fpathconf (desc, _PC_ACL_ENABLED) ++ that allows to determine which of the two kinds of ACLs is supported ++ for the given file. But some file systems may implement this call ++ incorrectly, so better not use it. ++ When fetching the source ACL, we simply fetch both ACL types. ++ When setting the destination ACL, we try either ACL types, assuming ++ that the kernel will translate the ACL from one form to the other. ++ (See in ++ the description of ENOTSUP.) */ ++ for (;;) ++ { ++ ace_count = (source_desc != -1 ++ ? facl (source_desc, ACE_GETACLCNT, 0, NULL) ++ : acl (src_name, ACE_GETACLCNT, 0, NULL)); ++ ++ if (ace_count < 0) ++ { ++ if (errno == ENOSYS || errno == EINVAL) ++ { ++ ace_count = 0; ++ ace_entries = NULL; ++ break; ++ } ++ else ++ return -2; ++ } ++ ++ if (ace_count == 0) ++ { ++ ace_entries = NULL; ++ break; ++ } ++ ++ ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t)); ++ if (ace_entries == NULL) ++ { ++ errno = ENOMEM; ++ return -2; ++ } ++ ++ ret = (source_desc != -1 ++ ? facl (source_desc, ACE_GETACL, ace_count, ace_entries) ++ : acl (src_name, ACE_GETACL, ace_count, ace_entries)); ++ if (ret < 0) ++ { ++ free (ace_entries); ++ if (errno == ENOSYS || errno == EINVAL) ++ { ++ ace_count = 0; ++ ace_entries = NULL; ++ break; ++ } ++ else ++ return -2; ++ } ++ if (ret == ace_count) ++ break; ++ /* Huh? The number of ACL entries changed since the last call. ++ Repeat. */ ++ } ++# endif ++ ++ for (;;) ++ { ++ count = (source_desc != -1 ++ ? facl (source_desc, GETACLCNT, 0, NULL) ++ : acl (src_name, GETACLCNT, 0, NULL)); ++ ++ if (count < 0) ++ { ++ if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP) ++ { ++ count = 0; ++ entries = NULL; ++ break; ++ } ++ else ++ return -2; ++ } ++ ++ if (count == 0) ++ { ++ entries = NULL; ++ break; ++ } ++ ++ entries = (aclent_t *) malloc (count * sizeof (aclent_t)); ++ if (entries == NULL) ++ { ++ errno = ENOMEM; ++ return -2; ++ } ++ ++ if ((source_desc != -1 ++ ? facl (source_desc, GETACL, count, entries) ++ : acl (src_name, GETACL, count, entries)) ++ == count) ++ break; ++ /* Huh? The number of ACL entries changed since the last call. ++ Repeat. */ ++ } ++ ++ /* Is there an ACL of either kind? */ ++# ifdef ACE_GETACL ++ if (ace_count == 0) ++# endif ++ if (count == 0) ++ return qset_acl (dst_name, dest_desc, mode); ++ ++ did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ ++ saved_errno = 0; /* the first non-ignorable error code */ ++ ++ if (!MODE_INSIDE_ACL) ++ { ++ /* On Cygwin, it is necessary to call chmod before acl, because ++ chmod can change the contents of the ACL (in ways that don't ++ change the allowed accesses, but still visible). */ ++ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) ++ saved_errno = errno; ++ did_chmod = 1; ++ } ++ ++ /* If both ace_entries and entries are available, try SETACL before ++ ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL ++ can. */ ++ ++ if (count > 0) ++ { ++ ret = (dest_desc != -1 ++ ? facl (dest_desc, SETACL, count, entries) ++ : acl (dst_name, SETACL, count, entries)); ++ if (ret < 0 && saved_errno == 0) ++ { ++ saved_errno = errno; ++ if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) ++ && !acl_nontrivial (count, entries)) ++ saved_errno = 0; ++ } ++ else ++ did_chmod = 1; ++ } ++ free (entries); ++ ++# ifdef ACE_GETACL ++ if (ace_count > 0) ++ { ++ ret = (dest_desc != -1 ++ ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries) ++ : acl (dst_name, ACE_SETACL, ace_count, ace_entries)); ++ if (ret < 0 && saved_errno == 0) ++ { ++ saved_errno = errno; ++ if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) ++ && !acl_ace_nontrivial (ace_count, ace_entries)) ++ saved_errno = 0; ++ } ++ } ++ free (ace_entries); ++# endif ++ ++ if (MODE_INSIDE_ACL ++ && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) ++ { ++ /* We did not call chmod so far, and either the mode and the ACL are ++ separate or special bits are to be set which don't fit into ACLs. */ ++ ++ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) ++ { ++ if (saved_errno == 0) ++ saved_errno = errno; ++ } ++ } ++ ++ if (saved_errno) ++ { ++ errno = saved_errno; ++ return -1; ++ } ++ return 0; ++ ++#elif USE_ACL && HAVE_GETACL /* HP-UX */ ++ ++ struct acl_entry entries[NACLENTRIES]; ++ int count; ++# if HAVE_ACLV_H ++ struct acl aclv_entries[NACLVENTRIES]; ++ int aclv_count; ++# endif ++ int did_chmod; ++ int saved_errno; ++ int ret; ++ ++ count = (source_desc != -1 ++ ? fgetacl (source_desc, NACLENTRIES, entries) ++ : getacl (src_name, NACLENTRIES, entries)); ++ ++ if (count < 0) ++ { ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) ++ count = 0; ++ else ++ return -2; ++ } ++ else if (count > 0) ++ { ++ if (count > NACLENTRIES) ++ /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ ++ abort (); ++ } ++ ++# if HAVE_ACLV_H ++ aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries); ++ ++ if (aclv_count < 0) ++ { ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) ++ count = 0; ++ else ++ return -2; ++ } ++ else if (aclv_count > 0) ++ { ++ if (aclv_count > NACLVENTRIES) ++ /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */ ++ abort (); ++ } ++# endif ++ ++ if (count == 0) ++# if HAVE_ACLV_H ++ if (aclv_count == 0) ++# endif ++ return qset_acl (dst_name, dest_desc, mode); ++ ++ did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */ ++ saved_errno = 0; /* the first non-ignorable error code */ ++ ++ if (count > 0) ++ { ++ ret = (dest_desc != -1 ++ ? fsetacl (dest_desc, count, entries) ++ : setacl (dst_name, count, entries)); ++ if (ret < 0 && saved_errno == 0) ++ { ++ saved_errno = errno; ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) ++ { ++ struct stat source_statbuf; ++ ++ if ((source_desc != -1 ++ ? fstat (source_desc, &source_statbuf) ++ : stat (src_name, &source_statbuf)) == 0) ++ { ++ if (!acl_nontrivial (count, entries, &source_statbuf)) ++ saved_errno = 0; ++ } ++ else ++ saved_errno = errno; ++ } ++ } ++ else ++ did_chmod = 1; ++ } ++ ++# if HAVE_ACLV_H ++ if (aclv_count > 0) ++ { ++ ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries); ++ if (ret < 0 && saved_errno == 0) ++ { ++ saved_errno = errno; ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) ++ { ++ if (!aclv_nontrivial (aclv_count, aclv_entries)) ++ saved_errno = 0; ++ } ++ } ++ else ++ did_chmod = 1; ++ } ++# endif ++ ++ if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0)) ++ { ++ /* We did not call chmod so far, and special bits are to be set which ++ don't fit into ACLs. */ ++ ++ if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0) ++ { ++ if (saved_errno == 0) ++ saved_errno = errno; ++ } ++ } ++ ++ if (saved_errno) ++ { ++ errno = saved_errno; ++ return -1; ++ } ++ return 0; ++ ++#elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */ ++ ++ /* TODO */ ++ ++#elif USE_ACL && HAVE_STATACL /* older AIX */ ++ ++ union { struct acl a; char room[4096]; } u; ++ int ret; ++ ++ if ((source_desc != -1 ++ ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u)) ++ : statacl (src_name, STX_NORMAL, &u.a, sizeof (u))) ++ < 0) ++ return -2; ++ ++ ret = (dest_desc != -1 ++ ? fchacl (dest_desc, &u.a, u.a.acl_len) ++ : chacl (dst_name, &u.a, u.a.acl_len)); ++ if (ret < 0) ++ { ++ int saved_errno = errno; ++ ++ chmod_or_fchmod (dst_name, dest_desc, mode); ++ errno = saved_errno; ++ return -1; ++ } ++ ++ /* No need to call chmod_or_fchmod at this point, since the mode bits ++ S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ ++ ++ return 0; ++ ++#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ ++ ++ struct acl entries[NACLENTRIES]; ++ int count; ++ int ret; ++ ++ count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries); ++ ++ if (count < 0) ++ { ++ if (0) ++ count = 0; ++ else ++ return -2; ++ } ++ else if (count > 0) ++ { ++ if (count > NACLENTRIES) ++ /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */ ++ abort (); ++ } ++ ++ if (count == 0) ++ return qset_acl (dst_name, dest_desc, mode); ++ ++ ret = acl ((char *) dst_name, ACL_SET, count, entries); ++ if (ret < 0) ++ { ++ int saved_errno = errno; ++ ++ if (0) ++ { ++ if (!acl_nontrivial (count, entries)) ++ return chmod_or_fchmod (dst_name, dest_desc, mode); ++ } ++ ++ chmod_or_fchmod (dst_name, dest_desc, mode); ++ errno = saved_errno; ++ return -1; ++ } ++ ++ if (mode & (S_ISUID | S_ISGID | S_ISVTX)) ++ { ++ /* We did not call chmod so far, and either the mode and the ACL are ++ separate or special bits are to be set which don't fit into ACLs. */ ++ ++ return chmod_or_fchmod (dst_name, dest_desc, mode); ++ } ++ return 0; ++ ++#else ++ ++ return qset_acl (dst_name, dest_desc, mode); ++ ++#endif ++} ++ ++ ++/* Copy access control lists from one file to another. If SOURCE_DESC is ++ a valid file descriptor, use file descriptor operations, else use ++ filename based operations on SRC_NAME. Likewise for DEST_DESC and ++ DST_NAME. ++ If access control lists are not available, fchmod the target file to ++ MODE. Also sets the non-permission bits of the destination file ++ (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set. ++ Return 0 if successful, otherwise output a diagnostic and return a ++ negative error code. */ ++ ++int ++copy_acl (const char *src_name, int source_desc, const char *dst_name, ++ int dest_desc, mode_t mode) ++{ ++ int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode); ++ switch (ret) ++ { ++ case -2: ++ error (0, errno, "%s", quote (src_name)); ++ break; ++ ++ case -1: ++ error (0, errno, _("preserving permissions for %s"), quote (dst_name)); ++ break; ++ ++ default: ++ break; ++ } ++ return ret; ++} +diff --git a/gnu/file-has-acl.c b/gnu/file-has-acl.c +new file mode 100644 +index 0000000..17872a5 +--- /dev/null ++++ b/gnu/file-has-acl.c +@@ -0,0 +1,920 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* Test whether a file has a nontrivial access control list. ++ ++ Copyright (C) 2002-2003, 2005-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 . ++ ++ Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ ++ ++/* Without this pragma, gcc 4.7.0 20120126 may suggest that the ++ file_has_acl function might be candidate for attribute 'const' */ ++#if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__ ++# pragma GCC diagnostic ignored "-Wsuggest-attribute=const" ++#endif ++ ++#include ++ ++#include "acl.h" ++ ++#include "acl-internal.h" ++ ++ ++#if USE_ACL && HAVE_ACL_GET_FILE ++ ++# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ ++ ++/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED. ++ Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial. */ ++int ++acl_extended_nontrivial (acl_t acl) ++{ ++ /* acl is non-trivial if it is non-empty. */ ++ return (acl_entries (acl) > 0); ++} ++ ++# else /* Linux, FreeBSD, IRIX, Tru64 */ ++ ++/* ACL is an ACL, from a file, stored as type ACL_TYPE_ACCESS. ++ Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. ++ Return -1 and set errno upon failure to determine it. */ ++int ++acl_access_nontrivial (acl_t acl) ++{ ++ /* acl is non-trivial if it has some entries other than for "user::", ++ "group::", and "other::". Normally these three should be present ++ at least, allowing us to write ++ return (3 < acl_entries (acl)); ++ but the following code is more robust. */ ++# if HAVE_ACL_FIRST_ENTRY /* Linux, FreeBSD */ ++ ++ acl_entry_t ace; ++ int got_one; ++ ++ for (got_one = acl_get_entry (acl, ACL_FIRST_ENTRY, &ace); ++ got_one > 0; ++ got_one = acl_get_entry (acl, ACL_NEXT_ENTRY, &ace)) ++ { ++ acl_tag_t tag; ++ if (acl_get_tag_type (ace, &tag) < 0) ++ return -1; ++ if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) ++ return 1; ++ } ++ return got_one; ++ ++# else /* IRIX, Tru64 */ ++# if HAVE_ACL_TO_SHORT_TEXT /* IRIX */ ++ /* Don't use acl_get_entry: it is undocumented. */ ++ ++ int count = acl->acl_cnt; ++ int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ acl_entry_t ace = &acl->acl_entry[i]; ++ acl_tag_t tag = ace->ae_tag; ++ ++ if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ ++ || tag == ACL_OTHER_OBJ)) ++ return 1; ++ } ++ return 0; ++ ++# endif ++# if HAVE_ACL_FREE_TEXT /* Tru64 */ ++ /* Don't use acl_get_entry: it takes only one argument and does not work. */ ++ ++ int count = acl->acl_num; ++ acl_entry_t ace; ++ ++ for (ace = acl->acl_first; count > 0; ace = ace->next, count--) ++ { ++ acl_tag_t tag; ++ acl_perm_t perm; ++ ++ tag = ace->entry->acl_type; ++ if (!(tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || tag == ACL_OTHER)) ++ return 1; ++ ++ perm = ace->entry->acl_perm; ++ /* On Tru64, perm can also contain non-standard bits such as ++ PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ... */ ++ if ((perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)) != 0) ++ return 1; ++ } ++ return 0; ++ ++# endif ++# endif ++} ++ ++# endif ++ ++ ++#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ ++ ++/* Test an ACL retrieved with GETACL. ++ Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++acl_nontrivial (int count, aclent_t *entries) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ aclent_t *ace = &entries[i]; ++ ++ /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). ++ If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). ++ We don't need to check ace->a_id in these cases. */ ++ if (!(ace->a_type == USER_OBJ ++ || ace->a_type == GROUP_OBJ ++ || ace->a_type == OTHER_OBJ ++ /* Note: Cygwin does not return a CLASS_OBJ ("mask:") entry ++ sometimes. */ ++ || ace->a_type == CLASS_OBJ)) ++ return 1; ++ } ++ return 0; ++} ++ ++# ifdef ACE_GETACL ++ ++/* A shortcut for a bitmask. */ ++# define NEW_ACE_WRITEA_DATA (NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA) ++ ++/* Test an ACL retrieved with ACE_GETACL. ++ Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++acl_ace_nontrivial (int count, ace_t *entries) ++{ ++ int i; ++ ++ /* The flags in the ace_t structure changed in a binary incompatible way ++ when ACL_NO_TRIVIAL etc. were introduced in version 1.15. ++ How to distinguish the two conventions at runtime? ++ In the old convention, usually three ACEs have a_flags = ACE_OWNER / ++ ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new ++ convention, these values are not used. */ ++ int old_convention = 0; ++ ++ for (i = 0; i < count; i++) ++ if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) ++ { ++ old_convention = 1; ++ break; ++ } ++ ++ if (old_convention) ++ /* Running on Solaris 10. */ ++ for (i = 0; i < count; i++) ++ { ++ ace_t *ace = &entries[i]; ++ ++ /* Note: ++ If ace->a_flags = ACE_OWNER, ace->a_who is the st_uid from stat(). ++ If ace->a_flags = ACE_GROUP, ace->a_who is the st_gid from stat(). ++ We don't need to check ace->a_who in these cases. */ ++ if (!(ace->a_type == OLD_ALLOW ++ && (ace->a_flags == OLD_ACE_OWNER ++ || ace->a_flags == OLD_ACE_GROUP ++ || ace->a_flags == OLD_ACE_OTHER))) ++ return 1; ++ } ++ else ++ { ++ /* Running on Solaris 10 (newer version) or Solaris 11. */ ++ unsigned int access_masks[6] = ++ { ++ 0, /* owner@ deny */ ++ 0, /* owner@ allow */ ++ 0, /* group@ deny */ ++ 0, /* group@ allow */ ++ 0, /* everyone@ deny */ ++ 0 /* everyone@ allow */ ++ }; ++ ++ for (i = 0; i < count; i++) ++ { ++ ace_t *ace = &entries[i]; ++ unsigned int index1; ++ unsigned int index2; ++ ++ if (ace->a_type == NEW_ACE_ACCESS_ALLOWED_ACE_TYPE) ++ index1 = 1; ++ else if (ace->a_type == NEW_ACE_ACCESS_DENIED_ACE_TYPE) ++ index1 = 0; ++ else ++ return 1; ++ ++ if (ace->a_flags == NEW_ACE_OWNER) ++ index2 = 0; ++ else if (ace->a_flags == (NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP)) ++ index2 = 2; ++ else if (ace->a_flags == NEW_ACE_EVERYONE) ++ index2 = 4; ++ else ++ return 1; ++ ++ access_masks[index1 + index2] |= ace->a_access_mask; ++ } ++ ++ /* The same bit shouldn't be both allowed and denied. */ ++ if (access_masks[0] & access_masks[1]) ++ return 1; ++ if (access_masks[2] & access_masks[3]) ++ return 1; ++ if (access_masks[4] & access_masks[5]) ++ return 1; ++ ++ /* Check minimum masks. */ ++ if ((NEW_ACE_WRITE_NAMED_ATTRS ++ | NEW_ACE_WRITE_ATTRIBUTES ++ | NEW_ACE_WRITE_ACL ++ | NEW_ACE_WRITE_OWNER) ++ & ~ access_masks[1]) ++ return 1; ++ access_masks[1] &= ~(NEW_ACE_WRITE_NAMED_ATTRS ++ | NEW_ACE_WRITE_ATTRIBUTES ++ | NEW_ACE_WRITE_ACL ++ | NEW_ACE_WRITE_OWNER); ++ if ((NEW_ACE_READ_NAMED_ATTRS ++ | NEW_ACE_READ_ATTRIBUTES ++ | NEW_ACE_READ_ACL ++ | NEW_ACE_SYNCHRONIZE) ++ & ~ access_masks[5]) ++ return 1; ++ access_masks[5] &= ~(NEW_ACE_READ_NAMED_ATTRS ++ | NEW_ACE_READ_ATTRIBUTES ++ | NEW_ACE_READ_ACL ++ | NEW_ACE_SYNCHRONIZE); ++ ++ /* Check the allowed or denied bits. */ ++ switch ((access_masks[0] | access_masks[1]) ++ & ~(NEW_ACE_READ_NAMED_ATTRS ++ | NEW_ACE_READ_ATTRIBUTES ++ | NEW_ACE_READ_ACL ++ | NEW_ACE_SYNCHRONIZE)) ++ { ++ case 0: ++ case NEW_ACE_READ_DATA: ++ case NEW_ACE_WRITEA_DATA: ++ case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: ++ case NEW_ACE_EXECUTE: ++ case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: ++ case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: ++ case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: ++ break; ++ default: ++ return 1; ++ } ++ switch ((access_masks[2] | access_masks[3]) ++ & ~(NEW_ACE_READ_NAMED_ATTRS ++ | NEW_ACE_READ_ATTRIBUTES ++ | NEW_ACE_READ_ACL ++ | NEW_ACE_SYNCHRONIZE)) ++ { ++ case 0: ++ case NEW_ACE_READ_DATA: ++ case NEW_ACE_WRITEA_DATA: ++ case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: ++ case NEW_ACE_EXECUTE: ++ case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: ++ case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: ++ case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: ++ break; ++ default: ++ return 1; ++ } ++ switch ((access_masks[4] | access_masks[5]) ++ & ~(NEW_ACE_WRITE_NAMED_ATTRS ++ | NEW_ACE_WRITE_ATTRIBUTES ++ | NEW_ACE_WRITE_ACL ++ | NEW_ACE_WRITE_OWNER)) ++ { ++ case 0: ++ case NEW_ACE_READ_DATA: ++ case NEW_ACE_WRITEA_DATA: ++ case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA: ++ case NEW_ACE_EXECUTE: ++ case NEW_ACE_READ_DATA | NEW_ACE_EXECUTE: ++ case NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: ++ case NEW_ACE_READ_DATA | NEW_ACE_WRITEA_DATA | NEW_ACE_EXECUTE: ++ break; ++ default: ++ return 1; ++ } ++ ++ /* Check that the NEW_ACE_WRITE_DATA and NEW_ACE_APPEND_DATA bits are ++ either both allowed or both denied. */ ++ if (((access_masks[0] & NEW_ACE_WRITE_DATA) != 0) ++ != ((access_masks[0] & NEW_ACE_APPEND_DATA) != 0)) ++ return 1; ++ if (((access_masks[2] & NEW_ACE_WRITE_DATA) != 0) ++ != ((access_masks[2] & NEW_ACE_APPEND_DATA) != 0)) ++ return 1; ++ if (((access_masks[4] & NEW_ACE_WRITE_DATA) != 0) ++ != ((access_masks[4] & NEW_ACE_APPEND_DATA) != 0)) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++# endif ++ ++#elif USE_ACL && HAVE_GETACL /* HP-UX */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++acl_nontrivial (int count, struct acl_entry *entries, struct stat *sb) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ struct acl_entry *ace = &entries[i]; ++ ++ if (!((ace->uid == sb->st_uid && ace->gid == ACL_NSGROUP) ++ || (ace->uid == ACL_NSUSER && ace->gid == sb->st_gid) ++ || (ace->uid == ACL_NSUSER && ace->gid == ACL_NSGROUP))) ++ return 1; ++ } ++ return 0; ++} ++ ++# if HAVE_ACLV_H /* HP-UX >= 11.11 */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++aclv_nontrivial (int count, struct acl *entries) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ struct acl *ace = &entries[i]; ++ ++ /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). ++ If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). ++ We don't need to check ace->a_id in these cases. */ ++ if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */ ++ || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */ ++ || ace->a_type == CLASS_OBJ ++ || ace->a_type == OTHER_OBJ)) ++ return 1; ++ } ++ return 0; ++} ++ ++# endif ++ ++#elif USE_ACL && (HAVE_ACLX_GET || HAVE_STATACL) /* AIX */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++acl_nontrivial (struct acl *a) ++{ ++ /* The normal way to iterate through an ACL is like this: ++ struct acl_entry *ace; ++ for (ace = a->acl_ext; ace != acl_last (a); ace = acl_nxt (ace)) ++ { ++ struct ace_id *aei; ++ switch (ace->ace_type) ++ { ++ case ACC_PERMIT: ++ case ACC_DENY: ++ case ACC_SPECIFY: ++ ...; ++ } ++ for (aei = ace->ace_id; aei != id_last (ace); aei = id_nxt (aei)) ++ ... ++ } ++ */ ++ return (acl_last (a) != a->acl_ext ? 1 : 0); ++} ++ ++# if HAVE_ACLX_GET && defined ACL_AIX_WIP /* newer AIX */ ++ ++/* Return 1 if the given ACL is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++acl_nfs4_nontrivial (nfs4_acl_int_t *a) ++{ ++# if 1 /* let's try this first */ ++ return (a->aclEntryN > 0 ? 1 : 0); ++# else ++ int count = a->aclEntryN; ++ int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ nfs4_ace_int_t *ace = &a->aclEntry[i]; ++ ++ if (!((ace->flags & ACE4_ID_SPECIAL) != 0 ++ && (ace->aceWho.special_whoid == ACE4_WHO_OWNER ++ || ace->aceWho.special_whoid == ACE4_WHO_GROUP ++ || ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) ++ && ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE ++ && ace->aceFlags == 0 ++ && (ace->aceMask & ~(ACE4_READ_DATA | ACE4_LIST_DIRECTORY ++ | ACE4_WRITE_DATA | ACE4_ADD_FILE ++ | ACE4_EXECUTE)) == 0)) ++ return 1; ++ } ++ return 0; ++# endif ++} ++ ++# endif ++ ++#elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */ ++ ++/* Test an ACL retrieved with ACL_GET. ++ Return 1 if the given ACL, consisting of COUNT entries, is non-trivial. ++ Return 0 if it is trivial, i.e. equivalent to a simple stat() mode. */ ++int ++acl_nontrivial (int count, struct acl *entries) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) ++ { ++ struct acl *ace = &entries[i]; ++ ++ /* Note: If ace->a_type = USER_OBJ, ace->a_id is the st_uid from stat(). ++ If ace->a_type = GROUP_OBJ, ace->a_id is the st_gid from stat(). ++ We don't need to check ace->a_id in these cases. */ ++ if (!(ace->a_type == USER_OBJ /* no need to check ace->a_id here */ ++ || ace->a_type == GROUP_OBJ /* no need to check ace->a_id here */ ++ || ace->a_type == CLASS_OBJ ++ || ace->a_type == OTHER_OBJ)) ++ return 1; ++ } ++ return 0; ++} ++ ++#endif ++ ++ ++/* Return 1 if NAME has a nontrivial access control list, 0 if NAME ++ only has no or a base access control list, and -1 (setting errno) ++ on error. SB must be set to the stat buffer of NAME, obtained ++ through stat() or lstat(). */ ++ ++int ++file_has_acl (char const *name, struct stat const *sb) ++{ ++#if USE_ACL ++ if (! S_ISLNK (sb->st_mode)) ++ { ++# if HAVE_ACL_GET_FILE ++ ++ /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ ++ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ ++ int ret; ++ ++ if (HAVE_ACL_EXTENDED_FILE) /* Linux */ ++ { ++ /* On Linux, acl_extended_file is an optimized function: It only ++ makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for ++ ACL_TYPE_DEFAULT. */ ++ ret = acl_extended_file (name); ++ } ++ else /* FreeBSD, Mac OS X, IRIX, Tru64 */ ++ { ++# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */ ++ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) ++ always return NULL / EINVAL. There is no point in making ++ these two useless calls. The real ACL is retrieved through ++ acl_get_file (name, ACL_TYPE_EXTENDED). */ ++ acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED); ++ if (acl) ++ { ++ ret = acl_extended_nontrivial (acl); ++ acl_free (acl); ++ } ++ else ++ ret = -1; ++# else /* FreeBSD, IRIX, Tru64 */ ++ acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS); ++ if (acl) ++ { ++ int saved_errno; ++ ++ ret = acl_access_nontrivial (acl); ++ saved_errno = errno; ++ acl_free (acl); ++ errno = saved_errno; ++# if HAVE_ACL_FREE_TEXT /* Tru64 */ ++ /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always ++ returns NULL with errno not set. There is no point in ++ making this call. */ ++# else /* FreeBSD, IRIX */ ++ /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory ++ either both succeed or both fail; it depends on the ++ file system. Therefore there is no point in making the second ++ call if the first one already failed. */ ++ if (ret == 0 && S_ISDIR (sb->st_mode)) ++ { ++ acl = acl_get_file (name, ACL_TYPE_DEFAULT); ++ if (acl) ++ { ++ ret = (0 < acl_entries (acl)); ++ acl_free (acl); ++ } ++ else ++ ret = -1; ++ } ++# endif ++ } ++ else ++ ret = -1; ++# endif ++ } ++ if (ret < 0) ++ return ACL_NOT_WELL_SUPPORTED (errno) ? 0 : -1; ++ return ret; ++ ++# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ ++ ++# if defined ACL_NO_TRIVIAL ++ ++ /* Solaris 10 (newer version), which has additional API declared in ++ (acl_t) and implemented in libsec (acl_set, acl_trivial, ++ acl_fromtext, ...). */ ++ return acl_trivial (name); ++ ++# else /* Solaris, Cygwin, general case */ ++ ++ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions ++ of Unixware. The acl() call returns the access and default ACL both ++ at once. */ ++ { ++ /* Initially, try to read the entries into a stack-allocated buffer. ++ Use malloc if it does not fit. */ ++ enum ++ { ++ alloc_init = 4000 / sizeof (aclent_t), /* >= 3 */ ++ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t)) ++ }; ++ aclent_t buf[alloc_init]; ++ size_t alloc = alloc_init; ++ aclent_t *entries = buf; ++ aclent_t *malloced = NULL; ++ int count; ++ ++ for (;;) ++ { ++ count = acl (name, GETACL, alloc, entries); ++ if (count < 0 && errno == ENOSPC) ++ { ++ /* Increase the size of the buffer. */ ++ free (malloced); ++ if (alloc > alloc_max / 2) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ alloc = 2 * alloc; /* <= alloc_max */ ++ entries = malloced = ++ (aclent_t *) malloc (alloc * sizeof (aclent_t)); ++ if (entries == NULL) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ continue; ++ } ++ break; ++ } ++ if (count < 0) ++ { ++ if (errno == ENOSYS || errno == ENOTSUP) ++ ; ++ else ++ { ++ int saved_errno = errno; ++ free (malloced); ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ else if (count == 0) ++ ; ++ else ++ { ++ /* Don't use MIN_ACL_ENTRIES: It's set to 4 on Cygwin, but Cygwin ++ returns only 3 entries for files with no ACL. But this is safe: ++ If there are more than 4 entries, there cannot be only the ++ "user::", "group::", "other:", and "mask:" entries. */ ++ if (count > 4) ++ { ++ free (malloced); ++ return 1; ++ } ++ ++ if (acl_nontrivial (count, entries)) ++ { ++ free (malloced); ++ return 1; ++ } ++ } ++ free (malloced); ++ } ++ ++# ifdef ACE_GETACL ++ /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 ++ file systems (whereas the other ones are used in UFS file systems). */ ++ { ++ /* Initially, try to read the entries into a stack-allocated buffer. ++ Use malloc if it does not fit. */ ++ enum ++ { ++ alloc_init = 4000 / sizeof (ace_t), /* >= 3 */ ++ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) ++ }; ++ ace_t buf[alloc_init]; ++ size_t alloc = alloc_init; ++ ace_t *entries = buf; ++ ace_t *malloced = NULL; ++ int count; ++ ++ for (;;) ++ { ++ count = acl (name, ACE_GETACL, alloc, entries); ++ if (count < 0 && errno == ENOSPC) ++ { ++ /* Increase the size of the buffer. */ ++ free (malloced); ++ if (alloc > alloc_max / 2) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ alloc = 2 * alloc; /* <= alloc_max */ ++ entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t)); ++ if (entries == NULL) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ continue; ++ } ++ break; ++ } ++ if (count < 0) ++ { ++ if (errno == ENOSYS || errno == EINVAL) ++ ; ++ else ++ { ++ int saved_errno = errno; ++ free (malloced); ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ else if (count == 0) ++ ; ++ else ++ { ++ /* In the old (original Solaris 10) convention: ++ If there are more than 3 entries, there cannot be only the ++ ACE_OWNER, ACE_GROUP, ACE_OTHER entries. ++ In the newer Solaris 10 and Solaris 11 convention: ++ If there are more than 6 entries, there cannot be only the ++ ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with ++ NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with ++ NEW_ACE_ACCESS_DENIED_ACE_TYPE. */ ++ if (count > 6) ++ { ++ free (malloced); ++ return 1; ++ } ++ ++ if (acl_ace_nontrivial (count, entries)) ++ { ++ free (malloced); ++ return 1; ++ } ++ } ++ free (malloced); ++ } ++# endif ++ ++ return 0; ++# endif ++ ++# elif HAVE_GETACL /* HP-UX */ ++ ++ { ++ struct acl_entry entries[NACLENTRIES]; ++ int count; ++ ++ count = getacl (name, NACLENTRIES, entries); ++ ++ if (count < 0) ++ { ++ /* ENOSYS is seen on newer HP-UX versions. ++ EOPNOTSUPP is typically seen on NFS mounts. ++ ENOTSUP was seen on Quantum StorNext file systems (cvfs). */ ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) ++ ; ++ else ++ return -1; ++ } ++ else if (count == 0) ++ return 0; ++ else /* count > 0 */ ++ { ++ if (count > NACLENTRIES) ++ /* If NACLENTRIES cannot be trusted, use dynamic memory ++ allocation. */ ++ abort (); ++ ++ /* If there are more than 3 entries, there cannot be only the ++ (uid,%), (%,gid), (%,%) entries. */ ++ if (count > 3) ++ return 1; ++ ++ { ++ struct stat statbuf; ++ ++ if (stat (name, &statbuf) < 0) ++ return -1; ++ ++ return acl_nontrivial (count, entries, &statbuf); ++ } ++ } ++ } ++ ++# if HAVE_ACLV_H /* HP-UX >= 11.11 */ ++ ++ { ++ struct acl entries[NACLVENTRIES]; ++ int count; ++ ++ count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries); ++ ++ if (count < 0) ++ { ++ /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23. ++ EINVAL is seen on NFS in HP-UX 11.31. */ ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) ++ ; ++ else ++ return -1; ++ } ++ else if (count == 0) ++ return 0; ++ else /* count > 0 */ ++ { ++ if (count > NACLVENTRIES) ++ /* If NACLVENTRIES cannot be trusted, use dynamic memory ++ allocation. */ ++ abort (); ++ ++ /* If there are more than 4 entries, there cannot be only the ++ four base ACL entries. */ ++ if (count > 4) ++ return 1; ++ ++ return aclv_nontrivial (count, entries); ++ } ++ } ++ ++# endif ++ ++# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ ++ ++ acl_type_t type; ++ char aclbuf[1024]; ++ void *acl = aclbuf; ++ size_t aclsize = sizeof (aclbuf); ++ mode_t mode; ++ ++ for (;;) ++ { ++ /* The docs say that type being 0 is equivalent to ACL_ANY, but it ++ is not true, in AIX 5.3. */ ++ type.u64 = ACL_ANY; ++ if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0) ++ break; ++ if (errno == ENOSYS) ++ return 0; ++ if (errno != ENOSPC) ++ { ++ if (acl != aclbuf) ++ { ++ int saved_errno = errno; ++ free (acl); ++ errno = saved_errno; ++ } ++ return -1; ++ } ++ aclsize = 2 * aclsize; ++ if (acl != aclbuf) ++ free (acl); ++ acl = malloc (aclsize); ++ if (acl == NULL) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ } ++ ++ if (type.u64 == ACL_AIXC) ++ { ++ int result = acl_nontrivial ((struct acl *) acl); ++ if (acl != aclbuf) ++ free (acl); ++ return result; ++ } ++ else if (type.u64 == ACL_NFS4) ++ { ++ int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl); ++ if (acl != aclbuf) ++ free (acl); ++ return result; ++ } ++ else ++ { ++ /* A newer type of ACL has been introduced in the system. ++ We should better support it. */ ++ if (acl != aclbuf) ++ free (acl); ++ errno = EINVAL; ++ return -1; ++ } ++ ++# elif HAVE_STATACL /* older AIX */ ++ ++ union { struct acl a; char room[4096]; } u; ++ ++ if (statacl (name, STX_NORMAL, &u.a, sizeof (u)) < 0) ++ return -1; ++ ++ return acl_nontrivial (&u.a); ++ ++# elif HAVE_ACLSORT /* NonStop Kernel */ ++ ++ { ++ struct acl entries[NACLENTRIES]; ++ int count; ++ ++ count = acl ((char *) name, ACL_GET, NACLENTRIES, entries); ++ ++ if (count < 0) ++ { ++ if (errno == ENOSYS || errno == ENOTSUP) ++ ; ++ else ++ return -1; ++ } ++ else if (count == 0) ++ return 0; ++ else /* count > 0 */ ++ { ++ if (count > NACLENTRIES) ++ /* If NACLENTRIES cannot be trusted, use dynamic memory ++ allocation. */ ++ abort (); ++ ++ /* If there are more than 4 entries, there cannot be only the ++ four base ACL entries. */ ++ if (count > 4) ++ return 1; ++ ++ return acl_nontrivial (count, entries); ++ } ++ } ++ ++# endif ++ } ++#endif ++ ++ return 0; ++} +diff --git a/gnu/getfilecon.c b/gnu/getfilecon.c +new file mode 100644 +index 0000000..4a0f40d +--- /dev/null ++++ b/gnu/getfilecon.c +@@ -0,0 +1,88 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* wrap getfilecon, lgetfilecon, and fgetfilecon ++ 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 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 . */ ++ ++/* written by Jim Meyering */ ++ ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++/* FIXME: remove this once there is an errno-gnu module ++ that guarantees the definition of ENODATA. */ ++#ifndef ENODATA ++# define ENODATA ENOTSUP ++#endif ++ ++#undef getfilecon ++#undef lgetfilecon ++#undef fgetfilecon ++int getfilecon (char const *file, security_context_t *con); ++int lgetfilecon (char const *file, security_context_t *con); ++int fgetfilecon (int fd, security_context_t *con); ++ ++/* getfilecon, lgetfilecon, and fgetfilecon can all misbehave, be it ++ via an old version of libselinux where these would return 0 and set the ++ result context to NULL, or via a modern kernel+lib operating on a file ++ from a disk whose attributes were set by a kernel from around 2006. ++ In that latter case, the functions return a length of 10 for the ++ "unlabeled" context. Map both failures to a return value of -1, and ++ set errno to ENOTSUP in the first case, and ENODATA in the latter. */ ++ ++static inline int ++map_to_failure (int ret, security_context_t *con) ++{ ++ if (ret == 0) ++ { ++ errno = ENOTSUP; ++ return -1; ++ } ++ ++ if (ret == 10 && strcmp (*con, "unlabeled") == 0) ++ { ++ freecon (*con); ++ errno = ENODATA; ++ return -1; ++ } ++ ++ return ret; ++} ++ ++int ++rpl_getfilecon (char const *file, security_context_t *con) ++{ ++ int ret = getfilecon (file, con); ++ return map_to_failure (ret, con); ++} ++ ++int ++rpl_lgetfilecon (char const *file, security_context_t *con) ++{ ++ int ret = lgetfilecon (file, con); ++ return map_to_failure (ret, con); ++} ++ ++int ++rpl_fgetfilecon (int fd, security_context_t *con) ++{ ++ int ret = fgetfilecon (fd, con); ++ return map_to_failure (ret, con); ++} +diff --git a/gnu/se-context.in.h b/gnu/se-context.in.h +new file mode 100644 +index 0000000..adb13ba +--- /dev/null ++++ b/gnu/se-context.in.h +@@ -0,0 +1,30 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++#ifndef SELINUX_CONTEXT_H ++# define SELINUX_CONTEXT_H ++ ++# include ++ ++/* The definition of _GL_UNUSED_PARAMETER is copied here. */ ++ ++typedef int context_t; ++static inline context_t context_new (char const *s _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return 0; } ++static inline char *context_str (context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return (void *) 0; } ++static inline void context_free (context_t c _GL_UNUSED_PARAMETER) {} ++ ++static inline int context_user_set (context_t sc _GL_UNUSED_PARAMETER, ++ char const *s _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int context_role_set (context_t sc _GL_UNUSED_PARAMETER, ++ char const *s _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int context_range_set (context_t sc _GL_UNUSED_PARAMETER, ++ char const *s _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int context_type_set (context_t sc _GL_UNUSED_PARAMETER, ++ char const *s _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++ ++#endif +diff --git a/gnu/se-selinux.in.h b/gnu/se-selinux.in.h +new file mode 100644 +index 0000000..34205a1 +--- /dev/null ++++ b/gnu/se-selinux.in.h +@@ -0,0 +1,99 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* Replacement for platforms that lack it. ++ Copyright (C) 2008-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 . */ ++ ++#ifndef _@GUARD_PREFIX@_SELINUX_SELINUX_H ++# define _@GUARD_PREFIX@_SELINUX_SELINUX_H ++ ++# if __GNUC__ >= 3 ++@PRAGMA_SYSTEM_HEADER@ ++# endif ++@PRAGMA_COLUMNS@ ++ ++# if HAVE_SELINUX_SELINUX_H ++ ++#@INCLUDE_NEXT@ @NEXT_SELINUX_SELINUX_H@ ++ ++# else ++ ++# include ++# include ++ ++/* The definition of _GL_UNUSED_PARAMETER is copied here. */ ++ ++# if !GNULIB_defined_security_types ++ ++typedef unsigned short security_class_t; ++# define security_context_t char* ++# define is_selinux_enabled() 0 ++ ++static inline int getcon (security_context_t *con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline void freecon (security_context_t con _GL_UNUSED_PARAMETER) {} ++ ++ ++static inline int getfscreatecon (security_context_t *con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int setfscreatecon (security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int matchpathcon (char const *file _GL_UNUSED_PARAMETER, ++ mode_t m _GL_UNUSED_PARAMETER, ++ security_context_t *con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int getfilecon (char const *file _GL_UNUSED_PARAMETER, ++ security_context_t *con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int lgetfilecon (char const *file _GL_UNUSED_PARAMETER, ++ security_context_t *con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int fgetfilecon (int fd, ++ security_context_t *con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int setfilecon (char const *file _GL_UNUSED_PARAMETER, ++ security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int lsetfilecon (char const *file _GL_UNUSED_PARAMETER, ++ security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int fsetfilecon (int fd _GL_UNUSED_PARAMETER, ++ security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++ ++static inline int security_check_context ++ (security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int security_check_context_raw ++ (security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int setexeccon (security_context_t con _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int security_compute_create ++ (security_context_t scon _GL_UNUSED_PARAMETER, ++ security_context_t tcon _GL_UNUSED_PARAMETER, ++ security_class_t tclass _GL_UNUSED_PARAMETER, ++ security_context_t *newcon _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++static inline int matchpathcon_init_prefix ++ (char const *path _GL_UNUSED_PARAMETER, ++ char const *prefix _GL_UNUSED_PARAMETER) ++ { errno = ENOTSUP; return -1; } ++ ++# define GNULIB_defined_security_types 1 ++# endif ++ ++# endif ++#endif /* _@GUARD_PREFIX@_SELINUX_SELINUX_H */ +diff --git a/gnu/selinux-at.c b/gnu/selinux-at.c +new file mode 100644 +index 0000000..f6619fa +--- /dev/null ++++ b/gnu/selinux-at.c +@@ -0,0 +1,74 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* openat-style fd-relative functions for SE Linux ++ Copyright (C) 2007, 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 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 . */ ++ ++/* written by Jim Meyering */ ++ ++#include ++ ++#include "selinux-at.h" ++#include "openat.h" ++ ++#include ++#include ++#include ++#include ++ ++#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */ ++#include "save-cwd.h" ++ ++#include "openat-priv.h" ++ ++#define AT_FUNC_NAME getfileconat ++#define AT_FUNC_F1 getfilecon ++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t *con ++#define AT_FUNC_POST_FILE_ARGS , con ++#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 ++ ++#define AT_FUNC_NAME lgetfileconat ++#define AT_FUNC_F1 lgetfilecon ++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t *con ++#define AT_FUNC_POST_FILE_ARGS , con ++#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 ++ ++#define AT_FUNC_NAME setfileconat ++#define AT_FUNC_F1 setfilecon ++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t con ++#define AT_FUNC_POST_FILE_ARGS , con ++#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 ++ ++#define AT_FUNC_NAME lsetfileconat ++#define AT_FUNC_F1 lsetfilecon ++#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t con ++#define AT_FUNC_POST_FILE_ARGS , con ++#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 +diff --git a/gnu/selinux-at.h b/gnu/selinux-at.h +new file mode 100644 +index 0000000..4ab3109 +--- /dev/null ++++ b/gnu/selinux-at.h +@@ -0,0 +1,54 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* Prototypes for openat-style fd-relative SELinux functions ++ Copyright (C) 2007, 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 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 . */ ++ ++#include ++#include ++ ++/* These are the dir-fd-relative variants of the functions without the ++ "at" suffix. For example, getfileconat (AT_FDCWD, file, &c) is usually ++ equivalent to getfilecon (file, &c). The emulation is accomplished ++ by first attempting getfilecon ("/proc/self/fd/DIR_FD/FILE", &c). ++ Failing that, simulate it via save_cwd/fchdir/getfilecon/restore_cwd. ++ If either the save_cwd or the restore_cwd fails (relatively unlikely), ++ then give a diagnostic and exit nonzero. */ ++ ++/* dir-fd-relative getfilecon. Set *CON to the SELinux security context ++ of the file specified by DIR_FD and FILE and return the length of *CON. ++ DIR_FD and FILE are interpreted as for fstatat[*]. A non-NULL *CON ++ must be freed with freecon. Upon error, set *CON to NULL, set errno ++ and return -1. ++ [*] with flags=0 here, with flags=AT_SYMLINK_NOFOLLOW for lgetfileconat */ ++int getfileconat (int dir_fd, char const *file, security_context_t *con); ++ ++/* dir-fd-relative lgetfilecon. This function is just like getfileconat, ++ except when DIR_FD and FILE specify a symlink: lgetfileconat operates on ++ the symlink, while getfileconat operates on the referent of the symlink. */ ++int lgetfileconat (int dir_fd, char const *file, security_context_t *con); ++ ++/* dir-fd-relative setfilecon. Set the SELinux security context of ++ the file specified by DIR_FD and FILE to CON. DIR_FD and FILE are ++ interpreted as for fstatat[*]. Upon success, return 0. ++ Otherwise, return -1 and set errno. */ ++int setfileconat (int dir_fd, char const *file, security_context_t con); ++ ++/* dir-fd-relative lsetfilecon. This function is just like setfileconat, ++ except that rather than dereferencing a symlink, this function affects it. */ ++/* dir-fd-relative lsetfilecon. This function is just like setfileconat, ++ except when DIR_FD and FILE specify a symlink: lsetfileconat operates on ++ the symlink, while setfileconat operates on the referent of the symlink. */ ++int lsetfileconat (int dir_fd, char const *file, security_context_t con); +diff --git a/gnu/set-mode-acl.c b/gnu/set-mode-acl.c +new file mode 100644 +index 0000000..edc8e26 +--- /dev/null ++++ b/gnu/set-mode-acl.c +@@ -0,0 +1,699 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* set-mode-acl.c - set access control list equivalent to a mode ++ ++ Copyright (C) 2002-2003, 2005-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 . ++ ++ Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */ ++ ++#include ++ ++#include "acl.h" ++ ++#include "acl-internal.h" ++ ++#include "gettext.h" ++#define _(msgid) gettext (msgid) ++ ++ ++/* If DESC is a valid file descriptor use fchmod to change the ++ file's mode to MODE on systems that have fchown. On systems ++ that don't have fchown and if DESC is invalid, use chown on ++ NAME instead. ++ Return 0 if successful. Return -1 and set errno upon failure. */ ++ ++int ++chmod_or_fchmod (const char *name, int desc, mode_t mode) ++{ ++ if (HAVE_FCHMOD && desc != -1) ++ return fchmod (desc, mode); ++ else ++ return chmod (name, mode); ++} ++ ++/* Set the access control lists of a file. If DESC is a valid file ++ descriptor, use file descriptor operations where available, else use ++ filename based operations on NAME. If access control lists are not ++ available, fchmod the target file to MODE. Also sets the ++ non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX) ++ to those from MODE if any are set. ++ Return 0 if successful. Return -1 and set errno upon failure. */ ++ ++int ++qset_acl (char const *name, int desc, mode_t mode) ++{ ++#if USE_ACL ++# if HAVE_ACL_GET_FILE ++ /* POSIX 1003.1e draft 17 (abandoned) specific version. */ ++ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */ ++# if !HAVE_ACL_TYPE_EXTENDED ++ /* Linux, FreeBSD, IRIX, Tru64 */ ++ ++ /* We must also have acl_from_text and acl_delete_def_file. ++ (acl_delete_def_file could be emulated with acl_init followed ++ by acl_set_file, but acl_set_file with an empty acl is ++ unspecified.) */ ++ ++# ifndef HAVE_ACL_FROM_TEXT ++# error Must have acl_from_text (see POSIX 1003.1e draft 17). ++# endif ++# ifndef HAVE_ACL_DELETE_DEF_FILE ++# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). ++# endif ++ ++ acl_t acl; ++ int ret; ++ ++ if (HAVE_ACL_FROM_MODE) /* Linux */ ++ { ++ acl = acl_from_mode (mode); ++ if (!acl) ++ return -1; ++ } ++ else /* FreeBSD, IRIX, Tru64 */ ++ { ++ /* If we were to create the ACL using the functions acl_init(), ++ acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(), ++ acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we ++ would need to create a qualifier. I don't know how to do this. ++ So create it using acl_from_text(). */ ++ ++# if HAVE_ACL_FREE_TEXT /* Tru64 */ ++ char acl_text[] = "u::---,g::---,o::---,"; ++# else /* FreeBSD, IRIX */ ++ char acl_text[] = "u::---,g::---,o::---"; ++# endif ++ ++ if (mode & S_IRUSR) acl_text[ 3] = 'r'; ++ if (mode & S_IWUSR) acl_text[ 4] = 'w'; ++ if (mode & S_IXUSR) acl_text[ 5] = 'x'; ++ if (mode & S_IRGRP) acl_text[10] = 'r'; ++ if (mode & S_IWGRP) acl_text[11] = 'w'; ++ if (mode & S_IXGRP) acl_text[12] = 'x'; ++ if (mode & S_IROTH) acl_text[17] = 'r'; ++ if (mode & S_IWOTH) acl_text[18] = 'w'; ++ if (mode & S_IXOTH) acl_text[19] = 'x'; ++ ++ acl = acl_from_text (acl_text); ++ if (!acl) ++ return -1; ++ } ++ if (HAVE_ACL_SET_FD && desc != -1) ++ ret = acl_set_fd (desc, acl); ++ else ++ ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); ++ if (ret != 0) ++ { ++ int saved_errno = errno; ++ acl_free (acl); ++ ++ if (ACL_NOT_WELL_SUPPORTED (errno)) ++ return chmod_or_fchmod (name, desc, mode); ++ else ++ { ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ else ++ acl_free (acl); ++ ++ if (S_ISDIR (mode) && acl_delete_def_file (name)) ++ return -1; ++ ++ if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) ++ { ++ /* We did not call chmod so far, and either the mode and the ACL are ++ separate or special bits are to be set which don't fit into ACLs. */ ++ return chmod_or_fchmod (name, desc, mode); ++ } ++ return 0; ++ ++# else /* HAVE_ACL_TYPE_EXTENDED */ ++ /* Mac OS X */ ++ ++ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS) ++ and acl_get_file (name, ACL_TYPE_DEFAULT) ++ always return NULL / EINVAL. You have to use ++ acl_get_file (name, ACL_TYPE_EXTENDED) ++ or acl_get_fd (open (name, ...)) ++ to retrieve an ACL. ++ On the other hand, ++ acl_set_file (name, ACL_TYPE_ACCESS, acl) ++ and acl_set_file (name, ACL_TYPE_DEFAULT, acl) ++ have the same effect as ++ acl_set_file (name, ACL_TYPE_EXTENDED, acl): ++ Each of these calls sets the file's ACL. */ ++ ++ acl_t acl; ++ int ret; ++ ++ /* Remove the ACL if the file has ACLs. */ ++ if (HAVE_ACL_GET_FD && desc != -1) ++ acl = acl_get_fd (desc); ++ else ++ acl = acl_get_file (name, ACL_TYPE_EXTENDED); ++ if (acl) ++ { ++ acl_free (acl); ++ ++ acl = acl_init (0); ++ if (acl) ++ { ++ if (HAVE_ACL_SET_FD && desc != -1) ++ ret = acl_set_fd (desc, acl); ++ else ++ ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); ++ if (ret != 0) ++ { ++ int saved_errno = errno; ++ ++ acl_free (acl); ++ ++ if (ACL_NOT_WELL_SUPPORTED (saved_errno)) ++ return chmod_or_fchmod (name, desc, mode); ++ else ++ { ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ acl_free (acl); ++ } ++ } ++ ++ /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ ++ return chmod_or_fchmod (name, desc, mode); ++# endif ++ ++# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */ ++ ++ int done_setacl = 0; ++ ++# ifdef ACE_GETACL ++ /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 ++ file systems (whereas the other ones are used in UFS file systems). */ ++ ++ /* The flags in the ace_t structure changed in a binary incompatible way ++ when ACL_NO_TRIVIAL etc. were introduced in version 1.15. ++ How to distinguish the two conventions at runtime? ++ We fetch the existing ACL. In the old convention, usually three ACEs have ++ a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. ++ In the new convention, these values are not used. */ ++ int convention; ++ ++ { ++ /* Initially, try to read the entries into a stack-allocated buffer. ++ Use malloc if it does not fit. */ ++ enum ++ { ++ alloc_init = 4000 / sizeof (ace_t), /* >= 3 */ ++ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) ++ }; ++ ace_t buf[alloc_init]; ++ size_t alloc = alloc_init; ++ ace_t *entries = buf; ++ ace_t *malloced = NULL; ++ int count; ++ ++ for (;;) ++ { ++ count = (desc != -1 ++ ? facl (desc, ACE_GETACL, alloc, entries) ++ : acl (name, ACE_GETACL, alloc, entries)); ++ if (count < 0 && errno == ENOSPC) ++ { ++ /* Increase the size of the buffer. */ ++ free (malloced); ++ if (alloc > alloc_max / 2) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ alloc = 2 * alloc; /* <= alloc_max */ ++ entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t)); ++ if (entries == NULL) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ continue; ++ } ++ break; ++ } ++ ++ if (count <= 0) ++ convention = -1; ++ else ++ { ++ int i; ++ ++ convention = 0; ++ for (i = 0; i < count; i++) ++ if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) ++ { ++ convention = 1; ++ break; ++ } ++ } ++ free (malloced); ++ } ++ ++ if (convention >= 0) ++ { ++ ace_t entries[6]; ++ int count; ++ int ret; ++ ++ if (convention) ++ { ++ /* Running on Solaris 10. */ ++ entries[0].a_type = OLD_ALLOW; ++ entries[0].a_flags = OLD_ACE_OWNER; ++ entries[0].a_who = 0; /* irrelevant */ ++ entries[0].a_access_mask = (mode >> 6) & 7; ++ entries[1].a_type = OLD_ALLOW; ++ entries[1].a_flags = OLD_ACE_GROUP; ++ entries[1].a_who = 0; /* irrelevant */ ++ entries[1].a_access_mask = (mode >> 3) & 7; ++ entries[2].a_type = OLD_ALLOW; ++ entries[2].a_flags = OLD_ACE_OTHER; ++ entries[2].a_who = 0; ++ entries[2].a_access_mask = mode & 7; ++ count = 3; ++ } ++ else ++ { ++ /* Running on Solaris 10 (newer version) or Solaris 11. ++ The details here were found through "/bin/ls -lvd somefiles". */ ++ entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; ++ entries[0].a_flags = NEW_ACE_OWNER; ++ entries[0].a_who = 0; /* irrelevant */ ++ entries[0].a_access_mask = 0; ++ entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; ++ entries[1].a_flags = NEW_ACE_OWNER; ++ entries[1].a_who = 0; /* irrelevant */ ++ entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS ++ | NEW_ACE_WRITE_ATTRIBUTES ++ | NEW_ACE_WRITE_ACL ++ | NEW_ACE_WRITE_OWNER; ++ if (mode & 0400) ++ entries[1].a_access_mask |= NEW_ACE_READ_DATA; ++ else ++ entries[0].a_access_mask |= NEW_ACE_READ_DATA; ++ if (mode & 0200) ++ entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; ++ else ++ entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; ++ if (mode & 0100) ++ entries[1].a_access_mask |= NEW_ACE_EXECUTE; ++ else ++ entries[0].a_access_mask |= NEW_ACE_EXECUTE; ++ entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; ++ entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; ++ entries[2].a_who = 0; /* irrelevant */ ++ entries[2].a_access_mask = 0; ++ entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; ++ entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; ++ entries[3].a_who = 0; /* irrelevant */ ++ entries[3].a_access_mask = 0; ++ if (mode & 0040) ++ entries[3].a_access_mask |= NEW_ACE_READ_DATA; ++ else ++ entries[2].a_access_mask |= NEW_ACE_READ_DATA; ++ if (mode & 0020) ++ entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; ++ else ++ entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; ++ if (mode & 0010) ++ entries[3].a_access_mask |= NEW_ACE_EXECUTE; ++ else ++ entries[2].a_access_mask |= NEW_ACE_EXECUTE; ++ entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; ++ entries[4].a_flags = NEW_ACE_EVERYONE; ++ entries[4].a_who = 0; ++ entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS ++ | NEW_ACE_WRITE_ATTRIBUTES ++ | NEW_ACE_WRITE_ACL ++ | NEW_ACE_WRITE_OWNER; ++ entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; ++ entries[5].a_flags = NEW_ACE_EVERYONE; ++ entries[5].a_who = 0; ++ entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS ++ | NEW_ACE_READ_ATTRIBUTES ++ | NEW_ACE_READ_ACL ++ | NEW_ACE_SYNCHRONIZE; ++ if (mode & 0004) ++ entries[5].a_access_mask |= NEW_ACE_READ_DATA; ++ else ++ entries[4].a_access_mask |= NEW_ACE_READ_DATA; ++ if (mode & 0002) ++ entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; ++ else ++ entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; ++ if (mode & 0001) ++ entries[5].a_access_mask |= NEW_ACE_EXECUTE; ++ else ++ entries[4].a_access_mask |= NEW_ACE_EXECUTE; ++ count = 6; ++ } ++ if (desc != -1) ++ ret = facl (desc, ACE_SETACL, count, entries); ++ else ++ ret = acl (name, ACE_SETACL, count, entries); ++ if (ret < 0 && errno != EINVAL && errno != ENOTSUP) ++ { ++ if (errno == ENOSYS) ++ return chmod_or_fchmod (name, desc, mode); ++ return -1; ++ } ++ if (ret == 0) ++ done_setacl = 1; ++ } ++# endif ++ ++ if (!done_setacl) ++ { ++ aclent_t entries[3]; ++ int ret; ++ ++ entries[0].a_type = USER_OBJ; ++ entries[0].a_id = 0; /* irrelevant */ ++ entries[0].a_perm = (mode >> 6) & 7; ++ entries[1].a_type = GROUP_OBJ; ++ entries[1].a_id = 0; /* irrelevant */ ++ entries[1].a_perm = (mode >> 3) & 7; ++ entries[2].a_type = OTHER_OBJ; ++ entries[2].a_id = 0; ++ entries[2].a_perm = mode & 7; ++ ++ if (desc != -1) ++ ret = facl (desc, SETACL, ++ sizeof (entries) / sizeof (aclent_t), entries); ++ else ++ ret = acl (name, SETACL, ++ sizeof (entries) / sizeof (aclent_t), entries); ++ if (ret < 0) ++ { ++ if (errno == ENOSYS || errno == EOPNOTSUPP) ++ return chmod_or_fchmod (name, desc, mode); ++ return -1; ++ } ++ } ++ ++ if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) ++ { ++ /* We did not call chmod so far, so the special bits have not yet ++ been set. */ ++ return chmod_or_fchmod (name, desc, mode); ++ } ++ return 0; ++ ++# elif HAVE_GETACL /* HP-UX */ ++ ++ struct stat statbuf; ++ int ret; ++ ++ if (desc != -1) ++ ret = fstat (desc, &statbuf); ++ else ++ ret = stat (name, &statbuf); ++ if (ret < 0) ++ return -1; ++ ++ { ++ struct acl_entry entries[3]; ++ ++ entries[0].uid = statbuf.st_uid; ++ entries[0].gid = ACL_NSGROUP; ++ entries[0].mode = (mode >> 6) & 7; ++ entries[1].uid = ACL_NSUSER; ++ entries[1].gid = statbuf.st_gid; ++ entries[1].mode = (mode >> 3) & 7; ++ entries[2].uid = ACL_NSUSER; ++ entries[2].gid = ACL_NSGROUP; ++ entries[2].mode = mode & 7; ++ ++ if (desc != -1) ++ ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries); ++ else ++ ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries); ++ } ++ if (ret < 0) ++ { ++ if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)) ++ return -1; ++ ++# if HAVE_ACLV_H /* HP-UX >= 11.11 */ ++ { ++ struct acl entries[4]; ++ ++ entries[0].a_type = USER_OBJ; ++ entries[0].a_id = 0; /* irrelevant */ ++ entries[0].a_perm = (mode >> 6) & 7; ++ entries[1].a_type = GROUP_OBJ; ++ entries[1].a_id = 0; /* irrelevant */ ++ entries[1].a_perm = (mode >> 3) & 7; ++ entries[2].a_type = CLASS_OBJ; ++ entries[2].a_id = 0; ++ entries[2].a_perm = (mode >> 3) & 7; ++ entries[3].a_type = OTHER_OBJ; ++ entries[3].a_id = 0; ++ entries[3].a_perm = mode & 7; ++ ++ ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries); ++ if (ret > 0) ++ abort (); ++ if (ret < 0) ++ { ++ if (0) ++ return chmod_or_fchmod (name, desc, mode); ++ return -1; ++ } ++ ++ ret = acl ((char *) name, ACL_SET, ++ sizeof (entries) / sizeof (struct acl), entries); ++ if (ret < 0) ++ { ++ if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) ++ return chmod_or_fchmod (name, desc, mode); ++ return -1; ++ } ++ } ++# else ++ return chmod_or_fchmod (name, desc, mode); ++# endif ++ } ++ ++ if (mode & (S_ISUID | S_ISGID | S_ISVTX)) ++ { ++ /* We did not call chmod so far, so the special bits have not yet ++ been set. */ ++ return chmod_or_fchmod (name, desc, mode); ++ } ++ return 0; ++ ++# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ ++ ++ acl_type_list_t types; ++ size_t types_size = sizeof (types); ++ acl_type_t type; ++ ++ if (aclx_gettypes (name, &types, &types_size) < 0 ++ || types.num_entries == 0) ++ return chmod_or_fchmod (name, desc, mode); ++ ++ /* XXX Do we need to clear all types of ACLs for the given file, or is it ++ sufficient to clear the first one? */ ++ type = types.entries[0]; ++ if (type.u64 == ACL_AIXC) ++ { ++ union { struct acl a; char room[128]; } u; ++ int ret; ++ ++ u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ ++ u.a.acl_mode = mode & ~(S_IXACL | 0777); ++ u.a.u_access = (mode >> 6) & 7; ++ u.a.g_access = (mode >> 3) & 7; ++ u.a.o_access = mode & 7; ++ ++ if (desc != -1) ++ ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, ++ type, &u.a, u.a.acl_len, mode); ++ else ++ ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, ++ type, &u.a, u.a.acl_len, mode); ++ if (!(ret < 0 && errno == ENOSYS)) ++ return ret; ++ } ++ else if (type.u64 == ACL_NFS4) ++ { ++ union { nfs4_acl_int_t a; char room[128]; } u; ++ nfs4_ace_int_t *ace; ++ int ret; ++ ++ u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION; ++ u.a.aclEntryN = 0; ++ ace = &u.a.aclEntry[0]; ++ { ++ ace->flags = ACE4_ID_SPECIAL; ++ ace->aceWho.special_whoid = ACE4_WHO_OWNER; ++ ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->aceFlags = 0; ++ ace->aceMask = ++ (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) ++ | (mode & 0200 ++ ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA ++ | ACE4_ADD_SUBDIRECTORY ++ : 0) ++ | (mode & 0100 ? ACE4_EXECUTE : 0); ++ ace->aceWhoString[0] = '\0'; ++ ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; ++ ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; ++ u.a.aclEntryN++; ++ } ++ { ++ ace->flags = ACE4_ID_SPECIAL; ++ ace->aceWho.special_whoid = ACE4_WHO_GROUP; ++ ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->aceFlags = 0; ++ ace->aceMask = ++ (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) ++ | (mode & 0020 ++ ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA ++ | ACE4_ADD_SUBDIRECTORY ++ : 0) ++ | (mode & 0010 ? ACE4_EXECUTE : 0); ++ ace->aceWhoString[0] = '\0'; ++ ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; ++ ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; ++ u.a.aclEntryN++; ++ } ++ { ++ ace->flags = ACE4_ID_SPECIAL; ++ ace->aceWho.special_whoid = ACE4_WHO_EVERYONE; ++ ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->aceFlags = 0; ++ ace->aceMask = ++ (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) ++ | (mode & 0002 ++ ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA ++ | ACE4_ADD_SUBDIRECTORY ++ : 0) ++ | (mode & 0001 ? ACE4_EXECUTE : 0); ++ ace->aceWhoString[0] = '\0'; ++ ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; ++ ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; ++ u.a.aclEntryN++; ++ } ++ u.a.aclLength = (char *) ace - (char *) &u.a; ++ ++ if (desc != -1) ++ ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, ++ type, &u.a, u.a.aclLength, mode); ++ else ++ ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, ++ type, &u.a, u.a.aclLength, mode); ++ if (!(ret < 0 && errno == ENOSYS)) ++ return ret; ++ } ++ ++ return chmod_or_fchmod (name, desc, mode); ++ ++# elif HAVE_STATACL /* older AIX */ ++ ++ union { struct acl a; char room[128]; } u; ++ int ret; ++ ++ u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ ++ u.a.acl_mode = mode & ~(S_IXACL | 0777); ++ u.a.u_access = (mode >> 6) & 7; ++ u.a.g_access = (mode >> 3) & 7; ++ u.a.o_access = mode & 7; ++ ++ if (desc != -1) ++ ret = fchacl (desc, &u.a, u.a.acl_len); ++ else ++ ret = chacl (name, &u.a, u.a.acl_len); ++ ++ if (ret < 0 && errno == ENOSYS) ++ return chmod_or_fchmod (name, desc, mode); ++ ++ return ret; ++ ++# elif HAVE_ACLSORT /* NonStop Kernel */ ++ ++ struct acl entries[4]; ++ int ret; ++ ++ entries[0].a_type = USER_OBJ; ++ entries[0].a_id = 0; /* irrelevant */ ++ entries[0].a_perm = (mode >> 6) & 7; ++ entries[1].a_type = GROUP_OBJ; ++ entries[1].a_id = 0; /* irrelevant */ ++ entries[1].a_perm = (mode >> 3) & 7; ++ entries[2].a_type = CLASS_OBJ; ++ entries[2].a_id = 0; ++ entries[2].a_perm = (mode >> 3) & 7; ++ entries[3].a_type = OTHER_OBJ; ++ entries[3].a_id = 0; ++ entries[3].a_perm = mode & 7; ++ ++ ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries); ++ if (ret > 0) ++ abort (); ++ if (ret < 0) ++ { ++ if (0) ++ return chmod_or_fchmod (name, desc, mode); ++ return -1; ++ } ++ ++ ret = acl ((char *) name, ACL_SET, ++ sizeof (entries) / sizeof (struct acl), entries); ++ if (ret < 0) ++ { ++ if (0) ++ return chmod_or_fchmod (name, desc, mode); ++ return -1; ++ } ++ ++ if (mode & (S_ISUID | S_ISGID | S_ISVTX)) ++ { ++ /* We did not call chmod so far, so the special bits have not yet ++ been set. */ ++ return chmod_or_fchmod (name, desc, mode); ++ } ++ return 0; ++ ++# else /* Unknown flavor of ACLs */ ++ return chmod_or_fchmod (name, desc, mode); ++# endif ++#else /* !USE_ACL */ ++ return chmod_or_fchmod (name, desc, mode); ++#endif ++} ++ ++/* As with qset_acl, but also output a diagnostic on failure. */ ++ ++int ++set_acl (char const *name, int desc, mode_t mode) ++{ ++ int ret = qset_acl (name, desc, mode); ++ if (ret != 0) ++ error (0, errno, _("setting permissions for %s"), quote (name)); ++ return ret; ++} +diff --git a/gnu/xattr-at.c b/gnu/xattr-at.c +new file mode 100644 +index 0000000..73e8a6e +--- /dev/null ++++ b/gnu/xattr-at.c +@@ -0,0 +1,112 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++/* 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 . */ ++ ++#include ++ ++#include "xattr-at.h" ++#include "openat.h" ++ ++#include ++#include ++#include ++#include ++ ++#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/gnu/xattr-at.h b/gnu/xattr-at.h +new file mode 100644 +index 0000000..d7c3901 +--- /dev/null ++++ b/gnu/xattr-at.h +@@ -0,0 +1,68 @@ ++/* -*- buffer-read-only: t -*- vi: set ro: */ ++/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ ++#ifndef XATTRS_AT_H ++#define XATTRS_AT_H ++ ++/* 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 . */ ++ ++#include ++#include ++ ++/* 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/m4/acl.m4 b/m4/acl.m4 +new file mode 100644 +index 0000000..19aa548 +--- /dev/null ++++ b/m4/acl.m4 +@@ -0,0 +1,165 @@ ++# acl.m4 - check for access control list (ACL) primitives ++# serial 14 ++ ++# Copyright (C) 2002, 2004-2012 Free Software Foundation, Inc. ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# Written by Paul Eggert and Jim Meyering. ++ ++AC_DEFUN([gl_FUNC_ACL], ++[ ++ AC_ARG_ENABLE([acl], ++ AS_HELP_STRING([--disable-acl], [do not support ACLs]), ++ , [enable_acl=auto]) ++ ++ LIB_ACL= ++ use_acl=0 ++ AC_REQUIRE([AC_C_INLINE]) ++ if test "x$enable_acl" != "xno"; then ++ dnl On all platforms, the ACL related API is declared in . ++ AC_CHECK_HEADERS([sys/acl.h]) ++ if test $ac_cv_header_sys_acl_h = yes; then ++ ac_save_LIBS=$LIBS ++ ++ dnl Test for POSIX-draft-like API (Linux, FreeBSD, Mac OS X, IRIX, Tru64). ++ dnl -lacl is needed on Linux, -lpacl is needed on OSF/1. ++ if test $use_acl = 0; then ++ AC_SEARCH_LIBS([acl_get_file], [acl pacl], ++ [if test "$ac_cv_search_acl_get_file" != "none required"; then ++ LIB_ACL=$ac_cv_search_acl_get_file ++ fi ++ AC_CHECK_FUNCS( ++ [acl_get_file acl_get_fd acl_set_file acl_set_fd \ ++ acl_free acl_from_mode acl_from_text \ ++ acl_delete_def_file acl_extended_file \ ++ acl_delete_fd_np acl_delete_file_np \ ++ acl_copy_ext_native acl_create_entry_np \ ++ acl_to_short_text acl_free_text]) ++ # If the acl_get_file bug is detected, don't enable the ACL support. ++ gl_ACL_GET_FILE([use_acl=1], []) ++ if test $use_acl = 1; then ++ dnl On Linux, additional API is declared in . ++ AC_CHECK_HEADERS([acl/libacl.h]) ++ AC_REPLACE_FUNCS([acl_entries]) ++ AC_CACHE_CHECK([for ACL_FIRST_ENTRY], ++ [gl_cv_acl_ACL_FIRST_ENTRY], ++ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM( ++[[#include ++#include ++int type = ACL_FIRST_ENTRY;]])], ++ [gl_cv_acl_ACL_FIRST_ENTRY=yes], ++ [gl_cv_acl_ACL_FIRST_ENTRY=no])]) ++ if test $gl_cv_acl_ACL_FIRST_ENTRY = yes; then ++ AC_DEFINE([HAVE_ACL_FIRST_ENTRY], [1], ++ [Define to 1 if the constant ACL_FIRST_ENTRY exists.]) ++ fi ++ dnl On Mac OS X, other types of ACLs are supported. ++ AC_CACHE_CHECK([for ACL_TYPE_EXTENDED], ++ [gl_cv_acl_ACL_TYPE_EXTENDED], ++ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM( ++[[#include ++#include ++int type = ACL_TYPE_EXTENDED;]])], ++ [gl_cv_acl_ACL_TYPE_EXTENDED=yes], ++ [gl_cv_acl_ACL_TYPE_EXTENDED=no])]) ++ if test $gl_cv_acl_ACL_TYPE_EXTENDED = yes; then ++ AC_DEFINE([HAVE_ACL_TYPE_EXTENDED], [1], ++ [Define to 1 if the ACL type ACL_TYPE_EXTENDED exists.]) ++ fi ++ else ++ LIB_ACL= ++ fi ++ ]) ++ fi ++ ++ dnl Test for Solaris API (Solaris, Cygwin). ++ if test $use_acl = 0; then ++ AC_CHECK_FUNCS([facl]) ++ if test $ac_cv_func_facl = yes; then ++ AC_SEARCH_LIBS([acl_trivial], [sec], ++ [if test "$ac_cv_search_acl_trivial" != "none required"; then ++ LIB_ACL=$ac_cv_search_acl_trivial ++ fi ++ ]) ++ AC_CHECK_FUNCS([acl_trivial]) ++ use_acl=1 ++ fi ++ fi ++ ++ dnl Test for HP-UX API. ++ if test $use_acl = 0; then ++ AC_CHECK_FUNCS([getacl]) ++ if test $ac_cv_func_getacl = yes; then ++ use_acl=1 ++ fi ++ dnl Test for HP-UX 11.11 API. ++ AC_CHECK_HEADERS([aclv.h], [], [], [#include ]) ++ fi ++ ++ dnl Test for AIX API (AIX 5.3 or newer). ++ if test $use_acl = 0; then ++ AC_CHECK_FUNCS([aclx_get]) ++ if test $ac_cv_func_aclx_get = yes; then ++ use_acl=1 ++ fi ++ fi ++ ++ dnl Test for older AIX API. ++ if test $use_acl = 0 || test "$ac_cv_func_aclx_get" = yes; then ++ AC_CHECK_FUNCS([statacl]) ++ if test $ac_cv_func_statacl = yes; then ++ use_acl=1 ++ fi ++ fi ++ ++ dnl Test for NonStop Kernel API. ++ if test $use_acl = 0; then ++ AC_CHECK_FUNCS([aclsort]) ++ if test $ac_cv_func_aclsort = yes; then ++ use_acl=1 ++ fi ++ fi ++ ++ LIBS=$ac_save_LIBS ++ fi ++ if test "x$enable_acl$use_acl" = "xyes0"; then ++ AC_MSG_ERROR([ACLs enabled but support not detected]) ++ elif test "x$enable_acl$use_acl" = "xauto0"; then ++ AC_MSG_WARN([libacl development library was not found or not usable.]) ++ AC_MSG_WARN([AC_PACKAGE_NAME will be built without ACL support.]) ++ fi ++ fi ++ AC_SUBST([LIB_ACL]) ++ AC_DEFINE_UNQUOTED([USE_ACL], [$use_acl], ++ [Define to nonzero if you want access control list support.]) ++ USE_ACL=$use_acl ++ AC_SUBST([USE_ACL]) ++]) ++ ++# gl_ACL_GET_FILE(IF-WORKS, IF-NOT) ++# ------------------------------------- ++# If 'acl_get_file' works (does not have a particular bug), ++# run IF-WORKS, otherwise, IF-NOT. ++# This tests for a Darwin 8.7.0 bug, whereby acl_get_file returns NULL, ++# but sets errno = ENOENT for an existing file or directory. ++AC_DEFUN([gl_ACL_GET_FILE], ++[ ++ AC_CACHE_CHECK([for working acl_get_file], [gl_cv_func_working_acl_get_file], ++ [AC_RUN_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[#include ++ #include ++ #include ++ ]], ++ [[if (!acl_get_file (".", ACL_TYPE_ACCESS) && errno == ENOENT) ++ return 1; ++ return 0; ++ ]])], ++ [gl_cv_func_working_acl_get_file=yes], ++ [gl_cv_func_working_acl_get_file=no], ++ [gl_cv_func_working_acl_get_file=cross-compiling])]) ++ ++ AS_IF([test $gl_cv_func_working_acl_get_file = yes], [$1], [$2]) ++]) +diff --git a/m4/attr-xattr-h.m4 b/m4/attr-xattr-h.m4 +new file mode 100644 +index 0000000..271834b +--- /dev/null ++++ b/m4/attr-xattr-h.m4 +@@ -0,0 +1,28 @@ ++# Copyright (C) 2012 Free Software Foundation, Inc. ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# Provide , if necessary ++ ++AC_DEFUN([gl_HEADERS_ATTR_XATTR_H], ++[ ++ AC_ARG_WITH([xattrs], ++ AS_HELP_STRING([--without-xattrs], [don't use linux exteneded atttributes]), ++ [], [with_xattrs=maybe] ++ ) ++ ++ AC_CHECK_HEADERS([attr/xattr.h]) ++ 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 header]) ++ if test "$with_xattrs" != no; then ++ AC_DEFINE([HAVE_XATTRS],,[Define when we have working linux xattrs.]) ++ fi ++ ) ++ fi ++]) +diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 +index 837538e..428ad04 100644 +--- a/m4/gnulib-comp.m4 ++++ b/m4/gnulib-comp.m4 +@@ -1,6 +1,3 @@ +-# -*- buffer-read-only: t -*- vi: set ro: +-# DO NOT EDIT! GENERATED AUTOMATICALLY! +-# DO NOT EDIT! GENERATED AUTOMATICALLY! + # Copyright (C) 2002-2011 Free Software Foundation, Inc. + # + # This file is free software, distributed under the terms of the GNU +@@ -256,6 +253,7 @@ AC_DEFUN([gl_INIT], + gl_source_base='gnu' + # Code from module alloca: + # Code from module alloca-opt: ++ gl_FUNC_ACL + gl_FUNC_ALLOCA + # Code from module areadlink: + # Code from module areadlinkat: +@@ -565,6 +563,15 @@ AC_DEFUN([gl_INIT], + gl_SAVE_CWD + # Code from module savedir: + gl_SAVEDIR ++ # Code from module acl: ++ AC_CHECK_HEADERS([selinux/flask.h]) ++ AC_LIBOBJ([selinux-at]) ++ gl_HEADERS_SELINUX_SELINUX_H ++ gl_HEADERS_SELINUX_CONTEXT_H ++ AC_REQUIRE([AC_C_INLINE]) ++ if test "$with_selinux" != no && test "$ac_cv_header_selinux_selinux_h" = yes; then ++ AC_LIBOBJ([getfilecon]) ++ fi + # Code from module setenv: + gl_FUNC_SETENV + gl_STDLIB_MODULE_INDICATOR([setenv]) +@@ -719,6 +726,9 @@ AC_DEFUN([gl_INIT], + gl_UNISTD_MODULE_INDICATOR([write]) + # Code from module xalloc: + gl_XALLOC ++ AC_CHECK_HEADERS([attr/xattr.h]) ++ AC_LIBOBJ([xattr-at]) ++ gl_HEADERS_ATTR_XATTR_H + # Code from module xalloc-die: + # Code from module xgetcwd: + gl_XGETCWD +@@ -875,10 +885,14 @@ AC_DEFUN([gltests_LIBSOURCES], [ + AC_DEFUN([gl_FILE_LIST], [ + build-aux/arg-nonnull.h + build-aux/c++defs.h ++ build-aux/snippet/unused-parameter.h + build-aux/config.rpath + build-aux/gitlog-to-changelog + build-aux/warn-on-use.h + doc/parse-datetime.texi ++ lib/acl-internal.h ++ lib/acl.h ++ lib/acl_entries.c + lib/alloca.c + lib/alloca.in.h + lib/anytostr.c +@@ -928,6 +942,7 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/closeout.c + lib/closeout.h + lib/config.charset ++ lib/copy-acl.c + lib/dirent--.h + lib/dirent-safer.h + lib/dirent.in.h +@@ -955,6 +970,7 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/fd-safer.c + lib/fdopendir.c + lib/fdutimensat.c ++ lib/file-has-acl.c + lib/fileblocks.c + lib/filenamecat-lgpl.c + lib/filenamecat.h +@@ -1083,6 +1099,11 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/save-cwd.h + lib/savedir.c + lib/savedir.h ++ lib/se-context.in.h ++ lib/se-selinux.in.h ++ lib/selinux-at.c ++ lib/selinux-at.h ++ lib/set-mode-acl.c + lib/setenv.c + lib/size_max.h + lib/sleep.c +@@ -1162,6 +1183,8 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/xalloc-die.c + lib/xalloc.h + lib/xasprintf.c ++ lib/xattr-at.c ++ lib/xattr-at.h + lib/xgetcwd.c + lib/xgetcwd.h + lib/xmalloc.c +@@ -1176,9 +1199,11 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/xvasprintf.c + lib/xvasprintf.h + m4/00gnulib.m4 ++ m4/acl.m4 + m4/alloca.m4 + m4/argmatch.m4 + m4/argp.m4 ++ m4/attr-xattr-h.m4 + m4/backupfile.m4 + m4/bison.m4 + m4/btowc.m4 +@@ -1314,6 +1339,8 @@ AC_DEFUN([gl_FILE_LIST], [ + m4/safe-write.m4 + m4/save-cwd.m4 + m4/savedir.m4 ++ m4/selinux-context-h.m4 ++ m4/selinux-selinux-h.m4 + m4/setenv.m4 + m4/size_max.m4 + m4/sleep.m4 +diff --git a/m4/selinux-context-h.m4 b/m4/selinux-context-h.m4 +new file mode 100644 +index 0000000..7ad67bb +--- /dev/null ++++ b/m4/selinux-context-h.m4 +@@ -0,0 +1,22 @@ ++# serial 3 -*- Autoconf -*- ++# Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc. ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# From Jim Meyering ++# Provide , if necessary. ++ ++AC_DEFUN([gl_HEADERS_SELINUX_CONTEXT_H], ++[ ++ AC_REQUIRE([gl_LIBSELINUX]) ++ if test "$with_selinux" != no; then ++ AC_CHECK_HEADERS([selinux/context.h], ++ [SELINUX_CONTEXT_H=], ++ [SELINUX_CONTEXT_H=selinux/context.h]) ++ else ++ SELINUX_CONTEXT_H=selinux/context.h ++ fi ++ AC_SUBST([SELINUX_CONTEXT_H]) ++ AM_CONDITIONAL([GL_GENERATE_SELINUX_CONTEXT_H], [test -n "$SELINUX_CONTEXT_H"]) ++]) +diff --git a/m4/selinux-selinux-h.m4 b/m4/selinux-selinux-h.m4 +new file mode 100644 +index 0000000..ed5215b +--- /dev/null ++++ b/m4/selinux-selinux-h.m4 +@@ -0,0 +1,69 @@ ++# serial 5 -*- Autoconf -*- ++# Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc. ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# From Jim Meyering ++# Provide , if necessary. ++# If it is already present, provide wrapper functions to guard against ++# misbehavior from getfilecon, lgetfilecon, and fgetfilecon. ++ ++AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H], ++[ ++ AC_REQUIRE([gl_LIBSELINUX]) ++ if test "$with_selinux" != no; then ++ AC_CHECK_HEADERS([selinux/selinux.h]) ++ ++ if test "$ac_cv_header_selinux_selinux_h" = yes; then ++ # We do have , so do compile getfilecon.c ++ # and arrange to use its wrappers. ++ gl_CHECK_NEXT_HEADERS([selinux/selinux.h]) ++ AC_DEFINE([getfilecon], [rpl_getfilecon], ++ [Always use our getfilecon wrapper.]) ++ AC_DEFINE([lgetfilecon], [rpl_lgetfilecon], ++ [Always use our lgetfilecon wrapper.]) ++ AC_DEFINE([fgetfilecon], [rpl_fgetfilecon], ++ [Always use our fgetfilecon wrapper.]) ++ fi ++ ++ case "$ac_cv_search_setfilecon:$ac_cv_header_selinux_selinux_h" in ++ no:*) # already warned ++ ;; ++ *:no) ++ AC_MSG_WARN([libselinux was found but selinux/selinux.h is missing.]) ++ AC_MSG_WARN([AC_PACKAGE_NAME will be compiled without SELinux support.]) ++ esac ++ else ++ # Do as if does not exist, even if ++ # AC_CHECK_HEADERS_ONCE has already determined that it exists. ++ AC_DEFINE([HAVE_SELINUX_SELINUX_H], [0]) ++ fi ++]) ++ ++AC_DEFUN([gl_LIBSELINUX], ++[ ++ AC_REQUIRE([AC_CANONICAL_HOST]) ++ AC_REQUIRE([AC_CANONICAL_BUILD]) ++ ++ AC_ARG_WITH([selinux], ++ AS_HELP_STRING([--without-selinux], [do not use SELinux, even on systems with SELinux]), ++ [], [with_selinux=maybe]) ++ ++ LIB_SELINUX= ++ if test "$with_selinux" != no; then ++ gl_save_LIBS=$LIBS ++ AC_SEARCH_LIBS([setfilecon], [selinux], ++ [test "$ac_cv_search_setfilecon" = "none required" || ++ LIB_SELINUX=$ac_cv_search_setfilecon]) ++ LIBS=$gl_save_LIBS ++ fi ++ AC_SUBST([LIB_SELINUX]) ++ ++ # Warn if SELinux is found but libselinux is absent; ++ if test "$ac_cv_search_setfilecon" = no && ++ test "$host" = "$build" && test -d /selinux; then ++ AC_MSG_WARN([This system supports SELinux but libselinux is missing.]) ++ AC_MSG_WARN([AC_PACKAGE_NAME will be compiled without SELinux support.]) ++ fi ++]) +-- +1.7.11.2 + diff --git a/tar-1.26-xattrs.patch b/tar-1.26-xattrs.patch new file mode 100644 index 0000000..17ef8e2 --- /dev/null +++ b/tar-1.26-xattrs.patch @@ -0,0 +1,3061 @@ +From f661d5c38ec585f364e47a981ebada092936d38a Mon Sep 17 00:00:00 2001 +From: Pavel Raiskup +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 ]) + AC_CHECK_DECLS([getpwuid],,, [#include ]) + AC_CHECK_DECLS([time],,, [#include ]) +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, ¤t_stat_info, invert_permissions, ++ typeflag, &file_created)) ++ { ++ skip_member (); ++ open_error (file_name); ++ return 1; ++ } ++ + while ((fd = open_output_file (file_name, typeflag, mode, +- ¤t_mode, ¤t_mode_mask)) ++ file_created, ¤t_mode, ++ ¤t_mode_mask)) + < 0) + { + int recover = maybe_recoverable (file_name, true, &interdir_made); +@@ -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 (¤t_stat_info, &p->xattr_map, &p->xattr_map_size); + strcpy (p->target, current_stat_info.link_name); + + h = delayed_set_stat_head; +@@ -1525,6 +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 ++ ++#include ++#include ++ ++#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 ++#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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 . ++# ++# 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 + diff --git a/tar.spec b/tar.spec index 553d041..cb8b204 100644 --- a/tar.spec +++ b/tar.spec @@ -5,7 +5,7 @@ Summary: A GNU file archiving program Name: tar Epoch: 2 Version: 1.26 -Release: 9%{?dist} +Release: 10%{?dist} License: GPLv3+ Group: Applications/Archiving URL: http://www.gnu.org/software/tar/ @@ -18,10 +18,10 @@ Patch1: tar-1.14-loneZeroWarning.patch #Fix extracting sparse files to a filesystem like vfat, #when ftruncate may fail to grow the size of a file.(#179507) Patch2: tar-1.15.1-vfatTruncate.patch -#Add support for selinux, acl and extended attributes -Patch3: tar-1.24-xattrs.patch #change inclusion defaults of tar to "--wildcards --anchored #--wildcards-match-slash" for compatibility reasons (#206841) +#Add support for selinux, acl and extended attributes +#Patch3: tar-1.24-xattrs.patch Patch4: tar-1.17-wildcards.patch #ignore errors from setting utime() for source file #on read-only filesystem (#500742) @@ -36,7 +36,14 @@ Patch8: tar-1.24-openat-partial-revert.patch Patch9: tar-1.26-update-with-change-directory.patch # fix rawhide buildfailure with undefined gets Patch10: tar-1.26-stdio.in.patch +# Prepare gnulib for xattrs and apply xattrs & acls & selinux (#850291) +Patch11: tar-1.26-xattrs-gnulib-prepare.patch +Patch12: tar-1.26-xattrs.patch + BuildRequires: autoconf automake gzip texinfo gettext libacl-devel gawk rsh +# allow proper tests for extended attributes +BuildRequires: attr acl coreutils policycoreutils + %if %{WITH_SELINUX} BuildRequires: libselinux-devel %endif @@ -61,7 +68,7 @@ the rmt package. %setup -q %patch1 -p1 -b .loneZeroWarning %patch2 -p1 -b .vfatTruncate -%patch3 -p1 -b .xattrs +#%patch3 -p1 -b .xattrs %patch4 -p1 -b .wildcards %patch5 -p1 -b .rofs %patch6 -p1 -b .oldarchive @@ -69,13 +76,15 @@ the rmt package. %patch8 -p1 -b .openat %patch9 -p1 -b .update_and_changedir %patch10 -p1 -b .gets +%patch11 -p1 -b .xattrs_gnulib_prep +%patch12 -p1 -b .xattrs2 autoreconf %build %configure --bindir=/bin --libexecdir=/sbin \ -%if %{WITH_SELINUX} - --enable-selinux +%if %{WITH_SELINUX} == 0 + --without-selinux %endif make @@ -130,11 +139,16 @@ fi %{_infodir}/tar.info* %changelog +* Tue Aug 21 2012 Pavel Raiskup - 2:1.26-9 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Thu Jul 12 2012 Pavel Raiskup 2:1.26-7 - store&restore security.capability extended attributes category (#771927)