[PATCH] linux-user: implement mount_setattr(2)
Matt Turner <[email protected]> Wed, 5 Aug 2026 13:16:05 -0400
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
mount_setattr() was in the syscall tables but had no implementation, so guests always got -ENOSYS. systemd uses it when setting up per-unit credential mounts, which fails the affected units with EXIT_CREDENTIALS. struct mount_attr is an extensible struct like open_how, so handle it the same way openat2() does: reject sizes smaller than the ver0 struct and require any unknown trailing bytes to be zero. All of its fields are 64-bit, and the MOUNT_ATTR_* and MS_* propagation values are identical on every target, so only the byte order needs fixing up. Signed-off-by: Matt Turner <[email protected]> --- linux-user/strace.list | 3 +++ linux-user/syscall.c | 44 +++++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 13 ++++++++++++ 3 files changed, 60 insertions(+) diff --git ./linux-user/strace.list ./linux-user/strace.list index 25ace05187..e952f15d20 100644 --- ./linux-user/strace.list +++ ./linux-user/strace.list @@ -1737,3 +1737,6 @@ #ifdef TARGET_NR_fspick { TARGET_NR_fspick, "fspick", "%s(%d,%s,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_mount_setattr +{ TARGET_NR_mount_setattr, "mount_setattr", "%s(%d,%s,%d,%p,%d)", NULL, NULL }, +#endif diff --git ./linux-user/syscall.c ./linux-user/syscall.c index dc028686f4..cfa68dfbdb 100644 --- ./linux-user/syscall.c +++ ./linux-user/syscall.c @@ -9735,6 +9735,13 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname, int, __to_dfd, const char *, __to_pathname, unsigned int, flag) #endif +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) +#define __NR_sys_mount_setattr __NR_mount_setattr +_syscall5(int, sys_mount_setattr, int, dfd, const char *, path, + unsigned int, flags, struct mount_attr_ver0 *, uattr, + size_t, usize) +#endif + #if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) #define __NR_sys_fsopen __NR_fsopen _syscall2(int, sys_fsopen, const char *, fs_name, unsigned int, flags); @@ -14480,6 +14487,43 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_map_shadow_stack(cpu_env, arg1, arg2, arg3); #endif +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) + case TARGET_NR_mount_setattr: + { + struct mount_attr_ver0 attr = {}; + abi_ulong usize = arg5; + + if (usize < sizeof(struct target_mount_attr_ver0)) { + return -TARGET_EINVAL; + } + ret = copy_struct_from_user(&attr, sizeof(attr), arg4, usize); + if (ret) { + if (ret == -TARGET_E2BIG) { + qemu_log_mask(LOG_UNIMP, + "Unimplemented mount_setattr mount_attr " + "size: " TARGET_ABI_FMT_lu "\n", usize); + } + return ret; + } + /* + * MOUNT_ATTR_* and the MS_* propagation flags have the same + * values on all targets, so only byte order needs fixing up. + */ + attr.attr_set = tswap64(attr.attr_set); + attr.attr_clr = tswap64(attr.attr_clr); + attr.propagation = tswap64(attr.propagation); + attr.userns_fd = tswap64(attr.userns_fd); + + p = lock_user_string(arg2); + if (!p) { + return -TARGET_EFAULT; + } + ret = get_errno(sys_mount_setattr(arg1, p, arg3, &attr, + sizeof(attr))); + unlock_user(p, arg2, 0); + } + return ret; +#endif #if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) case TARGET_NR_fsopen: { diff --git ./linux-user/syscall_defs.h ./linux-user/syscall_defs.h index e033c7db34..e28853c93b 100644 --- ./linux-user/syscall_defs.h +++ ./linux-user/syscall_defs.h @@ -2770,6 +2770,19 @@ struct target_open_how_ver0 { abi_ullong mode; abi_ullong resolve; }; +/* from kernel's include/uapi/linux/mount.h */ +struct mount_attr_ver0 { + uint64_t attr_set; + uint64_t attr_clr; + uint64_t propagation; + uint64_t userns_fd; +}; +struct target_mount_attr_ver0 { + abi_ullong attr_set; + abi_ullong attr_clr; + abi_ullong propagation; + abi_ullong userns_fd; +}; #ifndef RESOLVE_NO_MAGICLINKS #define RESOLVE_NO_MAGICLINKS 0x02 #endif -- 2.54.0