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