SafeAPI Backend POSIX
POSIX/Linux OAL backend for safeAPIFreamwork
Loading...
Searching...
No Matches
sapi_posix_backend_nvm.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
2
30
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <sys/stat.h>
36#include <unistd.h>
37
38typedef struct
39{
40 int fd;
41 size_t region_size;
43
44/* C99-compatible static assert: posix_nvm_state_t must fit inside
45 * sapi_nvm_storage_t's reserved bytes. */
46typedef char posix_nvm_storage_fits_[(sizeof(posix_nvm_state_t) <= sizeof(sapi_nvm_storage_t)) ? 1 : -1];
47
48#define SAPI_POSIX_NVM_TRAILER_SIZE 8U
49#define SAPI_POSIX_NVM_PATH_MAX 256U
50#define SAPI_POSIX_NVM_CHUNK_SIZE 256U
51
52static const uint64_t FNV_OFFSET_BASIS = 0xcbf29ce484222325ULL;
53static const uint64_t FNV_PRIME = 0x100000001b3ULL;
54
55static uint64_t fnv1a64_update(uint64_t hash, const uint8_t *data, size_t len)
56{
57 size_t i;
58
59 for (i = 0U; i < len; i++)
60 {
61 hash ^= (uint64_t)data[i];
62 hash *= FNV_PRIME;
63 }
64 return hash;
65}
66
67static void hash_to_bytes(uint64_t hash, uint8_t out[SAPI_POSIX_NVM_TRAILER_SIZE])
68{
69 size_t i;
70
71 for (i = 0U; i < SAPI_POSIX_NVM_TRAILER_SIZE; i++)
72 {
73 out[i] = (uint8_t)((hash >> (8U * i)) & 0xFFU);
74 }
75}
76
77static uint64_t bytes_to_hash(const uint8_t in[SAPI_POSIX_NVM_TRAILER_SIZE])
78{
79 uint64_t hash = 0U;
80 size_t i;
81
82 for (i = 0U; i < SAPI_POSIX_NVM_TRAILER_SIZE; i++)
83 {
84 hash |= ((uint64_t)in[i]) << (8U * i);
85 }
86 return hash;
87}
88
92static bool read_at(int fd, size_t offset, void *buffer, size_t len)
93{
94 uint8_t *dst = (uint8_t *)buffer;
95 size_t done = 0U;
96
97 if (lseek(fd, (off_t)offset, SEEK_SET) < 0)
98 {
99 return false;
100 }
101 while (done < len)
102 {
103 ssize_t n = read(fd, dst + done, len - done);
104
105 if (n < 0)
106 {
107 if (errno == EINTR)
108 {
109 continue;
110 }
111 return false;
112 }
113 if (n == 0)
114 {
115 return false; /* short read: not enough data in the file */
116 }
117 done += (size_t)n;
118 }
119 return true;
120}
121
123static bool write_at(int fd, size_t offset, const void *buffer, size_t len)
124{
125 const uint8_t *src = (const uint8_t *)buffer;
126 size_t done = 0U;
127
128 if (lseek(fd, (off_t)offset, SEEK_SET) < 0)
129 {
130 return false;
131 }
132 while (done < len)
133 {
134 ssize_t n = write(fd, src + done, len - done);
135
136 if (n < 0)
137 {
138 if (errno == EINTR)
139 {
140 continue;
141 }
142 return false;
143 }
144 done += (size_t)n;
145 }
146 return true;
147}
148
151static bool compute_region_hash(int fd, size_t region_size, uint64_t *out_hash)
152{
153 uint8_t chunk[SAPI_POSIX_NVM_CHUNK_SIZE];
154 uint64_t hash = FNV_OFFSET_BASIS;
155 size_t remaining = region_size;
156 size_t offset = 0U;
157
158 while (remaining > 0U)
159 {
160 size_t this_chunk = (remaining < sizeof(chunk)) ? remaining : sizeof(chunk);
161
162 if (!read_at(fd, offset, chunk, this_chunk))
163 {
164 return false;
165 }
166 hash = fnv1a64_update(hash, chunk, this_chunk);
167 offset += this_chunk;
168 remaining -= this_chunk;
169 }
170
171 *out_hash = hash;
172 return true;
173}
174
176static bool rewrite_trailer(int fd, size_t region_size)
177{
178 uint64_t hash;
179 uint8_t trailer[SAPI_POSIX_NVM_TRAILER_SIZE];
180
181 if (!compute_region_hash(fd, region_size, &hash))
182 {
183 return false;
184 }
185 hash_to_bytes(hash, trailer);
186 return write_at(fd, region_size, trailer, sizeof(trailer));
187}
188
189static sapi_status_t backend_open(sapi_nvm_storage_t *storage, const sapi_nvm_config_t *config,
190 sapi_nvm_handle_t *out_handle)
191{
192 posix_nvm_state_t *state;
193 char path[SAPI_POSIX_NVM_PATH_MAX];
194 const char *dir;
195 int written;
196 int fd;
197 struct stat st;
198 off_t expected_size;
199
200 if ((storage == NULL) || (config == NULL) || (out_handle == NULL) || (config->region_name == NULL)
201 || (config->region_size == 0U))
202 {
203 return SAPI_STATUS_INVALID_PARAM;
204 }
205
206 dir = getenv("SAPI_POSIX_NVM_DIR");
207 if (dir == NULL)
208 {
209 dir = ".";
210 }
211 written = snprintf(path, sizeof(path), "%s/sapi_nvm_%s.dat", dir, config->region_name);
212 if ((written < 0) || ((size_t)written >= sizeof(path)))
213 {
214 return SAPI_STATUS_INVALID_PARAM;
215 }
216
217 fd = open(path, O_RDWR | O_CREAT, 0600);
218 if (fd < 0)
219 {
220 return SAPI_STATUS_INTERNAL_ERROR;
221 }
222
223 expected_size = (off_t)(config->region_size + SAPI_POSIX_NVM_TRAILER_SIZE);
224 if ((fstat(fd, &st) != 0) || (st.st_size != expected_size))
225 {
226 /* New or mismatched-size file: (re)initialize as an empty,
227 * freshly-hashed region rather than trusting stale/partial
228 * content of the wrong size. */
229 if ((ftruncate(fd, 0) != 0) || (ftruncate(fd, expected_size) != 0))
230 {
231 (void)close(fd);
232 return SAPI_STATUS_INTERNAL_ERROR;
233 }
234 if (!rewrite_trailer(fd, config->region_size))
235 {
236 (void)close(fd);
237 return SAPI_STATUS_INTERNAL_ERROR;
238 }
239 }
240
241 state = (posix_nvm_state_t *)(void *)storage;
242 state->fd = fd;
243 state->region_size = config->region_size;
244 *out_handle = (sapi_nvm_handle_t)(void *)storage;
245 return SAPI_STATUS_OK;
246}
247
248static sapi_status_t backend_read(sapi_nvm_handle_t handle, size_t offset, void *out_buffer, size_t buffer_size)
249{
250 posix_nvm_state_t *state = (posix_nvm_state_t *)(void *)handle;
251 uint64_t computed_hash;
252 uint64_t stored_hash;
253 uint8_t trailer[SAPI_POSIX_NVM_TRAILER_SIZE];
254
255 if ((state == NULL) || (out_buffer == NULL) || (buffer_size == 0U))
256 {
257 return SAPI_STATUS_INVALID_PARAM;
258 }
259 if ((offset > state->region_size) || (buffer_size > (state->region_size - offset)))
260 {
261 return SAPI_STATUS_INVALID_PARAM;
262 }
263
264 if (!compute_region_hash(state->fd, state->region_size, &computed_hash)
265 || !read_at(state->fd, state->region_size, trailer, sizeof(trailer)))
266 {
267 return SAPI_STATUS_INTERNAL_ERROR;
268 }
269 stored_hash = bytes_to_hash(trailer);
270 if (computed_hash != stored_hash)
271 {
272 return SAPI_STATUS_DATA_CORRUPTION;
273 }
274
275 if (!read_at(state->fd, offset, out_buffer, buffer_size))
276 {
277 return SAPI_STATUS_INTERNAL_ERROR;
278 }
279 return SAPI_STATUS_OK;
280}
281
282static sapi_status_t backend_write(sapi_nvm_handle_t handle, size_t offset, const void *buffer, size_t buffer_size)
283{
284 posix_nvm_state_t *state = (posix_nvm_state_t *)(void *)handle;
285
286 if ((state == NULL) || (buffer == NULL) || (buffer_size == 0U))
287 {
288 return SAPI_STATUS_INVALID_PARAM;
289 }
290 if ((offset > state->region_size) || (buffer_size > (state->region_size - offset)))
291 {
292 return SAPI_STATUS_INVALID_PARAM;
293 }
294
295 if (!write_at(state->fd, offset, buffer, buffer_size))
296 {
297 return SAPI_STATUS_INTERNAL_ERROR;
298 }
299 if (!rewrite_trailer(state->fd, state->region_size))
300 {
301 return SAPI_STATUS_INTERNAL_ERROR;
302 }
303 return SAPI_STATUS_OK;
304}
305
306static sapi_status_t backend_sync(sapi_nvm_handle_t handle)
307{
308 posix_nvm_state_t *state = (posix_nvm_state_t *)(void *)handle;
309
310 if (state == NULL)
311 {
312 return SAPI_STATUS_INVALID_PARAM;
313 }
314 if (fsync(state->fd) != 0)
315 {
316 return SAPI_STATUS_INTERNAL_ERROR;
317 }
318 return SAPI_STATUS_OK;
319}
320
321static sapi_status_t backend_close(sapi_nvm_handle_t handle)
322{
323 posix_nvm_state_t *state = (posix_nvm_state_t *)(void *)handle;
324
325 if (state == NULL)
326 {
327 return SAPI_STATUS_INVALID_PARAM;
328 }
329 (void)close(state->fd);
330 return SAPI_STATUS_OK;
331}
332
333static const sapi_nvm_backend_t s_posix_nvm_backend = { backend_open, backend_read, backend_write, backend_sync,
334 backend_close };
335
336const sapi_nvm_backend_t *sapi_posix_backend_nvm(void)
337{
338 return &s_posix_nvm_backend;
339}
Real POSIX/Linux backend for every OAL service (ADR-018).
const sapi_nvm_backend_t * sapi_posix_backend_nvm(void)
The POSIX sapi_nvm backend (file-backed, FNV-1a-64 integrity).
static bool write_at(int fd, size_t offset, const void *buffer, size_t len)
static bool rewrite_trailer(int fd, size_t region_size)
static bool compute_region_hash(int fd, size_t region_size, uint64_t *out_hash)
static bool read_at(int fd, size_t offset, void *buffer, size_t len)