linux-user: implement fsmount(2) series of syscalls
This series of syscalls replaces the old mount(2) syscall with a series of syscalls that operates around a filesystem context. This series of syscalls is available since Linux 5.2 and glibc 2.36+. Their users include systemd since v259 and libmount from util-linux, and possibly other widely used projects. Preliminary checks are implemented to ensure the validity of the interface. v2: Add syscall wrappers in case the build machine does not support the fsmount() syscalls. (added by Helge Deller) Signed-off-by: Xinhui Yang <cyan@cyano.uk> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Signed-off-by: Helge Deller <deller@gmx.de>
Xinhui Yang committed
May 29, 2026 at 14:53 UTC
767c32fe69834344bf71f4071ff33292cd46f626
1 file changed
+104
linux-user/syscall.c
+104
@@ -9713,6 +9713,19 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname,
9713
int, __to_dfd, const char *, __to_pathname, unsigned int, flag)
9714
#endif
9715
9716
+#if defined(TARGET_NR_fsopen) && defined(NR_fsopen)
9717
+#define __NR_sys_fsopen __NR_fsopen
9718
+_syscall2(int, sys_fsopen, const char *, fs_name, unsigned int, flags);
9719
+#define __NR_sys_fsconfig __NR_fsconfig
9720
+_syscall5(int, sys_fsconfig, int, fs_fd, unsigned int, cmd, const char *, key,
9721
+ const void *, value, int, aux)
9722
+#define __NR_sys_fsmount __NR_fsmount
9723
+_syscall3(int, sys_fsmount, int, fs_fd, unsigned int, flags,
9724
+ unsigned int, ms_flags)
9725
+#define __NR_sys_fspick __NR_fspick
9726
+_syscall3(int, sys_fspick, int, dfd, const char *, path, unsigned int, flags)
9727
+#endif
9728
+
9729
/* This is an internal helper for do_syscall so that it is easier
9730
* to have a single return point, so that actions, such as logging
9731
* of syscall results, can be performed.
@@ -14412,6 +14425,97 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
14425
return do_map_shadow_stack(cpu_env, arg1, arg2, arg3);
14426
#endif
14427
14428
+#if defined(TARGET_NR_fsopen) && defined(NR_fsopen)
14429
+ case TARGET_NR_fsopen:
14430
+ {
14431
+ p = lock_user_string(arg1);
14432
+ if (!p) {
14433
+ return -TARGET_EFAULT;
14434
+ }
14435
+ ret = get_errno(sys_fsopen(p, arg2));
14436
+ unlock_user(p, arg1, 0);
14437
+ }
14438
+ return ret;
14439
+ case TARGET_NR_fsconfig:
14440
+ {
14441
+ /*
14442
+ * fsconfig(int, int, char *, void *, int)
14443
+ * NOTE: p4 is nullable and its type might not be a string.
14444
+ */
14445
+ void *p3, *p4;
14446
+ int cmd = (int) arg2;
14447
+ switch (cmd) {
14448
+ case FSCONFIG_SET_BINARY:
14449
+ case FSCONFIG_SET_STRING:
14450
+ case FSCONFIG_SET_PATH:
14451
+ case FSCONFIG_SET_PATH_EMPTY:
14452
+ p3 = lock_user_string(arg3);
14453
+ if (!p3) {
14454
+ return -TARGET_EFAULT;
14455
+ }
14456
+ if (cmd != FSCONFIG_SET_BINARY) {
14457
+ /* key and value must be strings. */
14458
+ p4 = lock_user_string(arg4);
14459
+ } else {
14460
+ /*
14461
+ * Otherwise the value must be a raw buffer with its
14462
+ * length specified in arg5 (aux).
14463
+ */
14464
+ p4 = lock_user(VERIFY_READ, arg4, arg5, 1);
14465
+ }
14466
+ if (!p4) {
14467
+ unlock_user(p3, arg3, 0);
14468
+ return -TARGET_EFAULT;
14469
+ }
14470
+ ret = get_errno(sys_fsconfig(arg1, arg2, p3, p4, arg5));
14471
+ unlock_user(p3, arg3, 0);
14472
+ unlock_user(p4, arg4, 0);
14473
+ break;
14474
+
14475
+ case FSCONFIG_SET_FLAG:
14476
+ case FSCONFIG_SET_FD:
14477
+ /* arg4 (value) must be NULL. */
14478
+ if (arg4) {
14479
+ return -TARGET_EFAULT;
14480
+ }
14481
+ p3 = lock_user_string(arg3);
14482
+ if (!p3) {
14483
+ return -TARGET_EFAULT;
14484
+ }
14485
+ ret = get_errno(sys_fsconfig(arg1, arg2, p3, NULL, arg5));
14486
+ unlock_user(p3, arg3, 0);
14487
+ break;
14488
+ case FSCONFIG_CMD_CREATE:
14489
+ case FSCONFIG_CMD_RECONFIGURE:
14490
+#ifdef FSCONFIG_CMD_CREATE_EXCL
14491
+ /*
14492
+ * FSCONFIG_CMD_CREATE_EXCL is only available since Linux
14493
+ * 6.6. Guarding it to allow building with pre-6.6 headers.
14494
+ */
14495
+ case FSCONFIG_CMD_CREATE_EXCL:
14496
+#endif
14497
+ /* key and value must be NULL, aux must be 0. */
14498
+ if (arg3 || arg4 || arg5) {
14499
+ return -TARGET_EFAULT;
14500
+ }
14501
+ ret = get_errno(sys_fsconfig(arg1, arg2, NULL, NULL, 0));
14502
+ break;
14503
+ default:
14504
+ return -TARGET_EFAULT;
14505
+ }
14506
+ }
14507
+ return ret;
14508
+ case TARGET_NR_fsmount:
14509
+ ret = get_errno(sys_fsmount(arg1, arg2, arg3));
14510
+ return ret;
14511
+ case TARGET_NR_fspick:
14512
+ {
14513
+ p = lock_user_string(arg2);
14514
+ ret = get_errno(sys_fspick(arg1, p, arg3));
14515
+ unlock_user(p, arg2, 0);
14516
+ }
14517
+ return ret;
14518
+#endif
14519
default:
14520
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
14521
return -TARGET_ENOSYS;