subsys/settings: stream-style encoding and decoding to/from storage

This patch reworks routines used to store and read the settings data.
Provide stream-style encoding and decoding to/from flash, so the the
API only requires a pointer to binary data, and the settings
implementation takes care of encoding/decoding to/from base64 and
writing/reading to/from flash on the fly. This would eliminate the
need of a separate base64 value buffer on the application-side, thereby
further contributing to the stack footprint reduction.

Above changes allows to remove:
  256-byte value length limitation.
  removing enum settings_type usage so all settings data are treated
  now as a byte array (i.e. what's previously SETTINGS_BYTES)

Introduced routine settings_val_read_cb for read and decode the
settings data from storage inside h_set handler implementations.
h_set settings handler now provide persistent value's context
used along with read routine instead of immediately value.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
This commit is contained in:
Andrzej Puzdrowski 2018-05-15 14:25:29 +02:00 committed by Carles Cufí
commit 76f8b97871
11 changed files with 929 additions and 541 deletions

View file

@ -8,6 +8,7 @@
#ifndef ZEPHYR_INCLUDE_SETTINGS_SETTINGS_H_
#define ZEPHYR_INCLUDE_SETTINGS_SETTINGS_H_
#include <sys/types.h>
#include <misc/util.h>
#include <misc/slist.h>
#include <stdint.h>
@ -34,76 +35,58 @@ extern "C" {
#define SETTINGS_NMGR_OP 0
/**
* Type of settings value.
*/
enum settings_type {
SETTINGS_NONE = 0,
SETTINGS_INT8,
SETTINGS_INT16,
SETTINGS_INT32,
SETTINGS_INT64,
SETTINGS_STRING,
SETTINGS_BYTES,
SETTINGS_FLOAT,
SETTINGS_DOUBLE,
SETTINGS_BOOL,
} __attribute__((__packed__));
/**
* Parameter to commit handler describing where data is going to.
*/
enum settings_export_tgt {
SETTINGS_EXPORT_PERSIST, /* Value is to be persisted. */
SETTINGS_EXPORT_SHOW /* Value is to be displayed. */
};
/**
* @struct settings_handler
* Config handlers for subtree implement a set of handler functions.
* These are registered using a call to settings_register().
* These are registered using a call to @ref settings_register.
*/
struct settings_handler {
sys_snode_t node;
/**< Linked list node info for module internal usage. */
char *name;
/**< Name of subtree. */
int (*h_get)(int argc, char **argv, char *val, int val_len_max);
/**< Get values handler of settings items identified by keyword names.
*
* @param settings_handler::node Linked list node info for module internal usage.
*
* @param settings_handler::name Name of subtree.
*
* @param settings_handler::h_get Get values handler of settings items
* identified by keyword names.Parameters:
* Parameters:
* - argc - count of item in argv.
* - argv - array of pointers to keyword names.
* - val - buffer for a value.
* - val_len_max - size of that buffer.
*
* @param settings_handler::h_set Set value handler of settings items
* identified by keyword names. Parameters:
* - argc - count of item in argv, argv - array of pointers to keyword names.
* - val- pointer to value to be set.
*
* @param settings_handler::h_commit This handler gets called after settings
* has been loaded in full. User might use it to apply setting to
* the application.
*
* @param settings_handler::h_export This gets called to dump all current
* settings items.
* This happens when settings_save() tries to save the settings. Parameters:
* - tgt: indicates where data is going to.
* - Export_function: the pointer to the internal function which appends
* a single key-value pair to persisted settings. Don't store duplicated
* value. The name is subtree/key string, val is the string with
* value.
*
* @remarks The User might limit a implementations of handler to serving only
* one keyword at one call - what will impose limit to get/set values using full
* subtree/key name.
*/
struct settings_handler {
sys_snode_t node;
char *name;
char *(*h_get)(int argc, char **argv, char *val, int val_len_max);
int (*h_set)(int argc, char **argv, char *val);
int (*h_set)(int argc, char **argv, void *value_ctx);
/**< Set value handler of settings items identified by keyword names.
*
* Parameters:
* - argc - count of item in argv, argv - array of pointers to keyword
* names.
* - value_ctx - pointer to the value contex which is used paramiter
* for data extracting routine (@ref settings_val_read_cb).
*/
int (*h_commit)(void);
int (*h_export)(int (*export_func)(const char *name, char *val),
enum settings_export_tgt tgt);
/**< This handler gets called after settings has been loaded in full.
* User might use it to apply setting to the application.
*/
int (*h_export)(int (*export_func)(const char *name, void *val,
size_t val_len));
/**< This gets called to dump all current settings items.
*
* This happens when @ref settings_save tries to save the settings.
* Parameters:
* - export_func: the pointer to the internal function which appends
* a single key-value pair to persisted settings. Don't store
* duplicated value. The name is subtree/key string, val is the string
* with value.
*
* @remarks The User might limit a implementations of handler to serving
* only one keyword at one call - what will impose limit to get/set
* values using full subtree/key name.
*/
};
/**
@ -148,29 +131,33 @@ int settings_save(void);
* changed value).
*
* @param name Name/key of the settings item.
* @param var Value of the settings item.
* @param value Pointer to the value of the settings item. This value will
* be transferred to the @ref settings_handler::h_export handler implementation.
* @param val_len Length of the value.
*
* @return 0 on success, non-zero on failure.
*/
int settings_save_one(const char *name, char *var);
int settings_save_one(const char *name, void *value, size_t val_len);
/**
* Set settings item identified by @p name to be value @p val_str.
* Set settings item identified by @p name to be value @p value.
* This finds the settings handler for this subtree and calls it's
* set handler.
*
* @param name Name/key of the settings item.
* @param val_str Value of the settings item.
* @param value Pointer to the value of the settings item. This value will
* be transferred to the @ref settings_handler::h_set handler implementation.
* @param len Length of value string.
*
* @return 0 on success, non-zero on failure.
*/
int settings_set_value(char *name, char *val_str);
int settings_set_value(char *name, void *value, size_t len);
/**
* Get value of settings item identified by @p name.
* This calls the settings handler h_get for the subtree.
*
* Configuration handler can copy the string to @p buf, the maximum
* Configuration handler should copy the string to @p buf, the maximum
* number of bytes it will copy is limited by @p buf_len.
*
* @param name Name/key of the settings item.
@ -180,10 +167,9 @@ int settings_set_value(char *name, char *val_str);
*
* @param buf_len size of buf.
*
* @return value will be pointer to beginning of the buf,
* except for string it will pointer to beginning of string source.
* @return Positive: Length of copied dat. Negative: -ERCODE
*/
char *settings_get_value(char *name, char *buf, int buf_len);
int settings_get_value(char *name, char *buf, int buf_len);
/**
* Call commit for all settings handler. This should apply all
@ -196,60 +182,20 @@ char *settings_get_value(char *name, char *buf, int buf_len);
int settings_commit(char *name);
/**
* Convenience routine for converting value passed as a string to native
* data type.
* Persistent data extracting routine.
*
* @param val_str Value of the settings item as string.
* @param type Type of the value to convert to.
* @param vp Pointer to variable to fill with the decoded value.
* @param maxlen the vp buffer size.
* This function read and decode data from non volatile storage to user buffer
* This function should be used inside set handler in order to read the settings
* data from backend storage.
*
* @return 0 on success, non-zero on failure.
* @param[in] value_ctx Data contex provided by the <p>h_set</p> handler.
* @param[out] buf Buffer for data read.
* @param[in] len Length of <p>buf</p>.
*
* @retval Negative value on failure. 0 and positive: Length of data loaded to
* the <p>buf</p>.
*/
int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
int maxlen);
/**
* Convenience routine for converting byte array passed as a base64
* encoded string.
*
* @param val_str Value of the settings item as string.
* @param vp Pointer to variable to fill with the decoded value.
* @param len Size of that variable. On return the number of bytes in the array.
*
* @return 0 on success, non-zero on failure.
*/
int settings_bytes_from_str(char *val_str, void *vp, int *len);
/**
* Convenience routine for converting native data type to a string.
*
* @param type Type of the value to convert from.
* @param vp Pointer to variable to convert.
* @param buf Buffer where string value will be stored.
* @param buf_len Size of the buffer.
*
* @return 0 on success, non-zero on failure.
*/
char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
int buf_len);
#define SETTINGS_STR_FROM_BYTES_LEN(len) (((len) * 4 / 3) + 4)
/**
* Convenience routine for converting byte array into a base64
* encoded string.
*
* @param vp Pointer to variable to convert.
* @param vp_len Number of bytes to convert.
* @param buf Buffer where string value will be stored.
* @param buf_len Size of the buffer.
*
* @return 0 on success, non-zero on failure.
*/
char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len);
#define SETTINGS_VALUE_SET(str, type, val) \
settings_value_from_str((str), (type), &(val), sizeof(val))
int settings_val_read_cb(void *value_ctx, void *buf, size_t len);
/**
* @} settings

View file

@ -16,6 +16,11 @@ menuconfig SETTINGS
It supports several back-ends to store and load serialized data from
and it can do so atomically for all involved modules.
# Hidden option to enable encoding length into settings entry
config SETTINGS_ENCODE_LEN
depends on SETTINGS
bool
choice
prompt "Storage back-end"
default SETTINGS_FCB if FCB
@ -32,6 +37,7 @@ config SETTINGS_FCB
config SETTINGS_FS
bool "File System"
depends on FILE_SYSTEM
select SETTINGS_ENCODE_LEN
help
Use a file system as a settings storage back-end.
endchoice

View file

@ -22,6 +22,7 @@ struct settings_fcb {
extern int settings_fcb_src(struct settings_fcb *cf);
extern int settings_fcb_dst(struct settings_fcb *cf);
void settings_mount_fcb_backend(struct settings_fcb *cf);
#ifdef __cplusplus
}

View file

@ -29,6 +29,8 @@ int settings_file_src(struct settings_file *cf);
/* settings saves go to a file */
int settings_file_dst(struct settings_file *cf);
void settings_mount_fs_backend(struct settings_file *cf);
#ifdef __cplusplus
}
#endif

View file

@ -8,25 +8,17 @@
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include "base64.h"
#include "settings/settings.h"
#include "settings_priv.h"
#include <zephyr/types.h>
/* mbedtls-base64 lib encodes data to null-terminated string */
#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
sys_slist_t settings_handlers;
static u8_t settings_cmd_inited;
void settings_store_init(void);
static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base);
static s64_t dec_to_s64(char *p_str, char **e_ptr);
void settings_init(void)
{
@ -102,234 +94,61 @@ static struct settings_handler *settings_parse_and_lookup(char *name,
return settings_handler_lookup(name_argv[0]);
}
int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
int maxlen)
{
s32_t val;
s64_t val64;
char *eptr;
if (!val_str) {
goto err;
}
switch (type) {
case SETTINGS_INT8:
case SETTINGS_INT16:
case SETTINGS_INT32:
case SETTINGS_BOOL:
val = strtol(val_str, &eptr, 0);
if (*eptr != '\0') {
goto err;
}
if (type == SETTINGS_BOOL) {
if (val < 0 || val > 1) {
goto err;
}
*(bool *)vp = val;
} else if (type == SETTINGS_INT8) {
if (val < INT8_MIN || val > UINT8_MAX) {
goto err;
}
*(int8_t *)vp = val;
} else if (type == SETTINGS_INT16) {
if (val < INT16_MIN || val > UINT16_MAX) {
goto err;
}
*(int16_t *)vp = val;
} else if (type == SETTINGS_INT32) {
*(s32_t *)vp = val;
}
break;
case SETTINGS_INT64:
val64 = dec_to_s64(val_str, &eptr);
if (*eptr != '\0') {
goto err;
}
*(s64_t *)vp = val64;
break;
case SETTINGS_STRING:
val = strlen(val_str);
if (val + 1 > maxlen) {
goto err;
}
strcpy(vp, val_str);
break;
default:
goto err;
}
return 0;
err:
return -EINVAL;
}
int settings_bytes_from_str(char *val_str, void *vp, int *len)
{
int err;
int rc;
err = base64_decode(vp, *len, &rc, val_str, strlen(val_str));
if (err) {
return -1;
}
*len = rc;
return 0;
}
char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
int buf_len)
{
s32_t val;
if (type == SETTINGS_STRING) {
return vp;
}
switch (type) {
case SETTINGS_INT8:
case SETTINGS_INT16:
case SETTINGS_INT32:
case SETTINGS_BOOL:
if (type == SETTINGS_BOOL) {
val = *(bool *)vp;
} else if (type == SETTINGS_INT8) {
val = *(int8_t *)vp;
} else if (type == SETTINGS_INT16) {
val = *(int16_t *)vp;
} else {
val = *(s32_t *)vp;
}
snprintf(buf, buf_len, "%ld", (long)val);
return buf;
case SETTINGS_INT64:
s64_to_dec(buf, buf_len, *(s64_t *)vp, 10);
return buf;
default:
return NULL;
}
}
static void u64_to_dec(char *ptr, int buf_len, u64_t value, int base)
{
u64_t t = 0U, res = 0U;
u64_t tmp = value;
int count = 0;
if (ptr == NULL) {
return;
}
if (tmp == 0) {
count++;
}
while (tmp > 0) {
tmp = tmp/base;
count++;
}
ptr += count;
*ptr = '\0';
do {
res = value - base * (t = value / base);
if (res < 10) {
*--ptr = '0' + res;
} else if ((res >= 10) && (res < 16)) {
*--ptr = 'A' - 10 + res;
}
value = t;
} while (value != 0);
}
static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base)
{
u64_t val64;
if (ptr == NULL || buf_len < 1) {
return;
}
if (value < 0) {
*ptr = '-';
ptr++;
buf_len--;
val64 = value * (-1);
} else {
val64 = value;
}
u64_to_dec(ptr, buf_len, val64, base);
}
static s64_t dec_to_s64(char *p_str, char **e_ptr)
{
u64_t val = 0U, prev_val = 0U;
bool neg = false;
int digit;
if (*p_str == '-') {
neg = true;
p_str++;
} else if (*p_str == '+') {
p_str++;
}
while (1) {
if (*p_str >= '0' && *p_str <= '9') {
digit = *p_str - '0';
} else {
break;
}
val *= 10;
val += digit;
/* this is only a fuse */
if (val < prev_val) {
break;
}
prev_val = val;
p_str++;
}
if (e_ptr != 0)
*e_ptr = p_str;
if (neg) {
return -val;
} else {
return val;
}
}
char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len)
{
if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
return NULL;
}
size_t enc_len;
base64_encode(buf, buf_len, &enc_len, vp, vp_len);
return buf;
}
int settings_set_value(char *name, char *val_str)
int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
u8_t is_runtime)
{
int name_argc;
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
struct settings_handler *ch;
struct read_value_cb_ctx value_ctx;
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
return -EINVAL;
}
return ch->h_set(name_argc - 1, &name_argv[1], val_str);
value_ctx.read_cb_ctx = val_read_cb_ctx;
value_ctx.off = off;
value_ctx.runtime = is_runtime;
return ch->h_set(name_argc - 1, &name_argv[1], (void *)&value_ctx);
}
/* API */
int settings_set_value(char *name, void *val_str, size_t len)
{
struct runtime_value_ctx rt_ctx;
rt_ctx.p_value = val_str;
rt_ctx.size = len;
return settings_set_value_priv(name, &rt_ctx, 0, 1);
}
/* API */
int settings_val_read_cb(void *value_ctx, void *buf, size_t len)
{
struct read_value_cb_ctx *value_context = value_ctx;
size_t len_read;
int rc;
struct runtime_value_ctx *rt_ctx;
if (value_context->runtime) {
rt_ctx = value_context->read_cb_ctx;
len_read = min(len, rt_ctx->size);
memcpy(buf, rt_ctx->p_value, len_read);
return len_read;
} else {
rc = settings_line_val_read(value_context->off, 0, buf, len,
&len_read,
value_context->read_cb_ctx);
}
if (rc == 0) {
return len_read;
}
return rc;
}
/*
@ -338,7 +157,7 @@ int settings_set_value(char *name, char *val_str)
* Return value will be pointer to beginning of that buffer,
* except for string it will pointer to beginning of string.
*/
char *settings_get_value(char *name, char *buf, int buf_len)
int settings_get_value(char *name, char *buf, int buf_len)
{
int name_argc;
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
@ -346,11 +165,11 @@ char *settings_get_value(char *name, char *buf, int buf_len)
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
return NULL;
return -EINVAL;
}
if (!ch->h_get) {
return NULL;
return -EINVAL;
}
return ch->h_get(name_argc - 1, &name_argv[1], buf, buf_len);
}

