/* * demo.c — a runnable, human-readable demonstration of PackFS, not another * automated test. Run it and read the output; each step prints what it did * and what it found, so the library's core behaviors (mem backend, a * pack-backed overlay with copy-up and whiteout, compaction, persistence * across a re-open, and a sandboxed dir mount) are visible directly, * without reading test assertions. * * Usage: packfs_demo [pack-file-path] * (defaults to /tmp/packfs_demo.pack; re-run it to see the previous run's * data survive via the pack file written by the first run's vfs_sync.) */ #include #include #include #include "packfs.h" static void die(const char *what, int err) { fprintf(stderr, "FAILED: %s (error code %d)\n", what, err); exit(1); } static void print_stat(Vfs *v, const char *path) { VfsStat st; int rc = vfs_stat(v, path, &st); if (rc != VFS_OK) { printf(" stat %-20s -> error %d\n", path, rc); return; } printf(" stat %-20s -> %s, size=%llu\n", path, st.kind == VFS_KIND_DIR ? "dir " : "file", (unsigned long long)st.size); } static void list_dir(Vfs *v, const char *path) { VfsDir dir; int rc = vfs_readdir(v, path, &dir); if (rc != VFS_OK) { printf(" readdir %s -> error %d\n", path, rc); return; } printf(" readdir %s -> %zu entr%s:\n", path, (size_t)dir.count, dir.count == 1 ? "y" : "ies"); for (pfs_usize i = 0; i < dir.count; i++) { printf(" %s%s\n", dir.entries[i].name, dir.entries[i].kind == VFS_KIND_DIR ? "/" : ""); } vfs_dir_free(&dir); } static void write_file(Vfs *v, const char *path, const char *content) { int err = 0; VfsFile *f = vfs_open(v, path, VFS_O_WRONLY | VFS_O_CREAT | VFS_O_TRUNC, &err); if (!f) die(path, err); pfs_usize len = (pfs_usize)strlen(content); if (vfs_write(f, content, len) != (pfs_isize)len) die("short write", VFS_ERR_IO); vfs_close(f); printf(" wrote %-20s (%zu bytes)\n", path, (size_t)len); } static void read_file(Vfs *v, const char *path) { int err = 0; VfsFile *f = vfs_open(v, path, VFS_O_RDONLY, &err); if (!f) { printf(" read %-20s -> error %d\n", path, err); return; } char buf[256] = {0}; pfs_isize n = vfs_read(f, buf, sizeof(buf) - 1); vfs_close(f); printf(" read %-20s -> \"%.*s\"\n", path, (int)n, buf); } int main(int argc, char **argv) { const char *pack_path = argc > 1 ? argv[1] : "/tmp/packfs_demo.pack"; printf("=== PackFS demo: pack-backed overlay at %s ===\n", pack_path); printf("(if this file already exists from a previous run, its contents\n" " should reappear below, proving compaction + reload persistence)\n\n"); Vfs *v = vfs_new(); Backend *mem = backend_mem_new(); int oerr = 0; Backend *overlay = backend_overlay_new(pack_path, mem, &oerr); if (!overlay) die("backend_overlay_new", oerr); if (vfs_mount(v, "/", overlay) != VFS_OK) die("vfs_mount /", 0); printf("-- initial state (from the pack file, if one existed) --\n"); list_dir(v, "/"); printf("\n-- writing files --\n"); write_file(v, "/hello.txt", "Hello from PackFS!"); write_file(v, "/config.json", "{\"greeting\":\"hi\"}"); if (vfs_mkdir(v, "/notes") != VFS_OK) printf(" (/notes already existed)\n"); else printf(" mkdir /notes\n"); write_file(v, "/notes/todo.txt", "buy milk"); printf("\n-- reading back --\n"); read_file(v, "/hello.txt"); read_file(v, "/notes/todo.txt"); printf("\n-- directory listing --\n"); list_dir(v, "/"); list_dir(v, "/notes"); printf("\n-- stat --\n"); print_stat(v, "/hello.txt"); print_stat(v, "/notes"); print_stat(v, "/does-not-exist.txt"); printf("\n-- copy-up + whiteout: deleting a file that lives in the pack --\n"); /* On the second run, /hello.txt (if it survived compaction) lives in * the read-only pack layer; this unlink is served by a whiteout * (Section 4.2), not a mutation of the pack. */ int rc = vfs_unlink(v, "/config.json"); printf(" unlink /config.json -> %s\n", rc == VFS_OK ? "ok (whiteout or plain removal)" : "error"); list_dir(v, "/"); printf("\n-- compacting the overlay into a fresh pack (vfs_sync) --\n"); if (vfs_sync(v, "/") != VFS_OK) die("vfs_sync", 0); printf(" wrote %s\n", pack_path); vfs_unmount(v, "/"); backend_free(overlay); backend_free(mem); vfs_free(v); printf("\n=== sandboxed dir mount demo ===\n"); { char tmpl[] = "/tmp/packfs_demo_sandbox_XXXXXX"; char *sandbox = mkdtemp(tmpl); if (!sandbox) die("mkdtemp", 0); printf("sandbox root: %s\n", sandbox); Vfs *v2 = vfs_new(); int derr = 0; Backend *dir = backend_dir_new(sandbox, &derr); if (!dir) die("backend_dir_new", derr); vfs_mount(v2, "/data", dir); write_file(v2, "/data/inside.txt", "this file is contained to the sandbox root"); read_file(v2, "/data/inside.txt"); printf(" attempting /data/../../../etc/passwd (must be rejected) ...\n"); int err2 = 0; VfsFile *escape = vfs_open(v2, "/data/../../../etc/passwd", VFS_O_RDONLY, &err2); printf(" -> %s (error code %d)\n", escape ? "UNEXPECTEDLY OPENED (bug!)" : "rejected, as required", err2); if (escape) vfs_close(escape); vfs_unmount(v2, "/data"); backend_free(dir); vfs_free(v2); } printf("\n=== done ===\n"); printf("Re-run this program (same pack path) to see /hello.txt and\n" "/notes/todo.txt survive, and /config.json stay deleted.\n"); return 0; }