diff --git a/busybox/include/libbb.h b/busybox/include/libbb.h index e520060..663ebd8 100644 --- a/busybox/include/libbb.h +++ b/busybox/include/libbb.h @@ -342,6 +342,9 @@ extern int remove_file(const char *path, int flags) FAST_FUNC; * This makes "cp /dev/null file" and "install /dev/null file" (!!!) * work coreutils-compatibly. */ extern int copy_file(const char *source, const char *dest, int flags) FAST_FUNC; +#if ENABLE_XATTR +extern int copy_file_attr(const char *src_path, const char *dst_path) FAST_FUNC; +#endif enum { ACTION_RECURSE = (1 << 0), diff --git a/busybox/libbb/Config.src b/busybox/libbb/Config.src index 19021fe..8615cb2 100644 --- a/busybox/libbb/Config.src +++ b/busybox/libbb/Config.src @@ -232,4 +232,10 @@ config FEATURE_HWIB Support for printing infiniband addresses in network applets. +config XATTR + bool "Support preserve extended attributes for cp command" + default y + help + Support preserve extended attributes for cp and mv command. + endmenu diff --git a/busybox/libbb/Kbuild.src b/busybox/libbb/Kbuild.src index d3d9754..11e9318 100644 --- a/busybox/libbb/Kbuild.src +++ b/busybox/libbb/Kbuild.src @@ -172,6 +172,7 @@ lib-$(CONFIG_TRACEROUTE6) += inet_cksum.o lib-$(CONFIG_UDHCPC) += inet_cksum.o lib-$(CONFIG_UDHCPC6) += inet_cksum.o lib-$(CONFIG_UDHCPD) += inet_cksum.o +lib-$(CONFIG_XATTR) += copy_file_attr.o # We shouldn't build xregcomp.c if we don't need it - this ensures we don't # require regex.h to be in the include dir even if we don't need it thereby diff --git a/busybox/libbb/copy_file.c b/busybox/libbb/copy_file.c index 9333a8d..e93024c 100644 --- a/busybox/libbb/copy_file.c +++ b/busybox/libbb/copy_file.c @@ -314,6 +314,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) } } #endif + if (bb_copyfd_eof(src_fd, dst_fd) == -1) retval = -1; /* Careful with writing... */ @@ -385,6 +386,13 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) source_stat.st_mode &= ~(S_ISUID | S_ISGID); bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); } +#if ENABLE_XATTR + /* Preserve extended attributes. We must copy it after chown() + * because it resets capabilities. */ + if (copy_file_attr(source, dest) == -1) + bb_perror_msg("can't preserve %s of '%s'", + "extended attributes", dest); +#endif if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); } diff --git a/busybox/libbb/copy_file_attr.c b/busybox/libbb/copy_file_attr.c new file mode 100644 index 0000000..180b031 --- /dev/null +++ b/busybox/libbb/copy_file_attr.c @@ -0,0 +1,125 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copy extended attributes between files + * + * Copyright (C) 2014 Dmitry Falko , digiFLAK + * Fixed by Igor Zhbanov + * + * based on libattr code, original copyright: + * Copyright (C) 1999-2005 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include +#include + +#include "libbb.h" + +#if !defined(ENOTSUP) +# define ENOTSUP (-1) +#endif + +#if defined(HAVE_ALLOCA) +# define bb_alloc(size) alloca (size) +# define bb_free(ptr) do { } while(0) +#else +# define bb_alloc(size) xmalloc (size) +# define bb_free(ptr) free (ptr) +#endif + +/* Copy extended attributes from src_path to dst_path. If the file + * has an extended Access ACL (system.posix_acl_access) and that is + * copied successfully, the file mode permission bits are copied as + * a side effect. This may not always the case, so the file mode + * and/or ownership must be copied separately. */ +int FAST_FUNC copy_file_attr(const char *src_path, const char *dst_path) +{ + int ret = 0; + ssize_t size; + char *names = NULL, *end_names, *name, *value = NULL; + unsigned int setxattr_ENOTSUP = 0; + + if ((size = listxattr(src_path, NULL, 0)) < 0) { + if (errno != ENOSYS && errno != ENOTSUP) { + bb_perror_msg("listing attributes of %s", src_path); + ret = -1; + } + + goto getout; + } + + if (!(names = (char *)bb_alloc(size + 1))) { + bb_error_msg("cannot allocate buffer"); + ret = -1; + goto getout; + } + + if ((size = listxattr(src_path, names, size)) < 0) { + bb_error_msg("listing attributes of %s", src_path); + ret = -1; + goto getout; + } else { + names[size] = '\0'; + end_names = names + size; + } + + for (name = names; name != end_names; name = strchr(name, '\0') + 1) { + void *old_value; + + if (!*name) + continue; + + if ((size = getxattr(src_path, name, NULL, 0)) < 0) { + bb_error_msg("getting attribute %s of %s", + src_path, name); + ret = -1; + continue; + } + + value = (char *)xrealloc(old_value = value, size); + if (size != 0 && !value) { + free(old_value); + bb_error_msg("failed to realloc"); + ret = -1; + } + + if ((size = getxattr(src_path, name, value, size)) < 0) { + bb_error_msg("getting attribute %s of %s", + src_path, name); + ret = -1; + continue; + } + + if (setxattr(dst_path, name, value, size, 0) != 0) { + if (errno == ENOTSUP) + setxattr_ENOTSUP++; + else { + if (errno == ENOSYS) { + bb_error_msg("setting attributes for " + "%s", dst_path); + ret = -1; + /* no hope of getting any further */ + break; + } else { + bb_error_msg("setting attribute %s " + "for %s", name, + dst_path); + ret = -1; + } + } + } + } + + if (setxattr_ENOTSUP) { + errno = ENOTSUP; + /* ignore this error */ + bb_error_msg("setting attributes for %s", dst_path); + ret = 0; + } + +getout: + free(value); + bb_free(names); + return ret; +} diff --git a/rpm/busybox-static.config b/rpm/busybox-static.config index 30d0c5a..7c94a6f 100644 --- a/rpm/busybox-static.config +++ b/rpm/busybox-static.config @@ -1011,3 +1011,5 @@ CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y CONFIG_KLOGD=y CONFIG_FEATURE_KLOGD_KLOGCTL=y CONFIG_LOGGER=y + +CONFIG_XATTR=y