Merge pull request #14081 from poettering/xattr-list-rework

xattr-util rework and addition of flistxattr_malloc() helper plus test
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2019-11-22 09:18:24 +01:00 committed by GitHub
commit 353a6f293e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 86 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <sys/xattr.h>
#include <unistd.h>
#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) {