diff --git a/src/basic/copy.c b/src/basic/copy.c index bac321fe68..9028868f69 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -20,6 +20,7 @@ #include "macro.h" #include "missing_syscall.h" #include "mountpoint-util.h" +#include "nulstr-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -913,63 +914,28 @@ int copy_times(int fdf, int fdt, CopyFlags flags) { } int copy_xattr(int fdf, int fdt) { - _cleanup_free_ char *bufa = NULL, *bufb = NULL; - size_t sza = 100, szb = 100; - ssize_t n; - int ret = 0; + _cleanup_free_ char *names = NULL; + int ret = 0, r; const char *p; - for (;;) { - bufa = malloc(sza); - if (!bufa) - return -ENOMEM; + r = flistxattr_malloc(fdf, &names); + if (r < 0) + return r; - n = flistxattr(fdf, bufa, sza); - if (n == 0) - return 0; - if (n > 0) - break; - if (errno != ERANGE) - return -errno; + NULSTR_FOREACH(p, names) { + _cleanup_free_ char *value = NULL; - sza *= 2; + if (!startswith(p, "user.")) + continue; - bufa = mfree(bufa); - } + r = fgetxattr_malloc(fdf, p, &value); + if (r == -ENODATA) + continue; /* gone by now */ + if (r < 0) + return r; - p = bufa; - while (n > 0) { - size_t l; - - l = strlen(p); - assert(l < (size_t) n); - - if (startswith(p, "user.")) { - ssize_t m; - - if (!bufb) { - bufb = malloc(szb); - if (!bufb) - return -ENOMEM; - } - - m = fgetxattr(fdf, p, bufb, szb); - if (m < 0) { - if (errno == ERANGE) { - szb *= 2; - bufb = mfree(bufb); - continue; - } - - return -errno; - } - - if (fsetxattr(fdt, p, bufb, m, 0) < 0) - ret = -errno; - } - - p += l + 1; - n -= l + 1; + if (fsetxattr(fdt, p, value, r, 0) < 0) + ret = -errno; } return ret; diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index f4ac2b6f7b..0125a9c2c0 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -17,17 +17,23 @@ #include "time-util.h" #include "xattr-util.h" -int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) { - char *v; - size_t l; - ssize_t n; +int getxattr_malloc( + const char *path, + const char *name, + char **ret, + bool allow_symlink) { + + size_t l = 100; assert(path); assert(name); - assert(value); + assert(ret); - for (l = 100; ; l = (size_t) n + 1 /* extra byte to make sure this remains NUL suffixed */) { - v = new0(char, l); + for(;;) { + _cleanup_free_ char *v = NULL; + ssize_t n; + + v = new0(char, l+1); if (!v) return -ENOMEM; @@ -35,53 +41,64 @@ int getxattr_malloc(const char *path, const char *name, char **value, bool allow n = lgetxattr(path, name, v, l); else n = getxattr(path, name, v, l); - if (n >= 0 && (size_t) n < l) { - *value = v; - return n; + if (n < 0) { + if (errno != ERANGE) + return -errno; + } else { + v[n] = 0; /* NUL terminate */ + *ret = TAKE_PTR(v); + return (int) n; } - free(v); - - if (n < 0 && errno != ERANGE) - return -errno; - if (allow_symlink) n = lgetxattr(path, name, NULL, 0); else n = getxattr(path, name, NULL, 0); if (n < 0) return -errno; + if (n > INT_MAX) /* We couldn't return this as 'int' anymore */ + return -E2BIG; + + l = (size_t) n; } } -int fgetxattr_malloc(int fd, const char *name, char **value) { - char *v; - size_t l; - ssize_t n; +int fgetxattr_malloc( + int fd, + const char *name, + char **ret) { + + size_t l = 100; assert(fd >= 0); assert(name); - assert(value); + assert(ret); - for (l = 100;; l = (size_t) n + 1 /* extra byte to make sure this remains NUL suffixed */) { - v = new0(char, l); + for (;;) { + _cleanup_free_ char *v = NULL; + ssize_t n; + + v = new(char, l+1); if (!v) return -ENOMEM; n = fgetxattr(fd, name, v, l); - if (n >= 0 && (size_t) n < l) { - *value = v; - return n; + if (n < 0) { + if (errno != ERANGE) + return -errno; + } else { + v[n] = 0; /* NUL terminate */ + *ret = TAKE_PTR(v); + return (int) n; } - free(v); - - if (n < 0 && errno != ERANGE) - return -errno; - n = fgetxattr(fd, name, NULL, 0); if (n < 0) return -errno; + if (n > INT_MAX) /* We couldn't return this as 'int' anymore */ + return -E2BIG; + + l = (size_t) n; } } @@ -217,3 +234,37 @@ int fd_setcrtime(int fd, usec_t usec) { return 0; } + +int flistxattr_malloc(int fd, char **ret) { + size_t l = 100; + + assert(fd >= 0); + assert(ret); + + for (;;) { + _cleanup_free_ char *v = NULL; + ssize_t n; + + v = new(char, l+1); + if (!v) + return -ENOMEM; + + n = flistxattr(fd, v, l); + if (n < 0) { + if (errno != ERANGE) + return -errno; + } else { + v[n] = 0; /* NUL terminate */ + *ret = TAKE_PTR(v); + return (int) n; + } + + n = flistxattr(fd, NULL, 0); + if (n < 0) + return -errno; + if (n > INT_MAX) /* We couldn't return this as 'int' anymore */ + return -E2BIG; + + l = (size_t) n; + } +} diff --git a/src/basic/xattr-util.h b/src/basic/xattr-util.h index 9fa85d7129..a69e913b7f 100644 --- a/src/basic/xattr-util.h +++ b/src/basic/xattr-util.h @@ -23,3 +23,5 @@ int fd_setcrtime(int fd, usec_t usec); int fd_getcrtime(int fd, usec_t *usec); int path_getcrtime(const char *p, usec_t *usec); int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags); + +int flistxattr_malloc(int fd, char **ret); diff --git a/src/test/test-copy.c b/src/test/test-copy.c index ffabf95663..68905c662d 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include "alloc-util.h" @@ -7,6 +8,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "log.h" #include "macro.h" #include "mkdir.h" @@ -18,6 +20,7 @@ #include "tmpfile-util.h" #include "user-util.h" #include "util.h" +#include "xattr-util.h" static void test_copy_file(void) { _cleanup_free_ char *buf = NULL; @@ -75,14 +78,16 @@ static void test_copy_file_fd(void) { } static void test_copy_tree(void) { - char original_dir[] = "/tmp/test-copy_tree/"; - char copy_dir[] = "/tmp/test-copy_tree-copy/"; + char original_dir[] = "/var/tmp/test-copy_tree/"; + char copy_dir[] = "/var/tmp/test-copy_tree-copy/"; char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file"); char **links = STRV_MAKE("link", "file", "link2", "dir1/file"); - char **p, **link; const char *unixsockp; + char **p, **link; struct stat st; + int xattr_worked = -1; /* xattr support is optional in temporary directories, hence use it if we can, + * but don't fail if we can't */ log_info("%s", __func__); @@ -90,12 +95,19 @@ static void test_copy_tree(void) { (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL); STRV_FOREACH(p, files) { - _cleanup_free_ char *f; + _cleanup_free_ char *f, *c; + int k; assert_se(f = path_join(original_dir, *p)); assert_se(mkdir_parents(f, 0755) >= 0); assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0); + + assert_se(base64mem(*p, strlen(*p), &c) >= 0); + + k = setxattr(f, "user.testxattr", c, strlen(c), 0); + assert_se(xattr_worked < 0 || ((k >= 0) == !!xattr_worked)); + xattr_worked = k >= 0; } STRV_FOREACH_PAIR(link, p, links) { @@ -114,14 +126,25 @@ static void test_copy_tree(void) { assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0); STRV_FOREACH(p, files) { - _cleanup_free_ char *buf, *f; + _cleanup_free_ char *buf, *f, *c = NULL; size_t sz; + int k; assert_se(f = path_join(copy_dir, *p)); assert_se(access(f, F_OK) == 0); assert_se(read_full_file(f, &buf, &sz) == 0); assert_se(streq(buf, "file\n")); + + k = getxattr_malloc(f, "user.testxattr", &c, false); + assert_se(xattr_worked < 0 || ((k >= 0) == !!xattr_worked)); + + if (k >= 0) { + _cleanup_free_ char *d = NULL; + + assert_se(base64mem(*p, strlen(*p), &d) >= 0); + assert_se(streq(d, c)); + } } STRV_FOREACH_PAIR(link, p, links) {