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