[PATCH] linux-user: implement mount_setattr(2)

Matt Turner posted 1 patch 2 weeks, 5 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260805171605.3119981-1-mattst88@gmail.com
Maintainers: Laurent Vivier <laurent@vivier.eu>, Helge Deller <deller@gmx.de>, Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
linux-user/strace.list    |  3 +++
linux-user/syscall.c      | 44 +++++++++++++++++++++++++++++++++++++++
linux-user/syscall_defs.h | 13 ++++++++++++
3 files changed, 60 insertions(+)
[PATCH] linux-user: implement mount_setattr(2)
Posted by Matt Turner 2 weeks, 5 days ago
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>
---
 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
Re: [PATCH] linux-user: implement mount_setattr(2)
Posted by Helge Deller 2 weeks, 3 days ago
On 8/5/26 19:16, Matt Turner wrote:
> 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>
> ---
>   linux-user/strace.list    |  3 +++
>   linux-user/syscall.c      | 44 +++++++++++++++++++++++++++++++++++++++
>   linux-user/syscall_defs.h | 13 ++++++++++++
>   3 files changed, 60 insertions(+)
Reviewed-by: Helge Deller <deller@gmx.de>

I'll queue that patch (and the others) up for after qemu-v11.1 has been released.

Thanks!
Helge