schfs: mount a snapshot by name, read-only
`mount -t schfs @golden /mnt` mounts a snapshot. The source argument
finally means something: sys_mount ignored it outright, because there was
one volume and nothing to choose between. `@name` rather than a path
because a snapshot is not a device -- there is no node in /dev for it and
there should not be, since it lives inside the volume already mounted.
A second volume rather than a parameter threaded through all 23 schfs_k*
entry points. Only the read side can ever address it, so the write path
keeps talking to the live volume and needed no change; the four read
entry points grew _dev variants and pick the volume from the device the
inode already carries. One fs_ops table serves both, because every entry
takes an inode and dispatches on it -- a second table would be the same
pointers under a different name.
It needs its own device number: a snapshot's object ids are the *same
numbers* as the live tree's, so one device would make the two views alias
in the inode cache and hand a reader whichever was cached first.
Three things this turned up, none of them new but all of them reachable
for the first time:
- sys_mount refused with `dev == SCHFS_DEV`, meaning "do not mount SchFS
onto SchFS". A snapshot's mount point is necessarily on the live root,
so that refused the entire feature. The real condition is mounting a
device onto *itself*, which is `dev == fsdev`.
- createat allocates an inode and then panics if dirlink fails --
reasonable about a writable filesystem, a crash on a read-only one.
The refusal now happens before anything is allocated. Unlink and
rename already handled a refusal properly.
- a refused create reported ENOENT, so writing to a snapshot said "No
such file or directory" -- the sentence a shell prints for every
create failure. It reports EROFS now.
Verified in the running system against an image built with
--snapshot golden: the mount succeeds, ls lists the snapshot's tree,
/mnt2/etc/motd reads back through it, and a write is refused with
"Read-only file system".
One at a time, and no umount path for it yet: mount_del_by_root removes
the mount table entry, but ksnap_unmount is only called on the failure
paths. schfs-check passes; bootcheck 108 [ok].
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
36614b69c5
commit
3919f1446e
6 changed files with 345 additions and 18 deletions
|
|
@ -66,6 +66,9 @@ int getdents(struct inode*, char *out, int cap, uint64 *poff);
|
|||
// Mount table: a substitution applied during path resolution. Nothing on disk
|
||||
// records a mount, so unmounting is forgetting.
|
||||
void mountinit(void);
|
||||
// 1 if the filesystem on this device refuses writes -- a mounted snapshot.
|
||||
// Ask before mutating, not after: the create path panics on a failed dirlink.
|
||||
int fs_readonly(uint dev);
|
||||
int mount_add(uint dev, uint64 inum, uint fsdev, uint64 root);
|
||||
int mount_del_by_root(uint fsdev, uint64 root);
|
||||
struct inode* ialloc(uint, short);
|
||||
|
|
@ -173,6 +176,23 @@ int schfs_kdirdel(uint64 dir, const char *name, unsigned len);
|
|||
int schfs_kfree(uint64 ino);
|
||||
uint64 schfs_kroot(void);
|
||||
|
||||
// A snapshot mounted read-only: the same block device with an older tree root.
|
||||
// The read path takes a device number so the VFS can address either view; there
|
||||
// is no write path, because a snapshot that could be written to would not be
|
||||
// one. See kernel/fs/schfs/kmount.c.
|
||||
int schfs_ksnap_mount(const char *name, unsigned namelen);
|
||||
void schfs_ksnap_unmount(void);
|
||||
uint64 schfs_ksnap_root(void);
|
||||
long schfs_kread_dev(uint dev, uint64 ino, uint64 off, void *buf,
|
||||
unsigned n);
|
||||
int schfs_kstat_dev(uint dev, uint64 ino, struct schfs_inode *out);
|
||||
uint64 schfs_klookup_at_dev(uint dev, uint64 dir, const char *name,
|
||||
unsigned len);
|
||||
int schfs_kreaddir_dev(uint dev, uint64 dir,
|
||||
int (*fn)(uint64, unsigned short, const char *,
|
||||
unsigned, void *),
|
||||
void *arg);
|
||||
|
||||
// ramdisk.c
|
||||
uint uptime(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,13 @@ struct inode {
|
|||
|
||||
// Device number that means "the synthetic /proc". Like SCHFS_DEV, it is
|
||||
// deliberately nowhere near a real disk's 0 or 1.
|
||||
// A mounted snapshot. Same block device, a different tree root, read-only.
|
||||
// It needs its own device number because an inode is identified by (dev, inum)
|
||||
// and a snapshot's object ids are the *same numbers* as the live tree's -- one
|
||||
// number would make the two views alias in the inode cache and hand a reader
|
||||
// whichever was cached first.
|
||||
#define SCHFS_SNAP_DEV 66
|
||||
|
||||
#define PROCFS_DEV 65
|
||||
|
||||
// table mapping major device number to
|
||||
|
|
|
|||
Loading…
Reference in a new issue