FUSE: notify kernel when /mfs content changes via RPC
- Dominant language
- Go
- Stars
- 17.1k
- Forks
- 3.2k
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 11
Description
> [!NOTE]
> Filling as issue for now, as this just got released upstream, and we may want to wait a while for any bugfixes before we wire this up.
## Problem
When a process opens a file under `/mfs` via FUSE and another path modifies the same MFS node (e.g. `ipfs files write`, `ipfs files cp`, `ipfs files rm` over the HTTP RPC), the kernel can serve stale cached
data to the holding process indefinitely. The kernel doesn't know its page cache for that inode is invalid because nothing tells it.
Concrete scenarios:
- A file watcher (entr, inotifywait) running against `/mfs` doesn't see RPC-driven changes until the process next opens the file fresh.
- `tail -f /mfs/log` shows nothing when `ipfs files write --offset $end /mfs/log` appends from another shell.
- A media player streaming `/mfs/video.mp4` keeps reading the old DAG even after `ipfs files cp` swapped the entry to a new CID.
- An editor with the file open keeps showing pre-RPC content; saving silently overwrites the RPC-driven changes.
## Proposal
Use FUSE's notification API to push invalidation events to the kernel whenever the MFS root publishes a change.
go-fuse exposes notifications via `(*fs.Inode).NotifyContent(off, sz)` for byte-range invalidation, `NotifyEntry(parent, name)` for directory entry changes, and `NotifyDelete(parent, child, name)` for unlinks.
These map to FUSE's `NOTIFY_INVAL_INODE`, `NOTIFY_INVAL_ENTRY`, `NOTIFY_DELETE` upcalls.
Wire-up:
1. Subscribe to MFS root publish events (the existing event hook used by IPNS-publish-on-flush plumbing).
2. For each change, walk the kubo FUSE inode tree and call the appropriate `Notify*` method.
3. For file content changes, invalidate the full inode (start=0, len=-1) — we don't track byte-range modifications cheaply, and full-inode invalidation is correct.
4. For directory mutations, invalidate the entry rather than the directory inode itself, so unrelated entries stay cached.
## Out of scope (initial pass)
- inotify(7) compat (`fanotify_mark`, etc.) — FUSE notifications don't generate inotify events; that's a kernel limitation. We can revisit if it becomes a felt pain.
- `/ipfs` invalidation: it's read-only and content-addressed, so the cache is always coherent. No work needed.
- `/ipns` invalidation on remote-sourced changes: out of scope for this issue; it's a different signal path (waiting for new IPNS records vs. local MFS changes).
## Why now
- The v0.41 FUSE rewrite onto `hanwen/go-fuse` puts kubo in a position to implement notifications cleanly. The legacy `bazil.org/fuse` API was awkward for this.
- Most FUSE-mount issues users have filed against kubo are downstream of the cache-coherence gap (e.g. "edits don't show up", "rsync sees old data").
## Acceptance
- `tail -f /mfs/foo` in one terminal sees data appended via `ipfs files write` from another terminal within ~1s.
- `ls /mfs/dir` in one process sees a file removed via `ipfs files rm` from another process without re-opening the directory.
- Editors (vim, emacs) that have a file open under `/mfs` show the latest content after RPC-driven writes (after manual reload or with autoread).
- No regression in throughput on the sequential-write fast path.
## References
- go-fuse Notify API — https://pkg.go.dev/github.com/hanwen/go-fuse/v2/fs#Inode (`NotifyContent`, `NotifyEntry`, `NotifyDelete`)
- Linux FUSE protocol notifications — https://www.kernel.org/doc/html/latest/filesystems/fuse-io.html
- v2.10.1 notify refactor — https://github.com/hanwen/go-fuse/compare/v2.9.0...v2.10.1 (the work moved notify writes into the protocol server)
Contributor guide
Assessment
This issue has not been assessed yet.