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 (#36228)
While killing cgroup v1 I want to turn all cg_* helpers to take cgroupfs_fd + cg path, which means all relevant path operations must support openat() semantics. `removexattrat()` is currently missing. I took the chance to do a bigger refactor for xattr-util.
This commit is contained in:
commit
aaa274ed7f
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.
|
||||
|
@ -625,7 +625,7 @@ foreach ident : [
|
||||
#include <unistd.h>'''], # no known header declares pivot_root
|
||||
['ioprio_get', '''#include <sched.h>'''], # no known header declares ioprio_get
|
||||
['ioprio_set', '''#include <sched.h>'''], # no known header declares ioprio_set
|
||||
['sched_setattr', '''#include <sched.h>'''], # no known header declares sched_setattr
|
||||
['sched_setattr', '''#include <sched.h>'''],
|
||||
['name_to_handle_at', '''#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>'''],
|
||||
@ -682,6 +682,8 @@ foreach ident : [
|
||||
['strerrorname_np', '''#include <string.h>'''],
|
||||
['getrandom', '''#include <sys/random.h>'''],
|
||||
['quotactl_fd', '''#include <sys/quota.h>'''],
|
||||
['setxattrat', '''#include <sys/xattr.h>'''], # no known header declares setxattrat
|
||||
['removexattrat', '''#include <sys/xattr.h>'''], # no known header declares removexattrat
|
||||
]
|
||||
|
||||
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')
|
||||
@ -770,7 +772,6 @@ if not cc.has_header('sys/capability.h')
|
||||
endif
|
||||
foreach header : ['crypt.h',
|
||||
'linux/ioprio.h',
|
||||
'linux/time_types.h',
|
||||
'sys/sdt.h',
|
||||
'threads.h',
|
||||
'valgrind/memcheck.h',
|
||||
|
@ -22,10 +22,10 @@ typedef enum Compression {
|
||||
_COMPRESSION_INVALID = -EINVAL,
|
||||
} Compression;
|
||||
|
||||
const char* compression_to_string(Compression compression);
|
||||
Compression compression_from_string(const char *compression);
|
||||
const char* compression_lowercase_to_string(Compression compression);
|
||||
Compression compression_lowercase_from_string(const char *compression);
|
||||
const char* compression_to_string(Compression compression) _const_;
|
||||
Compression compression_from_string(const char *compression) _pure_;
|
||||
const char* compression_lowercase_to_string(Compression compression) _const_;
|
||||
Compression compression_lowercase_from_string(const char *compression) _pure_;
|
||||
|
||||
bool compression_supported(Compression c);
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#if WANT_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#include <linux/kcmp.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* Match values uses by the kernel internally, as no public header seems to exist. */
|
||||
|
||||
#ifndef IOPRIO_N_CLASSES
|
||||
# define IOPRIO_N_CLASSES 8
|
||||
#else
|
||||
|
@ -5,11 +5,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#if HAVE_LINUX_TIME_TYPES_H
|
||||
/* This header defines __kernel_timespec for us, but is only available since Linux 5.1, hence conditionally
|
||||
* include this. */
|
||||
#include <linux/time_types.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
@ -26,11 +22,6 @@
|
||||
#include "missing_stat.h"
|
||||
#include "missing_syscall_def.h"
|
||||
|
||||
/* linux/kcmp.h */
|
||||
#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
|
||||
#define KCMP_FILE 0
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FCHMODAT2
|
||||
@ -646,13 +637,47 @@ int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags,
|
||||
#if !HAVE_QUOTACTL_FD
|
||||
|
||||
static inline int missing_quotactl_fd(int fd, int cmd, int id, void *addr) {
|
||||
#if defined __NR_quotactl_fd
|
||||
# ifdef __NR_quotactl_fd
|
||||
return syscall(__NR_quotactl_fd, fd, cmd, id, addr);
|
||||
#else
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
# define quotactl_fd missing_quotactl_fd
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_SETXATTRAT
|
||||
struct xattr_args {
|
||||
_align_(8) uint64_t value;
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static inline int missing_setxattrat(int fd, const char *path, int at_flags, const char *name, const struct xattr_args *args, size_t size) {
|
||||
# ifdef __NR_setxattrat
|
||||
return syscall(__NR_setxattrat, fd, path, at_flags, name, args, size);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define setxattrat missing_setxattrat
|
||||
#endif
|
||||
|
||||
#if !HAVE_REMOVEXATTRAT
|
||||
static inline int missing_removexattrat(int fd, const char *path, int at_flags, const char *name) {
|
||||
# ifdef __NR_removexattrat
|
||||
return syscall(__NR_removexattrat, fd, path, at_flags, name);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define removexattrat missing_removexattrat
|
||||
#endif
|
||||
|
@ -994,6 +994,142 @@ assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __IGNORE_quotactl_fd
|
||||
# if defined(__aarch64__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__alpha__)
|
||||
# define systemd_NR_quotactl_fd 553
|
||||
# elif defined(__arc__) || defined(__tilegx__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__arm__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__i386__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__ia64__)
|
||||
# define systemd_NR_quotactl_fd 1467
|
||||
# elif defined(__loongarch_lp64)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__m68k__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(_MIPS_SIM)
|
||||
# if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
# define systemd_NR_quotactl_fd 4443
|
||||
# elif _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
# define systemd_NR_quotactl_fd 6443
|
||||
# elif _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
# define systemd_NR_quotactl_fd 5443
|
||||
# else
|
||||
# error "Unknown MIPS ABI"
|
||||
# endif
|
||||
# elif defined(__hppa__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__powerpc__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__riscv)
|
||||
# if __riscv_xlen == 32
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif __riscv_xlen == 64
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# else
|
||||
# error "Unknown RISC-V ABI"
|
||||
# endif
|
||||
# elif defined(__s390__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__sparc__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__x86_64__)
|
||||
# if defined(__ILP32__)
|
||||
# define systemd_NR_quotactl_fd (443 | /* __X32_SYSCALL_BIT */ 0x40000000)
|
||||
# else
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# endif
|
||||
# elif !defined(missing_arch_template)
|
||||
# warning "quotactl_fd() syscall number is unknown for your architecture"
|
||||
# endif
|
||||
|
||||
/* may be an (invalid) negative number due to libseccomp, see PR 13319 */
|
||||
# if defined __NR_quotactl_fd && __NR_quotactl_fd >= 0
|
||||
# if defined systemd_NR_quotactl_fd
|
||||
assert_cc(__NR_quotactl_fd == systemd_NR_quotactl_fd);
|
||||
# endif
|
||||
# else
|
||||
# if defined __NR_quotactl_fd
|
||||
# undef __NR_quotactl_fd
|
||||
# endif
|
||||
# if defined systemd_NR_quotactl_fd && systemd_NR_quotactl_fd >= 0
|
||||
# define __NR_quotactl_fd systemd_NR_quotactl_fd
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __IGNORE_removexattrat
|
||||
# if defined(__aarch64__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__alpha__)
|
||||
# define systemd_NR_removexattrat 576
|
||||
# elif defined(__arc__) || defined(__tilegx__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__arm__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__i386__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__ia64__)
|
||||
# define systemd_NR_removexattrat -1
|
||||
# elif defined(__loongarch_lp64)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__m68k__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(_MIPS_SIM)
|
||||
# if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
# define systemd_NR_removexattrat 4466
|
||||
# elif _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
# define systemd_NR_removexattrat 6466
|
||||
# elif _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
# define systemd_NR_removexattrat 5466
|
||||
# else
|
||||
# error "Unknown MIPS ABI"
|
||||
# endif
|
||||
# elif defined(__hppa__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__powerpc__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__riscv)
|
||||
# if __riscv_xlen == 32
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif __riscv_xlen == 64
|
||||
# define systemd_NR_removexattrat 466
|
||||
# else
|
||||
# error "Unknown RISC-V ABI"
|
||||
# endif
|
||||
# elif defined(__s390__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__sparc__)
|
||||
# define systemd_NR_removexattrat 466
|
||||
# elif defined(__x86_64__)
|
||||
# if defined(__ILP32__)
|
||||
# define systemd_NR_removexattrat (466 | /* __X32_SYSCALL_BIT */ 0x40000000)
|
||||
# else
|
||||
# define systemd_NR_removexattrat 466
|
||||
# endif
|
||||
# elif !defined(missing_arch_template)
|
||||
# warning "removexattrat() syscall number is unknown for your architecture"
|
||||
# endif
|
||||
|
||||
/* may be an (invalid) negative number due to libseccomp, see PR 13319 */
|
||||
# if defined __NR_removexattrat && __NR_removexattrat >= 0
|
||||
# if defined systemd_NR_removexattrat
|
||||
assert_cc(__NR_removexattrat == systemd_NR_removexattrat);
|
||||
# endif
|
||||
# else
|
||||
# if defined __NR_removexattrat
|
||||
# undef __NR_removexattrat
|
||||
# endif
|
||||
# if defined systemd_NR_removexattrat && systemd_NR_removexattrat >= 0
|
||||
# define __NR_removexattrat systemd_NR_removexattrat
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __IGNORE_renameat2
|
||||
# if defined(__aarch64__)
|
||||
# define systemd_NR_renameat2 276
|
||||
@ -1130,6 +1266,74 @@ assert_cc(__NR_setns == systemd_NR_setns);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __IGNORE_setxattrat
|
||||
# if defined(__aarch64__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__alpha__)
|
||||
# define systemd_NR_setxattrat 573
|
||||
# elif defined(__arc__) || defined(__tilegx__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__arm__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__i386__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__ia64__)
|
||||
# define systemd_NR_setxattrat -1
|
||||
# elif defined(__loongarch_lp64)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__m68k__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(_MIPS_SIM)
|
||||
# if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
# define systemd_NR_setxattrat 4463
|
||||
# elif _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
# define systemd_NR_setxattrat 6463
|
||||
# elif _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
# define systemd_NR_setxattrat 5463
|
||||
# else
|
||||
# error "Unknown MIPS ABI"
|
||||
# endif
|
||||
# elif defined(__hppa__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__powerpc__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__riscv)
|
||||
# if __riscv_xlen == 32
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif __riscv_xlen == 64
|
||||
# define systemd_NR_setxattrat 463
|
||||
# else
|
||||
# error "Unknown RISC-V ABI"
|
||||
# endif
|
||||
# elif defined(__s390__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__sparc__)
|
||||
# define systemd_NR_setxattrat 463
|
||||
# elif defined(__x86_64__)
|
||||
# if defined(__ILP32__)
|
||||
# define systemd_NR_setxattrat (463 | /* __X32_SYSCALL_BIT */ 0x40000000)
|
||||
# else
|
||||
# define systemd_NR_setxattrat 463
|
||||
# endif
|
||||
# elif !defined(missing_arch_template)
|
||||
# warning "setxattrat() syscall number is unknown for your architecture"
|
||||
# endif
|
||||
|
||||
/* may be an (invalid) negative number due to libseccomp, see PR 13319 */
|
||||
# if defined __NR_setxattrat && __NR_setxattrat >= 0
|
||||
# if defined systemd_NR_setxattrat
|
||||
assert_cc(__NR_setxattrat == systemd_NR_setxattrat);
|
||||
# endif
|
||||
# else
|
||||
# if defined __NR_setxattrat
|
||||
# undef __NR_setxattrat
|
||||
# endif
|
||||
# if defined systemd_NR_setxattrat && systemd_NR_setxattrat >= 0
|
||||
# define __NR_setxattrat systemd_NR_setxattrat
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __IGNORE_statx
|
||||
# if defined(__aarch64__)
|
||||
# define systemd_NR_statx 291
|
||||
@ -1197,71 +1401,3 @@ assert_cc(__NR_statx == systemd_NR_statx);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __IGNORE_quotactl_fd
|
||||
# if defined(__aarch64__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__alpha__)
|
||||
# define systemd_NR_quotactl_fd 553
|
||||
# elif defined(__arc__) || defined(__tilegx__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__arm__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__i386__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__ia64__)
|
||||
# define systemd_NR_quotactl_fd 1467
|
||||
# elif defined(__loongarch_lp64)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__m68k__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(_MIPS_SIM)
|
||||
# if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
# define systemd_NR_quotactl_fd 4443
|
||||
# elif _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
# define systemd_NR_quotactl_fd 6443
|
||||
# elif _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
# define systemd_NR_quotactl_fd 5443
|
||||
# else
|
||||
# error "Unknown MIPS ABI"
|
||||
# endif
|
||||
# elif defined(__hppa__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__powerpc__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__riscv)
|
||||
# if __riscv_xlen == 32
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif __riscv_xlen == 64
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# else
|
||||
# error "Unknown RISC-V ABI"
|
||||
# endif
|
||||
# elif defined(__s390__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__sparc__)
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# elif defined(__x86_64__)
|
||||
# if defined(__ILP32__)
|
||||
# define systemd_NR_quotactl_fd (443 | /* __X32_SYSCALL_BIT */ 0x40000000)
|
||||
# else
|
||||
# define systemd_NR_quotactl_fd 443
|
||||
# endif
|
||||
# elif !defined(missing_arch_template)
|
||||
# warning "quotactl_fd() syscall number is unknown for your architecture"
|
||||
# endif
|
||||
|
||||
/* may be an (invalid) negative number due to libseccomp, see PR 13319 */
|
||||
# if defined __NR_quotactl_fd && __NR_quotactl_fd >= 0
|
||||
# if defined systemd_NR_quotactl_fd
|
||||
assert_cc(__NR_quotactl_fd == systemd_NR_quotactl_fd);
|
||||
# endif
|
||||
# else
|
||||
# if defined __NR_quotactl_fd
|
||||
# undef __NR_quotactl_fd
|
||||
# endif
|
||||
# if defined systemd_NR_quotactl_fd && systemd_NR_quotactl_fd >= 0
|
||||
# define __NR_quotactl_fd systemd_NR_quotactl_fd
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
@ -21,8 +21,10 @@ SYSCALLS = [
|
||||
'pidfd_send_signal',
|
||||
'pkey_mprotect',
|
||||
'quotactl_fd',
|
||||
'removexattrat',
|
||||
'renameat2',
|
||||
'setns',
|
||||
'setxattrat',
|
||||
'statx',
|
||||
]
|
||||
|
||||
|
@ -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,110 +21,164 @@
|
||||
#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;
|
||||
r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (!FLAGS_SET(flags, AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
at_flags = at_flags_normalize_nofollow(at_flags);
|
||||
|
||||
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 (;;) {
|
||||
size_t l = 100;
|
||||
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;
|
||||
n_attempts--;
|
||||
|
||||
v = new0(char, l+1);
|
||||
v = new(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 */
|
||||
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) ? 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;
|
||||
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 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) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
int r;
|
||||
|
||||
r = getxattr_at_malloc(fd, path, name, flags, &v);
|
||||
r = getxattr_at_malloc(fd, path, name, at_flags, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -133,23 +188,200 @@ int getxattr_at_bool(int fd, const char *path, const char *name, int flags) {
|
||||
return parse_boolean(v);
|
||||
}
|
||||
|
||||
static int parse_crtime(le64_t le, usec_t *usec) {
|
||||
uint64_t u;
|
||||
static int listxattr_pinned_internal(
|
||||
int fd,
|
||||
const char *path,
|
||||
int at_flags,
|
||||
bool by_procfs,
|
||||
char *buf,
|
||||
size_t size) {
|
||||
|
||||
assert(usec);
|
||||
ssize_t n;
|
||||
|
||||
u = le64toh(le);
|
||||
if (IN_SET(u, 0, UINT64_MAX))
|
||||
return -EIO;
|
||||
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;
|
||||
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);
|
||||
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(value);
|
||||
|
||||
if (have_xattrat && !isempty(path)) {
|
||||
struct xattr_args args = {
|
||||
.value = PTR_TO_UINT64(value),
|
||||
.size = size,
|
||||
.flags = xattr_flags,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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(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, xattr_flags)
|
||||
: fsetxattr(fd, name, value, size, xattr_flags);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
*usec = (usec_t) u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_getcrtime_at(
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_crtime(le64_t le, usec_t *ret) {
|
||||
usec_t u;
|
||||
|
||||
assert(ret);
|
||||
|
||||
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
|
||||
assert_cc((usec_t) UINT64_MAX == USEC_INFINITY);
|
||||
|
||||
u = (usec_t) le64toh(le);
|
||||
if (!timestamp_is_set(u))
|
||||
return -EIO;
|
||||
|
||||
*ret = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getcrtime_at(
|
||||
int fd,
|
||||
const char *path,
|
||||
int flags,
|
||||
int at_flags,
|
||||
usec_t *ret) {
|
||||
|
||||
_cleanup_free_ le64_t *le = NULL;
|
||||
@ -158,11 +390,10 @@ int fd_getcrtime_at(
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(ret);
|
||||
assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
|
||||
if (!path)
|
||||
flags |= AT_EMPTY_PATH;
|
||||
if (isempty(path))
|
||||
at_flags |= AT_EMPTY_PATH;
|
||||
|
||||
/* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
|
||||
* on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
|
||||
@ -175,17 +406,15 @@ int fd_getcrtime_at(
|
||||
* most sense. */
|
||||
|
||||
if (statx(fd, strempty(path),
|
||||
at_flags_normalize_nofollow(flags)|AT_STATX_DONT_SYNC,
|
||||
at_flags_normalize_nofollow(at_flags)|AT_STATX_DONT_SYNC,
|
||||
STATX_BTIME,
|
||||
&sx) >= 0 &&
|
||||
(sx.stx_mask & STATX_BTIME) &&
|
||||
sx.stx_btime.tv_sec != 0)
|
||||
a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
|
||||
(usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
|
||||
FLAGS_SET(sx.stx_mask, STATX_BTIME) && sx.stx_btime.tv_sec != 0)
|
||||
a = statx_timestamp_load(&sx.stx_btime);
|
||||
else
|
||||
a = USEC_INFINITY;
|
||||
|
||||
r = getxattr_at_malloc(fd, path, "user.crtime_usec", flags, (char**) &le);
|
||||
r = getxattr_at_malloc(fd, path, "user.crtime_usec", at_flags, (char**) &le);
|
||||
if (r >= 0) {
|
||||
if (r != sizeof(*le))
|
||||
r = -EIO;
|
||||
@ -194,18 +423,16 @@ int fd_getcrtime_at(
|
||||
}
|
||||
if (r < 0) {
|
||||
if (a != USEC_INFINITY) {
|
||||
*ret = a;
|
||||
if (ret)
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (a != USEC_INFINITY)
|
||||
if (ret)
|
||||
*ret = MIN(a, b);
|
||||
else
|
||||
*ret = b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -218,162 +445,7 @@ int fd_setcrtime(int fd, usec_t usec) {
|
||||
usec = now(CLOCK_REALTIME);
|
||||
|
||||
le = htole64((uint64_t) usec);
|
||||
return RET_NERRNO(fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0));
|
||||
}
|
||||
|
||||
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 (;;) {
|
||||
_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 = new(char, l+1);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
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 */
|
||||
*ret = TAKE_PTR(v);
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
l = (size_t) n;
|
||||
}
|
||||
}
|
||||
|
||||
int xsetxattr(int fd,
|
||||
const char *path,
|
||||
const char *name,
|
||||
const char *value,
|
||||
size_t size,
|
||||
int flags) {
|
||||
|
||||
_cleanup_close_ int opened_fd = -EBADF;
|
||||
bool by_procfs = false;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
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 (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */
|
||||
path = ".";
|
||||
else {
|
||||
r = fd_is_opath(fd);
|
||||
if (r < 0)
|
||||
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 */
|
||||
}
|
||||
|
||||
if (path)
|
||||
r = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, 0)
|
||||
: lsetxattr(path, name, value, size, 0);
|
||||
else
|
||||
r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, 0)
|
||||
: fsetxattr(fd, name, value, size, 0);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
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,16 +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 fd_setcrtime(int fd, usec_t usec);
|
||||
|
||||
int fd_getcrtime_at(int fd, const char *name, int flags, usec_t *ret);
|
||||
static inline int fd_getcrtime(int fd, usec_t *ret) {
|
||||
return fd_getcrtime_at(fd, NULL, 0, ret);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -38,4 +31,27 @@ 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);
|
||||
static inline int fd_getcrtime(int fd, usec_t *ret) {
|
||||
return getcrtime_at(fd, NULL, 0, ret);
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ static void patch_realtime(
|
||||
* FS might provide, but unfortunately there's currently no sane API to query it. Hence let's
|
||||
* implement this manually... */
|
||||
|
||||
if (fd_getcrtime_at(fd, fn, AT_SYMLINK_FOLLOW, &x) >= 0 && x < *realtime)
|
||||
if (getcrtime_at(fd, fn, AT_SYMLINK_FOLLOW, &x) >= 0 && x < *realtime)
|
||||
*realtime = x;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "chown-recursive.h"
|
||||
#include "dirent-util.h"
|
||||
@ -13,6 +12,7 @@
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
static int chown_one(
|
||||
int fd,
|
||||
@ -26,14 +26,12 @@ static int chown_one(
|
||||
assert(fd >= 0);
|
||||
assert(st);
|
||||
|
||||
/* We change ACLs through the /proc/self/fd/%i path, so that we have a stable reference that works
|
||||
* with O_PATH. */
|
||||
|
||||
/* Drop any ACL if there is one */
|
||||
FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default")
|
||||
if (removexattr(FORMAT_PROC_FD_PATH(fd), n) < 0)
|
||||
if (!ERRNO_IS_XATTR_ABSENT(errno))
|
||||
return -errno;
|
||||
FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default") {
|
||||
r = xremovexattr(fd, /* path = */ NULL, AT_EMPTY_PATH, n);
|
||||
if (r < 0 && !ERRNO_IS_NEG_XATTR_ABSENT(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
r = fchmod_and_chown(fd, st->st_mode & mask, uid, gid);
|
||||
if (r < 0)
|
||||
|
@ -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;
|
||||
|
@ -26,6 +26,10 @@
|
||||
#include "xattr-util.h"
|
||||
|
||||
#if ENABLE_SMACK
|
||||
|
||||
#define SMACK_FLOOR_LABEL "_"
|
||||
#define SMACK_STAR_LABEL "*"
|
||||
|
||||
bool mac_smack_use(void) {
|
||||
static int cached_use = -1;
|
||||
|
||||
@ -44,80 +48,45 @@ static const char* const smack_attr_table[_SMACK_ATTR_MAX] = {
|
||||
[SMACK_ATTR_IPOUT] = "security.SMACK64IPOUT",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(smack_attr, SmackAttr);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(smack_attr, SmackAttr);
|
||||
|
||||
int mac_smack_read(const char *path, SmackAttr attr, char **label) {
|
||||
assert(path);
|
||||
int mac_smack_read_at(int fd, const char *path, SmackAttr attr, char **ret) {
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
assert(label);
|
||||
assert(ret);
|
||||
|
||||
if (!mac_smack_use())
|
||||
if (!mac_smack_use()) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getxattr_malloc(path, smack_attr_to_string(attr), label);
|
||||
return getxattr_at_malloc(fd, path, smack_attr_to_string(attr), /* at_flags = */ 0, ret);
|
||||
}
|
||||
|
||||
int mac_smack_read_fd(int fd, SmackAttr attr, char **label) {
|
||||
assert(fd >= 0);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
assert(label);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
return fgetxattr_malloc(fd, smack_attr_to_string(attr), label);
|
||||
}
|
||||
|
||||
int mac_smack_apply_at(int dir_fd, const char *path, SmackAttr attr, const char *label) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
assert(path);
|
||||
int mac_smack_apply_at(int fd, const char *path, SmackAttr attr, const char *label) {
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
if (!label)
|
||||
return xremovexattr(fd, path, /* at_flags = */ 0, smack_attr_to_string(attr));
|
||||
|
||||
return mac_smack_apply_fd(fd, attr, label);
|
||||
}
|
||||
|
||||
int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
if (label)
|
||||
r = setxattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr), label, strlen(label), 0);
|
||||
else
|
||||
r = removexattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return xsetxattr(fd, path, /* at_flags = */ 0, smack_attr_to_string(attr), label);
|
||||
}
|
||||
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(label);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
p = procfs_file_alloca(pid, "attr/current");
|
||||
r = write_string_file(p, label, WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
return write_string_file(p, label, WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
}
|
||||
|
||||
static int smack_fix_fd(
|
||||
@ -156,25 +125,22 @@ static int smack_fix_fd(
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (setxattr(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", label, strlen(label), 0) < 0) {
|
||||
r = xsetxattr(fd, /* path = */ NULL, AT_EMPTY_PATH, "security.SMACK64", label);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) /* If the FS doesn't support labels, then exit without warning */
|
||||
return 0;
|
||||
if (r == -EROFS && FLAGS_SET(flags, LABEL_IGNORE_EROFS)) /* If the FS is read-only and we were told
|
||||
to ignore failures caused by that,
|
||||
suppress error */
|
||||
return 0;
|
||||
if (r < 0) {
|
||||
/* If the old label is identical to the new one, suppress any kind of error */
|
||||
_cleanup_free_ char *old_label = NULL;
|
||||
|
||||
r = -errno;
|
||||
|
||||
/* If the FS doesn't support labels, then exit without warning */
|
||||
if (ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return 0;
|
||||
|
||||
/* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
|
||||
if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
|
||||
return 0;
|
||||
|
||||
/* If the old label is identical to the new one, suppress any kind of error */
|
||||
if (lgetxattr_malloc(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", &old_label) >= 0 &&
|
||||
if (fgetxattr_malloc(fd, "security.SMACK64", &old_label) >= 0 &&
|
||||
streq(old_label, label))
|
||||
return 0;
|
||||
|
||||
return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", label_path);
|
||||
return log_debug_errno(r, "Unable to fix SMACK label of '%s': %m", label_path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -190,8 +156,7 @@ int mac_smack_fix_full(
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r, inode_fd;
|
||||
|
||||
assert(atfd >= 0 || atfd == AT_FDCWD);
|
||||
assert(atfd >= 0 || inode_path);
|
||||
assert(atfd >= 0 || (atfd == AT_FDCWD && inode_path));
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
@ -199,7 +164,7 @@ int mac_smack_fix_full(
|
||||
if (inode_path) {
|
||||
opened_fd = openat(atfd, inode_path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
|
||||
if (opened_fd < 0) {
|
||||
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
|
||||
if (errno == ENOENT && FLAGS_SET(flags, LABEL_IGNORE_ENOENT))
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
@ -224,8 +189,8 @@ int mac_smack_fix_full(
|
||||
}
|
||||
|
||||
int mac_smack_copy(const char *dest, const char *src) {
|
||||
int r;
|
||||
_cleanup_free_ char *label = NULL;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
assert(src);
|
||||
@ -240,39 +205,6 @@ int mac_smack_copy(const char *dest, const char *src) {
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#else
|
||||
bool mac_smack_use(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int mac_smack_read(const char *path, SmackAttr attr, char **label) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mac_smack_read_fd(int fd, SmackAttr attr, char **label) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mac_smack_apply_at(int dir_fd, const char *path, SmackAttr attr, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_copy(const char *dest, const char *src) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int renameat_and_apply_smack_floor_label(int fdf, const char *from, int fdt, const char *to) {
|
||||
|
@ -13,9 +13,6 @@
|
||||
#include "label-util.h"
|
||||
#include "macro.h"
|
||||
|
||||
#define SMACK_FLOOR_LABEL "_"
|
||||
#define SMACK_STAR_LABEL "*"
|
||||
|
||||
typedef enum SmackAttr {
|
||||
SMACK_ATTR_ACCESS,
|
||||
SMACK_ATTR_EXEC,
|
||||
@ -27,25 +24,61 @@ typedef enum SmackAttr {
|
||||
_SMACK_ATTR_INVALID = -EINVAL,
|
||||
} SmackAttr;
|
||||
|
||||
#if ENABLE_SMACK
|
||||
bool mac_smack_use(void);
|
||||
int mac_smack_init(void);
|
||||
|
||||
int mac_smack_read_at(int fd, const char *path, SmackAttr attr, char **ret);
|
||||
int mac_smack_apply_at(int fd, const char *path, SmackAttr attr, const char *label);
|
||||
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label);
|
||||
int mac_smack_copy(const char *dest, const char *src);
|
||||
|
||||
int mac_smack_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags);
|
||||
static inline int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
return mac_smack_fix_full(AT_FDCWD, path, path, flags);
|
||||
#else
|
||||
static inline bool mac_smack_use(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int mac_smack_read_at(int fd, const char *path, SmackAttr attr, char **ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int mac_smack_apply_at(int fd, const char *path, SmackAttr attr, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mac_smack_apply_pid(pid_t pid, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mac_smack_copy(const char *dest, const char *src) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mac_smack_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mac_smack_init(void);
|
||||
|
||||
static inline int mac_smack_read(const char *path, SmackAttr attr, char **ret) {
|
||||
return mac_smack_read_at(AT_FDCWD, path, attr, ret);
|
||||
}
|
||||
static inline int mac_smack_read_fd(int fd, SmackAttr attr, char **ret) {
|
||||
return mac_smack_read_at(fd, NULL, attr, ret);
|
||||
}
|
||||
|
||||
const char* smack_attr_to_string(SmackAttr i) _const_;
|
||||
SmackAttr smack_attr_from_string(const char *s) _pure_;
|
||||
int mac_smack_read(const char *path, SmackAttr attr, char **label);
|
||||
int mac_smack_read_fd(int fd, SmackAttr attr, char **label);
|
||||
int mac_smack_apply_at(int dir_fd, const char *path, SmackAttr attr, const char *label);
|
||||
static inline int mac_smack_apply(const char *path, SmackAttr attr, const char *label) {
|
||||
return mac_smack_apply_at(AT_FDCWD, path, attr, label);
|
||||
}
|
||||
int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label);
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label);
|
||||
int mac_smack_copy(const char *dest, const char *src);
|
||||
static inline int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
|
||||
return mac_smack_apply_at(fd, NULL, attr, label);
|
||||
}
|
||||
|
||||
static inline int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
return mac_smack_fix_full(AT_FDCWD, path, path, flags);
|
||||
}
|
||||
|
||||
int renameat_and_apply_smack_floor_label(int fdf, const char *from, int fdt, const char *to);
|
||||
static inline int rename_and_apply_smack_floor_label(const char *from, const char *to) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
@ -82,10 +83,24 @@ 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);
|
||||
}
|
||||
|
||||
static void xattr_symlink_test_one(int fd, const char *path) {
|
||||
_cleanup_free_ char *value = NULL, *list = NULL;
|
||||
|
||||
ASSERT_OK(xsetxattr(fd, path, 0, "trusted.test", "schaffen"));
|
||||
ASSERT_OK_EQ(getxattr_at_malloc(fd, path, "trusted.test", 0, &value), (int) STRLEN("schaffen"));
|
||||
ASSERT_STREQ(value, "schaffen");
|
||||
|
||||
ASSERT_OK_EQ(listxattr_at_malloc(fd, path, 0, &list), (int) sizeof("trusted.test"));
|
||||
ASSERT_STREQ(list, "trusted.test");
|
||||
|
||||
ASSERT_OK(xremovexattr(fd, path, 0, "trusted.test"));
|
||||
ASSERT_ERROR(getxattr_at_malloc(fd, path, "trusted.test", 0, &value), ENODATA);
|
||||
}
|
||||
|
||||
TEST(xsetxattr) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
|
||||
_cleanup_close_ int dfd = -EBADF, fd = -EBADF;
|
||||
@ -98,32 +113,56 @@ 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");
|
||||
|
||||
fd = safe_close(fd);
|
||||
|
||||
/* user.* xattrs are not supported on symlinks. Use trusted.* which requires privilege. */
|
||||
if (have_effective_cap(CAP_SYS_ADMIN) > 0) {
|
||||
ASSERT_OK_ERRNO(symlinkat("empty", dfd, "symlink"));
|
||||
ASSERT_OK_ERRNO(fd = openat(dfd, "symlink", O_NOFOLLOW|O_PATH|O_CLOEXEC));
|
||||
|
||||
ASSERT_ERROR(xsetxattr(dfd, "symlink", AT_SYMLINK_FOLLOW, "trusted.test", "bogus"), ENOENT);
|
||||
|
||||
xattr_symlink_test_one(dfd, "symlink");
|
||||
xattr_symlink_test_one(fd, NULL);
|
||||
xattr_symlink_test_one(fd, "");
|
||||
|
||||
x = strjoina(t, "/symlink");
|
||||
xattr_symlink_test_one(AT_FDCWD, x);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <sysexits.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
@ -73,6 +72,7 @@
|
||||
#include "umask-util.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
|
||||
* them in the file system. This is intended to be used to create
|
||||
@ -1189,6 +1189,8 @@ static int fd_set_xattrs(
|
||||
const struct stat *st,
|
||||
CreationMode creation) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(i);
|
||||
assert(fd >= 0);
|
||||
@ -1198,10 +1200,12 @@ static int fd_set_xattrs(
|
||||
log_action("Would set", "Setting",
|
||||
"%s extended attribute '%s=%s' on %s", *name, *value, path);
|
||||
|
||||
if (!arg_dry_run &&
|
||||
setxattr(FORMAT_PROC_FD_PATH(fd), *name, *value, strlen(*value), 0) < 0)
|
||||
return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
|
||||
*name, *value, path);
|
||||
if (!arg_dry_run) {
|
||||
r = xsetxattr(fd, /* path = */ NULL, AT_EMPTY_PATH, *name, *value);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set extended attribute %s=%s on '%s': %m",
|
||||
*name, *value, path);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user