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:
parent
6e2e9e8d05
commit
36614b69c5
2 changed files with 76 additions and 2 deletions
|
|
@ -590,6 +590,10 @@ int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *out = NULL, *sysroot = NULL, *label = "schos";
|
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;
|
uint64_t nblocks = 6000;
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
|
|
@ -599,6 +603,8 @@ main(int argc, char **argv)
|
||||||
nblocks = strtoull(argv[++i], NULL, 0);
|
nblocks = strtoull(argv[++i], NULL, 0);
|
||||||
else if (strcmp(argv[i], "--label") == 0 && i + 1 < argc)
|
else if (strcmp(argv[i], "--label") == 0 && i + 1 < argc)
|
||||||
label = argv[++i];
|
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) {
|
else if (strcmp(argv[i], "--exclude") == 0 && i + 1 < argc) {
|
||||||
if (nexcludes >= MAXEXCLUDE) {
|
if (nexcludes >= MAXEXCLUDE) {
|
||||||
fprintf(stderr, "mkschfs: too many --exclude paths (max %d)\n",
|
fprintf(stderr, "mkschfs: too many --exclude paths (max %d)\n",
|
||||||
|
|
@ -628,7 +634,7 @@ main(int argc, char **argv)
|
||||||
if (!out || !sysroot) {
|
if (!out || !sysroot) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"usage: mkschfs out.img --sysroot DIR [--size BLOCKS] [--label NAME]\n"
|
"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"
|
" BLOCKS are %d-byte SchFS blocks (default 6000 = %d MB)\n"
|
||||||
" --exclude omits a sysroot-relative path and its subtree\n",
|
" --exclude omits a sysroot-relative path and its subtree\n",
|
||||||
SCHFS_BLKSIZE, (int)(6000 * SCHFS_BLKSIZE / (1024 * 1024)));
|
SCHFS_BLKSIZE, (int)(6000 * SCHFS_BLKSIZE / (1024 * 1024)));
|
||||||
|
|
@ -689,6 +695,17 @@ main(int argc, char **argv)
|
||||||
r = schfs_vol_commit(&VOL);
|
r = schfs_vol_commit(&VOL);
|
||||||
if (r != 0) die("committing the uberblock", r);
|
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; }
|
if (fflush(f) != 0 || fclose(f) != 0) { perror(out); return 1; }
|
||||||
|
|
||||||
uint64_t used = nblocks - VOL.free_blocks - SCHFS_FIRST_BLOCK;
|
uint64_t used = nblocks - VOL.free_blocks - SCHFS_FIRST_BLOCK;
|
||||||
|
|
|
||||||
|
|
@ -676,6 +676,60 @@ do_extract(const char *dir)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
/* --snapshots */
|
||||||
|
/* */
|
||||||
|
/* A snapshot is a retained tree root, stored as an ordinary record under */
|
||||||
|
/* SCHFS_SNAP_OBJECTID. Listing them is a range scan over that objectid. */
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_snapshots(void)
|
||||||
|
{
|
||||||
|
struct schfs_cursor c;
|
||||||
|
schfs_cursor_init(&c, &VOL.tree, CURBLK);
|
||||||
|
|
||||||
|
struct schfs_key k;
|
||||||
|
schfs_mkkey(&k, SCHFS_SNAP_OBJECTID, SCHFS_KEY_SNAP, 0);
|
||||||
|
|
||||||
|
int r = schfs_cursor_seek(&c, &k);
|
||||||
|
if (r == -2) {
|
||||||
|
fprintf(stderr, "schfs_fsck: tree is unreadable\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while (r == 0) {
|
||||||
|
const struct schfs_key *got = schfs_cursor_key(&c);
|
||||||
|
if (!got || got->objectid != SCHFS_SNAP_OBJECTID ||
|
||||||
|
got->type != SCHFS_KEY_SNAP)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned len;
|
||||||
|
const struct schfs_snap *sn =
|
||||||
|
(const struct schfs_snap *)schfs_cursor_val(&c, &len);
|
||||||
|
if (sn && len == sizeof(*sn)) {
|
||||||
|
if (n == 0)
|
||||||
|
printf("%-6s %-24s %-12s %s\n",
|
||||||
|
"id", "name", "root", "created at");
|
||||||
|
// The name is bounded but not guaranteed terminated by a bad
|
||||||
|
// image, so it is printed with an explicit precision.
|
||||||
|
printf("%-6llu %-24.*s %-12llu generation %llu\n",
|
||||||
|
(unsigned long long)got->offset,
|
||||||
|
SCHFS_SNAP_NAME_MAX, sn->name,
|
||||||
|
(unsigned long long)sn->root,
|
||||||
|
(unsigned long long)sn->created);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
r = schfs_cursor_next(&c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
printf("no snapshots\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -690,7 +744,8 @@ main(int argc, char **argv)
|
||||||
" --stat PATH one object's metadata\n"
|
" --stat PATH one object's metadata\n"
|
||||||
" --cat PATH a file's contents\n"
|
" --cat PATH a file's contents\n"
|
||||||
" --tree every path in the volume\n"
|
" --tree every path in the volume\n"
|
||||||
" --extract DIR write the whole tree onto the host\n");
|
" --extract DIR write the whole tree onto the host\n"
|
||||||
|
" --snapshots list the retained tree roots\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -751,6 +806,8 @@ main(int argc, char **argv)
|
||||||
return do_cat(arg);
|
return do_cat(arg);
|
||||||
if (strcmp(action, "--extract") == 0 && arg)
|
if (strcmp(action, "--extract") == 0 && arg)
|
||||||
return do_extract(arg);
|
return do_extract(arg);
|
||||||
|
if (strcmp(action, "--snapshots") == 0)
|
||||||
|
return do_snapshots();
|
||||||
|
|
||||||
fprintf(stderr, "schfs_fsck: unknown or incomplete action '%s'\n", action);
|
fprintf(stderr, "schfs_fsck: unknown or incomplete action '%s'\n", action);
|
||||||
return 2;
|
return 2;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue