ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reader_vmem_host.c
Go to the documentation of this file.
1
14
15#define _POSIX_C_SOURCE (200809L)
16
17#include <errno.h>
18#include <fcntl.h>
19#include <limits.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <stdio.h> /* POSIX renameat declaration; no hosted stream is used. */
23#include <string.h>
24#include <sys/types.h>
25#include <unistd.h>
26
28
29#ifndef O_CLOEXEC
31#define O_CLOEXEC (0)
32#endif
33
34#ifndef O_DIRECTORY
36#define O_DIRECTORY (0)
37#endif
38
39#ifndef O_NOFOLLOW
41#define O_NOFOLLOW (0)
42#endif
43
51
52void priv_rv_diag(const char* text)
53{
54 if (text == nullptr) {
55 return;
56 }
57 size_t offset = 0U;
58 const size_t length = strlen(text);
59 while (offset < length) {
60 const ssize_t put = write(STDERR_FILENO, &text[offset], length - offset);
61 if (put < 0 && errno == EINTR) {
62 continue;
63 }
64 if (put <= 0) {
65 return;
66 }
67 offset += (size_t)put;
68 }
69}
70
85RA8_INTERNAL static size_t internal_decimal(uint64_t value, char out[k_host_decimal_digits + 1U])
86{
87 char reverse[k_host_decimal_digits];
88 size_t digits = 0U;
89 do {
90 reverse[digits++] = (char)('0' + (value % (uint64_t)k_host_decimal_base));
91 value /= (uint64_t)k_host_decimal_base;
92 } while (value != 0U);
93 for (size_t i = 0U; i < digits; ++i) {
94 out[i] = reverse[digits - i - 1U];
95 }
96 out[digits] = '\0';
97 return digits;
98}
99
100void priv_rv_diag_u64(uint64_t value)
101{
102 char text[k_host_decimal_digits + 1U];
103 (void)internal_decimal(value, text);
104 priv_rv_diag(text);
105}
106
124RA8_INTERNAL static bool
125internal_pwrite_exact(int fd, uint64_t offset, const uint8_t* bytes, size_t length)
126{
127 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
128 return false;
129 }
130 size_t done = 0U;
131 while (done < length) {
132 size_t request = length - done;
133 if (request > (size_t)SSIZE_MAX) {
134 request = (size_t)SSIZE_MAX;
135 }
136 const ssize_t put = pwrite(fd, &bytes[done], request, (off_t)(offset + done));
137 if (put < 0 && errno == EINTR) {
138 continue;
139 }
140 if (put <= 0) {
141 return false;
142 }
143 done += (size_t)put;
144 }
145 return true;
146}
147
164RA8_INTERNAL static bool internal_split_path(const char* path,
165 char parent[k_rv_host_path_cap],
166 char leaf[k_rv_host_name_cap])
167{
168 const size_t length = strlen(path);
169 if (length == 0U || length >= (size_t)k_rv_host_path_cap) {
170 return false;
171 }
172 size_t slash = length;
173 while (slash > 0U && path[slash - 1U] != '/') {
174 --slash;
175 }
176 const size_t leaf_bytes = length - slash;
177 if (leaf_bytes == 0U || leaf_bytes >= (size_t)k_rv_host_name_cap) {
178 return false;
179 }
180 (void)memcpy(leaf, &path[slash], leaf_bytes);
181 leaf[leaf_bytes] = '\0';
182 if (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0) {
183 return false;
184 }
185 if (slash == 0U) {
186 (void)memcpy(parent, ".", 2U);
187 } else if (slash == 1U) {
188 (void)memcpy(parent, "/", 2U);
189 } else {
190 (void)memcpy(parent, path, slash - 1U);
191 parent[slash - 1U] = '\0';
192 }
193 return true;
194}
195
212RA8_INTERNAL static bool
213internal_temp_name(char out[k_rv_host_name_cap], uint64_t process, uint32_t attempt)
214{
215 static const char s_prefix[] = ".reader_vmem.tmp.";
216 char number[k_host_decimal_digits + 1U];
217 size_t offset = sizeof(s_prefix) - 1U;
218 (void)memcpy(out, s_prefix, offset);
219 const uint64_t values[2] = {process, attempt};
220 for (size_t field = 0U; field < 2U; ++field) {
221 const size_t digits = internal_decimal(values[field], number);
222 if (offset + digits + 2U > (size_t)k_rv_host_name_cap) {
223 return false;
224 }
225 (void)memcpy(&out[offset], number, digits);
226 offset += digits;
227 out[offset++] = (field == 0U) ? '.' : '\0';
228 }
229 return true;
230}
231
232bool priv_rv_trace_begin(const char* path, rv_trace_t* trace)
233{
234 if (path == nullptr || trace == nullptr) {
235 return false;
236 }
237 *trace = (rv_trace_t){.directory_fd = -1, .trace_fd = -1};
238 char parent[k_rv_host_path_cap];
239 if (!internal_split_path(path, parent, trace->final_name)) {
240 return false;
241 }
242 trace->directory_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
243 if (trace->directory_fd < 0) {
244 return false;
245 }
246 for (uint32_t attempt = 0U; attempt < (uint32_t)k_host_temp_attempts; ++attempt) {
247 if (!internal_temp_name(trace->temp_name, (uint64_t)getpid(), attempt)) {
248 break;
249 }
250 trace->trace_fd = openat(trace->directory_fd,
251 trace->temp_name,
252 O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
253 (mode_t)k_host_create_mode);
254 if (trace->trace_fd >= 0 || errno != EEXIST) {
255 break;
256 }
257 }
258 if (trace->trace_fd < 0) {
259 priv_rv_trace_abort(trace);
260 return false;
261 }
262 trace->temp_exists = true;
263 return true;
264}
265
266bool priv_rv_trace_reference(rv_trace_t* trace, uint32_t object_id, uint32_t frame)
267{
268 char line[(2U * k_host_decimal_digits) + 3U];
269 size_t length = internal_decimal(object_id, line);
270 line[length++] = ' ';
271 char number[k_host_decimal_digits + 1U];
272 const size_t frame_digits = internal_decimal(frame, number);
273 (void)memcpy(&line[length], number, frame_digits);
274 length += frame_digits;
275 line[length++] = '\n';
276 if (trace == nullptr || trace->trace_fd < 0 || trace->io_failed ||
277 !internal_pwrite_exact(trace->trace_fd, trace->offset, (const uint8_t*)line, length)) {
278 if (trace != nullptr) {
279 trace->io_failed = true;
280 }
281 return false;
282 }
283 trace->offset += length;
284 return true;
285}
286
288{
289 if (trace == nullptr || trace->trace_fd < 0 || trace->directory_fd < 0 || trace->io_failed) {
290 priv_rv_trace_abort(trace);
291 return false;
292 }
293 const bool file_synced = fsync(trace->trace_fd) == 0;
294 const bool file_closed = close(trace->trace_fd) == 0;
295 trace->trace_fd = -1;
296 if (!file_synced || !file_closed) {
297 priv_rv_trace_abort(trace);
298 return false;
299 }
300 if (renameat(trace->directory_fd, trace->temp_name, trace->directory_fd, trace->final_name) !=
301 0) {
302 priv_rv_trace_abort(trace);
303 return false;
304 }
305 trace->temp_exists = false;
306 const bool synced = fsync(trace->directory_fd) == 0;
307 (void)close(trace->directory_fd);
308 trace->directory_fd = -1;
309 return synced;
310}
311
313{
314 if (trace == nullptr) {
315 return;
316 }
317 if (trace->trace_fd >= 0) {
318 (void)close(trace->trace_fd);
319 trace->trace_fd = -1;
320 }
321 if (trace->temp_exists && trace->directory_fd >= 0) {
322 (void)unlinkat(trace->directory_fd, trace->temp_name, 0);
323 trace->temp_exists = false;
324 }
325 if (trace->directory_fd >= 0) {
326 (void)close(trace->directory_fd);
327 trace->directory_fd = -1;
328 }
329}
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
static bool internal_pwrite_exact(int fd, uint64_t offset, const uint8_t *bytes, size_t length)
Write an exact positioned range to a regular descriptor.
#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.
host_limit_t
Fixed I/O, naming, and formatting bounds.
@ k_host_temp_attempts
Exclusive-create retry bound.
@ k_host_decimal_base
Decimal conversion radix.
@ k_host_decimal_digits
Maximum uint64_t digits.
@ k_host_create_mode
Hosted output creation mode.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
int renameat(int old_dir_fd, const char *old_path, int new_dir_fd, const char *new_path)
Rename one directory-relative path to another atomically.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
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.
static const uint8_t s_prefix[k_ra8_net_provision_prefix_bytes]
Exact protocol prefix, including the first field separator.
static RA8_INTERNAL size_t internal_decimal(uint64_t value, char out[k_host_decimal_digits+1U])
Encode one unsigned value into a caller's fixed buffer.
bool priv_rv_trace_reference(rv_trace_t *trace, uint32_t object_id, uint32_t frame)
Append one exact object/frame reference to a trace transaction.
void priv_rv_diag_u64(uint64_t value)
Write one unsigned decimal diagnostic without stdio.
static RA8_INTERNAL bool internal_split_path(const char *path, char parent[k_rv_host_path_cap], char leaf[k_rv_host_name_cap])
Split a bounded output path into parent and leaf.
void priv_rv_diag(const char *text)
Write one best-effort diagnostic fragment to standard error.
static RA8_INTERNAL bool internal_temp_name(char out[k_rv_host_name_cap], uint64_t process, uint32_t attempt)
Build one hidden sibling-temporary leaf.
bool priv_rv_trace_begin(const char *path, rv_trace_t *trace)
Begin one unpublished same-directory trace transaction.
static RA8_INTERNAL bool internal_pwrite_exact(int fd, uint64_t offset, const uint8_t *bytes, size_t length)
Write an exact positioned byte range.
void priv_rv_trace_abort(rv_trace_t *trace)
Close and unlink an unpublished trace.
bool priv_rv_trace_commit(rv_trace_t *trace)
Durably and atomically publish a complete trace.
Private host sink and caller-owned workspace contracts for reader_vmem.
@ k_rv_host_path_cap
Hosted path capacity.
@ k_rv_host_name_cap
Hosted leaf capacity.
Caller-owned atomic trace-publication state.
int trace_fd
Temporary trace handle.
int directory_fd
Parent directory handle.
char temp_name[k_rv_host_name_cap]
Temporary leaf.
bool temp_exists
Temp cleanup guard.
bool io_failed
Sticky exact-I/O fault.
char final_name[k_rv_host_name_cap]
Destination leaf.
uint64_t offset
Next trace byte offset.