Context
The MST/2 FUSE layer is a separate, read-only filesystem: Mst2Fuse
(src/snapshot/fuse.rs) mounts a fixed snapshot through mount_filesystem, and
examples/mst2_mount.rs is what puts it on the host. The existing ScorpioFS
daemon path (dicfuse + the Antares overlay with its own upper/lower layers) is
untouched — the two do not share inodes, caching or handles.
That was the right way to close the read path, and it is explicitly the slice's
scope ("read-only slice; write/upper stay there"). It is not the end state: a
workspace should see the snapshot as the filesystem, with the overlay's
write path layered on top, rather than as a second mount the user has to
correlate by hand.
Spec 12 also asks for handle and invalidation semantics that only make sense once
the two meet: DirRef/NodeRef carry (snapshot_id, generation, fixed directory root) so handles never mean "whatever this path is now", and the upper
layer must be invalidated when the view or the workspace changes — not when the
client guesses.
Scope
- One mount. Make the fixed-view read layer feed the daemon's existing mount
instead of running beside it: reads for a snapshot scope resolve through
SnapshotReader (metadata from the page store, content from the local CAS),
while writes continue to go to the current upper layer.
- Identity binding. Node identity must be
(mount_id, generation, inode) →
NodeRef(snapshot_id, fixed directory root / content digest). A path is never
the identity, and a handle obtained before a view change must not silently
resolve into a different file (spec 12, spec 11 §3 inode row).
- Invalidation. When a workspace switches to a new snapshot, or an upper
file masks a lower one, the affected attributes and contents must be
invalidated deliberately (the kernel has been told a TTL; a stale entry must
not outlive it silently). Define what happens to open handles across the
switch: they keep reading the view they were opened on.
- Attribute semantics. Because a fixed view never mutates, attribute caching
for snapshot-backed entries can be much stronger than the current flat 60 s
TTL; state the rule (immutable ⇒ cacheable) instead of leaving a number.
- Keep the read-only mode. The standalone
mst2_mount must keep working —
it is what the acceptance stack and any "mount this exact version" use case
depends on.
Acceptance
- A workspace mount serves snapshot content through the shared path, with the
existing Antares tests still passing.
- An open file handle keeps returning the bytes of the view it was opened on
after the workspace advances to a new snapshot (test with two versions).
- A write through the upper layer masks the snapshot file and is visible
immediately afterwards, with the snapshot bytes still intact underneath.
- A test asserting attributes of snapshot-backed entries are not re-fetched
within the declared immutability window (measured, not assumed).
tests/mst2-e2e still passes unchanged (the standalone path is not broken).
References
- Spec 12 (FUSE and workspace interfaces), spec 11 §3 (cache identity, inode
isolation), spec 03 §6 (fixed view)
src/snapshot/fuse.rs, examples/mst2_mount.rs, src/antares/*,
src/dicfuse/*, src/server/*
Context
The MST/2 FUSE layer is a separate, read-only filesystem:
Mst2Fuse(
src/snapshot/fuse.rs) mounts a fixed snapshot throughmount_filesystem, andexamples/mst2_mount.rsis what puts it on the host. The existing ScorpioFSdaemon path (dicfuse + the Antares overlay with its own upper/lower layers) is
untouched — the two do not share inodes, caching or handles.
That was the right way to close the read path, and it is explicitly the slice's
scope ("read-only slice; write/upper stay there"). It is not the end state: a
workspace should see the snapshot as the filesystem, with the overlay's
write path layered on top, rather than as a second mount the user has to
correlate by hand.
Spec 12 also asks for handle and invalidation semantics that only make sense once
the two meet:
DirRef/NodeRefcarry(snapshot_id, generation, fixed directory root)so handles never mean "whatever this path is now", and the upperlayer must be invalidated when the view or the workspace changes — not when the
client guesses.
Scope
instead of running beside it: reads for a snapshot scope resolve through
SnapshotReader(metadata from the page store, content from the local CAS),while writes continue to go to the current upper layer.
(mount_id, generation, inode)→NodeRef(snapshot_id, fixed directory root / content digest). A path is neverthe identity, and a handle obtained before a view change must not silently
resolve into a different file (spec 12, spec 11 §3 inode row).
file masks a lower one, the affected attributes and contents must be
invalidated deliberately (the kernel has been told a TTL; a stale entry must
not outlive it silently). Define what happens to open handles across the
switch: they keep reading the view they were opened on.
for snapshot-backed entries can be much stronger than the current flat 60 s
TTL; state the rule (immutable ⇒ cacheable) instead of leaving a number.
mst2_mountmust keep working —it is what the acceptance stack and any "mount this exact version" use case
depends on.
Acceptance
existing Antares tests still passing.
after the workspace advances to a new snapshot (test with two versions).
immediately afterwards, with the snapshot bytes still intact underneath.
within the declared immutability window (measured, not assumed).
tests/mst2-e2estill passes unchanged (the standalone path is not broken).References
isolation), spec 03 §6 (fixed view)
src/snapshot/fuse.rs,examples/mst2_mount.rs,src/antares/*,src/dicfuse/*,src/server/*