ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fmt_host_spool.c
Go to the documentation of this file.
1
10
11#define _POSIX_C_SOURCE (200809L)
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stddef.h>
16#include <stdint.h>
17#include <string.h>
18#include <sys/stat.h>
19#include <sys/types.h>
20#include <unistd.h>
21
22#include "ra8_attributes.h"
25
27typedef enum : uint32_t {
30 k_spool_mode = 0600U,
33
50static ra8_err_t internal_parent(const char* path, char parent[k_ra8_fmt_host_path_cap])
51{
52 const size_t len = (path == nullptr) ? 0U : strlen(path);
53 if ((len == 0U) || (len >= (size_t)k_ra8_fmt_host_path_cap)) {
55 }
56 const char* slash = strrchr(path, '/');
57 size_t take = 1U;
58 const char* text = ".";
59 if (slash != nullptr) {
60 text = path;
61 take = (slash == path) ? 1U : (size_t)(slash - path);
62 }
63 (void)memcpy(parent, text, take);
64 parent[take] = '\0';
65 return k_ra8_ok;
66}
67
85static ra8_err_t internal_decimal(char name[k_ra8_fmt_host_name_cap], size_t* len, uint64_t value)
86{
87 char reverse[k_spool_digits];
88 size_t count = 0U;
89 do {
90 reverse[count++] = (char)('0' + (char)(value % k_spool_radix));
91 value /= k_spool_radix;
92 } while (value != 0U);
93 if ((*len + count + 1U) > (size_t)k_ra8_fmt_host_name_cap) {
95 }
96 while (count != 0U) {
97 name[(*len)++] = reverse[--count];
98 }
99 name[*len] = '\0';
100 return k_ra8_ok;
101}
102
119static ra8_err_t internal_name(uint32_t attempt, char name[k_ra8_fmt_host_name_cap])
120{
121 static const char prefix[] = ".ra8spool.";
122 size_t len = sizeof(prefix) - 1U;
123 (void)memcpy(name, prefix, len);
124 name[len] = '\0';
125 ra8_err_t rc = internal_decimal(name, &len, (uint64_t)getpid());
126 if ((rc == k_ra8_ok) && ((len + 2U) <= (size_t)k_ra8_fmt_host_name_cap)) {
127 name[len++] = '.';
128 name[len] = '\0';
129 rc = internal_decimal(name, &len, attempt);
130 }
131 return rc;
132}
133
152static ra8_err_t internal_append(void* ctx, const uint8_t* bytes, size_t len)
153{
155 if ((state == nullptr) || (state->fd < 0) || state->sealed ||
156 ((bytes == nullptr) && (len != 0U)) || ((uint64_t)len > (UINT64_MAX - state->position))) {
158 }
159 size_t done = 0U;
160 while (done < len) {
161 const ssize_t rc = pwrite(state->fd, &bytes[done], len - done, (off_t)(state->position + done));
162 if (rc > 0) {
163 done += (size_t)rc;
164 } else if ((rc < 0) && (errno == EINTR)) {
165 continue;
166 } else {
167 return k_ra8_fail;
168 }
169 }
170 state->position += len;
171 return k_ra8_ok;
172}
173
191static ra8_err_t internal_seal(void* ctx, uint64_t expected_size)
192{
194 if ((state == nullptr) || (state->fd < 0) || state->sealed) {
196 }
197 struct stat status = {};
198 if ((state->position != expected_size) || (fstat(state->fd, &status) != 0) ||
199 ((uint64_t)status.st_size != expected_size)) {
201 }
202 if (fsync(state->fd) != 0) {
203 return k_ra8_fail;
204 }
205 state->sealed = true;
206 return k_ra8_ok;
207}
208
229static ra8_err_t internal_read(void* ctx, uint64_t offset, uint8_t* bytes, size_t len, size_t* got)
230{
232 if ((state == nullptr) || (got == nullptr) || (state->fd < 0) || !state->sealed ||
233 ((bytes == nullptr) && (len != 0U))) {
235 }
236 *got = 0U;
237 if ((offset >= state->position) || (len == 0U)) {
238 return k_ra8_ok;
239 }
240 const uint64_t remain = state->position - offset;
241 if ((uint64_t)len > remain) {
242 len = (size_t)remain;
243 }
244 while (*got < len) {
245 const ssize_t rc = pread(state->fd, &bytes[*got], len - *got, (off_t)(offset + *got));
246 if (rc > 0) {
247 *got += (size_t)rc;
248 } else if (rc == 0) {
249 break;
250 } else if (errno != EINTR) {
251 return k_ra8_fail;
252 }
253 }
254 return k_ra8_ok;
255}
256
274static ra8_err_t internal_create(int parent_fd, ra8_fmt_host_spool_t* state)
275{
276 char name[k_ra8_fmt_host_name_cap];
277 for (uint32_t attempt = 0U; attempt < k_spool_attempts; ++attempt) {
278 ra8_err_t rc = internal_name(attempt, name);
279 if (rc != k_ra8_ok) {
280 return rc;
281 }
282 state->fd = openat(parent_fd,
283 name,
284 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
285 (mode_t)k_spool_mode);
286 if (state->fd >= 0) {
287 if (unlinkat(parent_fd, name, 0) == 0) {
288 return k_ra8_ok;
289 }
290 (void)close(state->fd);
291 state->fd = -1;
292 (void)unlinkat(parent_fd, name, 0);
293 return k_ra8_fail;
294 }
295 if (errno != EEXIST) {
296 return k_ra8_fail;
297 }
298 }
299 return k_ra8_err_exists;
300}
301
304 ra8_fmt_spool_t* out)
305{
306 if ((anchor_path == nullptr) || (state == nullptr) || (out == nullptr)) {
307 return k_ra8_err_null_ptr;
308 }
309 *state = (ra8_fmt_host_spool_t){.fd = -1};
310 *out = (ra8_fmt_spool_t){};
311 char parent[k_ra8_fmt_host_path_cap];
312 ra8_err_t rc = internal_parent(anchor_path, parent);
313 if (rc != k_ra8_ok) {
314 return rc;
315 }
316 const int parent_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
317 if (parent_fd < 0) {
318 return k_ra8_fail;
319 }
320 rc = internal_create(parent_fd, state);
321 (void)close(parent_fd);
322 if (rc == k_ra8_ok) {
323 *out = (ra8_fmt_spool_t){
324 .read_at = internal_read,
325 .append = internal_append,
326 .seal = internal_seal,
327 .ctx = state,
328 };
329 }
330 return rc;
331}
332
334{
335 if ((state != nullptr) && (state->fd >= 0)) {
336 (void)close(state->fd);
337 state->fd = -1;
338 }
339}
static uint32_t internal_read(void)
Read current GPT counter ticks, or return UINT32_MAX on err.
Definition main.c:150
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
#define O_NOFOLLOW
Zero fallback paired with explicit no-follow metadata validation.
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
static ra8_err_t internal_read(void *ctx, void *file_state, uint8_t *dst, uint32_t cap, uint32_t *out_read)
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_fail
Generic unspecified failure.
Definition ra8_err.h:133
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Raw file-descriptor adapters for the portable format-tool contracts.
@ k_ra8_fmt_host_path_cap
Parent-path storage including NUL.
@ k_ra8_fmt_host_name_cap
One leaf name including NUL.
static ra8_err_t internal_append(void *ctx, const uint8_t *bytes, size_t len)
Append exactly to one unsealed anonymous descriptor.
static ra8_err_t internal_parent(const char *path, char parent[k_ra8_fmt_host_path_cap])
Copy the anchor parent into fixed storage.
static ra8_err_t internal_seal(void *ctx, uint64_t expected_size)
Seal an exact spool extent before positioned reads.
static ra8_err_t internal_create(int parent_fd, ra8_fmt_host_spool_t *state)
Create and immediately unlink one exclusive scratch leaf.
spool_const_t
Scratch creation and spelling bounds.
@ k_spool_attempts
Exclusive-create collision ceiling.
@ k_spool_radix
Decimal filename radix.
@ k_spool_digits
Digits in one uint64_t spelling.
@ k_spool_mode
Owner-only scratch permissions.
void priv_fmt_host_spool_close(ra8_fmt_host_spool_t *state)
Close an anonymous scratch artifact.
ra8_err_t priv_fmt_host_spool_open(const char *anchor_path, ra8_fmt_host_spool_t *state, ra8_fmt_spool_t *out)
Create an anonymous scratch file beside an anchored input path.
static ra8_err_t internal_decimal(char name[k_ra8_fmt_host_name_cap], size_t *len, uint64_t value)
Append one unsigned decimal into a bounded scratch name.
static ra8_err_t internal_name(uint32_t attempt, char name[k_ra8_fmt_host_name_cap])
Form one collision-bounded private scratch leaf.
Anonymous raw-fd scratch artifacts for portable format verification.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
char * strrchr(const char *s, int c)
Locate last occurrence of character in string.
static void internal_append(ra8_kbd_text_t *t, char ch)
Append character ch to the text buffer if capacity allows.
Caller-owned state for one unlinked scratch file.
int fd
Owned anonymous descriptor, or -1.
bool sealed
Positioned reads are now allowed.
uint64_t position
Bytes appended before sealing.
Caller-owned scratch artifact with append, seal, and read seams.