diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-21 21:31:16 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-21 21:31:16 +0200 |
| commit | c36cb85503ce96a589a2dd38c1279b31c87164f2 (patch) | |
| tree | 8bf8390aff6c48d996b13fbdb3ca5d55cbbe7547 /integrationtests | |
| parent | b443f001cce8662a7fdccd4e0e7f707f7b2b47b8 (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.go | 82 | ||||
| -rw-r--r-- | integrationtests/dir_test.go | 32 |
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, + }, + }) +} |
