ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
wal_fs.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file wal_fs.h
6 * @brief Bind the WAL store's ::WalDev block-device seam to a file on a mounted store (PROTOCORE_ENABLE_WAL).
7 *
8 * The store in wal_store.h does all I/O through three function pointers so its logic stays pure and
9 * host-testable; this header is the thin adapter that points those pointers at a preallocated file on any
10 * ::protocore_mnt_backend. Random access is the backend's `seek`, and the durability barrier is its `sync`.
11 *
12 * Usage:
13 * @code
14 * const protocore_mnt_backend *store = protocore_mnt_active();
15 * protocore_wal_fs_prealloc(store, "/wal.bin", 256 * 1024); // once: fixed-size, zero-filled backing file
16 * protocore_wal_fs_ctx c;
17 * WalDev dev;
18 * if (protocore_wal_fs_open(&c, &dev, store, "/wal.bin", 256 * 1024))
19 * {
20 * WalStore s;
21 * protocore_wal_store_mount(&s, &dev) || protocore_wal_store_format(&s, &dev); // recover, or initialize
22 * }
23 * @endcode
24 *
25 * The backing file is preallocated to a fixed size so every store offset lands inside it and seek+write
26 * overwrites in place, rather than depending on past-EOF behavior that differs between FAT and littlefs.
27 * The ::protocore_wal_fs_ctx must outlive any ::WalDev bound to it.
28 *
29 * A backend with no `sync` is refused at open. The WAL's checkpoint is an ordering guarantee built on that
30 * barrier, so a store that cannot promise it cannot carry a power-loss-safe log, and saying so at mount is
31 * the only honest answer: a barrier that returns true having done nothing makes an unsafe store look safe.
32 */
33
34#ifndef PROTOCORE_WAL_FS_H
35#define PROTOCORE_WAL_FS_H
36
37#include "protocore_config.h"
38
39#if PROTOCORE_ENABLE_WAL
40
42
43#include "memoria_operor/memoria_operor.h"
44#include "server/storage/mnt/mnt.h" // protocore_mnt_backend - the store the log lives on
46
47/** @brief What the adapter needs to reach one open file: the store and the handle it returned. */
48typedef struct
49{
50 const protocore_mnt_backend *fs;
51 int handle;
52} protocore_wal_fs_ctx;
53
54PROTOCORE_INLINE size_t protocore_wal_fs_read(void *ctx, uint64_t off, uint8_t *buf, size_t len)
55{
56 protocore_wal_fs_ctx *c = (protocore_wal_fs_ctx *)ctx;
57 if (!c->fs->seek(c->handle, off))
58 {
59 return 0;
60 }
61 int n = c->fs->read(c->handle, buf, len);
62 return n < 0 ? 0 : (size_t)n;
63}
64
65PROTOCORE_INLINE size_t protocore_wal_fs_write(void *ctx, uint64_t off, const uint8_t *buf, size_t len)
66{
67 protocore_wal_fs_ctx *c = (protocore_wal_fs_ctx *)ctx;
68 if (!c->fs->seek(c->handle, off))
69 {
70 return 0;
71 }
72 int n = c->fs->write(c->handle, buf, len);
73 return n < 0 ? 0 : (size_t)n;
74}
75
76PROTOCORE_INLINE proto_bool protocore_wal_fs_sync(void *ctx)
77{
78 protocore_wal_fs_ctx *c = (protocore_wal_fs_ctx *)ctx;
79 return c->fs->sync(c->handle);
80}
81
82/**
83 * @brief Ensure @p path on @p fs exists and is at least @p size bytes (created zero-filled if missing/short).
84 * @return true on success. Call once before opening the file for the store.
85 */
86PROTOCORE_INLINE proto_bool protocore_wal_fs_prealloc(const protocore_mnt_backend *fs, const char *path, uint64_t size)
87{
88 if (!fs || !path)
89 {
90 return PROTO_FALSE;
91 }
92 if (fs->exists(path) && fs->size(path) >= 0 && (uint64_t)fs->size(path) >= size)
93 {
94 return PROTO_TRUE;
95 }
96 int h = fs->open(path, PROTOCORE_MNT_WRITE); // create / truncate
97 if (h < 0)
98 {
99 return PROTO_FALSE;
100 }
101 uint8_t z[256];
102 EMBED_CALL(memor.set, MemoriaCfg, .dst = z, .val = 0, .bytes = sizeof z);
103 uint64_t left = size;
105 while (left)
106 {
107 size_t n = left < sizeof z ? (size_t)left : sizeof z;
108 if (fs->write(h, z, n) != (int)n)
109 {
110 ok = PROTO_FALSE;
111 break;
112 }
113 left -= n;
114 }
115 if (ok && fs->sync)
116 {
117 ok = fs->sync(h);
118 }
119 fs->close(h);
120 return ok;
121}
122
123/**
124 * @brief Open @p path on @p fs and build a ::WalDev over it as a @p size-byte block device.
125 *
126 * @param c filled with the store and handle; must outlive @p dev and any ::WalStore mounted on it.
127 * @param dev filled with the three function pointers and @p size.
128 * @return false if @p fs has no durability barrier, or the file will not open. On false, nothing is
129 * left open and @p dev is not usable.
130 */
131PROTOCORE_INLINE proto_bool protocore_wal_fs_open(protocore_wal_fs_ctx *c, WalDev *dev, const protocore_mnt_backend *fs,
132 const char *path, uint64_t size)
133{
134 if (!c || !dev || !fs || !path || !fs->sync)
135 {
136 return PROTO_FALSE;
137 }
138 int h = fs->open(path, PROTOCORE_MNT_RDWR); // random read+write over the preallocated extent, no truncation
139 if (h < 0)
140 {
141 return PROTO_FALSE;
142 }
143 c->fs = fs;
144 c->handle = h;
145 dev->read = protocore_wal_fs_read;
146 dev->write = protocore_wal_fs_write;
147 dev->sync = protocore_wal_fs_sync;
148 dev->ctx = c;
149 dev->size = size;
150 return PROTO_TRUE;
151}
152
153/** @brief Close the file a ::protocore_wal_fs_ctx holds. The ::WalDev built over it is dead after this. */
154PROTOCORE_INLINE void protocore_wal_fs_close(protocore_wal_fs_ctx *c)
155{
156 if (c && c->fs)
157 {
158 c->fs->close(c->handle);
159 c->fs = NULL;
160 c->handle = -1;
161 }
162}
163
165
166#endif // PROTOCORE_ENABLE_WAL
167
168#endif // PROTOCORE_WAL_FS_H
#define PROTOCORE_INLINE
Linkage for a leaf primitive whose body is cheaper than the call that reaches it.
The mount: which store is behind the filesystem, and the vtable it answers through.
@ PROTOCORE_MNT_WRITE
Create/truncate for writing.
Definition mnt.h:52
@ PROTOCORE_MNT_RDWR
Open existing for random read+write, no truncation (fails if absent).
Definition mnt.h:54
A storage backend. Each open call returns a small handle (>= 0) or -1.
Definition mnt.h:88
long(* size)(const char *path)
file size, or -1 if absent.
Definition mnt.h:94
proto_bool(* exists)(const char *path)
true if the path exists.
Definition mnt.h:95
int(* open)(const char *path, int mode)
-> handle (>=0) or -1.
Definition mnt.h:89
int(* write)(int handle, const void *buf, size_t n)
bytes written, or -1.
Definition mnt.h:91
void(* close)(int handle)
release a file OR directory handle.
Definition mnt.h:92
proto_bool(* sync)(int handle)
Push everything written to handle past the backend's own buffering, and report whether it got there....
Definition mnt.h:114
int(* read)(int handle, void *buf, size_t n)
bytes read, or -1.
Definition mnt.h:90
#define PROTO_FALSE
the false value
Definition types.h:68
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTO_TRUE
the true value, spelled so a caller never writes a bare 1
Definition types.h:67
#define PROTOCORE_END_DECLS
Definition types.h:97