diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-21 22:03:57 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-21 22:03:57 +0200 |
| commit | 3ec3c117bb280a377fea1a3eef84a70e2a3d4150 (patch) | |
| tree | b017e330eeaa1cafe95d2a730675b46342afd92a /integrationtests/cmd/ioworkload/scenario_fcntl.go | |
| parent | 311b827599251d8d68526293815e8d4dcba632c8 (diff) | |
Split ioworkload scenarios.go into per-category files
Split the 2494-line scenarios.go monolith into 14 focused files by
syscall category: open, readwrite, close, dup, fcntl, rename, link,
unlink, dir, stat, sync, truncate, iouring, plus the slimmed-down
scenarios.go containing only the registry map, makeTempDir, and crash.
Extracted rawLink, rawSymlink, rawReadlink helpers in scenario_link.go
to reduce code duplication in linkBasic.
Task: #349 (Go best practices: split oversized scenarios file)
Amp-Thread-ID: https://ampcode.com/threads/T-019c81c6-e1b6-747a-9144-40f6be997e60
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'integrationtests/cmd/ioworkload/scenario_fcntl.go')
| -rw-r--r-- | integrationtests/cmd/ioworkload/scenario_fcntl.go | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/integrationtests/cmd/ioworkload/scenario_fcntl.go b/integrationtests/cmd/ioworkload/scenario_fcntl.go new file mode 100644 index 0000000..0d8e642 --- /dev/null +++ b/integrationtests/cmd/ioworkload/scenario_fcntl.go @@ -0,0 +1,129 @@ +package main + +import ( + "fmt" + "path/filepath" + "syscall" +) + +// fcntlDupfd uses fcntl F_DUPFD to duplicate a file descriptor. +func fcntlDupfd() error { + dir, cleanup, err := makeTempDir("fcntl-dupfd") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "fcntlfile.txt") + fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) + if err != nil { + return fmt.Errorf("open: %w", err) + } + defer syscall.Close(fd) + + newFd, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD, 0) + if errno != 0 { + return fmt.Errorf("fcntl F_DUPFD: %w", errno) + } + defer syscall.Close(int(newFd)) + + if _, err := syscall.Write(int(newFd), []byte("via fcntl")); err != nil { + return fmt.Errorf("write via fcntl dup: %w", err) + } + return nil +} + +// fcntlSetfl uses fcntl F_GETFL/F_SETFL to read and modify file status flags. +func fcntlSetfl() error { + dir, cleanup, err := makeTempDir("fcntl-setfl") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "fcntlsetflfile.txt") + fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) + if err != nil { + return fmt.Errorf("open: %w", err) + } + defer syscall.Close(fd) + + flags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_GETFL, 0) + if errno != 0 { + return fmt.Errorf("fcntl F_GETFL: %w", errno) + } + + _, _, errno = syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_SETFL, flags|syscall.O_APPEND) + if errno != 0 { + return fmt.Errorf("fcntl F_SETFL: %w", errno) + } + + if _, err := syscall.Write(fd, []byte("appended via fcntl setfl")); err != nil { + return fmt.Errorf("write: %w", err) + } + return nil +} + +// fcntlDupfdCloexec uses fcntl F_DUPFD_CLOEXEC to duplicate a file descriptor +// with the close-on-exec flag set. +func fcntlDupfdCloexec() error { + dir, cleanup, err := makeTempDir("fcntl-dupfd-cloexec") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "fcntlcloexecfile.txt") + fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) + if err != nil { + return fmt.Errorf("open: %w", err) + } + defer syscall.Close(fd) + + newFd, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) + if errno != 0 { + return fmt.Errorf("fcntl F_DUPFD_CLOEXEC: %w", errno) + } + defer syscall.Close(int(newFd)) + + if _, err := syscall.Write(int(newFd), []byte("via fcntl dupfd cloexec")); err != nil { + return fmt.Errorf("write via fcntl dup cloexec: %w", err) + } + return nil +} + +// fcntlInvalidFd calls fcntl F_GETFL on an invalid fd (99999). +// The syscall fails with EBADF, but ior should capture the enter_fcntl +// tracepoint because it is recorded on syscall entry. +func fcntlInvalidFd() error { + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, 99999, syscall.F_GETFL, 0) + if errno == 0 { + return fmt.Errorf("expected fcntl on invalid fd to fail") + } + return nil +} + +// fcntlDupfdMax opens a file and calls fcntl F_DUPFD with a minfd value +// that exceeds the process RLIMIT_NOFILE. The kernel rejects this with +// EINVAL, but ior should capture the enter_fcntl tracepoint. +func fcntlDupfdMax() error { + dir, cleanup, err := makeTempDir("fcntl-dupfd-max") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "fcntldupfdmaxfile.txt") + fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) + if err != nil { + return fmt.Errorf("open: %w", err) + } + defer syscall.Close(fd) + + // Use a minfd far beyond any realistic RLIMIT_NOFILE. + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD, 1<<30) + if errno == 0 { + return fmt.Errorf("expected fcntl F_DUPFD with extreme minfd to fail") + } + return nil +} |
