summaryrefslogtreecommitdiff
path: root/src/fs.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs.zig')
-rw-r--r--src/fs.zig65
1 files changed, 62 insertions, 3 deletions
diff --git a/src/fs.zig b/src/fs.zig
index 2a5102f4..db07672e 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -85,6 +85,14 @@ pub const WriteError = error{
NoSpaceLeft,
OpenFailed,
WriteFailed,
+ // The rest are open(2)'s errno, named so errorWords says them.
+ NoSuchFileOrDirectory,
+ NotDir,
+ InvalidArgument,
+ FileBusy,
+ SymLinkLoop,
+ NoSuchDeviceOrAddress,
+ FileTooBig,
};
/// The host takes the bytes. The caller has given the turn up (`write`),
@@ -102,6 +110,13 @@ pub fn writeFile(path: []const u8, bytes: []const u8) WriteError!void {
.ROFS => error.ReadOnlyFilesystem,
.NOSPC, .DQUOT => error.NoSpaceLeft,
.NAMETOOLONG => error.PathTooLong,
+ .NOENT => error.NoSuchFileOrDirectory,
+ .NOTDIR => error.NotDir,
+ .INVAL => error.InvalidArgument,
+ .BUSY, .TXTBSY => error.FileBusy,
+ .LOOP => error.SymLinkLoop,
+ .NXIO, .NODEV => error.NoSuchDeviceOrAddress,
+ .FBIG => error.FileTooBig,
else => error.OpenFailed,
};
var off: usize = 0;
@@ -1554,6 +1569,36 @@ fn readFileLimit(gpa: std.mem.Allocator, path: []const u8, limit: usize) ![]u8 {
return buf;
}
+/// A 9P write of an Edit held while its commands run (Namespace.edit_hold).
+pub const EditHold = struct {
+ /// The listener's: the connection that asked, and its park.
+ asker: *anyopaque,
+ slot: u16,
+ seq: u64,
+ tag: u64,
+ node: u64,
+ handle: u32,
+ written: u32,
+ /// The Edit is done: applied, or failed with `said`.
+ done: bool = false,
+ errno: u16 = 0,
+ said: [256]u8 = undefined,
+ said_len: u16 = 0,
+
+ pub fn names(h: *const EditHold, req: tree.Req) bool {
+ return req.op == .write and req.tag == h.tag and req.node == h.node and req.handle == h.handle;
+ }
+
+ /// Its answer, the held record let go: the words copied out first.
+ pub fn answer(ns: *Namespace) tree.Reply {
+ const h = ns.edit_hold.?;
+ ns.edit_hold = null;
+ if (h.errno == 0) return .{ .tag = h.tag, .written = h.written };
+ @memcpy(ns.edit_said[0..h.said_len], h.said[0..h.said_len]);
+ return tree.failText(h.tag, h.errno, ns.edit_said[0..h.said_len]);
+ }
+};
+
pub const Namespace = struct {
socket_path: []const u8 = "",
tcp_address: ?std.Io.net.IpAddress = null,
@@ -1613,9 +1658,15 @@ pub const Namespace = struct {
/// A 9P write asked a language server something: the answer count it
/// waits to change (`turn.lsp_answers`), so its failure is the write's.
lsp_answer_at: ?u64 = null,
- /// A write ran an Edit whose `<`, `|` or `>` commands are running: it is
- /// answered once `turn.edit_answers` passes this, the Edit done.
- edit_answer_at: ?u64 = null,
+ /// The request being served started an Edit whose `<`, `|` or `>`
+ /// commands run off the loop (edit_cmd.zig): the listener holds it.
+ edit_started: bool = false,
+ /// That write, held: answered once `done` (9p_io answerHeld), or
+ /// asked again by a retry of its park (Pardes.serveFs); a flush or a
+ /// hang-up stops the Edit (9p_io watchEdit).
+ edit_hold: ?EditHold = null,
+ /// The words a held Edit write was refused with, kept for its answer.
+ edit_said: [256]u8 = undefined,
/// The pane a language answer filled (a Rename's preview): what the
/// waiting 9P write answers once it is in (9p_io).
lsp_result: ?u32 = null,
@@ -1757,3 +1808,11 @@ test {
test "the tree's README lists src/ only when the build serves it" {
try std.testing.expectEqual(limits.embedded_sources, std.mem.indexOf(u8, help, "src/") != null);
}
+
+test "a write open(2) refuses says its errno in words" {
+ if (comptime @import("builtin").os.tag != .linux or !pardes.hosted) return error.SkipZigTest;
+ // /proc takes no new file: ENOENT, which used to read "open failed".
+ try std.testing.expectError(error.NoSuchFileOrDirectory, writeFile("/proc/pardes-no-such-file", "x"));
+ var words: [128]u8 = undefined;
+ try std.testing.expectEqualStrings("no such file or directory", pardes.Messages.errorWords(error.NoSuchFileOrDirectory, &words));
+}