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