mirror of
https://github.com/systemd/systemd.git
synced 2025-03-21 02:50:18 +03:00
xattr-util: try new *xattrat() family syscalls first
Added in6140be90ec
However, when O_PATH fds are encountered we'd have to go by /proc/self/fd/ still, since the kernel people are reluctant to make the new syscalls work with them (https://lore.kernel.org/linux-fsdevel/20250206-steril-raumplanung-733224062432@brauner/) Hence getxattrat() and listxattrat() are not employed. While at it, remove the discrepancy between path being NULL and empty - I don't grok the "security issue" claimed earlier, but nowadays even the kernel treats the two as identical:e896474fe4
This commit is contained in:
parent
baca3581ea
commit
b8df25dcfe
1
README
1
README
@ -67,6 +67,7 @@ REQUIREMENTS:
|
||||
and MOVE_MOUNT_BENEATH
|
||||
≥ 6.6 for quota support on tmpfs
|
||||
≥ 6.9 for pidfs
|
||||
≥ 6.13 for PIDFD_GET_INFO and {set,remove}xattrat()
|
||||
|
||||
✅ systemd utilizes several new kernel APIs, but will fall back gracefully
|
||||
when unavailable.
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_threads.h"
|
||||
#include "parse-util.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "stat-util.h"
|
||||
@ -20,162 +21,131 @@
|
||||
#include "time-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
/* Use a single cache for all of *xattrat syscalls (added in kernel 6.13) */
|
||||
static thread_local bool have_xattrat = true;
|
||||
|
||||
static int normalize_and_maybe_pin_inode(
|
||||
int *fd,
|
||||
const char **path,
|
||||
int *at_flags,
|
||||
int *ret_tfd,
|
||||
bool *ret_opath) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(fd);
|
||||
assert(*fd >= 0 || *fd == AT_FDCWD);
|
||||
assert(path);
|
||||
assert(at_flags);
|
||||
assert(ret_tfd);
|
||||
assert(ret_opath);
|
||||
|
||||
if (isempty(*path))
|
||||
*path = NULL; /* Normalize "" to NULL */
|
||||
|
||||
if (*fd == AT_FDCWD) {
|
||||
if (!*path) /* Both unspecified? Then operate on current working directory */
|
||||
*path = ".";
|
||||
|
||||
*ret_tfd = -EBADF;
|
||||
*ret_opath = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*at_flags |= AT_EMPTY_PATH;
|
||||
|
||||
if (!*path) {
|
||||
r = fd_is_opath(*fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret_opath = r;
|
||||
|
||||
*ret_tfd = -EBADF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If both have been specified, then we go via O_PATH */
|
||||
|
||||
int tfd = openat(*fd, *path, O_PATH|O_CLOEXEC|(FLAGS_SET(*at_flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
|
||||
if (tfd < 0)
|
||||
return -errno;
|
||||
|
||||
*fd = *ret_tfd = tfd;
|
||||
*path = NULL;
|
||||
*ret_opath = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getxattr_pinned_internal(
|
||||
int fd,
|
||||
const char *path,
|
||||
int at_flags,
|
||||
bool by_procfs,
|
||||
const char *name,
|
||||
char *buf,
|
||||
size_t size) {
|
||||
|
||||
ssize_t n;
|
||||
|
||||
assert(!path || !isempty(path));
|
||||
assert((fd >= 0) == !path);
|
||||
assert((at_flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(path || FLAGS_SET(at_flags, AT_EMPTY_PATH));
|
||||
assert(name);
|
||||
assert(buf || size == 0);
|
||||
|
||||
if (path)
|
||||
n = FLAGS_SET(at_flags, AT_SYMLINK_NOFOLLOW) ? lgetxattr(path, name, buf, size)
|
||||
: getxattr(path, name, buf, size);
|
||||
else
|
||||
n = by_procfs ? getxattr(FORMAT_PROC_FD_PATH(fd), name, buf, size)
|
||||
: fgetxattr(fd, name, buf, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
|
||||
assert((size_t) n <= size);
|
||||
|
||||
if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
|
||||
return -E2BIG;
|
||||
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
int getxattr_at_malloc(
|
||||
int fd,
|
||||
const char *path,
|
||||
const char *name,
|
||||
int flags,
|
||||
int at_flags,
|
||||
char **ret) {
|
||||
|
||||
_cleanup_close_ int opened_fd = -EBADF;
|
||||
unsigned n_attempts = 7;
|
||||
bool by_procfs = false;
|
||||
size_t l = 100;
|
||||
bool by_procfs;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert(name);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(ret);
|
||||
|
||||
/* So, this is single function that does what getxattr()/lgetxattr()/fgetxattr() does, but in one go,
|
||||
* and with additional bells and whistles. Specifically:
|
||||
*
|
||||
* 1. This works on O_PATH fds (which fgetxattr() does not)
|
||||
* 2. Provides full openat()-style semantics, i.e. by-fd, by-path and combination thereof
|
||||
* 3. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is NULL.
|
||||
* 4. Does a malloc() loop, automatically sizing the allocation
|
||||
* 5. NUL-terminates the returned buffer (for safety)
|
||||
* 1. This works on O_PATH fds (via /proc/self/fd/, since getxattrat() syscall refuses them...)
|
||||
* 2. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is empty
|
||||
* 3. Does a malloc() loop, automatically sizing the allocation
|
||||
* 4. NUL-terminates the returned buffer (for safety)
|
||||
*/
|
||||
|
||||
if (!path) /* If path is NULL, imply AT_EMPTY_PATH. – But if it's "", don't — for safety reasons. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (!FLAGS_SET(flags, AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
|
||||
if (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */
|
||||
path = ".";
|
||||
else
|
||||
path = NULL;
|
||||
|
||||
} else if (fd != AT_FDCWD) {
|
||||
|
||||
/* If both have been specified, then we go via O_PATH */
|
||||
opened_fd = openat(fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
path = NULL;
|
||||
by_procfs = true; /* fgetxattr() is not going to work, go via /proc/ link right-away */
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
ssize_t n;
|
||||
|
||||
if (n_attempts == 0) /* If someone is racing against us, give up eventually */
|
||||
return -EBUSY;
|
||||
n_attempts--;
|
||||
|
||||
v = new0(char, l+1);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
l = MALLOC_ELEMENTSOF(v) - 1;
|
||||
|
||||
if (path)
|
||||
n = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? getxattr(path, name, v, l) : lgetxattr(path, name, v, l);
|
||||
else
|
||||
n = by_procfs ? getxattr(FORMAT_PROC_FD_PATH(fd), name, v, l) : fgetxattr(fd, name, v, l);
|
||||
if (n < 0) {
|
||||
if (errno == EBADF) {
|
||||
if (by_procfs || path)
|
||||
return -EBADF;
|
||||
|
||||
by_procfs = true; /* Might be an O_PATH fd, try again via /proc/ link */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno != ERANGE)
|
||||
return -errno;
|
||||
} else {
|
||||
v[n] = 0; /* NUL terminate */
|
||||
*ret = TAKE_PTR(v);
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
if (path)
|
||||
n = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? getxattr(path, name, NULL, 0) : lgetxattr(path, name, NULL, 0);
|
||||
else
|
||||
n = by_procfs ? getxattr(FORMAT_PROC_FD_PATH(fd), name, NULL, 0) : 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;
|
||||
}
|
||||
}
|
||||
|
||||
int getxattr_at_bool(int fd, const char *path, const char *name, int flags) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
int r;
|
||||
|
||||
r = getxattr_at_malloc(fd, path, name, flags, &v);
|
||||
r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (memchr(v, 0, r)) /* Refuse embedded NUL byte */
|
||||
return -EINVAL;
|
||||
at_flags = at_flags_normalize_nofollow(at_flags);
|
||||
|
||||
return parse_boolean(v);
|
||||
}
|
||||
|
||||
int listxattr_at_malloc(
|
||||
int fd,
|
||||
const char *path,
|
||||
int flags,
|
||||
char **ret) {
|
||||
|
||||
_cleanup_close_ int opened_fd = -EBADF;
|
||||
bool by_procfs = false;
|
||||
unsigned n_attempts = 7;
|
||||
size_t l = 100;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(ret);
|
||||
|
||||
/* This is to listxattr()/llistattr()/flistattr() what getxattr_at_malloc() is to getxattr()/… */
|
||||
|
||||
if (!path) /* If path is NULL, imply AT_EMPTY_PATH. – But if it's "", don't. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (!FLAGS_SET(flags, AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
|
||||
if (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */
|
||||
path = ".";
|
||||
else
|
||||
path = NULL;
|
||||
|
||||
} else if (fd != AT_FDCWD) {
|
||||
/* If both have been specified, then we go via O_PATH */
|
||||
opened_fd = openat(fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
path = NULL;
|
||||
by_procfs = true;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
for (unsigned n_attempts = 7;;) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
ssize_t n;
|
||||
|
||||
if (n_attempts == 0) /* If someone is racing against us, give up eventually */
|
||||
return -EBUSY;
|
||||
@ -187,103 +157,205 @@ int listxattr_at_malloc(
|
||||
|
||||
l = MALLOC_ELEMENTSOF(v) - 1;
|
||||
|
||||
if (path)
|
||||
n = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? listxattr(path, v, l) : llistxattr(path, v, l);
|
||||
else
|
||||
n = by_procfs ? listxattr(FORMAT_PROC_FD_PATH(fd), v, l) : flistxattr(fd, v, l);
|
||||
if (n < 0) {
|
||||
if (errno == EBADF) {
|
||||
if (by_procfs || path)
|
||||
return -EBADF;
|
||||
|
||||
by_procfs = true; /* Might be an O_PATH fd, try again via /proc/ link */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno != ERANGE)
|
||||
return -errno;
|
||||
} else {
|
||||
v[n] = 0; /* NUL terminate */
|
||||
r = getxattr_pinned_internal(fd, path, at_flags, by_procfs, name, v, l);
|
||||
if (r >= 0) {
|
||||
v[r] = 0; /* NUL terminate */
|
||||
*ret = TAKE_PTR(v);
|
||||
return (int) n;
|
||||
return r;
|
||||
}
|
||||
if (r != -ERANGE)
|
||||
return r;
|
||||
|
||||
if (path)
|
||||
n = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? listxattr(path, NULL, 0) : llistxattr(path, NULL, 0);
|
||||
else
|
||||
n = by_procfs ? listxattr(FORMAT_PROC_FD_PATH(fd), NULL, 0) : flistxattr(fd, NULL, 0);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
|
||||
return -E2BIG;
|
||||
r = getxattr_pinned_internal(fd, path, at_flags, by_procfs, name, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
l = (size_t) n;
|
||||
l = (size_t) r;
|
||||
}
|
||||
}
|
||||
|
||||
int xsetxattr(int fd,
|
||||
const char *path,
|
||||
const char *name,
|
||||
const char *value,
|
||||
size_t size,
|
||||
int flags) {
|
||||
int getxattr_at_bool(int fd, const char *path, const char *name, int at_flags) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
int r;
|
||||
|
||||
r = getxattr_at_malloc(fd, path, name, at_flags, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (memchr(v, 0, r)) /* Refuse embedded NUL byte */
|
||||
return -EINVAL;
|
||||
|
||||
return parse_boolean(v);
|
||||
}
|
||||
|
||||
static int listxattr_pinned_internal(
|
||||
int fd,
|
||||
const char *path,
|
||||
int at_flags,
|
||||
bool by_procfs,
|
||||
char *buf,
|
||||
size_t size) {
|
||||
|
||||
ssize_t n;
|
||||
|
||||
assert(!path || !isempty(path));
|
||||
assert((fd >= 0) == !path);
|
||||
assert((at_flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(path || FLAGS_SET(at_flags, AT_EMPTY_PATH));
|
||||
assert(buf || size == 0);
|
||||
|
||||
if (path)
|
||||
n = FLAGS_SET(at_flags, AT_SYMLINK_NOFOLLOW) ? llistxattr(path, buf, size)
|
||||
: listxattr(path, buf, size);
|
||||
else
|
||||
n = by_procfs ? listxattr(FORMAT_PROC_FD_PATH(fd), buf, size)
|
||||
: flistxattr(fd, buf, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
|
||||
assert((size_t) n <= size);
|
||||
|
||||
if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
|
||||
return -E2BIG;
|
||||
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
int listxattr_at_malloc(int fd, const char *path, int at_flags, char **ret) {
|
||||
_cleanup_close_ int opened_fd = -EBADF;
|
||||
bool by_procfs = false;
|
||||
bool by_procfs;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(ret);
|
||||
|
||||
/* This is to listxattr()/llistattr()/flistattr() what getxattr_at_malloc() is to getxattr()/… */
|
||||
|
||||
r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
at_flags = at_flags_normalize_nofollow(at_flags);
|
||||
|
||||
size_t l = 100;
|
||||
for (unsigned n_attempts = 7;;) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
if (n_attempts == 0) /* If someone is racing against us, give up eventually */
|
||||
return -EBUSY;
|
||||
n_attempts--;
|
||||
|
||||
v = new(char, l+1);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
l = MALLOC_ELEMENTSOF(v) - 1;
|
||||
|
||||
r = listxattr_pinned_internal(fd, path, at_flags, by_procfs, v, l);
|
||||
if (r >= 0) {
|
||||
v[r] = 0; /* NUL terminate */
|
||||
*ret = TAKE_PTR(v);
|
||||
return r;
|
||||
}
|
||||
if (r != -ERANGE)
|
||||
return r;
|
||||
|
||||
r = listxattr_pinned_internal(fd, path, at_flags, by_procfs, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
l = (size_t) r;
|
||||
}
|
||||
}
|
||||
|
||||
int xsetxattr_full(
|
||||
int fd,
|
||||
const char *path,
|
||||
int at_flags,
|
||||
const char *name,
|
||||
const char *value,
|
||||
size_t size,
|
||||
int xattr_flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(name);
|
||||
assert(value);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
|
||||
/* So, this is a single function that does what setxattr()/lsetxattr()/fsetxattr() do, but in one go,
|
||||
* and with additional bells and whistles. Specifically:
|
||||
*
|
||||
* 1. This works on O_PATH fds (which fsetxattr() does not)
|
||||
* 2. Provides full openat()-style semantics, i.e. by-fd, by-path and combination thereof
|
||||
* 3. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is NULL.
|
||||
*/
|
||||
|
||||
if (!path) /* If path is NULL, imply AT_EMPTY_PATH. – But if it's "", don't — for safety reasons. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(value);
|
||||
|
||||
if (isempty(path)) {
|
||||
if (!FLAGS_SET(flags, AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
if (have_xattrat && !isempty(path)) {
|
||||
struct xattr_args args = {
|
||||
.value = PTR_TO_UINT64(value),
|
||||
.size = size,
|
||||
.flags = xattr_flags,
|
||||
};
|
||||
|
||||
if (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */
|
||||
path = ".";
|
||||
else {
|
||||
r = fd_is_opath(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = RET_NERRNO(setxattrat(fd, path,
|
||||
at_flags_normalize_nofollow(at_flags),
|
||||
name,
|
||||
&args, sizeof(args)));
|
||||
if (r != -ENOSYS) /* No ERRNO_IS_NOT_SUPPORTED here, as EOPNOTSUPP denotes the fs doesn't
|
||||
support xattr */
|
||||
return r;
|
||||
|
||||
by_procfs = r;
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
} else if (fd != AT_FDCWD) {
|
||||
|
||||
/* If both have been specified, then we go via O_PATH */
|
||||
opened_fd = openat(fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
path = NULL;
|
||||
by_procfs = true; /* fsetxattr() is not going to work, go via /proc/ link right-away */
|
||||
have_xattrat = false;
|
||||
}
|
||||
|
||||
_cleanup_close_ int opened_fd = -EBADF;
|
||||
bool by_procfs;
|
||||
|
||||
r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (path)
|
||||
r = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, 0)
|
||||
: lsetxattr(path, name, value, size, 0);
|
||||
r = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, xattr_flags)
|
||||
: lsetxattr(path, name, value, size, xattr_flags);
|
||||
else
|
||||
r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, 0)
|
||||
: fsetxattr(fd, name, value, size, 0);
|
||||
r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, xattr_flags)
|
||||
: fsetxattr(fd, name, value, size, xattr_flags);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xremovexattr(int fd, const char *path, int at_flags, const char *name) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(name);
|
||||
|
||||
if (have_xattrat && !isempty(path)) {
|
||||
r = RET_NERRNO(removexattrat(fd, path,
|
||||
at_flags_normalize_nofollow(at_flags),
|
||||
name));
|
||||
if (r != -ENOSYS) /* No ERRNO_IS_NOT_SUPPORTED here, as EOPNOTSUPP denotes the fs doesn't
|
||||
support xattr */
|
||||
return r;
|
||||
|
||||
have_xattrat = false;
|
||||
}
|
||||
|
||||
_cleanup_close_ int tfd = -EBADF;
|
||||
bool by_procfs;
|
||||
|
||||
r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &tfd, &by_procfs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (path)
|
||||
r = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? removexattr(path, name)
|
||||
: lremovexattr(path, name);
|
||||
else
|
||||
r = by_procfs ? removexattr(FORMAT_PROC_FD_PATH(fd), name)
|
||||
: fremovexattr(fd, name);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
@ -373,5 +445,7 @@ int fd_setcrtime(int fd, usec_t usec) {
|
||||
usec = now(CLOCK_REALTIME);
|
||||
|
||||
le = htole64((uint64_t) usec);
|
||||
return xsetxattr(fd, /* path = */ NULL, "user.crtime_usec", (const char*) &le, sizeof(le), AT_EMPTY_PATH);
|
||||
return xsetxattr_full(fd, /* path = */ NULL, AT_EMPTY_PATH,
|
||||
"user.crtime_usec", (const char*) &le, sizeof(le),
|
||||
/* xattr_flags = */ 0);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
int getxattr_at_malloc(int fd, const char *path, const char *name, int flags, char **ret);
|
||||
int getxattr_at_malloc(int fd, const char *path, const char *name, int at_flags, char **ret);
|
||||
static inline int getxattr_malloc(const char *path, const char *name, char **ret) {
|
||||
return getxattr_at_malloc(AT_FDCWD, path, name, AT_SYMLINK_FOLLOW, ret);
|
||||
}
|
||||
@ -18,9 +18,9 @@ static inline int fgetxattr_malloc(int fd, const char *name, char **ret) {
|
||||
return getxattr_at_malloc(fd, NULL, name, AT_EMPTY_PATH, ret);
|
||||
}
|
||||
|
||||
int getxattr_at_bool(int fd, const char *path, const char *name, int flags);
|
||||
int getxattr_at_bool(int fd, const char *path, const char *name, int at_flags);
|
||||
|
||||
int listxattr_at_malloc(int fd, const char *path, int flags, char **ret);
|
||||
int listxattr_at_malloc(int fd, const char *path, int at_flags, char **ret);
|
||||
static inline int listxattr_malloc(const char *path, char **ret) {
|
||||
return listxattr_at_malloc(AT_FDCWD, path, AT_SYMLINK_FOLLOW, ret);
|
||||
}
|
||||
@ -31,7 +31,24 @@ static inline int flistxattr_malloc(int fd, char **ret) {
|
||||
return listxattr_at_malloc(fd, NULL, AT_EMPTY_PATH, ret);
|
||||
}
|
||||
|
||||
int xsetxattr(int fd, const char *path, const char *name, const char *value, size_t size, int flags);
|
||||
int xsetxattr_full(
|
||||
int fd,
|
||||
const char *path,
|
||||
int at_flags,
|
||||
const char *name,
|
||||
const char *value,
|
||||
size_t size,
|
||||
int xattr_flags);
|
||||
static inline int xsetxattr(
|
||||
int fd,
|
||||
const char *path,
|
||||
int at_flags,
|
||||
const char *name,
|
||||
const char *value) {
|
||||
return xsetxattr_full(fd, path, at_flags, name, value, SIZE_MAX, 0);
|
||||
}
|
||||
|
||||
int xremovexattr(int fd, const char *path, int at_flags, const char *name);
|
||||
|
||||
int fd_setcrtime(int fd, usec_t usec);
|
||||
int getcrtime_at(int fd, const char *path, int at_flags, usec_t *ret);
|
||||
|
@ -1679,8 +1679,7 @@ int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (xsetxattr(dt, to, p, value, r, 0) < 0)
|
||||
ret = -errno;
|
||||
RET_GATHER(ret, xsetxattr_full(dt, to, /* at_flags = */ 0, p, value, r, /* xattr_flags = */ 0));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -82,7 +82,7 @@ TEST(getcrtime) {
|
||||
static void verify_xattr(int dfd, const char *expected) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
assert_se(getxattr_at_malloc(dfd, "test", "user.foo", 0, &value) == (int) strlen(expected));
|
||||
ASSERT_OK_EQ(getxattr_at_malloc(dfd, "test", "user.foo", 0, &value), (int) strlen(expected));
|
||||
ASSERT_STREQ(value, expected);
|
||||
}
|
||||
|
||||
@ -98,31 +98,38 @@ TEST(xsetxattr) {
|
||||
assert_se(touch(x) >= 0);
|
||||
|
||||
/* by full path */
|
||||
r = xsetxattr(AT_FDCWD, x, "user.foo", "fullpath", SIZE_MAX, 0);
|
||||
r = xsetxattr(AT_FDCWD, x, 0, "user.foo", "fullpath");
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return (void) log_tests_skipped_errno(r, "no xattrs supported on /var/tmp");
|
||||
assert_se(r >= 0);
|
||||
ASSERT_OK(r);
|
||||
verify_xattr(dfd, "fullpath");
|
||||
|
||||
/* by dirfd */
|
||||
assert_se(xsetxattr(dfd, "test", "user.foo", "dirfd", SIZE_MAX, 0) >= 0);
|
||||
ASSERT_ERROR(xsetxattr_full(dfd, "test", 0, "user.foo", "dirfd", SIZE_MAX, XATTR_CREATE), EEXIST);
|
||||
verify_xattr(dfd, "fullpath");
|
||||
|
||||
ASSERT_OK(xsetxattr_full(dfd, "test", 0, "user.foo", "dirfd", SIZE_MAX, XATTR_REPLACE));
|
||||
verify_xattr(dfd, "dirfd");
|
||||
|
||||
/* by fd (O_PATH) */
|
||||
fd = openat(dfd, "test", O_PATH|O_CLOEXEC);
|
||||
assert_se(fd >= 0);
|
||||
assert_se(xsetxattr(fd, NULL, "user.foo", "fd_opath", SIZE_MAX, 0) >= 0);
|
||||
ASSERT_OK_ERRNO(fd = openat(dfd, "test", O_PATH|O_CLOEXEC));
|
||||
|
||||
ASSERT_OK(xremovexattr(fd, "", 0, "user.foo"));
|
||||
|
||||
ASSERT_OK(xsetxattr_full(fd, NULL, AT_EMPTY_PATH, "user.foo", "fd_opath", SIZE_MAX, XATTR_CREATE));
|
||||
verify_xattr(dfd, "fd_opath");
|
||||
assert_se(xsetxattr(fd, "", "user.foo", "fd_opath", SIZE_MAX, 0) == -EINVAL);
|
||||
assert_se(xsetxattr(fd, "", "user.foo", "fd_opath_empty", SIZE_MAX, AT_EMPTY_PATH) >= 0);
|
||||
|
||||
ASSERT_OK(xsetxattr(fd, "", 0, "user.foo", "fd_opath_empty"));
|
||||
verify_xattr(dfd, "fd_opath_empty");
|
||||
|
||||
fd = safe_close(fd);
|
||||
|
||||
fd = openat(dfd, "test", O_RDONLY|O_CLOEXEC);
|
||||
assert_se(xsetxattr(fd, NULL, "user.foo", "fd_regular", SIZE_MAX, 0) >= 0);
|
||||
|
||||
ASSERT_OK(xsetxattr_full(fd, NULL, 0, "user.foo", "fd_regular", SIZE_MAX, XATTR_REPLACE));
|
||||
verify_xattr(dfd, "fd_regular");
|
||||
assert_se(xsetxattr(fd, "", "user.foo", "fd_regular_empty", SIZE_MAX, 0) == -EINVAL);
|
||||
assert_se(xsetxattr(fd, "", "user.foo", "fd_regular_empty", SIZE_MAX, AT_EMPTY_PATH) >= 0);
|
||||
|
||||
ASSERT_OK(xsetxattr(fd, "", 0, "user.foo", "fd_regular_empty"));
|
||||
verify_xattr(dfd, "fd_regular_empty");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user