From 3af826ec72c7a520f0993c0fa8842d347eba7fed Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 1 Jun 2026 23:23:33 +0300 Subject: test(integration): cover fsconfig/fspick/open_tree in mount-API scenario Extend the mountfs-management scenario with best-effort fsconfig (KindFd), fspick (KindPathname), and open_tree (KindOpen) calls to complete new-mount-API end-to-end coverage. fsconfig reuses the fscontext fd from fsopen (FSCONFIG_SET_STRING + FSCONFIG_CMD_CREATE), fspick targets "/" with FSPICK_NO_AUTOMOUNT, and open_tree clones the scenario mount point with OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC. All returned fds are closed and all errno values are ignored, so ENOSYS/EPERM/EINVAL/EBADF are tolerated; the sys_enter_ tracepoints fire on kernel entry regardless, creating no mounts on the host. Assert enter_fsconfig/enter_fspick/enter_open_tree (MinCount>=1) in TestMountFsManagementSyscalls and add the three syscalls to the trace filter. Gating is unchanged (root-only via the shared harness). Co-Authored-By: Claude Opus 4.8 --- cmd/ioworkload/scenario_mountfs.go | 44 ++++++++++++++++++++++++++++++++++++-- integrationtests/mountfs_test.go | 9 +++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/cmd/ioworkload/scenario_mountfs.go b/cmd/ioworkload/scenario_mountfs.go index 7aa8163..8736da6 100644 --- a/cmd/ioworkload/scenario_mountfs.go +++ b/cmd/ioworkload/scenario_mountfs.go @@ -41,16 +41,56 @@ func mountfsManagement() error { putOld := mustCStringPtr(dir) tmpfs := mustCStringPtr("tmpfs") none := mustCStringPtr("none") + rootPath := mustCStringPtr("/") atFDCWDInt := int64(unix.AT_FDCWD) atFDCWD := uintptr(atFDCWDInt) + keyName := mustCStringPtr("source") + keyValue := mustCStringPtr("none") + // Best-effort coverage: these calls are expected to fail on most hosts - // without CAP_SYS_ADMIN, but still exercise syscall tracing paths. + // without CAP_SYS_ADMIN, but still exercise syscall tracing paths. Every + // sys_enter_ tracepoint fires on kernel entry, before any permission or + // validity check, so the integration assertions only require the enter_ + // tracepoint to fire once (MinCount>=1) regardless of the syscall's return. + // // fsopen(fsname, flags) is the entry point of the new mount API: it takes a // filesystem TYPE name (e.g. "tmpfs"), NOT a path, in args[0] and the // FSOPEN_CLOEXEC flag in args[1], returning a new filesystem-context fd. We - // close the returned fd when the call succeeds so we do not leak it. + // keep the returned fd to feed fsconfig below, and close it afterwards so we + // do not leak it. + fsContextFd := -1 if fd, _, errno := syscall.RawSyscall(unix.SYS_FSOPEN, uintptr(unsafe.Pointer(tmpfs)), uintptr(unix.FSOPEN_CLOEXEC), 0); errno == 0 { + fsContextFd = int(fd) + } + + // fsconfig(fd, cmd, key, value, aux) configures a filesystem context obtained + // from fsopen. It is a KindFd syscall: args[0] is the fscontext fd. We issue + // two best-effort commands on whatever fd we have (the real fscontext fd when + // fsopen succeeded, otherwise an invalid -1 which still fires the enter_ + // tracepoint and returns EBADF): FSCONFIG_SET_STRING to set a parameter and + // FSCONFIG_CMD_CREATE to materialise the superblock. Errors (ENOSYS on old + // kernels, EPERM/EINVAL/EBADF otherwise) are tolerated; no mount is created. + _, _, _ = syscall.RawSyscall6(unix.SYS_FSCONFIG, uintptr(fsContextFd), uintptr(unix.FSCONFIG_SET_STRING), uintptr(unsafe.Pointer(keyName)), uintptr(unsafe.Pointer(keyValue)), 0, 0) + _, _, _ = syscall.RawSyscall6(unix.SYS_FSCONFIG, uintptr(fsContextFd), uintptr(unix.FSCONFIG_CMD_CREATE), 0, 0, 0, 0) + if fsContextFd >= 0 { + _ = syscall.Close(fsContextFd) + } + + // fspick(dfd, path, flags) creates a filesystem context for an EXISTING mount + // so it can be reconfigured. It is a KindPathname syscall: args[1] is the path. + // We point it at "/" (always present) with FSPICK_NO_AUTOMOUNT and close any + // returned fscontext fd. This reconfigures nothing and creates no mount. + if fd, _, errno := syscall.RawSyscall(unix.SYS_FSPICK, atFDCWD, uintptr(unsafe.Pointer(rootPath)), uintptr(unix.FSPICK_NO_AUTOMOUNT)); errno == 0 { + _ = syscall.Close(int(fd)) + } + + // open_tree(dfd, path, flags) clones or references a mount subtree, returning + // a detached O_PATH-like fd. It is a KindOpen syscall: args[0] is the dirfd. + // We clone the scenario mount point (OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC) and + // close any returned fd. A detached clone is not attached anywhere in the + // mount tree, so closing the fd releases it without touching host mounts. + if fd, _, errno := syscall.RawSyscall(unix.SYS_OPEN_TREE, atFDCWD, uintptr(unsafe.Pointer(mountPath)), uintptr(unix.OPEN_TREE_CLONE|unix.OPEN_TREE_CLOEXEC)); errno == 0 { _ = syscall.Close(int(fd)) } _, _, _ = syscall.RawSyscall6(unix.SYS_MOUNT, uintptr(unsafe.Pointer(none)), uintptr(unsafe.Pointer(mountPath)), uintptr(unsafe.Pointer(tmpfs)), 0, 0, 0) diff --git a/integrationtests/mountfs_test.go b/integrationtests/mountfs_test.go index 673e9c1..d50706f 100644 --- a/integrationtests/mountfs_test.go +++ b/integrationtests/mountfs_test.go @@ -4,7 +4,7 @@ import "testing" var mountfsTraceArgs = []string{ "-trace-syscalls", - "mount,umount,move_mount,fsopen,fsmount,pivot_root,quotactl,statmount,listmount,listns,swapon,swapoff", + "mount,umount,move_mount,fsopen,fsconfig,fspick,open_tree,fsmount,pivot_root,quotactl,statmount,listmount,listns,swapon,swapoff", } func TestMountFsManagementSyscalls(t *testing.T) { @@ -13,6 +13,13 @@ func TestMountFsManagementSyscalls(t *testing.T) { {Tracepoint: "enter_umount", MinCount: 1}, {Tracepoint: "enter_move_mount", MinCount: 1}, {Tracepoint: "enter_fsopen", MinCount: 1}, + // fsconfig (KindFd), fspick (KindPathname), and open_tree (KindOpen) are + // best-effort new-mount-API calls in the scenario. Their sys_enter_ + // tracepoints fire on kernel entry regardless of permission/validity, so + // MinCount>=1 holds even when the syscalls themselves return an error. + {Tracepoint: "enter_fsconfig", MinCount: 1}, + {Tracepoint: "enter_fspick", MinCount: 1}, + {Tracepoint: "enter_open_tree", MinCount: 1}, {Tracepoint: "enter_fsmount", MinCount: 1}, {Tracepoint: "enter_pivot_root", MinCount: 1}, {Tracepoint: "enter_quotactl", MinCount: 1}, -- cgit v1.2.3