/* * packfs.h — public API of PackFS. * * PackFS is a statically linked, in-process virtual file system. The design * rationale for every decision in this header is recorded in concept.md, * which this implementation follows; concept.md is frozen (see CLAUDE.md) * and is not re-derived here. * * SPDX-License-Identifier: MIT */ #ifndef PACKFS_H #define PACKFS_H #include #include #ifdef __cplusplus extern "C" { #endif /* concept.md Section 8 leaves isize/usize as project-defined typedefs. */ typedef ptrdiff_t pfs_isize; typedef size_t pfs_usize; typedef struct Vfs Vfs; typedef struct VfsFile VfsFile; typedef struct Backend Backend; typedef enum { VFS_OK = 0, VFS_ERR_NOENT = -1, /* path does not exist */ VFS_ERR_EXIST = -2, /* path already exists */ VFS_ERR_NOTDIR = -3, /* path component is not a directory */ VFS_ERR_ISDIR = -4, /* operation not valid on a directory */ VFS_ERR_INVAL = -5, /* invalid argument (includes path escapes, see Section 6) */ VFS_ERR_IO = -6, /* host I/O error */ VFS_ERR_NOSPC = -7, /* out of space / allocation failure */ VFS_ERR_PERM = -8, /* operation not permitted (e.g. write to read-only backend) */ VFS_ERR_NOTEMPTY = -9, /* directory not empty */ VFS_ERR_CORRUPT = -10, /* pack failed integrity validation, Section 7 */ VFS_ERR_NOMOUNT = -11 /* no backend mounted for this path */ } VfsStatus; typedef enum { VFS_KIND_FILE = 0, VFS_KIND_DIR = 1 } VfsEntryKind; typedef struct VfsStat { pfs_usize size; int64_t mtime; /* seconds since epoch */ VfsEntryKind kind; } VfsStat; typedef struct VfsDirEntry { char name[256]; VfsEntryKind kind; } VfsDirEntry; typedef struct VfsDir { VfsDirEntry *entries; pfs_usize count; } VfsDir; #define VFS_O_RDONLY 0x00 #define VFS_O_WRONLY 0x01 #define VFS_O_RDWR 0x02 #define VFS_O_CREAT 0x04 #define VFS_O_TRUNC 0x08 /* --- VFS lifecycle -------------------------------------------------- */ Vfs *vfs_new(void); void vfs_free(Vfs *v); /* --- Backend constructors ------------------------------------------- * * Every backend is a Backend* handed to vfs_mount. Backends not currently * mounted anywhere may be freed with backend_free; a backend still mounted * must be unmounted first. */ /* mem: default writable layer (Section 3.1). */ Backend *backend_mem_new(void); /* dir: capability-scoped host directory, containment per Section 6. */ Backend *backend_dir_new(const char *host_path, int *err); /* pack: read-only image loaded and validated per Section 7. */ Backend *backend_pack_new(const char *pack_path, int *err); /* * overlay: copy-on-write overlay per Sections 4-5. `pack_path` is the pack * image backing the read-only lower layer: if the file exists it is * loaded and validated (Section 7) at mount time; NULL means "start with * an empty lower layer" (vfs_sync then requires a non-NULL path to have * been set — pass one via backend_overlay_new even for a fresh overlay * that has no pack file yet). `upper` must be a `mem` or `dir` backend, * not otherwise mounted; the overlay does not take ownership of it — free * it explicitly after unmounting the overlay. A journal at * `pack_path` + ".jnl" (Section 4.4) is replayed at mount time if present, * and rewritten (not appended past its watermark) on every vfs_sync * (Section 5.3). */ Backend *backend_overlay_new(const char *pack_path, Backend *upper, int *err); void backend_free(Backend *b); /* --- Mounting -------------------------------------------------------- * * The mount table is itself part of the atomically-swapped snapshot * (Section 5.3): vfs_mount/vfs_unmount are structural writes, serialized * against each other and against every other structural write, and never * observed mid-change by a concurrent path resolution. */ int vfs_mount(Vfs *v, const char *prefix, Backend *b); int vfs_unmount(Vfs *v, const char *prefix); /* --- File operations -------------------------------------------------- */ VfsFile *vfs_open(Vfs *v, const char *path, int flags, int *err); pfs_isize vfs_read(VfsFile *f, void *buf, pfs_usize n); pfs_isize vfs_write(VfsFile *f, const void *buf, pfs_usize n); int vfs_close(VfsFile *f); int vfs_stat(Vfs *v, const char *path, VfsStat *out); int vfs_readdir(Vfs *v, const char *path, VfsDir *out); void vfs_dir_free(VfsDir *d); int vfs_mkdir(Vfs *v, const char *path); int vfs_unlink(Vfs *v, const char *path); int vfs_rename(Vfs *v, const char *from, const char *to); /* * vfs_sync compacts the overlay mounted exactly at `overlay_prefix` * (Section 4.1 step 5, Section 5.3): it freezes the current upper * snapshot, writes a new pack, and replaces the journal with only the * records written after the watermark (Section 5.3, Section 4.4). */ int vfs_sync(Vfs *v, const char *overlay_prefix); /* * Optional, opt-in, process-wide Landlock hardening (Section 6.2). This * is deliberately NOT applied automatically by vfs_mount: Landlock * restrictions are cumulative, irreversible, and apply to the whole * calling process/thread, which would be a surprising side effect for an * embeddable library to trigger on its own. Call it once, after all `dir` * mounts the process will ever need are in place, if the embedding * application wants the whole process confined to exactly those * directories at the kernel level. Returns VFS_OK on success; if the * running kernel does not support Landlock, it returns VFS_ERR_PERM and * changes nothing — this is the documented residual-risk fallback of * Section 6.3, not a fatal error. */ int vfs_harden_process_with_landlock(Vfs *v); #ifdef __cplusplus } #endif #endif /* PACKFS_H */