From 8251307ac3187b346ed12e9a54d9bf6d7cba7e53 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 1 Jun 2026 23:07:06 +0300 Subject: 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 --- integrationtests/xattr_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'integrationtests') 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 -- cgit v1.2.3