@samitouri / QOSamiQemu / commits / 4713988da1

linux-user: implement mount_setattr(2)

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 <mattst88@gmail.com> Reviewed-by: Helge Deller <deller@gmx.de> Signed-off-by: Helge Deller <deller@gmx.de>

Matt Turner committed Aug 5, 2026 at 13:16 UTC 4713988da1c3ed7150014c87c2429d7a940b77c8
3 files changed +60
linux-user/strace.list
+3
@@ -1737,3 +1737,6 @@
1737 #ifdef TARGET_NR_fspick
1738 { TARGET_NR_fspick, "fspick", "%s(%d,%s,%d)", NULL, NULL },
1739 #endif
1740 +#ifdef TARGET_NR_mount_setattr
1741 +{ TARGET_NR_mount_setattr, "mount_setattr", "%s(%d,%s,%d,%p,%d)", NULL, NULL },
1742 +#endif
linux-user/syscall.c
+44
@@ -9735,6 +9735,13 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname,
9735 int, __to_dfd, const char *, __to_pathname, unsigned int, flag)
9736 #endif
9737
9738 +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr)
9739 +#define __NR_sys_mount_setattr __NR_mount_setattr
9740 +_syscall5(int, sys_mount_setattr, int, dfd, const char *, path,
9741 + unsigned int, flags, struct mount_attr_ver0 *, uattr,
9742 + size_t, usize)
9743 +#endif
9744 +
9745 #if defined(TARGET_NR_fsopen) && defined(__NR_fsopen)
9746 #define __NR_sys_fsopen __NR_fsopen
9747 _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,
14487 return do_map_shadow_stack(cpu_env, arg1, arg2, arg3);
14488 #endif
14489
14490 +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr)
14491 + case TARGET_NR_mount_setattr:
14492 + {
14493 + struct mount_attr_ver0 attr = {};
14494 + abi_ulong usize = arg5;
14495 +
14496 + if (usize < sizeof(struct target_mount_attr_ver0)) {
14497 + return -TARGET_EINVAL;
14498 + }
14499 + ret = copy_struct_from_user(&attr, sizeof(attr), arg4, usize);
14500 + if (ret) {
14501 + if (ret == -TARGET_E2BIG) {
14502 + qemu_log_mask(LOG_UNIMP,
14503 + "Unimplemented mount_setattr mount_attr "
14504 + "size: " TARGET_ABI_FMT_lu "\n", usize);
14505 + }
14506 + return ret;
14507 + }
14508 + /*
14509 + * MOUNT_ATTR_* and the MS_* propagation flags have the same
14510 + * values on all targets, so only byte order needs fixing up.
14511 + */
14512 + attr.attr_set = tswap64(attr.attr_set);
14513 + attr.attr_clr = tswap64(attr.attr_clr);
14514 + attr.propagation = tswap64(attr.propagation);
14515 + attr.userns_fd = tswap64(attr.userns_fd);
14516 +
14517 + p = lock_user_string(arg2);
14518 + if (!p) {
14519 + return -TARGET_EFAULT;
14520 + }
14521 + ret = get_errno(sys_mount_setattr(arg1, p, arg3, &attr,
14522 + sizeof(attr)));
14523 + unlock_user(p, arg2, 0);
14524 + }
14525 + return ret;
14526 +#endif
14527 #if defined(TARGET_NR_fsopen) && defined(__NR_fsopen)
14528 case TARGET_NR_fsopen:
14529 {
linux-user/syscall_defs.h
+13
@@ -2770,6 +2770,19 @@ struct target_open_how_ver0 {
2770 abi_ullong mode;
2771 abi_ullong resolve;
2772 };
2773 +/* from kernel's include/uapi/linux/mount.h */
2774 +struct mount_attr_ver0 {
2775 + uint64_t attr_set;
2776 + uint64_t attr_clr;
2777 + uint64_t propagation;
2778 + uint64_t userns_fd;
2779 +};
2780 +struct target_mount_attr_ver0 {
2781 + abi_ullong attr_set;
2782 + abi_ullong attr_clr;
2783 + abi_ullong propagation;
2784 + abi_ullong userns_fd;
2785 +};
2786 #ifndef RESOLVE_NO_MAGICLINKS
2787 #define RESOLVE_NO_MAGICLINKS 0x02
2788 #endif