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