summaryrefslogtreecommitdiff
path: root/9ns/src/fuse.zig
diff options
context:
space:
mode:
Diffstat (limited to '9ns/src/fuse.zig')
-rw-r--r--9ns/src/fuse.zig45
1 files changed, 45 insertions, 0 deletions
diff --git a/9ns/src/fuse.zig b/9ns/src/fuse.zig
index 216e616..e45abc2 100644
--- a/9ns/src/fuse.zig
+++ b/9ns/src/fuse.zig
@@ -37,6 +37,12 @@ pub const FUSE_BIG_WRITES: u32 = 1 << 5;
/// Required for `--no-direct-io` correctness: 9P sizes change under us, and
/// without this the kernel trusts a stale cached size and truncates reads.
pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12;
+/// Without this the kernel takes the directory's inode lock around every
+/// LOOKUP and READDIR in it, so one walk parked on a server that never
+/// answers holds up every other name in the same directory — the whole
+/// registry root, for a mntgen mount. With it, walks into different names
+/// proceed side by side and only the parked one waits.
+pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18;
pub const FUSE_MAX_PAGES: u32 = 1 << 22;
pub const FATTR_MODE: u32 = 1 << 0;
@@ -389,6 +395,44 @@ pub fn replyError(fd: i32, unique: u64, err: linux.E) Error!void {
return writeAll(fd, &iov, 1, @sizeOf(OutHeader));
}
+/// Notification codes, sent to the kernel unsolicited: they ride an
+/// `OutHeader` with `unique = 0` and the code (positive) in `error`.
+pub const notify_inval_entry: i32 = 3;
+
+/// The body of a `FUSE_NOTIFY_INVAL_ENTRY`, followed by the name and a NUL.
+pub const NotifyInvalEntryOut = extern struct {
+ parent: u64,
+ namelen: u32,
+ padding: u32 = 0,
+};
+
+/// Drops the kernel's cached dentry for `name` under `parent`, so the next
+/// access of that path comes back as a fresh LOOKUP instead of reusing a
+/// node id the server no longer knows.
+///
+/// `unique = 0` marks the message as a notification rather than a reply, so
+/// it is safe to interleave with replies on the same fd, from any thread.
+///
+/// Best-effort by nature: the kernel answers ENOENT when it had nothing
+/// cached under that name (`writeAll` already treats that as success) and
+/// EINVAL when it does not support the notification, which surfaces here as
+/// `error.Io`. Callers ignore both — a notification that does not land
+/// leaves them exactly where they were without it.
+pub fn notifyInvalEntry(fd: i32, parent: u64, name: []const u8) Error!void {
+ if (name.len == 0 or name.len > std.math.maxInt(u32) - 1) return error.Protocol;
+ const out = NotifyInvalEntryOut{ .parent = parent, .namelen = @intCast(name.len) };
+ const total = @sizeOf(OutHeader) + @sizeOf(NotifyInvalEntryOut) + name.len + 1;
+ const header = OutHeader{ .len = @intCast(total), .@"error" = notify_inval_entry, .unique = 0 };
+ const nul = [_]u8{0};
+ var iov = [_]std.posix.iovec_const{
+ .{ .base = @ptrCast(&header), .len = @sizeOf(OutHeader) },
+ .{ .base = @ptrCast(&out), .len = @sizeOf(NotifyInvalEntryOut) },
+ .{ .base = name.ptr, .len = name.len },
+ .{ .base = &nul, .len = 1 },
+ };
+ return writeAll(fd, &iov, iov.len, total);
+}
+
fn writeAll(fd: i32, iov: [*]const std.posix.iovec_const, count: usize, total: usize) Error!void {
while (true) {
const rc = linux.writev(fd, iov, count);
@@ -462,6 +506,7 @@ pub fn initReply(in: *const InitIn, max_write: u32) InitOut {
out.flags |= FUSE_MAX_PAGES;
out.max_pages = 256;
}
+ if (in.flags & FUSE_PARALLEL_DIROPS != 0) out.flags |= FUSE_PARALLEL_DIROPS;
return out;
}