From 5c392c91880f78859189e45125ec1ff3e9992585 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 10 Nov 2008 19:07:44 +0100 Subject: [PATCH] cp/mv: xattr support * m4/xattr.m4: Check for libattr availability, new configure option --disable-xattr. * m4/prereq.m4: Require gl_FUNC_XATTR. * src/Makefile.am: Link cp, mv and ginstall with libattr. * src/copy.h: Add preserve_xattr to cp_options. * src/copy.c (copy_attr_error): New function to handle errors during xattr copying. (copy_extended_attributes): New function to copy xattr. (copy_reg, copy_internal): Call copy_extended_attributes function. * src/cp.c (usage): Mention new --preserve=xattr option. (decode_preserve_arg): Handle new --preserve=xattr option. * src/mv.c: Always attempt to preserve xattr. * src/install.c: Never attempt to preserve xattr. * tests/cp/xattr: New test for xattr support in cp. * tests/mv/xattr: New test for xattr support in mv. * tests/Makefile.am: Add new tests to list. * doc/coreutils.texi: Mention new --preserve=xattr option. * NEWS: Mention the change. --- NEWS | 2 + doc/coreutils.texi | 3 ++ m4/prereq.m4 | 1 + m4/xattr.m4 | 45 +++++++++++++++++++++++++++++++++++ src/Makefile.am | 6 ++-- src/copy.c | 51 ++++++++++++++++++++++++++++++++++++++++ src/copy.h | 4 +++ src/cp.c | 15 +++++++++-- src/install.c | 1 + src/mv.c | 1 + tests/Makefile.am | 2 + tests/cp/xattr | 59 ++++++++++++++++++++++++++++++++++++++++++++++ tests/mv/xattr | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 250 insertions(+), 6 deletions(-) create mode 100644 m4/xattr.m4 create mode 100755 tests/cp/xattr create mode 100755 tests/mv/xattr diff --git a/NEWS b/NEWS index cbea67c..b8992aa 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ GNU coreutils NEWS -*- outline -*- ** New features + cp/mv: xattr support, new option --preserve=xattr in cp + ls --color now highlights hard linked files, too stat -f recognizes the Lustre file system type diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 2d11388..18a8ffc 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -7372,6 +7372,9 @@ Preserve in the destination files any links between corresponding source files. @c Give examples illustrating how hard links are preserved. @c Also, show how soft links map to hard links with -L and -H. address@hidden xattr +Preserve extended (xattr) attributes if coreutils are compiled with xattr +support. @itemx all Preserve all file attributes. Equivalent to specifying all of the above. diff --git a/m4/prereq.m4 b/m4/prereq.m4 index e65682f..536070e 100644 --- a/m4/prereq.m4 +++ b/m4/prereq.m4 @@ -38,6 +38,7 @@ AC_DEFUN([gl_PREREQ], # handles that; see ../bootstrap.conf. AC_REQUIRE([gl_EUIDACCESS_STAT]) AC_REQUIRE([gl_FD_REOPEN]) + AC_REQUIRE([gl_FUNC_XATTR]) AC_REQUIRE([gl_FUNC_XFTS]) AC_REQUIRE([gl_MEMXFRM]) AC_REQUIRE([gl_STRINTCMP]) diff --git a/m4/xattr.m4 b/m4/xattr.m4 new file mode 100644 index 0000000..7edc04e --- /dev/null +++ b/m4/xattr.m4 @@ -0,0 +1,45 @@ +# 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. +# Note: original macro name was AC_FUNC_XATTR + +AC_DEFUN([gl_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..4953d4a 100644 --- a/src/copy.c +++ b/src/copy.c @@ -54,6 +54,12 @@ #include "areadlink.h" #include "yesno.h" +#if USE_XATTR +# include +# include +# include +#endif + #ifndef HAVE_FCHOWN # define HAVE_FCHOWN false # define fchown(fd, uid, gid) (-1) @@ -123,6 +129,45 @@ 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 bool +copy_extended_attributes (const char *src_path, const char *dst_path) +{ + return 0 == attr_copy_file (src_path, dst_path, attr_copy_check_permissions, + copy_attr_error); +} +#else /* USE_XATTR */ + +static bool +copy_extended_attributes (const char *src_path, const char *dst_path) +{ + 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 +726,9 @@ copy_reg (char const *src_name, char const *dst_name, set_author (dst_name, dest_desc, src_sb); + if (x->preserve_xattr && ! copy_extended_attributes (src_name, dst_name)) + 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 +2027,9 @@ copy_internal (char const *src_name, char const *dst_name, set_author (dst_name, -1, &src_sb); + if (x->preserve_xattr && ! copy_extended_attributes (src_name, dst_name)) + 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 12b7c2d..bc2ca41 100644 --- a/src/copy.h +++ b/src/copy.h @@ -173,6 +173,10 @@ struct cp_options fail if it is unable to do so. */ bool require_preserve_context; + /* If true, attempt to preserve extended attributes using libattr. + Ignored if coreutils are compiled without xattr support. */ + bool preserve_xattr; + /* If true, copy directories recursively and copy special files as themselves rather than copying their contents. */ bool recursive; diff --git a/src/cp.c b/src/cp.c index 95eba0c..9b081d4 100644 --- a/src/cp.c +++ b/src/cp.c @@ -197,7 +197,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\ -p same as --preserve=mode,ownership,timestamps\n\ --preserve[=ATTR_LIST] preserve the specified attributes (default:\n\ mode,ownership,timestamps), if possible\n\ - additional attributes: context, links, all\n\ + additional attributes: context, links, xattr,\n\ + all\n\ "), stdout); fputs (_("\ --no-preserve=ATTR_LIST don't preserve the specified attributes\n\ @@ -774,6 +775,7 @@ cp_option_init (struct cp_options *x) x->preserve_timestamps = false; x->preserve_security_context = false; x->require_preserve_context = false; + x->preserve_xattr = false; x->require_preserve = false; x->recursive = false; @@ -810,18 +812,20 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, + PRESERVE_XATTR, PRESERVE_ALL }; static enum File_attribute const preserve_vals[] = { PRESERVE_MODE, PRESERVE_TIMESTAMPS, - PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_ALL + PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_XATTR, + PRESERVE_ALL }; /* Valid arguments to the `--preserve' option. */ static char const* const preserve_args[] = { "mode", "timestamps", - "ownership", "links", "context", "all", NULL + "ownership", "links", "context", "xattr", "all", NULL }; ARGMATCH_VERIFY (preserve_args, preserve_vals); @@ -862,6 +866,10 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) x->require_preserve_context = on_off; break; + case PRESERVE_XATTR: + x->preserve_xattr = on_off; + break; + case PRESERVE_ALL: x->preserve_mode = on_off; x->preserve_timestamps = on_off; @@ -869,6 +877,7 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) x->preserve_links = on_off; if (selinux_enabled) x->preserve_security_context = on_off; + x->preserve_xattr = on_off; break; default: diff --git a/src/install.c b/src/install.c index a7c3b3d..5a67597 100644 --- a/src/install.c +++ b/src/install.c @@ -200,6 +200,7 @@ cp_option_init (struct cp_options *x) x->open_dangling_dest_symlink = false; x->update = false; x->preserve_security_context = false; + x->preserve_xattr = false; x->verbose = false; x->dest_info = NULL; x->src_info = NULL; diff --git a/src/mv.c b/src/mv.c index fc255f3..bbe863e 100644 --- a/src/mv.c +++ b/src/mv.c @@ -139,6 +139,7 @@ cp_option_init (struct cp_options *x) x->preserve_security_context = selinux_enabled; x->require_preserve = false; /* FIXME: maybe make this an option */ x->require_preserve_context = false; + x->preserve_xattr = true; x->recursive = true; x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ x->symbolic_link = false; diff --git a/tests/Makefile.am b/tests/Makefile.am index 0dabb56..0211886 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -280,6 +280,7 @@ TESTS = \ cp/src-base-dot \ cp/symlink-slash \ cp/thru-dangling \ + cp/xattr \ dd/misc \ dd/not-rewound \ dd/skip-seek \ @@ -389,6 +390,7 @@ TESTS = \ mv/to-symlink \ mv/trailing-slash \ mv/update \ + mv/xattr \ readlink/can-e \ readlink/can-f \ readlink/can-m \ diff --git a/tests/cp/xattr b/tests/cp/xattr new file mode 100755 index 0000000..d0b07b0 --- /dev/null +++ b/tests/cp/xattr @@ -0,0 +1,59 @@ +#!/bin/sh +# Ensure that cp --preserve=xattr works properly. + +# Copyright (C) 2008 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 test "$VERBOSE" = yes; then + set -x + cp --version +fi + +. $srcdir/test-lib.sh + +# Skip this test if cp was built without xattr support: +grep '^#define USE_XATTR 1' $CONFIG_HEADER > /dev/null || + skip_test_ "coreutils built without xattr support" + +# testing xattr name-value pair +xattr_name="user.foo" +xattr_value="bar" +xattr_pair="$xattr_name=\"$xattr_value\"" + +# create new file and check its xattrs +touch a || framework_failure +getfattr -d a > out_a || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_a > /dev/null && framework_failure + +# try to set user xattr on file +setfattr -n "$xattr_name" -v "$xattr_value" a > out_a \ + || skip_test_ "failed to set xattr of file" +getfattr -d a > out_a || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_a > /dev/null \ + || skip_test_ "failed to set xattr of file" + +fail=0 + +# cp should not preserve xattr by default +cp a b || fail=1 +getfattr -d b > out_b || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_b > /dev/null && fail=1 + +# test if --preserve=xattr option works +cp --preserve=xattr a b || fail=1 +getfattr -d b > out_b || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_b > /dev/null || fail=1 + +Exit $fail diff --git a/tests/mv/xattr b/tests/mv/xattr new file mode 100755 index 0000000..c0eb608 --- /dev/null +++ b/tests/mv/xattr @@ -0,0 +1,66 @@ +#!/bin/sh +# Check if mv always preserve extended attributes. + +# Copyright (C) 2008 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 test "$VERBOSE" = yes; then + set -x + mv --version +fi + +# this code was taken from test mv/backup-is-src +. $srcdir/test-lib.sh +cleanup_() { rm -rf "$other_partition_tmpdir"; } +. "$abs_srcdir/other-fs-tmpdir" +b_other="$other_partition_tmpdir/b" +rm -f $b_other || framework_failure + +# Skip this test if mv was built without xattr support: +grep '^#define USE_XATTR 1' $CONFIG_HEADER > /dev/null || + skip_test_ "coreutils built without xattr support" + +# testing xattr name-value pair +xattr_name="user.foo" +xattr_value="bar" +xattr_pair="$xattr_name=\"$xattr_value\"" + +# create new file and check its xattrs +touch a || framework_failure +getfattr -d a > out_a || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_a > /dev/null && framework_failure + +# try to set user xattr on file +setfattr -n "$xattr_name" -v "$xattr_value" a > out_a \ + || skip_test_ "failed to set xattr of file" +getfattr -d a > out_a || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_a > /dev/null \ + || skip_test_ "failed to set xattr of file" + +fail=0 + +# mv should preserve xattr when not copying content +# (implicit and probably expected behavior) +mv a b || fail=1 +getfattr -d b > out_b || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_b > /dev/null || fail=1 + +# mv should even preserve xattr when copying content from one partition +# to another +mv b $b_other || fail=1 +getfattr -d $b_other > out_b || skip_test_ "failed to get xattr of file" +fgrep "$xattr_pair" out_b > /dev/null || fail=1 + +Exit $fail -- 1.5.4.1