View file

@ -23,7 +23,7 @@ struct settings_fcb_load_cb_arg {
static int settings_fcb_load(struct settings_store *cs, load_cb cb,
void *cb_arg);
static int settings_fcb_save(struct settings_store *cs, const char *name,
const char *value);
const char *value, size_t val_len);
static struct settings_store_itf settings_fcb_itf = {
.csi_load = settings_fcb_load,
@ -79,33 +79,23 @@ int settings_fcb_dst(struct settings_fcb *cf)
static int settings_fcb_load_cb(struct fcb_entry_ctx *entry_ctx, void *arg)
{
struct settings_fcb_load_cb_arg *argp;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char *name_str;
char *val_str;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
int rc;
int len;
argp = (struct settings_fcb_load_cb_arg *)arg;
len = entry_ctx->loc.fe_data_len;
if (len >= sizeof(buf)) {
len = sizeof(buf) - 1;
}
rc = flash_area_read(entry_ctx->fap,
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf, len);
size_t len_read;
rc = settings_line_name_read(buf, sizeof(buf), &len_read,
(void *)&entry_ctx->loc);
if (rc) {
return 0;
}
buf[len] = '\0';
buf[len_read] = '\0';
rc = settings_line_parse(buf, &name_str, &val_str);
if (rc) {
return 0;
}
argp->cb(name_str, val_str, argp->cb_arg);
/*name, val-read_cb-ctx, val-off*/
/* take into account '=' separator after the name */
argp->cb(buf, (void *)&entry_ctx->loc, len_read + 1, argp->cb_arg);
return 0;
}
@ -125,64 +115,79 @@ static int settings_fcb_load(struct settings_store *cs, load_cb cb,
return 0;
}
static int settings_fcb_var_read(struct fcb_entry_ctx *entry_ctx, char *buf,
char **name, char **val)
static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
{
int rc;
struct fcb_entry_ctx *entry_ctx = ctx;
rc = flash_area_read(entry_ctx->fap,
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf,
entry_ctx->loc.fe_data_len);
if (rc) {
return rc;
if (off >= entry_ctx->loc.fe_data_len) {
return -EINVAL;
}
buf[entry_ctx->loc.fe_data_len] = '\0';
rc = settings_line_parse(buf, name, val);
return rc;
if ((off + *len) > entry_ctx->loc.fe_data_len) {
*len = entry_ctx->loc.fe_data_len - off;
}
return flash_area_read(entry_ctx->fap,
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off, buf,
*len);
}
static void settings_fcb_compress(struct settings_fcb *cf)
{
int rc;
char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
struct fcb_entry_ctx loc1;
struct fcb_entry_ctx loc2;
char *name1, *val1;
char *name2, *val2;
char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
int copy;
u8_t rbs;
rc = fcb_append_to_scratch(&cf->cf_fcb);
if (rc) {
return; /* XXX */
}
rbs = flash_area_align(cf->cf_fcb.fap);
loc1.fap = cf->cf_fcb.fap;
loc1.loc.fe_sector = NULL;
loc1.loc.fe_elem_off = 0;
while (fcb_getnext(&cf->cf_fcb, &loc1.loc) == 0) {
if (loc1.loc.fe_sector != cf->cf_fcb.f_oldest) {
break;
}
rc = settings_fcb_var_read(&loc1, buf1, &name1, &val1);
off_t val1_off;
rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
&loc1);
if (rc) {
continue;
}
if (!val1) {
/* No sense to copy empty entry from the oldest sector*/
if (val1_off + 1 == loc1.loc.fe_data_len) {
/* Lack of a value so the record is a deletion-record */
/* No sense to copy empty entry from */
/* the oldest sector */
continue;
}
loc2 = loc1;
copy = 1;
while (fcb_getnext(&cf->cf_fcb, &loc2.loc) == 0) {
rc = settings_fcb_var_read(&loc2, buf2, &name2, &val2);
off_t val2_off;
rc = settings_line_name_read(name2, sizeof(name2),
&val2_off, &loc2);
if (rc) {
continue;
}
if (!strcmp(name1, name2)) {
if ((val1_off == val2_off) &&
!memcmp(name1, name2, val1_off)) {
copy = 0;
break;
}
@ -194,17 +199,13 @@ static void settings_fcb_compress(struct settings_fcb *cf)
/*
* Can't find one. Must copy.
*/
rc = flash_area_read(loc1.fap, FCB_ENTRY_FA_DATA_OFF(loc1.loc),
buf1, loc1.loc.fe_data_len);
if (rc) {
continue;
}
rc = fcb_append(&cf->cf_fcb, loc1.loc.fe_data_len, &loc2.loc);
if (rc) {
continue;
}
rc = flash_area_write(loc2.fap, FCB_ENTRY_FA_DATA_OFF(loc2.loc),
buf1, loc1.loc.fe_data_len);
rc = settings_entry_copy(&loc2, 0, &loc1, 0,
loc1.loc.fe_data_len);
if (rc) {
continue;
}
@ -216,14 +217,35 @@ static void settings_fcb_compress(struct settings_fcb *cf)
__ASSERT(rc == 0, "Failed to fcb rotate.\n");
}
static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len)
static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
{
struct fcb_entry_ctx *entry_ctx = ctx;
return flash_area_write(entry_ctx->fap,
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off,
buf, len);
}
/* ::csi_save implementation */
static int settings_fcb_save(struct settings_store *cs, const char *name,
const char *value, size_t val_len)
{
struct settings_fcb *cf = (struct settings_fcb *)cs;
struct fcb_entry_ctx loc;
int len;
int rc;
int i;
struct fcb_entry loc;
u8_t wbs;
for (i = 0; i < 10; i++) {
rc = fcb_append(&cf->cf_fcb, len, &loc);
if (!name) {
return -EINVAL;
}
wbs = flash_area_align(cf->cf_fcb.fap);
len = settings_line_len_calc(name, val_len);
for (i = 0; i < CONFIG_SETTINGS_FCB_NUM_AREAS; i++) {
rc = fcb_append(&cf->cf_fcb, len, &loc.loc);
if (rc != FCB_ERR_NOSPACE) {
break;
}
@ -233,29 +255,24 @@ static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len)
return -EINVAL;
}
rc = flash_area_write(cf->cf_fcb.fap, FCB_ENTRY_FA_DATA_OFF(loc),
buf, len);
if (rc) {
return -EINVAL;
loc.fap = cf->cf_fcb.fap;
rc = settings_line_write(name, value, val_len, 0, (void *)&loc);
if (rc != -EIO) {
i = fcb_append_finish(&cf->cf_fcb, &loc.loc);
if (!rc) {
rc = i;
}
return fcb_append_finish(&cf->cf_fcb, &loc);
}
return rc;
}
static int settings_fcb_save(struct settings_store *cs, const char *name,
const char *value)
void settings_mount_fcb_backend(struct settings_fcb *cf)
{
struct settings_fcb *cf = (struct settings_fcb *)cs;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
int len;
u8_t rbs;
if (!name) {
return -EINVAL;
}
rbs = cf->cf_fcb.f_align;
len = settings_line_make(buf, sizeof(buf), name, value);
if (len < 0 || len + 2 > sizeof(buf)) {
return -EINVAL;
}
return settings_fcb_append(cf, buf, len);
settings_line_io_init(read_handler, write_handler, rbs);
}

View file

@ -18,7 +18,7 @@
static int settings_file_load(struct settings_store *cs, load_cb cb,
void *cb_arg);
static int settings_file_save(struct settings_store *cs, const char *name,
const char *value);
const char *value, size_t val_len);
static struct settings_store_itf settings_file_itf = {
.csi_load = settings_file_load,
@ -50,40 +50,6 @@ int settings_file_dst(struct settings_file *cf)
return 0;
}
int settings_getnext_line(struct fs_file_t *file, char *buf, int blen,
off_t *loc)
{
int rc;
char *end;
rc = fs_seek(file, *loc, FS_SEEK_SET);
if (rc < 0) {
*loc = 0;
return -1;
}
rc = fs_read(file, buf, blen);
if (rc <= 0) {
*loc = 0;
return -1;
}
if (rc == blen) {
rc--;
}
buf[rc] = '\0';
end = strchr(buf, '\n');
if (end) {
*end = '\0';
} else {
end = strchr(buf, '\0');
}
blen = end - buf;
*loc += (blen + 1);
return blen;
}
/*
* Called to load configuration items. cb must be called for every configuration
* item found.
@ -92,37 +58,53 @@ static int settings_file_load(struct settings_store *cs, load_cb cb,
void *cb_arg)
{
struct settings_file *cf = (struct settings_file *)cs;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
struct fs_dirent file_info;
struct fs_file_t file;
off_t loc;
char tmpbuf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char *name_str;
char *val_str;
int rc;
size_t len_read;
int lines;
int rc;
struct line_entry_ctx entry_ctx = {
.stor_ctx = (void *)&file,
.seek = 0,
.len = 0 /* unknown length */
};
lines = 0;
rc = fs_stat(cf->cf_name, &file_info);
if (rc) {
return rc;
}
rc = fs_open(&file, cf->cf_name);
if (rc != 0) {
return -EINVAL;
}
loc = 0;
lines = 0;
while (1) {
rc = settings_getnext_line(&file, tmpbuf, sizeof(tmpbuf), &loc);
if (loc == 0) {
rc = settings_next_line_ctx(&entry_ctx);
if (rc || entry_ctx.len == 0) {
break;
}
if (rc < 0) {
continue;
}
rc = settings_line_parse(tmpbuf, &name_str, &val_str);
if (rc != 0) {
continue;
rc = settings_line_name_read(buf, sizeof(buf), &len_read,
(void *)&entry_ctx);
if (rc || len_read == 0) {
break;
}
buf[len_read] = '\0';
/*name, val-read_cb-ctx, val-off*/
/* take into account '=' separator after the name */
cb(buf, (void *)&entry_ctx, len_read + 1, cb_arg);
lines++;
cb(name_str, val_str, cb_arg);
}
rc = fs_close(&file);
cf->cf_lines = lines;
@ -161,58 +143,101 @@ static int settings_file_create_or_replace(struct fs_file_t *zfp,
return fs_open(zfp, file_name);
}
/*
* Try to compress configuration file by keeping unique names only.
*/
void settings_file_compress(struct settings_file *cf)
int settings_file_save_and_compress(struct settings_file *cf, const char *name,
const char *value, size_t val_len)
{
int rc;
int rc, rc2;
struct fs_file_t rf;
struct fs_file_t wf;
char tmp_file[SETTINGS_FILE_NAME_MAX];
char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
off_t loc1, loc2;
char *name1, *val1;
char *name2, *val2;
char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
struct line_entry_ctx loc1 = {
.stor_ctx = &rf,
.seek = 0,
.len = 0 /* unknown length */
};
struct line_entry_ctx loc2;
struct line_entry_ctx loc3 = {
.stor_ctx = &wf
};
int copy;
int len, len2;
int lines;
size_t new_name_len;
off_t val1_off;
if (fs_open(&rf, cf->cf_name) != 0) {
return;
return -ENOEXEC;
}
settings_tmpfile(tmp_file, cf->cf_name, ".cmp");
if (settings_file_create_or_replace(&wf, tmp_file)) {
fs_close(&rf);
return;
return -ENOEXEC;
}
loc1 = 0;
lines = 0;
new_name_len = strlen(name);
while (1) {
len = settings_getnext_line(&rf, buf1, sizeof(buf1), &loc1);
if (loc1 == 0 || len < 0) {
rc = settings_next_line_ctx(&loc1);
if (rc || loc1.len == 0) {
/* try to amend new value to the commpresed file */
break;
}
rc = settings_line_parse(buf1, &name1, &val1);
rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
&loc1);
if (rc) {
/* try to process next line */
continue;
}
if (val1_off + 1 == loc1.len) {
/* Lack of a value so the record is a deletion-record */
/* No sense to copy empty entry from */
/* the oldest sector */
continue;
}
/* avoid copping value which will be overwritten by new value*/
if ((val1_off == new_name_len) &&
!memcmp(name1, name, val1_off)) {
continue;
}
loc2 = loc1;
copy = 1;
while ((len2 = settings_getnext_line(&rf, buf2, sizeof(buf2),
&loc2)) > 0) {
rc = settings_line_parse(buf2, &name2, &val2);
while (1) {
off_t val2_off;
rc = settings_next_line_ctx(&loc2);
if (rc || loc2.len == 0) {
/* try to amend new value to */
/* the commpresed file */
break;
}
rc = settings_line_name_read(name2, sizeof(name2),
&val2_off, &loc2);
if (rc) {
/* try to process next line */
continue;
}
if (!strcmp(name1, name2)) {
copy = 0;
if ((val1_off == val2_off) &&
!memcmp(name1, name2, val1_off)) {
copy = 0; /* newer version doesn't exist */
break;
}
}
@ -220,45 +245,58 @@ void settings_file_compress(struct settings_file *cf)
continue;
}
/*
* Can't find one. Must copy.
*/
len = settings_line_make(buf2, sizeof(buf2), name1, val1);
if (len < 0 || len + 2 > sizeof(buf2)) {
continue;
}
buf2[len++] = '\n';
if (fs_write(&wf, buf2, len) != len) {
ARG_UNUSED(fs_close(&rf));
ARG_UNUSED(fs_close(&wf));
return;
loc2 = loc1;
loc2.len += 2;
loc2.seek -= 2;
rc = settings_entry_copy(&loc3, 0, &loc2, 0, loc2.len);
if (rc) {
/* compressed file might be corrupted */
goto end_rolback;
}
lines++;
}
len = fs_close(&wf);
len2 = fs_close(&rf);
if (len == 0 && len2 == 0 && fs_unlink(cf->cf_name) == 0) {
ARG_UNUSED(fs_rename(tmp_file, cf->cf_name));
cf->cf_lines = lines;
/* at last store the new value */
rc = settings_line_write(name, value, val_len, 0, &loc3);
if (rc) {
/* compressed file might be corrupted */
goto end_rolback;
}
rc = fs_close(&wf);
rc2 = fs_close(&rf);
if (rc == 0 && rc2 == 0 && fs_unlink(cf->cf_name) == 0) {
if (fs_rename(tmp_file, cf->cf_name)) {
return -ENOENT;
}
cf->cf_lines = lines + 1;
} else {
rc = -EIO;
}
/*
* XXX at settings_file_load(), look for .cmp if actual file does not
* exist.
*/
return 0;
end_rolback:
(void)fs_close(&wf);
if (fs_close(&rf) == 0) {
(void)fs_unlink(tmp_file);
}
return -EIO;
}
/*
* Called to save configuration.
*/
static int settings_file_save(struct settings_store *cs, const char *name,
const char *value)
const char *value, size_t val_len)
{
struct settings_file *cf = (struct settings_file *)cs;
struct line_entry_ctx entry_ctx;
struct fs_file_t file;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
int len;
int rc2;
int rc;
@ -271,13 +309,9 @@ static int settings_file_save(struct settings_store *cs, const char *name,
* Compress before config file size exceeds
* the max number of lines.
*/
settings_file_compress(cf);
return settings_file_save_and_compress(cf, name, value,
val_len);
}
len = settings_line_make(buf, sizeof(buf), name, value);
if (len < 0 || len + 2 > sizeof(buf)) {
return -EINVAL;
}
buf[len++] = '\n';
/*
* Open the file to add this one value.
@ -286,8 +320,10 @@ static int settings_file_save(struct settings_store *cs, const char *name,
if (rc == 0) {
rc = fs_seek(&file, 0, FS_SEEK_END);
if (rc == 0) {
rc2 = fs_write(&file, buf, len);
if (rc2 == len) {
entry_ctx.stor_ctx = &file;
rc2 = settings_line_write(name, value, val_len, 0,
(void *)&entry_ctx);
if (rc2 == 0) {
cf->cf_lines++;
}
}
@ -300,3 +336,63 @@ static int settings_file_save(struct settings_store *cs, const char *name,
return rc;
}
static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
{
struct line_entry_ctx *entry_ctx = ctx;
struct fs_file_t *file = entry_ctx->stor_ctx;
ssize_t r_len;
int rc;
/* 0 is reserved for reding the length-field only */
if (entry_ctx->len != 0) {
if (off >= entry_ctx->len) {
return -EINVAL;
}
if ((off + *len) > entry_ctx->len) {
*len = entry_ctx->len - off;
}
}
rc = fs_seek(file, entry_ctx->seek + off, FS_SEEK_SET);
if (rc) {
goto end;
}
r_len = fs_read(file, buf, *len);
if (r_len >= 0) {
*len = r_len;
rc = 0;
} else {
rc = r_len;
}
end:
return rc;
}
static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
{
struct line_entry_ctx *entry_ctx = ctx;
struct fs_file_t *file = entry_ctx->stor_ctx;
int rc;
/* append to file only */
rc = fs_seek(file, 0, FS_SEEK_END);
if (rc == 0) {
rc = fs_write(file, buf, len);
if (rc > 0) {
rc = 0;
}
}
return rc;
}
void settings_mount_fs_backend(struct settings_file *cf)
{
settings_line_io_init(read_handler, write_handler, 1);
}

View file

@ -40,6 +40,8 @@ static void settings_init_fs(void)
if (rc) {
k_panic();
}
settings_mount_fs_backend(&config_init_settings_file);
}
#elif defined(CONFIG_SETTINGS_FCB)
@ -93,6 +95,8 @@ static void settings_init_fcb(void)
if (rc != 0) {
k_panic();
}
settings_mount_fcb_backend(&config_init_settings_fcb);
}
#endif

View file

@ -10,6 +10,8 @@
#include "settings/settings.h"
#include "settings_priv.h"
#include "base64.h"
#include "misc/printk.h"
int settings_line_parse(char *buf, char **namep, char **valp)
{
@ -87,3 +89,360 @@ int settings_line_make(char *dst, int dlen, const char *name, const char *value)
return off;
}
struct settings_io_cb_s {
int (*read_cb)(void *ctx, off_t off, char *buf, size_t *len);
int (*write_cb)(void *ctx, off_t off, char const *buf, size_t len);
u8_t rwbs;
} settings_io_cb;
#define MAX_ENC_BLOCK_SIZE 4
int settings_line_write(const char *name, const char *value, size_t val_len,
off_t w_loc, void *cb_arg)
{
size_t w_size, rem, add, enc_len;
/* minimal buffer for encoding base64 + EOL*/
char enc_buf[MAX_ENC_BLOCK_SIZE + 1];
char *p_enc = enc_buf;
bool done;
char w_buf[16]; /* write buff, must be aligned either to minimal */
/* base64 encoding size and write-block-size */
int rc;
u8_t wbs = settings_io_cb.rwbs;
#ifdef CONFIG_SETTINGS_ENCODE_LEN
u16_t len_field;
#endif
rem = strlen(name);
#ifdef CONFIG_SETTINGS_ENCODE_LEN
len_field = settings_line_len_calc(name, val_len);
memcpy(w_buf, &len_field, sizeof(len_field));
w_size = 0;
add = sizeof(len_field) % wbs;
if (add) {
w_size = wbs - add;
if (rem < w_size) {
w_size = rem;
}
memcpy(w_buf + sizeof(len_field), name, w_size);
name += w_size;
rem -= w_size;
}
w_size += sizeof(len_field);
if (w_size % wbs == 0) {
rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
if (rc) {
return -EIO;
}
}
/* The Alternative to condition above mean that `rem == 0` as `name` */
/* must have been consumed */
#endif
w_size = rem - rem % wbs;
rem %= wbs;
rc = settings_io_cb.write_cb(cb_arg, w_loc, name, w_size);
w_loc += w_size;
name += w_size;
w_size = rem;
if (rem) {
memcpy(w_buf, name, rem);
}
w_buf[rem] = '=';
w_size++;
rem = val_len;
enc_len = 0;
done = false;
while (1) {
while (w_size < sizeof(w_buf)) {
if (enc_len) {
add = min(enc_len, sizeof(w_buf) - w_size);
memcpy(&w_buf[w_size], p_enc, add);
enc_len -= add;
w_size += add;
p_enc += add;
} else {
if (rem) {
add = min(rem, MAX_ENC_BLOCK_SIZE/4*3);
rc = base64_encode(enc_buf, sizeof(enc_buf), &enc_len, value, add);
if (rc) {
return -EINVAL;
}
value += add;
rem -= add;
p_enc = enc_buf;
} else {
add = (w_size) % wbs;
if (add) {
add = wbs - add;
memset(&w_buf[w_size], '\0',
add);
w_size += add;
}
done = true;
break;
}
}
}
rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
if (rc) {
return -EIO;
}
if (done) {
break;
}
w_loc += w_size;
w_size = 0;
}
return 0;
}
#ifdef CONFIG_SETTINGS_ENCODE_LEN
int settings_next_line_ctx(struct line_entry_ctx *entry_ctx)
{
size_t len_read;
u16_t readout;
int rc;
entry_ctx->seek += entry_ctx->len; /* to begin of nex line */
entry_ctx->len = 0; /* ask read handler to ignore len */
rc = settings_line_raw_read(0, (char *)&readout, sizeof(readout),
&len_read, entry_ctx);
if (rc == 0) {
if (len_read != sizeof(readout)) {
if (len_read != 0) {
rc = -ESPIPE;
}
} else {
entry_ctx->seek += sizeof(readout);
entry_ctx->len = readout;
}
}
return rc;
}
#endif
int settings_line_len_calc(const char *name, size_t val_len)
{
int len, mod;
/* <enc(value)> */
len = val_len/3*4 + ((val_len%3) ? 4 : 0);
/* <name>=<enc(value)> */
len += strlen(name) + 1;
mod = len % settings_io_cb.rwbs;
if (mod) {
/*additional \0 for meet flash alignment */
len += settings_io_cb.rwbs - mod;
}
return len;
}
/**
* Read RAW settings line entry data until a char from the storage.
*
* @param seek offset form the line beginning.
* @param[out] out buffer for name
* @param[in] len_req size of <p>out</p> buffer
* @param[out] len_read length of read name
* @param[in] until_char pointer on char value until which all line data will
* be read. If Null entire data will be read.
* @param[in] cb_arg settings line storage context expected by the
* <p>read_cb</p> implementation
*
* @retval 0 on success,
* -ERCODE on storage errors
*/
static int settings_line_raw_read_until(off_t seek, char *out, size_t len_req,
size_t *len_read, char const *until_char,
void *cb_arg)
{
size_t rem_size, len;
char temp_buf[16]; /* buffer for fit read-block-size requirements */
size_t exp_size, read_size;
u8_t rbs = settings_io_cb.rwbs;
off_t off;
int rc;
rem_size = len_req;
while (rem_size) {
off = seek / rbs * rbs;
read_size = sizeof(temp_buf);
exp_size = read_size;
rc = settings_io_cb.read_cb(cb_arg, off, temp_buf, &read_size);
if (rc) {
return -EIO;
}
off = seek - off;
len = read_size - off;
len = min(rem_size, len);
if (until_char != NULL) {
char *pend;
pend = memchr(&temp_buf[off], *until_char, len);
if (pend != NULL) {
len = pend - &temp_buf[off];
rc = 1; /* will cause loop expiration */
}
}
memcpy(out, &temp_buf[off], len);
rem_size -= len;
if (exp_size > read_size || rc) {
break;
}
out += len;
seek += len;
}
*len_read = len_req - rem_size;
if (until_char != NULL) {
return (rc) ? 0 : 1;
}
return 0;
}
int settings_line_raw_read(off_t seek, char *out, size_t len_req,
size_t *len_read, void *cb_arg)
{
return settings_line_raw_read_until(seek, out, len_req, len_read,
NULL, cb_arg);
}
/* off from value begin */
int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
size_t *len_read, void *cb_arg)
{
char enc_buf[16 + 1];
char dec_buf[sizeof(enc_buf)/4 * 3 + 1];
size_t rem_size, read_size, exp_size, clen, olen;
off_t seek_begin, off_begin;
int rc;
rem_size = len_req;
while (rem_size) {
seek_begin = off / 3 * 4;
off_begin = seek_begin / 4 * 3;
read_size = rem_size / 3 * 4;
read_size += (rem_size % 3 != 0 || off_begin != off) ? 4 : 0;
read_size = min(read_size, sizeof(enc_buf) - 1);
exp_size = read_size;
rc = settings_line_raw_read(val_off + seek_begin, enc_buf,
read_size, &read_size, cb_arg);
if (rc) {
return rc;
}
enc_buf[read_size] = 0; /* breaking guaranted */
read_size = strlen(enc_buf);
if (read_size == 0 || read_size % 4) {
/* unexpected use case - a NULL value or an encoding */
/* problem */
return -EINVAL;
}
rc = base64_decode(dec_buf, sizeof(dec_buf), &olen, enc_buf,
read_size);
dec_buf[olen] = 0;
clen = min(olen + off_begin - off, rem_size);
memcpy(out, &dec_buf[off - off_begin], clen);
rem_size -= clen;
if (exp_size > read_size || olen < read_size/4*3) {
break;
}
out += clen;
off += clen;
}
*len_read = len_req - rem_size;
return 0;
}
int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
void *cb_arg)
{
char const until_char = '=';
return settings_line_raw_read_until(0, out, len_req, len_read,
&until_char, cb_arg);
}
int settings_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
off_t src_off, size_t len)
{
int rc;
char buf[16];
size_t chunk_size;
while (len) {
chunk_size = min(len, sizeof(buf));
rc = settings_io_cb.read_cb(src_ctx, src_off, buf, &chunk_size);
if (rc) {
break;
}
rc = settings_io_cb.write_cb(dst_ctx, dst_off, buf, chunk_size);
if (rc) {
break;
}
src_off += chunk_size;
dst_off += chunk_size;
len -= chunk_size;
}
return rc;
}
void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
size_t *len),
int (*write_cb)(void *ctx, off_t off, char const *buf,
size_t len),
u8_t io_rwbs)
{
settings_io_cb.read_cb = read_cb;
settings_io_cb.write_cb = write_cb;
settings_io_cb.rwbs = io_rwbs;
}

