schfs_fsck: list snapshots, and mkschfs can make one

--snapshots is a range scan over SCHFS_SNAP_OBJECTID, printing each
retained root's id, name, block and the generation it was taken at. The
name is printed with an explicit precision: it is bounded but a damaged
image need not have terminated it.

mkschfs grows --snapshot NAME, which takes one of the finished tree. It
runs after the commit, because a snapshot names a *committed* root --
taking it before would record a root that exists only in memory.

Verified end to end: an image built with --snapshot golden lists the
snapshot and still passes --check. An image without one prints "no
snapshots" rather than nothing at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-08-10 19:33:54 +02:00
commit 36614b69c5
2 changed files with 76 additions and 2 deletions

View file

@ -590,6 +590,10 @@ int
main(int argc, char **argv)
{
const char *out = NULL, *sysroot = NULL, *label = "schos";
// Take a snapshot of the finished tree. Mostly for testing the snapshot
// paths end to end -- an image that ships with one is a perfectly ordinary
// thing to want, and it is one call.
const char *snapname = NULL;
uint64_t nblocks = 6000;
for (int i = 1; i < argc; i++) {
@ -599,6 +603,8 @@ main(int argc, char **argv)
nblocks = strtoull(argv[++i], NULL, 0);
else if (strcmp(argv[i], "--label") == 0 && i + 1 < argc)
label = argv[++i];
else if (strcmp(argv[i], "--snapshot") == 0 && i + 1 < argc)
snapname = argv[++i];
else if (strcmp(argv[i], "--exclude") == 0 && i + 1 < argc) {
if (nexcludes >= MAXEXCLUDE) {
fprintf(stderr, "mkschfs: too many --exclude paths (max %d)\n",
@ -628,7 +634,7 @@ main(int argc, char **argv)
if (!out || !sysroot) {
fprintf(stderr,
"usage: mkschfs out.img --sysroot DIR [--size BLOCKS] [--label NAME]\n"
" [--exclude PATH]...\n"
" [--exclude PATH]... [--snapshot NAME]\n"
" BLOCKS are %d-byte SchFS blocks (default 6000 = %d MB)\n"
" --exclude omits a sysroot-relative path and its subtree\n",
SCHFS_BLKSIZE, (int)(6000 * SCHFS_BLKSIZE / (1024 * 1024)));
@ -689,6 +695,17 @@ main(int argc, char **argv)
r = schfs_vol_commit(&VOL);
if (r != 0) die("committing the uberblock", r);
// A snapshot of the finished tree, if one was asked for. After the commit
// above, because a snapshot names a *committed* root -- taking it before
// would record a root that only exists in memory.
if (snapname) {
unsigned n = (unsigned)strlen(snapname);
r = schfs_snap_create(&VOL, 1, snapname, n);
if (r != 0) die("creating the snapshot", r);
r = schfs_vol_commit(&VOL);
if (r != 0) die("committing the snapshot", r);
}
if (fflush(f) != 0 || fclose(f) != 0) { perror(out); return 1; }
uint64_t used = nblocks - VOL.free_blocks - SCHFS_FIRST_BLOCK;