diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-01 23:07:06 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-01 23:07:06 +0300 |
| commit | 8251307ac3187b346ed12e9a54d9bf6d7cba7e53 (patch) | |
| tree | 44d9f7f8485c906e19cd4ad0471d5c013c685b1c | |
| parent | 1fa1cc46a0e34a25b1926837ba663b2446b8dc7c (diff) | |
test(xattr): add path-based setxattr end-to-end integration coverage
The xattr integration tests previously only asserted on getxattrat/
listxattrat (path/dirfd READ-classified variants). The path-based
setxattr(2) was traced by the existing xattr-getxattrat scenario
(workload calls syscall.Setxattr; xattrTraceArgs includes setxattr) but
never asserted. Add TestXattrSetxattr to verify end-to-end that:
- enter_setxattr captures the filesystem PATH at args[0] (kind=pathname),
never the xattr NAME at args[1];
- exit_setxattr is UNCLASSIFIED: setxattr returns a 0/-1 status, not a
byte count (the size arg is the INPUT value length), so accounted
bytes must be exactly zero. This guards against the msgsnd-style bug
of treating a status return as bytes written, and contrasts with
getxattr/listxattr which DO return byte counts (READ-classified).
This is the PATH-based set complement to filed task 8i0 (fd-based
fsetxattr/fgetxattr/flistxattr/fremovexattr); it does not duplicate it.
Classification (FamilyFS, KindPathname enter, UNCLASSIFIED ret) is
verified by inspection per task guidance, not by a unit test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| -rw-r--r-- | integrationtests/xattr_test.go | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/integrationtests/xattr_test.go b/integrationtests/xattr_test.go index 2ba30ac..16efb4a 100644 --- a/integrationtests/xattr_test.go +++ b/integrationtests/xattr_test.go @@ -40,6 +40,44 @@ func TestXattrGetxattrat(t *testing.T) { assertEventDurationPositive(t, result, exp) } +// TestXattrSetxattr verifies ior traces the PATH-based setxattr(2) end-to-end. +// setxattr(const char *path, const char *name, const void *value, size_t size, +// int flags) takes a real filesystem PATH at args[0] (kind=pathname) and the +// xattr NAME at args[1]; only the path must be captured on entry, so +// enter_setxattr must carry the file path "xattrfile.txt" and never the xattr +// name "user.ior". Crucially, setxattr returns 0 on success / -1 on error — its +// `size` argument is the INPUT value length, NOT a byte count returned by the +// call. The exit is therefore UNCLASSIFIED (contrast getxattr/listxattr, which +// DO return byte counts and are READ-classified), so the recorded byte total +// must be exactly zero. This reuses the xattr-getxattrat scenario, whose +// workload performs syscall.Setxattr(path, "user.ior", ...) and is traced via +// xattrTraceArgs ("getxattrat,setxattr,openat"). +func TestXattrSetxattr(t *testing.T) { + result, _ := runScenarioResultWithIorArgs(t, "xattr-getxattrat", []ExpectedEvent{ + { + PathContains: "xattrfile.txt", + Tracepoint: "enter_setxattr", + Comm: "ioworkload", + MinCount: 1, + }, + }, xattrTraceArgs) + + // The captured path must be the filesystem path, never the xattr name. + for _, rec := range result.Records { + if rec.TraceID.String() == "enter_setxattr" && rec.Path == "user.ior" { + t.Errorf("setxattr captured xattr name %q as path instead of file path", rec.Path) + } + } + + // setxattr is UNCLASSIFIED: its return is a 0/-1 status, never a byte count + // (the `size` arg is the input value length). The accounted bytes for the + // setxattr events must therefore be exactly zero — guarding against the + // msgsnd-style bug of treating a status return as bytes written. + exp := ExpectedEvent{Tracepoint: "enter_setxattr", Comm: "ioworkload"} + assertEventBytesEqual(t, result, exp, 0) + assertEventDurationPositive(t, result, exp) +} + // TestXattrListxattrat verifies ior traces listxattrat(2) (Linux 6.13+) // end-to-end. listxattrat takes a dirfd plus a real filesystem path at args[1] // (NOT args[0]=dfd); only the path must be captured. The path is read on |