View file

@ -8,6 +8,9 @@
#ifndef __SETTINGS_PRIV_H_
#define __SETTINGS_PRIV_H_
#include <sys/types.h>
#include <errno.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -24,18 +27,107 @@ int settings_line_make(char *dst, int dlen, const char *name, const char *val);
int settings_line_make2(char *dst, int dlen, const char *name,
const char *value);
void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
size_t *len),
int (*write_cb)(void *ctx, off_t off,
char const *buf, size_t len),
u8_t io_rwbs);
int settings_line_write(const char *name, const char *value, size_t val_len,
off_t w_loc, void *cb_arg);
int settings_line_len_calc(const char *name, size_t val_len);
#ifdef CONFIG_SETTINGS_ENCODE_LEN
/* in storage line contex */
struct line_entry_ctx {
void *stor_ctx;
off_t seek; /* offset of id-value pair within the file */
size_t len; /* len of line without len value */
};
int settings_next_line_ctx(struct line_entry_ctx *entry_ctx);
#endif
/**
* Read RAW settings line entry data from the storage.
*
* @param seek offset form the line beginning.
* @param[out] out buffer for name
* @param[in] len_req size of <p>out</p> buffer
* @param[out] len_read length of read name
* @param[in] cb_arg settings line storage context expected by the
* <p>read_cb</p> implementatio
*
* @retval 0 on success,
* -ERCODE on storage errors
*/
int settings_line_raw_read(off_t seek, char *out, size_t len_req,
size_t *len_read, void *cb_arg);
/*
* @param val_off offset of the value-string.
* @param off from val_off (so within the value-string)
*/
int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
size_t *len_read, void *cb_arg);
/**
* Read the settings line entry name from the storage.
*
* @param[out] out buffer for name
* @param[in] len_req size of <p>out</p> buffer
* @param[out] len_read length of read name
* @param[in] cb_arg settings line storage context expected by the
* <p>read_cb</p> implementation
*
* @retval 0 on read proper name,
* 1 on when read improper name,
* -ERCODE on storage errors
*/
int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
void *cb_arg);
int settings_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
off_t src_off, size_t len);
void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
size_t *len),
int (*write_cb)(void *ctx, off_t off, char const *buf,
size_t len),
u8_t io_rwbs);
int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
u8_t is_runtime);
/*
* API for config storage.
*/
typedef void (*load_cb)(char *name, char *val, void *cb_arg);
typedef void (*load_cb)(char *name, void *val_read_cb_ctx, off_t off,
void *cb_arg);
struct settings_store_itf {
int (*csi_load)(struct settings_store *cs, load_cb cb, void *cb_arg);
int (*csi_save_start)(struct settings_store *cs);
int (*csi_save)(struct settings_store *cs, const char *name,
const char *value);
const char *value, size_t val_len);
int (*csi_save_end)(struct settings_store *cs);
};
struct read_value_cb_ctx {
void *read_cb_ctx;
off_t off;
u8_t runtime;
};
/* suports runtime settings_set_value operation */
struct runtime_value_ctx {
void *p_value;
size_t size;
};
void settings_src_register(struct settings_store *cs);
void settings_dst_register(struct settings_store *cs);

