diff options
| -rw-r--r-- | integrationtests/cmd/ioworkload/scenarios.go | 92 | ||||
| -rw-r--r-- | integrationtests/unlink_test.go | 33 |
2 files changed, 123 insertions, 2 deletions
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go index 4a96a8b..b4a153b 100644 --- a/integrationtests/cmd/ioworkload/scenarios.go +++ b/integrationtests/cmd/ioworkload/scenarios.go @@ -57,8 +57,11 @@ var scenarios = map[string]func() error{ "link-symlink-eexist": linkSymlinkEexist, "link-readlinkat-einval": linkReadlinkatEinval, "unlink-basic": unlinkBasic, - "unlink-unlinkat": unlinkUnlinkat, - "unlink-rmdir": unlinkRmdir, + "unlink-unlinkat": unlinkUnlinkat, + "unlink-rmdir": unlinkRmdir, + "unlink-enoent": unlinkEnoent, + "unlink-rmdir-notempty": unlinkRmdirNotempty, + "unlink-unlinkat-enoent": unlinkUnlinkatEnoent, "dir-basic": dirBasic, "dir-mkdirat": dirMkdirat, "dir-chdir": dirChdir, @@ -1539,6 +1542,91 @@ func unlinkRmdir() error { return nil } +// unlinkEnoent attempts to unlink a nonexistent file via raw SYS_UNLINK. +// The syscall fails with ENOENT, but ior captures the tracepoint on entry. +func unlinkEnoent() error { + dir, cleanup, err := makeTempDir("unlink-enoent") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "unlink-enoent-missing.txt") + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return fmt.Errorf("path bytes: %w", err) + } + _, _, errno := syscall.Syscall(syscall.SYS_UNLINK, uintptr(unsafe.Pointer(pathBytes)), 0, 0) + runtime.KeepAlive(pathBytes) + if errno == 0 { + return fmt.Errorf("expected ENOENT, but unlink succeeded") + } + return nil +} + +// unlinkRmdirNotempty attempts to rmdir a non-empty directory via raw SYS_RMDIR. +// The syscall fails with ENOTEMPTY, but ior captures the tracepoint on entry. +func unlinkRmdirNotempty() error { + dir, cleanup, err := makeTempDir("unlink-rmdir-notempty") + if err != nil { + return err + } + defer cleanup() + + subDir := filepath.Join(dir, "rmdir-notempty") + if err := syscall.Mkdir(subDir, 0o755); err != nil { + return fmt.Errorf("mkdir: %w", err) + } + + // Create a file inside so the directory is non-empty. + filePath := filepath.Join(subDir, "blocker.txt") + fd, err := syscall.Open(filePath, syscall.O_RDWR|syscall.O_CREAT, 0o644) + if err != nil { + return fmt.Errorf("create blocker: %w", err) + } + if err := syscall.Close(fd); err != nil { + return fmt.Errorf("close blocker: %w", err) + } + + pathBytes, err := syscall.BytePtrFromString(subDir) + if err != nil { + return fmt.Errorf("path bytes: %w", err) + } + _, _, errno := syscall.Syscall(syscall.SYS_RMDIR, uintptr(unsafe.Pointer(pathBytes)), 0, 0) + runtime.KeepAlive(pathBytes) + if errno == 0 { + return fmt.Errorf("expected ENOTEMPTY, but rmdir succeeded") + } + return nil +} + +// unlinkUnlinkatEnoent attempts to unlinkat a nonexistent file. +// The syscall fails with ENOENT, but ior captures the tracepoint on entry. +func unlinkUnlinkatEnoent() error { + dir, cleanup, err := makeTempDir("unlink-unlinkat-enoent") + if err != nil { + return err + } + defer cleanup() + + dirFD, err := syscall.Open(dir, syscall.O_RDONLY|syscall.O_DIRECTORY, 0) + if err != nil { + return fmt.Errorf("open dir: %w", err) + } + defer syscall.Close(dirFD) + + nameBytes, err := syscall.BytePtrFromString("unlinkat-enoent-missing.txt") + if err != nil { + return fmt.Errorf("name bytes: %w", err) + } + _, _, errno := syscall.Syscall(syscall.SYS_UNLINKAT, uintptr(dirFD), uintptr(unsafe.Pointer(nameBytes)), 0) + runtime.KeepAlive(nameBytes) + if errno == 0 { + return fmt.Errorf("expected ENOENT, but unlinkat succeeded") + } + return nil +} + // dirBasic creates a directory via raw SYS_MKDIR, checks access, then removes it // via raw SYS_RMDIR. We use raw syscalls because Go's syscall.Mkdir wraps mkdirat // and syscall.Rmdir wraps unlinkat on amd64. diff --git a/integrationtests/unlink_test.go b/integrationtests/unlink_test.go index 942f43d..3e61991 100644 --- a/integrationtests/unlink_test.go +++ b/integrationtests/unlink_test.go @@ -34,3 +34,36 @@ func TestUnlinkRmdir(t *testing.T) { }, }) } + +func TestUnlinkEnoent(t *testing.T) { + runScenario(t, "unlink-enoent", []ExpectedEvent{ + { + PathContains: "unlink-enoent-missing.txt", + Tracepoint: "enter_unlink", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} + +func TestUnlinkRmdirNotempty(t *testing.T) { + runScenario(t, "unlink-rmdir-notempty", []ExpectedEvent{ + { + PathContains: "rmdir-notempty", + Tracepoint: "enter_rmdir", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} + +func TestUnlinkUnlinkatEnoent(t *testing.T) { + runScenario(t, "unlink-unlinkat-enoent", []ExpectedEvent{ + { + PathContains: "unlinkat-enoent-missing.txt", + Tracepoint: "enter_unlinkat", + Comm: "ioworkload", + MinCount: 1, + }, + }) +} |
