summaryrefslogtreecommitdiff
path: root/src/fs.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-22 11:50:09 -0300
committerGabriel Schneider <[email protected]>2026-09-22 11:50:09 -0300
commiteb1a104f385f375a74319695e1a59f6f82e6384e (patch)
treeb6a1f48588512ce677f41adeaae85a53655c4781 /src/fs.zig
parentb7fc01550c7bde290cf14276d94193b5b4031dc8 (diff)
downloadcloud9-eb1a104f385f375a74319695e1a59f6f82e6384e.tar.gz
cloud9-eb1a104f385f375a74319695e1a59f6f82e6384e.zip
fs: a parked job whose fid was clunked is answered, not asked again
A client that gives up on a parked open sends Tclunk for its fid without a Tflush; the fid goes, the parked job stays. On retry the job went back to the backend, which did the work -- opened a handle, made an object -- and the reply then found no fid and dropped it, handle and all. Now the retry looks for the fid first and answers "fid unknown" without asking. Found by an adversarial review of pardes's use of the engine. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'src/fs.zig')
-rw-r--r--src/fs.zig30
1 files changed, 29 insertions, 1 deletions
diff --git a/src/fs.zig b/src/fs.zig
index bd9cbd9..83cea68 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -693,7 +693,17 @@ pub fn Server(comptime Backend: type, comptime opts: Options) type {
return null;
}
const sl = &s.slots[i];
- if (sl.holdsJob()) return s.resumeJob(i);
+ if (sl.holdsJob()) {
+ // Its fid may have been clunked while it waited: then there
+ // is nobody to answer, and asking the backend would make it
+ // do the work -- open a handle, make an object -- for no one.
+ if (s.findFid(sl.fid) == null) {
+ s.fail(sl.tag, e_unknown_fid);
+ sl.* = .{};
+ return s.retry();
+ }
+ return s.resumeJob(i);
+ }
sl.retried = true;
sl.parked = false;
return sl.req;
@@ -2221,6 +2231,24 @@ test "fs server: a parked open, truncate and clunk complete on retry, and a walk
try testing.expectEqualStrings(e_again, got.msg.rerror.ename);
h.fsys.park_lookups = false;
+ // A parked open whose fid is clunked meanwhile is answered for the
+ // clunk's sake and never asked again: the backend would otherwise open
+ // a handle for nobody.
+ _ = try h.walkTo(16, 5, &.{ "1", "tag" });
+ h.fsys.park_opens = true;
+ try h.send(17, .{ .topen = .{ .fid = 5, .mode = oread } });
+ try h.quiet();
+ try h.send(18, .{ .tclunk = .{ .fid = 5 } });
+ got = try h.reap();
+ try testing.expect(got.msg == .rclunk);
+ h.fsys.park_opens = false;
+ const asked = h.fsys.calls;
+ h.pump();
+ got = try h.reap();
+ try testing.expectEqual(@as(u16, 17), got.tag);
+ try testing.expectEqualStrings(e_unknown_fid, got.msg.rerror.ename);
+ try testing.expectEqual(asked, h.fsys.calls);
+
// A flush reaches a parked job the way it reaches a parked read.
h.fsys.park_opens = true;
try h.send(14, .{ .topen = .{ .fid = 2, .mode = oread } });