1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 * Copyright (c) 2015 Runtime Inc
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <zephyr/fs/fcb.h>
11 #include <string.h>
12
13 #include <zephyr/settings/settings.h>
14 #include "settings/settings_fcb.h"
15 #include "settings_priv.h"
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
19
20 #if DT_HAS_CHOSEN(zephyr_settings_partition)
21 #define SETTINGS_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
22 #else
23 #define SETTINGS_PARTITION FIXED_PARTITION_ID(storage_partition)
24 #endif
25
26 #define SETTINGS_FCB_VERS 1
27
28 int settings_backend_init(void);
29
30 static int settings_fcb_load(struct settings_store *cs,
31 const struct settings_load_arg *arg);
32 static int settings_fcb_save(struct settings_store *cs, const char *name,
33 const char *value, size_t val_len);
34 static void *settings_fcb_storage_get(struct settings_store *cs);
35
36 static const struct settings_store_itf settings_fcb_itf = {
37 .csi_load = settings_fcb_load,
38 .csi_save = settings_fcb_save,
39 .csi_storage_get = settings_fcb_storage_get
40 };
41
42 /**
43 * @brief Get the flash area id of the storage partition
44 *
45 * The implementation of this function provided is weak to let user defines its own function.
46 * This may prove useful for devices using bank swap, in that case the flash area id changes based
47 * on the bank swap state.
48 * See #47732
49 *
50 * @return Flash area id
51 */
settings_fcb_get_flash_area(void)52 __weak int settings_fcb_get_flash_area(void)
53 {
54 return SETTINGS_PARTITION;
55 }
56
settings_fcb_src(struct settings_fcb * cf)57 int settings_fcb_src(struct settings_fcb *cf)
58 {
59 int rc;
60
61 cf->cf_fcb.f_version = SETTINGS_FCB_VERS;
62 cf->cf_fcb.f_scratch_cnt = 1;
63
64 while (1) {
65 rc = fcb_init(settings_fcb_get_flash_area(), &cf->cf_fcb);
66 if (rc) {
67 return -EINVAL;
68 }
69
70 /*
71 * Check if system was reset in middle of emptying a sector.
72 * This situation is recognized by checking if the scratch block
73 * is missing.
74 */
75 if (fcb_free_sector_cnt(&cf->cf_fcb) < 1) {
76
77 rc = flash_area_erase(cf->cf_fcb.fap,
78 cf->cf_fcb.f_active.fe_sector->fs_off,
79 cf->cf_fcb.f_active.fe_sector->fs_size);
80
81 if (rc) {
82 return -EIO;
83 }
84 } else {
85 break;
86 }
87 }
88
89 cf->cf_store.cs_itf = &settings_fcb_itf;
90 settings_src_register(&cf->cf_store);
91
92 return 0;
93 }
94
settings_fcb_dst(struct settings_fcb * cf)95 int settings_fcb_dst(struct settings_fcb *cf)
96 {
97 cf->cf_store.cs_itf = &settings_fcb_itf;
98 settings_dst_register(&cf->cf_store);
99
100 return 0;
101 }
102
103 /**
104 * @brief Check if there is any duplicate of the current setting
105 *
106 * This function checks if there is any duplicated data further in the buffer.
107 *
108 * @param cf FCB handler
109 * @param entry_ctx Current entry context
110 * @param name The name of the current entry
111 *
112 * @retval false No duplicates found
113 * @retval true Duplicate found
114 */
settings_fcb_check_duplicate(struct settings_fcb * cf,const struct fcb_entry_ctx * entry_ctx,const char * const name)115 static bool settings_fcb_check_duplicate(struct settings_fcb *cf,
116 const struct fcb_entry_ctx *entry_ctx,
117 const char * const name)
118 {
119 struct fcb_entry_ctx entry2_ctx = *entry_ctx;
120
121 while (fcb_getnext(&cf->cf_fcb, &entry2_ctx.loc) == 0) {
122 char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
123 size_t name2_len;
124
125 if (settings_line_name_read(name2, sizeof(name2), &name2_len,
126 &entry2_ctx)) {
127 LOG_ERR("failed to load line");
128 continue;
129 }
130 name2[name2_len] = '\0';
131 if (!strcmp(name, name2)) {
132 return true;
133 }
134 }
135 return false;
136 }
137
read_entry_len(const struct fcb_entry_ctx * entry_ctx,off_t off)138 static int read_entry_len(const struct fcb_entry_ctx *entry_ctx, off_t off)
139 {
140 if (off >= entry_ctx->loc.fe_data_len) {
141 return 0;
142 }
143 return entry_ctx->loc.fe_data_len - off;
144 }
145
settings_fcb_load_priv(struct settings_store * cs,line_load_cb cb,void * cb_arg,bool filter_duplicates)146 static int settings_fcb_load_priv(struct settings_store *cs,
147 line_load_cb cb,
148 void *cb_arg,
149 bool filter_duplicates)
150 {
151 struct settings_fcb *cf = CONTAINER_OF(cs, struct settings_fcb, cf_store);
152 struct fcb_entry_ctx entry_ctx = {
153 {.fe_sector = NULL, .fe_elem_off = 0},
154 .fap = cf->cf_fcb.fap
155 };
156 int rc;
157
158 while ((rc = fcb_getnext(&cf->cf_fcb, &entry_ctx.loc)) == 0) {
159 char name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
160 size_t name_len;
161 int rc2;
162 bool pass_entry = true;
163
164 rc2 = settings_line_name_read(name, sizeof(name), &name_len,
165 (void *)&entry_ctx);
166 if (rc2) {
167 LOG_ERR("Failed to load line name: %d", rc2);
168 continue;
169 }
170 name[name_len] = '\0';
171
172 if (filter_duplicates &&
173 (!read_entry_len(&entry_ctx, name_len+1) ||
174 settings_fcb_check_duplicate(cf, &entry_ctx, name))) {
175 pass_entry = false;
176 }
177 /*name, val-read_cb-ctx, val-off*/
178 /* take into account '=' separator after the name */
179 if (pass_entry) {
180 cb(name, &entry_ctx, name_len + 1, cb_arg);
181 }
182 }
183 if (rc == -ENOTSUP) {
184 rc = 0;
185 }
186 return 0;
187 }
188
settings_fcb_load(struct settings_store * cs,const struct settings_load_arg * arg)189 static int settings_fcb_load(struct settings_store *cs,
190 const struct settings_load_arg *arg)
191 {
192 return settings_fcb_load_priv(
193 cs,
194 settings_line_load_cb,
195 (void *)arg,
196 true);
197 }
198
read_handler(void * ctx,off_t off,char * buf,size_t * len)199 static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
200 {
201 struct fcb_entry_ctx *entry_ctx = ctx;
202
203 if (off >= entry_ctx->loc.fe_data_len) {
204 *len = 0;
205 return 0;
206 }
207
208 if ((off + *len) > entry_ctx->loc.fe_data_len) {
209 *len = entry_ctx->loc.fe_data_len - off;
210 }
211
212 return flash_area_read(entry_ctx->fap,
213 FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off, buf,
214 *len);
215 }
216
settings_fcb_compress(struct settings_fcb * cf)217 static void settings_fcb_compress(struct settings_fcb *cf)
218 {
219 int rc;
220 struct fcb_entry_ctx loc1;
221 struct fcb_entry_ctx loc2;
222 char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
223 char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
224 int copy;
225 uint8_t rbs;
226
227 rc = fcb_append_to_scratch(&cf->cf_fcb);
228 if (rc) {
229 return; /* XXX */
230 }
231
232 rbs = flash_area_align(cf->cf_fcb.fap);
233
234 loc1.fap = cf->cf_fcb.fap;
235
236 loc1.loc.fe_sector = NULL;
237 loc1.loc.fe_elem_off = 0U;
238
239 while (fcb_getnext(&cf->cf_fcb, &loc1.loc) == 0) {
240 if (loc1.loc.fe_sector != cf->cf_fcb.f_oldest) {
241 break;
242 }
243
244 size_t val1_off;
245
246 rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
247 &loc1);
248 if (rc) {
249 continue;
250 }
251
252 if (val1_off + 1 == loc1.loc.fe_data_len) {
253 /* Lack of a value so the record is a deletion-record */
254 /* No sense to copy empty entry from */
255 /* the oldest sector */
256 continue;
257 }
258
259 loc2 = loc1;
260 copy = 1;
261
262 while (fcb_getnext(&cf->cf_fcb, &loc2.loc) == 0) {
263 size_t val2_off;
264
265 rc = settings_line_name_read(name2, sizeof(name2),
266 &val2_off, &loc2);
267 if (rc) {
268 continue;
269 }
270
271 if ((val1_off == val2_off) &&
272 !memcmp(name1, name2, val1_off)) {
273 copy = 0;
274 break;
275 }
276 }
277 if (!copy) {
278 continue;
279 }
280
281 /*
282 * Can't find one. Must copy.
283 */
284 rc = fcb_append(&cf->cf_fcb, loc1.loc.fe_data_len, &loc2.loc);
285 if (rc) {
286 continue;
287 }
288
289 rc = settings_line_entry_copy(&loc2, 0, &loc1, 0,
290 loc1.loc.fe_data_len);
291 if (rc) {
292 continue;
293 }
294 rc = fcb_append_finish(&cf->cf_fcb, &loc2.loc);
295
296 if (rc != 0) {
297 LOG_ERR("Failed to finish fcb_append (%d)", rc);
298 }
299 }
300 rc = fcb_rotate(&cf->cf_fcb);
301
302 if (rc != 0) {
303 LOG_ERR("Failed to fcb rotate (%d)", rc);
304 }
305 }
306
get_len_cb(void * ctx)307 static size_t get_len_cb(void *ctx)
308 {
309 struct fcb_entry_ctx *entry_ctx = ctx;
310
311 return entry_ctx->loc.fe_data_len;
312 }
313
write_handler(void * ctx,off_t off,char const * buf,size_t len)314 static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
315 {
316 struct fcb_entry_ctx *entry_ctx = ctx;
317
318 return flash_area_write(entry_ctx->fap,
319 FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off,
320 buf, len);
321 }
322
323 /* ::csi_save implementation */
settings_fcb_save_priv(struct settings_store * cs,const char * name,const char * value,size_t val_len)324 static int settings_fcb_save_priv(struct settings_store *cs, const char *name,
325 const char *value, size_t val_len)
326 {
327 struct settings_fcb *cf = CONTAINER_OF(cs, struct settings_fcb, cf_store);
328 struct fcb_entry_ctx loc;
329 int len;
330 int rc = -EINVAL;
331 int i;
332
333 if (!name) {
334 return -EINVAL;
335 }
336
337 len = settings_line_len_calc(name, val_len);
338
339 for (i = 0; i < cf->cf_fcb.f_sector_cnt; i++) {
340 rc = fcb_append(&cf->cf_fcb, len, &loc.loc);
341 if (rc != -ENOSPC) {
342 break;
343 }
344
345 /* FCB can compress up to cf->cf_fcb.f_sector_cnt - 1 times. */
346 if (i < (cf->cf_fcb.f_sector_cnt - 1)) {
347 settings_fcb_compress(cf);
348 }
349 }
350 if (rc) {
351 return -EINVAL;
352 }
353
354 loc.fap = cf->cf_fcb.fap;
355
356 rc = settings_line_write(name, value, val_len, 0, (void *)&loc);
357
358 if (rc != -EIO) {
359 i = fcb_append_finish(&cf->cf_fcb, &loc.loc);
360 if (!rc) {
361 rc = i;
362 }
363 }
364 return rc;
365 }
366
settings_fcb_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)367 static int settings_fcb_save(struct settings_store *cs, const char *name,
368 const char *value, size_t val_len)
369 {
370 struct settings_line_dup_check_arg cdca;
371
372 if (val_len > 0 && value == NULL) {
373 return -EINVAL;
374 }
375
376 /*
377 * Check if we're writing the same value again.
378 */
379 cdca.name = name;
380 cdca.val = (char *)value;
381 cdca.is_dup = 0;
382 cdca.val_len = val_len;
383 settings_fcb_load_priv(cs, settings_line_dup_check_cb, &cdca, false);
384 if (cdca.is_dup == 1) {
385 return 0;
386 }
387 return settings_fcb_save_priv(cs, name, value, val_len);
388 }
389
settings_mount_fcb_backend(struct settings_fcb * cf)390 void settings_mount_fcb_backend(struct settings_fcb *cf)
391 {
392 uint8_t rbs;
393
394 rbs = cf->cf_fcb.f_align;
395
396 settings_line_io_init(read_handler, write_handler, get_len_cb, rbs);
397 }
398
settings_backend_init(void)399 int settings_backend_init(void)
400 {
401 static struct flash_sector
402 settings_fcb_area[CONFIG_SETTINGS_FCB_NUM_AREAS + 1];
403 static struct settings_fcb config_init_settings_fcb = {
404 .cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC,
405 .cf_fcb.f_sectors = settings_fcb_area,
406 };
407 uint32_t cnt = sizeof(settings_fcb_area) /
408 sizeof(settings_fcb_area[0]);
409 int rc;
410 const struct flash_area *fap;
411
412 rc = flash_area_get_sectors(settings_fcb_get_flash_area(), &cnt,
413 settings_fcb_area);
414 if (rc != 0 && rc != -ENOMEM) {
415 return rc;
416 }
417
418 config_init_settings_fcb.cf_fcb.f_sector_cnt = cnt;
419
420 rc = settings_fcb_src(&config_init_settings_fcb);
421 if (rc != 0) {
422 rc = flash_area_open(settings_fcb_get_flash_area(), &fap);
423 if (rc != 0) {
424 return rc;
425 }
426
427 rc = flash_area_erase(fap, 0, fap->fa_size);
428 flash_area_close(fap);
429
430 if (rc != 0) {
431 return rc;
432 }
433
434 rc = settings_fcb_src(&config_init_settings_fcb);
435 if (rc != 0) {
436 return rc;
437 }
438 }
439
440 rc = settings_fcb_dst(&config_init_settings_fcb);
441 if (rc != 0) {
442 return rc;
443 }
444
445 settings_mount_fcb_backend(&config_init_settings_fcb);
446
447 return rc;
448 }
449
settings_fcb_storage_get(struct settings_store * cs)450 static void *settings_fcb_storage_get(struct settings_store *cs)
451 {
452 struct settings_fcb *cf = CONTAINER_OF(cs, struct settings_fcb, cf_store);
453
454 return &cf->cf_fcb;
455 }
456