diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-01 23:23:33 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-01 23:23:33 +0300 |
| commit | 3af826ec72c7a520f0993c0fa8842d347eba7fed (patch) | |
| tree | 7562d01329c29e98cc45abaa0e32748e55824ea0 /cmd | |
| parent | bd544509846fb7e1882aa466d24aea937c2b2804 (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.go | 44 |
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) |
