Files
packfs/examples/demo.c
T

157 lines
5.6 KiB
C
Raw Normal View History

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}