summaryrefslogtreecommitdiff
path: root/integrationtests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 21:31:16 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 21:31:16 +0200
commitc36cb85503ce96a589a2dd38c1279b31c87164f2 (patch)
tree8bf8390aff6c48d996b13fbdb3ca5d55cbbe7547 /integrationtests
parentb443f001cce8662a7fdccd4e0e7f707f7b2b47b8 (diff)
Add negative integration tests for dir syscalls (task 348)
Add three negative test scenarios for directory operations: - dir-mkdir-eexist: SYS_MKDIR on existing directory (EEXIST) - dir-chdir-enoent: SYS_CHDIR to nonexistent directory (ENOENT) - dir-getdents-ebadf: SYS_GETDENTS64 with invalid fd (EBADF) All use raw syscalls to hit exact tracepoints. Tests verify ior captures events even when syscalls fail (entry-side capture). Amp-Thread-ID: https://ampcode.com/threads/T-019c81ab-0d62-726e-b859-91b4898be6fe Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'integrationtests')
-rw-r--r--integrationtests/cmd/ioworkload/scenarios.go82
-rw-r--r--integrationtests/dir_test.go32
2 files changed, 110 insertions, 4 deletions
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go
index b4a153b..5eb8acb 100644
--- a/integrationtests/cmd/ioworkload/scenarios.go
+++ b/integrationtests/cmd/ioworkload/scenarios.go
@@ -62,10 +62,13 @@ var scenarios = map[string]func() error{
"unlink-enoent": unlinkEnoent,
"unlink-rmdir-notempty": unlinkRmdirNotempty,
"unlink-unlinkat-enoent": unlinkUnlinkatEnoent,
- "dir-basic": dirBasic,
- "dir-mkdirat": dirMkdirat,
- "dir-chdir": dirChdir,
- "dir-getdents": dirGetdents,
+ "dir-basic": dirBasic,
+ "dir-mkdirat": dirMkdirat,
+ "dir-chdir": dirChdir,
+ "dir-getdents": dirGetdents,
+ "dir-mkdir-eexist": dirMkdirEexist,
+ "dir-chdir-enoent": dirChdirEnoent,
+ "dir-getdents-ebadf": dirGetdentsEbadf,
"stat-basic": statBasic,
"stat-fstat": statFstat,
"stat-lstat": statLstat,
@@ -1732,6 +1735,77 @@ func dirGetdents() error {
return nil
}
+// dirMkdirEexist attempts to create a directory that already exists via raw
+// SYS_MKDIR. The syscall fails with EEXIST, but ior captures the tracepoint
+// on entry.
+func dirMkdirEexist() error {
+ dir, cleanup, err := makeTempDir("dir-mkdir-eexist")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ subDir := filepath.Join(dir, "mkdir-eexist-subdir")
+ pathBytes, err := syscall.BytePtrFromString(subDir)
+ if err != nil {
+ return fmt.Errorf("path bytes: %w", err)
+ }
+
+ // Create the directory first so the second attempt fails.
+ _, _, errno := syscall.Syscall(syscall.SYS_MKDIR, uintptr(unsafe.Pointer(pathBytes)), 0o755, 0)
+ runtime.KeepAlive(pathBytes)
+ if errno != 0 {
+ return fmt.Errorf("first mkdir: %w", errno)
+ }
+
+ // Second mkdir on the same path should fail with EEXIST.
+ pathBytes2, err := syscall.BytePtrFromString(subDir)
+ if err != nil {
+ return fmt.Errorf("path bytes: %w", err)
+ }
+ _, _, errno = syscall.Syscall(syscall.SYS_MKDIR, uintptr(unsafe.Pointer(pathBytes2)), 0o755, 0)
+ runtime.KeepAlive(pathBytes2)
+ if errno == 0 {
+ return fmt.Errorf("expected EEXIST, but mkdir succeeded")
+ }
+ return nil
+}
+
+// dirChdirEnoent attempts to change to a nonexistent directory via raw
+// SYS_CHDIR. The syscall fails with ENOENT, but ior captures the tracepoint
+// on entry.
+func dirChdirEnoent() error {
+ dir, cleanup, err := makeTempDir("dir-chdir-enoent")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ badPath := filepath.Join(dir, "chdir-enoent-missing")
+ pathBytes, err := syscall.BytePtrFromString(badPath)
+ if err != nil {
+ return fmt.Errorf("path bytes: %w", err)
+ }
+ _, _, errno := syscall.Syscall(syscall.SYS_CHDIR, uintptr(unsafe.Pointer(pathBytes)), 0, 0)
+ runtime.KeepAlive(pathBytes)
+ if errno == 0 {
+ return fmt.Errorf("expected ENOENT, but chdir succeeded")
+ }
+ return nil
+}
+
+// dirGetdentsEbadf calls getdents64(2) with an invalid file descriptor.
+// The syscall fails with EBADF, but ior captures the tracepoint on entry.
+func dirGetdentsEbadf() error {
+ buf := make([]byte, 4096)
+ _, _, errno := syscall.Syscall(syscall.SYS_GETDENTS64, uintptr(9999), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
+ runtime.KeepAlive(buf)
+ if errno == 0 {
+ return fmt.Errorf("expected EBADF, but getdents64 succeeded")
+ }
+ return nil
+}
+
// statBasic creates a file and stats it via raw SYS_STAT (newstat).
// We use the raw syscall because Go's syscall.Stat wraps newfstatat on amd64.
func statBasic() error {
diff --git a/integrationtests/dir_test.go b/integrationtests/dir_test.go
index 67bbe93..585f03f 100644
--- a/integrationtests/dir_test.go
+++ b/integrationtests/dir_test.go
@@ -45,3 +45,35 @@ func TestDirGetdents(t *testing.T) {
},
})
}
+
+func TestDirMkdirEexist(t *testing.T) {
+ runScenario(t, "dir-mkdir-eexist", []ExpectedEvent{
+ {
+ PathContains: "mkdir-eexist-subdir",
+ Tracepoint: "enter_mkdir",
+ Comm: "ioworkload",
+ MinCount: 2,
+ },
+ })
+}
+
+func TestDirChdirEnoent(t *testing.T) {
+ runScenario(t, "dir-chdir-enoent", []ExpectedEvent{
+ {
+ PathContains: "chdir-enoent-missing",
+ Tracepoint: "enter_chdir",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}
+
+func TestDirGetdentsEbadf(t *testing.T) {
+ runScenario(t, "dir-getdents-ebadf", []ExpectedEvent{
+ {
+ Tracepoint: "enter_getdents64",
+ Comm: "ioworkload",
+ MinCount: 1,
+ },
+ })
+}