summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-01 23:23:33 +0300
committerPaul Buetow <paul@buetow.org>2026-06-01 23:23:33 +0300
commit3af826ec72c7a520f0993c0fa8842d347eba7fed (patch)
tree7562d01329c29e98cc45abaa0e32748e55824ea0 /cmd
parentbd544509846fb7e1882aa466d24aea937c2b2804 (diff)
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 <noreply@anthropic.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/ioworkload/scenario_mountfs.go44
1 files changed, 42 insertions, 2 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)