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