diff options
Diffstat (limited to '9ns/src/fuse.zig')
| -rw-r--r-- | 9ns/src/fuse.zig | 45 |
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; } |
