diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 17:22:57 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | e626219c9e35b37dece1348f1349c40866871f4c (patch) | |
| tree | d6726a3571df9a1dd212ad057760ce5c481d2e56 | |
| parent | 8d25618ea575611e5b1cbe48f4ac48532f2e32be (diff) | |
| download | pardes-e626219c9e35b37dece1348f1349c40866871f4c.tar.gz pardes-e626219c9e35b37dece1348f1349c40866871f4c.zip | |
fs.md says that in a > to a tag cut into several writes, those taken before a refused one stand
A `>` to a tag that a client sends in several writes is checked a write
at a time. When a later one is refused, the earlier accepted ones stay in
the tag. Buffering the whole open to apply it or none would cost more
than it saves, so fs.md now says what happens: each write stands on its
own, and only the refused one changes nothing. A test covers a
truncation, an accepted write, then a refused one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | docs/fs.md | 4 | ||||
| -rw-r--r-- | src/ninep/cols.zig | 12 |
2 files changed, 15 insertions, 1 deletions
@@ -371,7 +371,9 @@ non-UTF-8 bytes are refused (`invalid tag text`). The editable text is at most 4096 bytes (`no space: over 4096 bytes`, ENOSPC; the log says `err <serial> tag: no space: ...`). All three kinds take the same checks, whole or not at all, and a `>` whose write is refused changes nothing: its truncation is done with the write that fits, or at the -close (or a read) when none came. A clear is an ordinary edit +close (or a read) when none came. Each write stands on its own: in a `>` +cut into several writes, those taken before a refused one stay in the +tag; only the refused write changes nothing. A clear is an ordinary edit and `u` in the tag undoes it. [tags.md](tags.md) covers tags on screen. ## Panes diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig index 4e497545..a1fe9ecc 100644 --- a/src/ninep/cols.zig +++ b/src/ninep/cols.zig @@ -253,6 +253,18 @@ test "pane/new takes rows from a pane in another column before refusing ENOSPC" try testing.expect(moved); } +test "in a > cut into several tag writes, those taken before a refused one stand" { + const p = try th.withFile(testing.allocator, "x\n"); + defer p.deinit(); + const node = tree.Node.of(th.serialOf(p), .tag); + _ = th.call(p, .{ .tag = 1, .op = .setattr, .node = node, .truncate = true }); + const h = th.call(p, .{ .tag = 2, .op = .open, .node = node, .omode = 1 }).reply.handle; + try testing.expectEqual(tree.Status.ok, th.call(p, .{ .tag = 3, .op = .write, .node = node, .handle = h, .data = "Make" }).reply.status); + try testing.expectEqual(E.INVAL, th.call(p, .{ .tag = 4, .op = .write, .node = node, .handle = h, .data = " \x01" }).errno()); + _ = th.call(p, .{ .tag = 5, .op = .release, .node = node, .handle = h, .opened = true }); + try testing.expect(std.mem.endsWith(u8, th.rd(p, node, 0, 4096).bytes, "Make")); +} + test "exactly 4096 bytes written with echo's newline after a truncation fit every kind of tag" { const p = try th.withFile(testing.allocator, "x\n"); defer p.deinit(); |
