diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-21 21:44:11 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-21 21:44:11 +0200 |
| commit | 08cb9dfe46f843114feb42cc9ffa599717ebcc32 (patch) | |
| tree | f09fdbce3bb8654bf52180ccf598173e3d8a8976 /integrationtests/cmd | |
| parent | d455543bf0838c7fe2250081c5ea8ed3e275d236 (diff) | |
Add negative integration tests for truncate syscalls (task 348)
Add two negative test scenarios:
- truncate-enoent: SYS_TRUNCATE on nonexistent file, expects ENOENT
- truncate-ftruncate-ebadf: SYS_FTRUNCATE on invalid fd 99999, expects EBADF
Both verify ior captures tracepoints even when syscalls fail.
Amp-Thread-ID: https://ampcode.com/threads/T-019c81b7-9641-763e-b99e-20a0d3552005
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'integrationtests/cmd')
| -rw-r--r-- | integrationtests/cmd/ioworkload/scenarios.go | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/integrationtests/cmd/ioworkload/scenarios.go b/integrationtests/cmd/ioworkload/scenarios.go index 58c78e1..f25b8a4 100644 --- a/integrationtests/cmd/ioworkload/scenarios.go +++ b/integrationtests/cmd/ioworkload/scenarios.go @@ -86,8 +86,10 @@ var scenarios = map[string]func() error{ "sync-fsync-ebadf": syncFsyncEbadf, "sync-fdatasync-ebadf": syncFdatasyncEbadf, "sync-file-range-ebadf": syncFileRangeEbadf, - "truncate-basic": truncateBasic, - "truncate-ftruncate": truncateFtruncate, + "truncate-basic": truncateBasic, + "truncate-ftruncate": truncateFtruncate, + "truncate-enoent": truncateEnoent, + "truncate-ftruncate-ebadf": truncateFtruncateEbadf, "iouring-setup": iouringSetup, "iouring-enter": iouringEnter, "iouring-register": iouringRegister, @@ -2225,6 +2227,40 @@ func truncateFtruncate() error { return syscall.Ftruncate(fd, 5) } +// truncateEnoent attempts to truncate a nonexistent file via raw SYS_TRUNCATE. +// The syscall fails with ENOENT, but ior captures the enter_truncate +// tracepoint because the path is read on entry. +func truncateEnoent() error { + dir, cleanup, err := makeTempDir("truncate-enoent") + if err != nil { + return err + } + defer cleanup() + + path := filepath.Join(dir, "truncate-enoent-missing.txt") + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return fmt.Errorf("path bytes: %w", err) + } + _, _, errno := syscall.Syscall(syscall.SYS_TRUNCATE, uintptr(unsafe.Pointer(pathBytes)), 0, 0) + runtime.KeepAlive(pathBytes) + if errno == 0 { + return fmt.Errorf("expected ENOENT, but truncate succeeded") + } + return nil +} + +// truncateFtruncateEbadf calls raw SYS_FTRUNCATE on an invalid fd (99999). +// The syscall fails with EBADF, but ior captures the enter_ftruncate +// tracepoint because it is recorded on syscall entry. +func truncateFtruncateEbadf() error { + _, _, errno := syscall.Syscall(syscall.SYS_FTRUNCATE, 99999, 0, 0) + if errno == 0 { + return fmt.Errorf("expected EBADF, but ftruncate succeeded") + } + return nil +} + // openByHandleAt creates a file, resolves its handle via name_to_handle_at, // then opens it via open_by_handle_at. Requires root (CAP_DAC_READ_SEARCH). // LockOSThread prevents goroutine migration between the two syscalls so that |
