diff --git a/meson.build b/meson.build index 55874ee6c6..127daa419b 100644 --- a/meson.build +++ b/meson.build @@ -607,6 +607,7 @@ foreach ident : [ ['pivot_root', '''#include '''], # no known header declares pivot_root ['setxattrat', '''#include '''], # no known header declares setxattrat ['removexattrat', '''#include '''], # no known header declares removexattrat + ['open_tree_attr', '''#include '''], # no known header declares open_tree_attr ] have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE') diff --git a/src/basic/forward.h b/src/basic/forward.h index 6b6d68dcb4..7b8ce78b98 100644 --- a/src/basic/forward.h +++ b/src/basic/forward.h @@ -36,6 +36,7 @@ struct in_addr; struct in6_addr; struct inotify_event; struct iovec; +struct mount_attr; struct msghdr; struct passwd; struct pollfd; diff --git a/src/basic/include/sys/mount.h b/src/basic/include/sys/mount.h index 3f42a61233..3c9cd8ef5b 100644 --- a/src/basic/include/sys/mount.h +++ b/src/basic/include/sys/mount.h @@ -120,3 +120,13 @@ static inline int missing_mount_setattr(int dfd, const char *path, unsigned flag } # define mount_setattr missing_mount_setattr #endif + +#if HAVE_OPEN_TREE_ATTR +extern int open_tree_attr(int __dfd, const char *__filename, unsigned int __flags, struct mount_attr *__uattr, size_t __usize) __THROW; +#else +static inline int missing_open_tree_attr(int dfd, const char *filename, unsigned int flags, struct mount_attr *attr, size_t size) { + return syscall(__NR_open_tree_attr, dfd, filename, flags, attr, size); +} + +# define open_tree_attr missing_open_tree_attr +#endif diff --git a/src/basic/missing_syscall_def.h b/src/basic/missing_syscall_def.h index fd8f3e5db5..a34cb36f14 100644 --- a/src/basic/missing_syscall_def.h +++ b/src/basic/missing_syscall_def.h @@ -6,8 +6,6 @@ */ #pragma once -#include "forward.h" - /* Note: if this code looks strange, this is because it is derived from the same * template as the per-syscall blocks below. */ # if defined(__aarch64__) @@ -100,7 +98,7 @@ /* may be an (invalid) negative number due to libseccomp, see PR 13319 */ # if defined __NR_close_range && __NR_close_range >= 0 # if defined systemd_NR_close_range -assert_cc(__NR_close_range == systemd_NR_close_range); +_Static_assert(__NR_close_range == systemd_NR_close_range, ""); # endif # else # if defined __NR_close_range @@ -168,7 +166,7 @@ assert_cc(__NR_close_range == systemd_NR_close_range); /* may be an (invalid) negative number due to libseccomp, see PR 13319 */ # if defined __NR_fchmodat2 && __NR_fchmodat2 >= 0 # if defined systemd_NR_fchmodat2 -assert_cc(__NR_fchmodat2 == systemd_NR_fchmodat2); +_Static_assert(__NR_fchmodat2 == systemd_NR_fchmodat2, ""); # endif # else # if defined __NR_fchmodat2 @@ -236,7 +234,7 @@ assert_cc(__NR_fchmodat2 == systemd_NR_fchmodat2); /* may be an (invalid) negative number due to libseccomp, see PR 13319 */ # if defined __NR_mount_setattr && __NR_mount_setattr >= 0 # if defined systemd_NR_mount_setattr -assert_cc(__NR_mount_setattr == systemd_NR_mount_setattr); +_Static_assert(__NR_mount_setattr == systemd_NR_mount_setattr, ""); # endif # else # if defined __NR_mount_setattr @@ -304,7 +302,7 @@ assert_cc(__NR_mount_setattr == systemd_NR_mount_setattr); /* may be an (invalid) negative number due to libseccomp, see PR 13319 */ # if defined __NR_openat2 && __NR_openat2 >= 0 # if defined systemd_NR_openat2 -assert_cc(__NR_openat2 == systemd_NR_openat2); +_Static_assert(__NR_openat2 == systemd_NR_openat2, ""); # endif # else # if defined __NR_openat2 @@ -372,7 +370,7 @@ assert_cc(__NR_openat2 == systemd_NR_openat2); /* 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); +_Static_assert(__NR_quotactl_fd == systemd_NR_quotactl_fd, ""); # endif # else # if defined __NR_quotactl_fd @@ -440,7 +438,7 @@ assert_cc(__NR_quotactl_fd == systemd_NR_quotactl_fd); /* 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); +_Static_assert(__NR_removexattrat == systemd_NR_removexattrat, ""); # endif # else # if defined __NR_removexattrat @@ -508,7 +506,7 @@ assert_cc(__NR_removexattrat == systemd_NR_removexattrat); /* 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); +_Static_assert(__NR_setxattrat == systemd_NR_setxattrat, ""); # endif # else # if defined __NR_setxattrat @@ -519,3 +517,71 @@ assert_cc(__NR_setxattrat == systemd_NR_setxattrat); # endif # endif #endif + +#ifndef __IGNORE_open_tree_attr +# if defined(__aarch64__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__alpha__) +# define systemd_NR_open_tree_attr 577 +# elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__arm__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__i386__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__ia64__) +# define systemd_NR_open_tree_attr -1 +# elif defined(__loongarch_lp64) +# define systemd_NR_open_tree_attr 467 +# elif defined(__m68k__) +# define systemd_NR_open_tree_attr 467 +# elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_open_tree_attr 4467 +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_open_tree_attr 6467 +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_open_tree_attr 5467 +# else +# error "Unknown MIPS ABI" +# endif +# elif defined(__hppa__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__powerpc__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__riscv) +# if __riscv_xlen == 32 +# define systemd_NR_open_tree_attr 467 +# elif __riscv_xlen == 64 +# define systemd_NR_open_tree_attr 467 +# else +# error "Unknown RISC-V ABI" +# endif +# elif defined(__s390__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__sparc__) +# define systemd_NR_open_tree_attr 467 +# elif defined(__x86_64__) +# if defined(__ILP32__) +# define systemd_NR_open_tree_attr (467 | /* __X32_SYSCALL_BIT */ 0x40000000) +# else +# define systemd_NR_open_tree_attr 467 +# endif +# elif !defined(missing_arch_template) +# warning "open_tree_attr() syscall number is unknown for your architecture" +# endif + +/* may be an (invalid) negative number due to libseccomp, see PR 13319 */ +# if defined __NR_open_tree_attr && __NR_open_tree_attr >= 0 +# if defined systemd_NR_open_tree_attr +_Static_assert(__NR_open_tree_attr == systemd_NR_open_tree_attr, ""); +# endif +# else +# if defined __NR_open_tree_attr +# undef __NR_open_tree_attr +# endif +# if defined systemd_NR_open_tree_attr && systemd_NR_open_tree_attr >= 0 +# define __NR_open_tree_attr systemd_NR_open_tree_attr +# endif +# endif +#endif diff --git a/src/basic/missing_syscalls.py b/src/basic/missing_syscalls.py index 0d351e322d..dd26b76b4c 100644 --- a/src/basic/missing_syscalls.py +++ b/src/basic/missing_syscalls.py @@ -9,6 +9,7 @@ SYSCALLS = [ 'close_range', # defined in glibc header since glibc-2.33 'fchmodat2', # defined in glibc header since glibc-2.39 'mount_setattr', # defined in glibc header since glibc-2.34 + 'open_tree_attr', 'openat2', # defined in glibc header since glibc-2.32 'quotactl_fd', # defined in glibc header since glibc-2.35 'removexattrat', @@ -96,7 +97,7 @@ DEF_TEMPLATE_C = '''\ /* may be an (invalid) negative number due to libseccomp, see PR 13319 */ # if defined __NR_{syscall} && __NR_{syscall} >= 0 # if defined systemd_NR_{syscall} -assert_cc(__NR_{syscall} == systemd_NR_{syscall}); +_Static_assert(__NR_{syscall} == systemd_NR_{syscall}, ""); # endif # else # if defined __NR_{syscall} diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index d4ac649c9f..2c53856272 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "chase.h" +#include "errno-util.h" #include "escape.h" #include "extract-word.h" #include "fd-util.h" @@ -814,7 +815,28 @@ static int mount_bind(const char *dest, CustomMount *m, uid_t uid_shift, uid_t u if (m->rm_rf_tmpdir && chown(m->source, uid_shift, uid_shift) < 0) return log_error_errno(errno, "Failed to chown %s: %m", m->source); - if (stat(m->source, &source_st) < 0) + /* UID/GIDs of idmapped mounts are always resolved in the caller's user namespace. In other + * words, they're not nested. If we're doing an idmapped mount from a bind mount that's + * already idmapped itself, the old idmap is replaced with the new one. This means that the + * source uid which we put in the idmap userns has to be the uid of mount source in the + * caller's userns *without* any mount idmapping in place. To get that uid, we clone the + * mount source tree and clear any existing idmapping and temporarily mount that tree over + * the mount source before we stat the mount source to figure out the source uid. */ + _cleanup_close_ int fd_clone = open_tree_attr_fallback( + AT_FDCWD, + m->source, + OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC, + &(struct mount_attr) { + .attr_clr = MOUNT_ATTR_IDMAP, + }); + if (ERRNO_IS_NEG_NOT_SUPPORTED(fd_clone)) + /* We can only clear idmapped mounts with open_tree_attr(), but there might not be one in + * the first place, so we keep going if we get a not supported error. */ + fd_clone = open_tree(AT_FDCWD, m->source, OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC); + if (fd_clone < 0) + return log_error_errno(errno, "Failed to clone %s: %m", m->source); + + if (fstat(fd_clone, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); r = chase(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where, NULL); @@ -859,9 +881,10 @@ static int mount_bind(const char *dest, CustomMount *m, uid_t uid_shift, uid_t u dest_uid = uid_shift; } - r = mount_nofollow_verbose(LOG_ERR, m->source, where, NULL, mount_flags, mount_opts); - if (r < 0) - return r; + if (move_mount(fd_clone, "", AT_FDCWD, where, MOVE_MOUNT_F_EMPTY_PATH) < 0) + return log_error_errno(errno, "Failed to mount %s to %s: %m", m->source, where); + + fd_clone = safe_close(fd_clone); if (m->read_only) { r = bind_remount_recursive(where, MS_RDONLY, MS_RDONLY, NULL); diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 71ee5cf734..a0db226c25 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -1441,6 +1441,28 @@ int make_userns(uid_t uid_shift, return TAKE_FD(userns_fd); } +int open_tree_attr_fallback(int dir_fd, const char *path, unsigned int flags, struct mount_attr *attr) { + assert(attr); + + _cleanup_close_ int fd = open_tree_attr(dir_fd, path, flags, attr, sizeof(struct mount_attr)); + if (fd >= 0) + return TAKE_FD(fd); + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return log_debug_errno(errno, "Failed to open tree and set mount attributes: %m"); + + if (attr->attr_clr & MOUNT_ATTR_IDMAP) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Cannot clear idmap from mount without open_tree_attr()"); + + fd = open_tree(dir_fd, path, flags); + if (fd < 0) + return log_debug_errno(errno, "Failed to open tree: %m"); + + if (mount_setattr(fd, "", AT_EMPTY_PATH | (flags & AT_RECURSIVE), attr, sizeof(struct mount_attr)) < 0) + return log_debug_errno(errno, "Failed to change mount attributes: %m"); + + return TAKE_FD(fd); +} + int remount_idmap_fd( char **paths, int userns_fd, @@ -1469,22 +1491,19 @@ int remount_idmap_fd( CLEANUP_ARRAY(mount_fds, n_mounts_fds, close_many_and_free); for (size_t i = 0; i < n; i++) { - int mntfd; - - /* Clone the mount point */ - mntfd = mount_fds[n_mounts_fds] = open_tree(-EBADF, paths[i], OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); - if (mount_fds[n_mounts_fds] < 0) - return log_debug_errno(errno, "Failed to open tree of mounted filesystem '%s': %m", paths[i]); - - n_mounts_fds++; - - /* Set the user namespace mapping attribute on the cloned mount point */ - if (mount_setattr(mntfd, "", AT_EMPTY_PATH, - &(struct mount_attr) { + /* Clone the mount point and et the user namespace mapping attribute on the cloned mount point. */ + mount_fds[n_mounts_fds] = open_tree_attr_fallback( + /* dir_fd= */ -EBADF, + paths[i], + OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC, + &(struct mount_attr) { .attr_set = MOUNT_ATTR_IDMAP | extra_mount_attr_set, .userns_fd = userns_fd, - }, sizeof(struct mount_attr)) < 0) - return log_debug_errno(errno, "Failed to change bind mount attributes for clone of '%s': %m", paths[i]); + }); + if (mount_fds[n_mounts_fds] < 0) + return mount_fds[n_mounts_fds]; + + n_mounts_fds++; } for (size_t i = n; i > 0; i--) { /* Unmount the paths right-to-left */ diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index 92ce4c5816..2f10bc896b 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -148,6 +148,8 @@ typedef enum RemountIdmapping { _REMOUNT_IDMAPPING_INVALID = -EINVAL, } RemountIdmapping; +int open_tree_attr_fallback(int dir_fd, const char *path, unsigned int flags, struct mount_attr *attr); + int make_userns(uid_t uid_shift, uid_t uid_range, uid_t host_owner, uid_t dest_owner, RemountIdmapping idmapping); int remount_idmap_fd(char **p, int userns_fd, uint64_t extra_mount_attr_set); int remount_idmap(char **p, uid_t uid_shift, uid_t uid_range, uid_t host_owner, uid_t dest_owner, RemountIdmapping idmapping);