summaryrefslogtreecommitdiff
path: root/integrationtests/cmd/ioworkload/scenario_fcntl.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 22:03:57 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 22:03:57 +0200
commit3ec3c117bb280a377fea1a3eef84a70e2a3d4150 (patch)
treeb017e330eeaa1cafe95d2a730675b46342afd92a /integrationtests/cmd/ioworkload/scenario_fcntl.go
parent311b827599251d8d68526293815e8d4dcba632c8 (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.go129
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
+}