View file

@ -20,6 +20,7 @@
struct settings_dup_check_arg {
const char *name;
const char *val;
size_t val_len;
int is_dup;
};
@ -44,9 +45,10 @@ void settings_dst_register(struct settings_store *cs)
settings_save_dst = cs;
}
static void settings_load_cb(char *name, char *val, void *cb_arg)
static void settings_load_cb(char *name, void *val_read_cb_ctx, off_t off,
void *cb_arg)
{
int rc = settings_set_value(name, val);
int rc = settings_set_value_priv(name, val_read_cb_ctx, off, 0);
__ASSERT(rc == 0, "set-value operation failure\n");
(void)rc;
}
@ -68,22 +70,66 @@ int settings_load(void)
return settings_commit(NULL);
}
static void settings_dup_check_cb(char *name, char *val, void *cb_arg)
/* val_off - offset of value-string within line entries */
static int settings_cmp(char const *val, size_t val_len, void *val_read_cb_ctx,
off_t val_off)
{
size_t len_read, exp_len;
size_t rem;
char buf[16];
int rc;
off_t off = 0;
for (rem = val_len; rem > 0; rem -= len_read) {
len_read = exp_len = min(sizeof(buf), rem);
rc = settings_line_val_read(val_off, off, buf, len_read,
&len_read, val_read_cb_ctx);
if (rc) {
break;
}
if (len_read != exp_len) {
rc = 1;
break;
}
rc = memcmp(val, buf, len_read);
if (rc) {
break;
}
val += len_read;
off += len_read;
}
return rc;
}
static void settings_dup_check_cb(char *name, void *val_read_cb_ctx, off_t off,
void *cb_arg)
{
struct settings_dup_check_arg *cdca = (struct settings_dup_check_arg *)
cb_arg;
size_t len_read;
char temp;
int rc;
if (strcmp(name, cdca->name)) {
return;
}
if (!val) {
rc = settings_line_raw_read(off, &temp, 1, &len_read,
val_read_cb_ctx);
if (rc || len_read == 0 || temp == '\0') {
/* treat as an empty entry */
if (!cdca->val || cdca->val[0] == '\0') {
cdca->is_dup = 1;
} else {
cdca->is_dup = 0;
}
} else {
if (cdca->val && !strcmp(val, cdca->val)) {
if (cdca->val && !settings_cmp(cdca->val, cdca->val_len,
val_read_cb_ctx, off)) {
cdca->is_dup = 1;
} else {
cdca->is_dup = 0;
@ -94,7 +140,7 @@ static void settings_dup_check_cb(char *name, char *val, void *cb_arg)
/*
* Append a single value to persisted config. Don't store duplicate value.
*/
int settings_save_one(const char *name, char *value)
int settings_save_one(const char *name, void *value, size_t val_len)
{
struct settings_store *cs;
struct settings_dup_check_arg cdca;
@ -108,13 +154,14 @@ int settings_save_one(const char *name, char *value)
* Check if we're writing the same value again.
*/
cdca.name = name;
cdca.val = value;
cdca.val = (char *)value;
cdca.is_dup = 0;
cdca.val_len = val_len;
cs->cs_itf->csi_load(cs, settings_dup_check_cb, &cdca);
if (cdca.is_dup == 1) {
return 0;
}
return cs->cs_itf->csi_save(cs, name, value);
return cs->cs_itf->csi_save(cs, name, (char *)value, val_len);
}
int settings_save(void)
@ -136,8 +183,7 @@ int settings_save(void)
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
if (ch->h_export) {
rc2 = ch->h_export(settings_save_one,
SETTINGS_EXPORT_PERSIST);
rc2 = ch->h_export(settings_save_one);
if (!rc) {
rc = rc2;
}