From 18a8185efddb9135eea1b8d698fd913450ea98d3 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 20 Oct 2008 14:18:18 +0200 Subject: [PATCH] cp/mv: xattr support --- configure.ac | 3 + doc/coreutils.texi | 12 ++++++ m4/xattr.m4 | 44 ++++++++++++++++++++ src/Makefile.am | 6 +- src/copy.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/copy.h | 4 ++ src/cp.c | 20 +++++++++- src/install.c | 2 + src/mv.c | 2 + 9 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 m4/xattr.m4 diff --git a/configure.ac b/configure.ac index 549c7ee..4828d6f 100644 --- a/configure.ac +++ b/configure.ac @@ -334,6 +334,9 @@ CONFIG_STATUS_DEPENDENCIES='$(top_srcdir)/src/Makefile.am' AC_SUBST([CONFIG_STATUS_DEPENDENCIES]) ############################################################################ +# Extended attribute copying. +AC_FUNC_XATTR + AM_GNU_GETTEXT([external], [need-formatstring-macros]) AM_GNU_GETTEXT_VERSION([0.15]) diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 6459870..f927a7a 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -6945,6 +6945,18 @@ These options change how file names themselves are printed. @table @samp address@hidden @address@hidden@var{regex}} address@hidden --attributes +Preserve extended attributes whose names match the specified regular +expression. The default behavior or @command{cp} if no address@hidden option is given is to preserve all extended +attributes except file permissions. If @var{regex} is address@hidden'', no +extended attributes are preserved. + +This option does not affect the preservation of file permissions. +File permission preservation is controlled by the @option{-p} or address@hidden options. + @item -b @itemx --escape @itemx --quoting-style=escape diff --git a/m4/xattr.m4 b/m4/xattr.m4 new file mode 100644 index 0000000..d749d56 --- /dev/null +++ b/m4/xattr.m4 @@ -0,0 +1,44 @@ +# xattr.m4 - check for Extended Attributes (Linux) + +# Copyright (C) 2003 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +# Written by Andreas Gruenbacher. + +AC_DEFUN([AC_FUNC_XATTR], +[ + AC_ARG_ENABLE(xattr, + [ --disable-xattr turn off support for extended attributes], + use_xattr=$enableval, use_xattr=yes) + + if test "$use_xattr" = "yes"; then + AC_CHECK_HEADERS(attr/error_context.h attr/libattr.h) + if test "$ac_cv_header_attr_libattr_h" = yes \ + && test "$ac_cv_header_attr_error_context_h" = yes; then + use_xattr=1 + else + use_xattr=0 + fi + AC_DEFINE_UNQUOTED(USE_XATTR, $use_xattr, + [Define if you want extended attribute support.]) + xattr_saved_LIBS=$LIBS + AC_SEARCH_LIBS(attr_copy_file, attr, + [test "$ac_cv_search_attr_copy_file" = "none required" || LIB_XATTR=$ac_cv_search_attr_copy_file]) + AC_SUBST(LIB_XATTR) + AC_CHECK_FUNCS(attr_copy_file) + LIBS=$xattr_saved_LIBS + fi +]) diff --git a/src/Makefile.am b/src/Makefile.am index 097cc7a..aab67d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -149,9 +149,9 @@ su_LDADD = $(LDADD) $(LIB_CRYPT) dir_LDADD += $(LIB_ACL) ls_LDADD += $(LIB_ACL) vdir_LDADD += $(LIB_ACL) -cp_LDADD += $(LIB_ACL) -mv_LDADD += $(LIB_ACL) -ginstall_LDADD += $(LIB_ACL) +cp_LDADD += $(LIB_ACL) $(LIB_XATTR) +mv_LDADD += $(LIB_ACL) $(LIB_XATTR) +ginstall_LDADD += $(LIB_ACL) $(LIB_XATTR) stat_LDADD = $(LDADD) $(LIB_SELINUX) diff --git a/src/copy.c b/src/copy.c index bc1b20e..3d91e9d 100644 --- a/src/copy.c +++ b/src/copy.c @@ -54,6 +54,13 @@ #include "areadlink.h" #include "yesno.h" +#if USE_XATTR +# include "regex.h" +# include +# include +# include +#endif + #ifndef HAVE_FCHOWN # define HAVE_FCHOWN false # define fchown(fd, uid, gid) (-1) @@ -123,6 +130,104 @@ is_ancestor (const struct stat *sb, const struct dir_list *ancestors) return false; } +#if USE_XATTR +static void +copy_attr_error (struct error_context *ctx, const char *fmt, ...) +{ + int err = errno; + va_list ap; + int len; + char *buffer; + + /* There is no error function that takes a va_list argument, + so we print the message in a buffer first. */ + + va_start (ap, fmt); + len = vsnprintf (NULL, 0, fmt, ap); + if (len > 0) + { + buffer = xmalloc (len + 1); + vsnprintf (buffer, len + 1, fmt, ap); + error (0, err, "%s", buffer); + free (buffer); + } + va_end (ap); +} + +static const char * +copy_attr_quote (struct error_context *ctx, const char *str) +{ + return xstrdup (quote (str)); +} + +static void +copy_attr_free (struct error_context *ctx, const char *str) +{ + free ((void *) str); +} + +struct copy_attr_context + { + struct error_context ctx; + const char *re_pattern; + struct re_pattern_buffer re_compiled; + } copy_attr_ctx = { + { copy_attr_error, + copy_attr_quote, + copy_attr_free } + }; + +static int +copy_attr_filter (const char *name, struct error_context *ctx) +{ + struct copy_attr_context *copy_ctx = (struct copy_attr_context *) ctx; + + return (attr_copy_check_permissions (name, ctx) + && copy_ctx->re_pattern != NULL + && re_search (©_ctx->re_compiled, name, strlen (name), 0, + strlen (name), NULL) >= 0); +} +#endif /* USE_XATTR */ + +static bool +copy_extended_attributes (const char *src_path, const char *dst_path, + const struct cp_options *x) +{ +#if USE_XATTR + if (x->attr_pattern == NULL) + return true; + + if (copy_attr_ctx.re_pattern != x->attr_pattern) + { + struct re_pattern_buffer *c = ©_attr_ctx.re_compiled; + size_t len = strlen (x->attr_pattern); + const char *err; + + free (c->fastmap); + free (c->buffer); + + copy_attr_ctx.re_pattern = x->attr_pattern; + c->allocated = 2 * len; + c->buffer = xmalloc (c->allocated); + c->fastmap = xmalloc (256); + c->translate = 0; + err = re_compile_pattern (x->attr_pattern, len, c); + if (err) + { + free (c->fastmap); + free (c->buffer); + copy_attr_ctx.re_pattern = NULL; + error (EXIT_FAILURE, 0, _("%s: invalid regular expression: %s"), + x->attr_pattern, err); + } + } + return attr_copy_file (src_path, dst_path, + copy_attr_filter, ©_attr_ctx.ctx) == 0; +#else /* USE_XATTR */ + return true; +#endif /* USE_XATTR */ +} + /* Read the contents of the directory SRC_NAME_IN, and recursively copy the contents to DST_NAME_IN. NEW_DST is true if DST_NAME_IN is a directory that was created previously in the @@ -681,6 +786,9 @@ copy_reg (char const *src_name, char const *dst_name, set_author (dst_name, dest_desc, src_sb); + if (! copy_extended_attributes (src_name, dst_name, x)) + return_val = false; + if (x->preserve_mode || x->move_mode) { if (copy_acl (src_name, source_desc, dst_name, dest_desc, src_mode) != 0 @@ -1979,6 +2087,9 @@ copy_internal (char const *src_name, char const *dst_name, set_author (dst_name, -1, &src_sb); + if (! copy_extended_attributes (src_name, dst_name, x)) + delayed_ok = false; + if (x->preserve_mode || x->move_mode) { if (copy_acl (src_name, -1, dst_name, -1, src_mode) != 0 diff --git a/src/copy.h b/src/copy.h index 86a8161..6b0add3 100644 --- a/src/copy.h +++ b/src/copy.h @@ -141,6 +141,10 @@ struct cp_options bool preserve_mode; bool preserve_timestamps; + /* Regular expression pattern that specifies which extended attributes to + copy. NULL stands for copying no extended attributes. */ + const char *attr_pattern; + /* Enabled for mv, and for cp by the --preserve=links option. If true, attempt to preserve in the destination files any logical hard links between the source files. If used with cp's diff --git a/src/cp.c b/src/cp.c index 95eba0c..94f0fb2 100644 --- a/src/cp.c +++ b/src/cp.c @@ -79,7 +79,8 @@ enum REPLY_OPTION, SPARSE_OPTION, STRIP_TRAILING_SLASHES_OPTION, - UNLINK_DEST_BEFORE_OPENING + UNLINK_DEST_BEFORE_OPENING, + PRESERVE_XATTRS_OPTION }; /* Initial number of entries in each hash table entry's table of inodes. */ @@ -136,6 +137,7 @@ static struct option const long_opts[] = {"parents", no_argument, NULL, PARENTS_OPTION}, {"path", no_argument, NULL, PARENTS_OPTION}, /* Deprecated. */ {"preserve", optional_argument, NULL, PRESERVE_ATTRIBUTES_OPTION}, + {"attributes", required_argument, NULL, PRESERVE_XATTRS_OPTION}, {"recursive", no_argument, NULL, 'R'}, {"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING}, {"reply", required_argument, NULL, REPLY_OPTION}, /* Deprecated 2005-07-03, @@ -226,6 +228,13 @@ Mandatory arguments to long options are mandatory for short options too.\n\ -v, --verbose explain what is being done\n\ -x, --one-file-system stay on this file system\n\ "), stdout); + fputs(_("\n\ + --attributes=regex preserve extended attributes whose name\n\ + matches the specified regular expression\n\ + (defaults to preserving all extended\n\ + attributes except file permissions;\n\ + regex=`-' preserves no extended attributes).\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\ @@ -796,6 +805,8 @@ cp_option_init (struct cp_options *x) x->dest_info = NULL; x->src_info = NULL; + + x->attr_pattern = ""; /* all extended attributes */ } /* Given a string, ARG, containing a comma-separated list of arguments @@ -992,6 +1003,13 @@ main (int argc, char **argv) x.require_preserve = true; break; + case PRESERVE_XATTRS_OPTION: + if (strcmp (optarg, "-") == 0) + x.attr_pattern = NULL; + else + x.attr_pattern = optarg; + break; + case PARENTS_OPTION: parents_option = true; break; diff --git a/src/install.c b/src/install.c index a7c3b3d..0bc5987 100644 --- a/src/install.c +++ b/src/install.c @@ -203,6 +203,8 @@ cp_option_init (struct cp_options *x) x->verbose = false; x->dest_info = NULL; x->src_info = NULL; + + x->attr_pattern = NULL; /* no extended attributes */ } #ifdef ENABLE_MATCHPATHCON diff --git a/src/mv.c b/src/mv.c index fc255f3..3f42f78 100644 --- a/src/mv.c +++ b/src/mv.c @@ -151,6 +151,8 @@ cp_option_init (struct cp_options *x) x->verbose = false; x->dest_info = NULL; x->src_info = NULL; + + x->attr_pattern = ""; /* all extended attributes */ } /* FILE is the last operand of this command. Return true if FILE is a -- 1.5.4.1