diff --git a/README b/README index 975f5e5a5e5..1f3d1df2754 100644 --- a/README +++ b/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. diff --git a/meson.build b/meson.build index 5cabf68031d..d38f6ef20f9 100644 --- a/meson.build +++ b/meson.build @@ -625,7 +625,7 @@ foreach ident : [ #include '''], # no known header declares pivot_root ['ioprio_get', '''#include '''], # no known header declares ioprio_get ['ioprio_set', '''#include '''], # no known header declares ioprio_set - ['sched_setattr', '''#include '''], # no known header declares sched_setattr + ['sched_setattr', '''#include '''], ['name_to_handle_at', '''#include #include #include '''], @@ -682,6 +682,8 @@ foreach ident : [ ['strerrorname_np', '''#include '''], ['getrandom', '''#include '''], ['quotactl_fd', '''#include '''], + ['setxattrat', '''#include '''], # no known header declares setxattrat + ['removexattrat', '''#include '''], # 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', diff --git a/src/basic/compress.h b/src/basic/compress.h index fd265bb0261..c2f336e9d7d 100644 --- a/src/basic/compress.h +++ b/src/basic/compress.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); diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index be22d6a04f2..539e8ca9250 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -5,6 +5,7 @@ #if WANT_LINUX_FS_H #include #endif +#include #include #include #include diff --git a/src/basic/missing_ioprio.h b/src/basic/missing_ioprio.h index 13ce7928a74..1f7323d8130 100644 --- a/src/basic/missing_ioprio.h +++ b/src/basic/missing_ioprio.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 diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 37048e1bc08..f4eb4dd57b2 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -5,11 +5,7 @@ #include #include -#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 -#endif #include #include #include @@ -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 diff --git a/src/basic/missing_syscall_def.h b/src/basic/missing_syscall_def.h index cd22058cdd4..48116fc0f7c 100644 --- a/src/basic/missing_syscall_def.h +++ b/src/basic/missing_syscall_def.h @@ -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 diff --git a/src/basic/missing_syscalls.py b/src/basic/missing_syscalls.py index a15fed03585..d0e9d7dec4b 100644 --- a/src/basic/missing_syscalls.py +++ b/src/basic/missing_syscalls.py @@ -21,8 +21,10 @@ SYSCALLS = [ 'pidfd_send_signal', 'pkey_mprotect', 'quotactl_fd', + 'removexattrat', 'renameat2', 'setns', + 'setxattrat', 'statx', ] diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 896d85bf5cc..411103b83e9 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -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); } diff --git a/src/basic/xattr-util.h b/src/basic/xattr-util.h index 17c3684280a..03e6a46cdbc 100644 --- a/src/basic/xattr-util.h +++ b/src/basic/xattr-util.h @@ -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); +} diff --git a/src/libsystemd/sd-journal/journal-vacuum.c b/src/libsystemd/sd-journal/journal-vacuum.c index 829edb31a70..f7dee86209e 100644 --- a/src/libsystemd/sd-journal/journal-vacuum.c +++ b/src/libsystemd/sd-journal/journal-vacuum.c @@ -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; } diff --git a/src/shared/chown-recursive.c b/src/shared/chown-recursive.c index 6aa5f6723ec..06c5adb1e50 100644 --- a/src/shared/chown-recursive.c +++ b/src/shared/chown-recursive.c @@ -3,7 +3,6 @@ #include #include #include -#include #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) diff --git a/src/shared/copy.c b/src/shared/copy.c index cb3944b279c..17c027a9e89 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -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; diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c index d0a79b26359..8537f3eda99 100644 --- a/src/shared/smack-util.c +++ b/src/shared/smack-util.c @@ -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) { diff --git a/src/shared/smack-util.h b/src/shared/smack-util.h index f6ed2ece38a..d42d2b553a3 100644 --- a/src/shared/smack-util.h +++ b/src/shared/smack-util.h @@ -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) { diff --git a/src/test/test-xattr-util.c b/src/test/test-xattr-util.c index c754ac526e8..25f982e1fd9 100644 --- a/src/test/test-xattr-util.c +++ b/src/test/test-xattr-util.c @@ -7,6 +7,7 @@ #include #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); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 6ce4a78adc1..e401eaa88fe 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -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; }