1#define _POSIX_C_SOURCE 200809L
46typedef char posix_nvm_storage_fits_[(
sizeof(
posix_nvm_state_t) <=
sizeof(sapi_nvm_storage_t)) ? 1 : -1];
48#define SAPI_POSIX_NVM_TRAILER_SIZE 8U
49#define SAPI_POSIX_NVM_PATH_MAX 256U
50#define SAPI_POSIX_NVM_CHUNK_SIZE 256U
52static const uint64_t FNV_OFFSET_BASIS = 0xcbf29ce484222325ULL;
53static const uint64_t FNV_PRIME = 0x100000001b3ULL;
55static uint64_t fnv1a64_update(uint64_t hash,
const uint8_t *data,
size_t len)
59 for (i = 0U; i < len; i++)
61 hash ^= (uint64_t)data[i];
67static void hash_to_bytes(uint64_t hash, uint8_t out[SAPI_POSIX_NVM_TRAILER_SIZE])
71 for (i = 0U; i < SAPI_POSIX_NVM_TRAILER_SIZE; i++)
73 out[i] = (uint8_t)((hash >> (8U * i)) & 0xFFU);
77static uint64_t bytes_to_hash(
const uint8_t in[SAPI_POSIX_NVM_TRAILER_SIZE])
82 for (i = 0U; i < SAPI_POSIX_NVM_TRAILER_SIZE; i++)
84 hash |= ((uint64_t)in[i]) << (8U * i);
92static bool read_at(
int fd,
size_t offset,
void *buffer,
size_t len)
94 uint8_t *dst = (uint8_t *)buffer;
97 if (lseek(fd, (off_t)offset, SEEK_SET) < 0)
103 ssize_t n = read(fd, dst + done, len - done);
123static bool write_at(
int fd,
size_t offset,
const void *buffer,
size_t len)
125 const uint8_t *src = (
const uint8_t *)buffer;
128 if (lseek(fd, (off_t)offset, SEEK_SET) < 0)
134 ssize_t n = write(fd, src + done, len - done);
153 uint8_t chunk[SAPI_POSIX_NVM_CHUNK_SIZE];
154 uint64_t hash = FNV_OFFSET_BASIS;
155 size_t remaining = region_size;
158 while (remaining > 0U)
160 size_t this_chunk = (remaining <
sizeof(chunk)) ? remaining :
sizeof(chunk);
162 if (!
read_at(fd, offset, chunk, this_chunk))
166 hash = fnv1a64_update(hash, chunk, this_chunk);
167 offset += this_chunk;
168 remaining -= this_chunk;
179 uint8_t trailer[SAPI_POSIX_NVM_TRAILER_SIZE];
185 hash_to_bytes(hash, trailer);
186 return write_at(fd, region_size, trailer,
sizeof(trailer));
189static sapi_status_t backend_open(sapi_nvm_storage_t *storage,
const sapi_nvm_config_t *config,
190 sapi_nvm_handle_t *out_handle)
193 char path[SAPI_POSIX_NVM_PATH_MAX];
200 if ((storage == NULL) || (config == NULL) || (out_handle == NULL) || (config->region_name == NULL)
201 || (config->region_size == 0U))
203 return SAPI_STATUS_INVALID_PARAM;
206 dir = getenv(
"SAPI_POSIX_NVM_DIR");
211 written = snprintf(path,
sizeof(path),
"%s/sapi_nvm_%s.dat", dir, config->region_name);
212 if ((written < 0) || ((
size_t)written >=
sizeof(path)))
214 return SAPI_STATUS_INVALID_PARAM;
217 fd = open(path, O_RDWR | O_CREAT, 0600);
220 return SAPI_STATUS_INTERNAL_ERROR;
223 expected_size = (off_t)(config->region_size + SAPI_POSIX_NVM_TRAILER_SIZE);
224 if ((fstat(fd, &st) != 0) || (st.st_size != expected_size))
229 if ((ftruncate(fd, 0) != 0) || (ftruncate(fd, expected_size) != 0))
232 return SAPI_STATUS_INTERNAL_ERROR;
237 return SAPI_STATUS_INTERNAL_ERROR;
243 state->region_size = config->region_size;
244 *out_handle = (sapi_nvm_handle_t)(
void *)storage;
245 return SAPI_STATUS_OK;
248static sapi_status_t backend_read(sapi_nvm_handle_t handle,
size_t offset,
void *out_buffer,
size_t buffer_size)
251 uint64_t computed_hash;
252 uint64_t stored_hash;
253 uint8_t trailer[SAPI_POSIX_NVM_TRAILER_SIZE];
255 if ((state == NULL) || (out_buffer == NULL) || (buffer_size == 0U))
257 return SAPI_STATUS_INVALID_PARAM;
259 if ((offset > state->region_size) || (buffer_size > (state->region_size - offset)))
261 return SAPI_STATUS_INVALID_PARAM;
265 || !
read_at(state->fd, state->region_size, trailer,
sizeof(trailer)))
267 return SAPI_STATUS_INTERNAL_ERROR;
269 stored_hash = bytes_to_hash(trailer);
270 if (computed_hash != stored_hash)
272 return SAPI_STATUS_DATA_CORRUPTION;
275 if (!
read_at(state->fd, offset, out_buffer, buffer_size))
277 return SAPI_STATUS_INTERNAL_ERROR;
279 return SAPI_STATUS_OK;
282static sapi_status_t backend_write(sapi_nvm_handle_t handle,
size_t offset,
const void *buffer,
size_t buffer_size)
286 if ((state == NULL) || (buffer == NULL) || (buffer_size == 0U))
288 return SAPI_STATUS_INVALID_PARAM;
290 if ((offset > state->region_size) || (buffer_size > (state->region_size - offset)))
292 return SAPI_STATUS_INVALID_PARAM;
295 if (!
write_at(state->fd, offset, buffer, buffer_size))
297 return SAPI_STATUS_INTERNAL_ERROR;
301 return SAPI_STATUS_INTERNAL_ERROR;
303 return SAPI_STATUS_OK;
306static sapi_status_t backend_sync(sapi_nvm_handle_t handle)
312 return SAPI_STATUS_INVALID_PARAM;
314 if (fsync(state->fd) != 0)
316 return SAPI_STATUS_INTERNAL_ERROR;
318 return SAPI_STATUS_OK;
321static sapi_status_t backend_close(sapi_nvm_handle_t handle)
327 return SAPI_STATUS_INVALID_PARAM;
329 (void)close(state->fd);
330 return SAPI_STATUS_OK;
333static const sapi_nvm_backend_t s_posix_nvm_backend = { backend_open, backend_read, backend_write, backend_sync,
338 return &s_posix_nvm_backend;
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)