1 /* Copyright (c) 2024 BayLibre SAS
2  *
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #undef _POSIX_C_SOURCE
7 #define _POSIX_C_SOURCE 200809L /* for strnlen() */
8 
9 #include <errno.h>
10 #include <string.h>
11 
12 #include "settings/settings_zms.h"
13 #include "settings_priv.h"
14 
15 #include <zephyr/settings/settings.h>
16 #include <zephyr/sys/hash_function.h>
17 #include <zephyr/storage/flash_map.h>
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
20 
21 #if DT_HAS_CHOSEN(zephyr_settings_partition)
22 #define SETTINGS_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
23 #else
24 #define SETTINGS_PARTITION FIXED_PARTITION_ID(storage_partition)
25 #endif
26 
27 struct settings_zms_read_fn_arg {
28 	struct zms_fs *fs;
29 	uint32_t id;
30 };
31 
32 static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg);
33 static ssize_t settings_zms_load_one(struct settings_store *cs, const char *name, char *buf,
34 				     size_t buf_len);
35 static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
36 			     size_t val_len);
37 static void *settings_zms_storage_get(struct settings_store *cs);
38 static int settings_zms_get_last_hash_ids(struct settings_zms *cf);
39 static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name);
40 
41 static struct settings_store_itf settings_zms_itf = {.csi_load = settings_zms_load,
42 						     .csi_load_one = settings_zms_load_one,
43 						     .csi_save = settings_zms_save,
44 						     .csi_storage_get = settings_zms_storage_get,
45 						     .csi_get_val_len = settings_zms_get_val_len};
46 
settings_zms_read_fn(void * back_end,void * data,size_t len)47 static ssize_t settings_zms_read_fn(void *back_end, void *data, size_t len)
48 {
49 	struct settings_zms_read_fn_arg *rd_fn_arg;
50 
51 	rd_fn_arg = (struct settings_zms_read_fn_arg *)back_end;
52 
53 	return zms_read(rd_fn_arg->fs, rd_fn_arg->id, data, len);
54 }
55 
settings_zms_src(struct settings_zms * cf)56 static int settings_zms_src(struct settings_zms *cf)
57 {
58 	cf->cf_store.cs_itf = &settings_zms_itf;
59 	settings_src_register(&cf->cf_store);
60 
61 	return 0;
62 }
63 
settings_zms_dst(struct settings_zms * cf)64 static int settings_zms_dst(struct settings_zms *cf)
65 {
66 	cf->cf_store.cs_itf = &settings_zms_itf;
67 	settings_dst_register(&cf->cf_store);
68 
69 	return 0;
70 }
71 
72 #ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
settings_zms_unlink_ll_node(struct settings_zms * cf,uint32_t name_hash)73 static int settings_zms_unlink_ll_node(struct settings_zms *cf, uint32_t name_hash)
74 {
75 	int rc = 0;
76 	struct settings_hash_linked_list settings_element;
77 	struct settings_hash_linked_list settings_update_element;
78 
79 	/* let's update the linked list */
80 	rc = zms_read(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
81 		      sizeof(struct settings_hash_linked_list));
82 	if (rc < 0) {
83 		return rc;
84 	}
85 
86 	/* update the previous element */
87 	if (settings_element.previous_hash) {
88 		rc = zms_read(&cf->cf_zms, settings_element.previous_hash, &settings_update_element,
89 			      sizeof(struct settings_hash_linked_list));
90 		if (rc < 0) {
91 			return rc;
92 		}
93 		if (!settings_element.next_hash) {
94 			/* we are deleting the last element of the linked list,
95 			 * let's update the second_to_last_hash_id
96 			 */
97 			cf->second_to_last_hash_id = settings_update_element.previous_hash;
98 		}
99 		settings_update_element.next_hash = settings_element.next_hash;
100 		rc = zms_write(&cf->cf_zms, settings_element.previous_hash,
101 			       &settings_update_element, sizeof(struct settings_hash_linked_list));
102 		if (rc < 0) {
103 			return rc;
104 		}
105 	}
106 
107 	/* Now delete the current linked list element */
108 	rc = zms_delete(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash));
109 	if (rc < 0) {
110 		return rc;
111 	}
112 
113 	/* update the next element */
114 	if (settings_element.next_hash) {
115 		rc = zms_read(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
116 			      sizeof(struct settings_hash_linked_list));
117 		if (rc < 0) {
118 			return rc;
119 		}
120 		settings_update_element.previous_hash = settings_element.previous_hash;
121 		rc = zms_write(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
122 			       sizeof(struct settings_hash_linked_list));
123 		if (rc < 0) {
124 			return rc;
125 		}
126 		if (!settings_update_element.next_hash) {
127 			/* update second_to_last_hash_id */
128 			cf->second_to_last_hash_id = settings_element.previous_hash;
129 		}
130 	} else {
131 		/* we are deleting the last element of the linked list
132 		 * let's update the last_hash_id.
133 		 */
134 		cf->last_hash_id = settings_element.previous_hash;
135 	}
136 
137 	return 0;
138 }
139 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
140 
settings_zms_delete(struct settings_zms * cf,uint32_t name_hash)141 static int settings_zms_delete(struct settings_zms *cf, uint32_t name_hash)
142 {
143 	int rc = 0;
144 
145 	rc = zms_delete(&cf->cf_zms, name_hash);
146 	if (rc >= 0) {
147 		rc = zms_delete(&cf->cf_zms, ZMS_DATA_ID_FROM_NAME(name_hash));
148 	}
149 	if (rc < 0) {
150 		return rc;
151 	}
152 
153 #ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
154 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
155 	cf->ll_has_changed = true;
156 #endif
157 	rc = settings_zms_unlink_ll_node(cf, name_hash);
158 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
159 
160 	return rc;
161 }
162 
163 #ifdef CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH
164 /* Loads first the key which is defined by the name found in "subtree" root.
165  * If the key is not found or further keys under the same subtree are needed
166  * by the caller, returns 0.
167  */
settings_zms_load_subtree(struct settings_store * cs,const struct settings_load_arg * arg)168 static int settings_zms_load_subtree(struct settings_store *cs, const struct settings_load_arg *arg)
169 {
170 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
171 	struct settings_zms_read_fn_arg read_fn_arg;
172 	char name[SETTINGS_FULL_NAME_LEN];
173 	ssize_t rc1;
174 	ssize_t rc2;
175 	uint32_t name_hash;
176 	size_t name_len = strnlen(arg->subtree, SETTINGS_FULL_NAME_LEN);
177 
178 	name_hash = sys_hash32(arg->subtree, name_len) & ZMS_HASH_MASK;
179 	for (int i = 0; i <= cf->hash_collision_num; i++) {
180 		name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i);
181 		/* Get the name entry from ZMS */
182 		rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), &name,
183 			       sizeof(name) - 1);
184 		/* get the length of data and verify that it exists */
185 		rc2 = zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_HASH(name_hash));
186 		if ((rc1 <= 0) || (rc2 <= 0)) {
187 			/* Name or data doesn't exist */
188 			continue;
189 		}
190 		/* Found a name, this might not include a trailing \0 */
191 		name[rc1] = '\0';
192 		if (strcmp(arg->subtree, name)) {
193 			/* Names are not equal let's continue to the next collision hash
194 			 * if it exists.
195 			 */
196 			continue;
197 		}
198 		/* At this steps the names are equal, let's set the handler */
199 		read_fn_arg.fs = &cf->cf_zms;
200 		read_fn_arg.id = ZMS_DATA_ID_FROM_HASH(name_hash);
201 
202 		/* We should return here as there is no need to look for the next
203 		 * hash collision
204 		 */
205 		return settings_call_set_handler(arg->subtree, rc2, settings_zms_read_fn,
206 						 &read_fn_arg, arg);
207 	}
208 
209 	return 0;
210 }
211 #endif /* CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH */
212 
213 /* Search for the name_hash that corresponds to name.
214  * If no hash that corresponds to name is found in the persistent storage,
215  * returns 0.
216  */
settings_zms_find_hash_from_name(struct settings_zms * cf,const char * name)217 static uint32_t settings_zms_find_hash_from_name(struct settings_zms *cf, const char *name)
218 {
219 	uint32_t name_hash = 0;
220 	int rc = 0;
221 	char r_name[SETTINGS_FULL_NAME_LEN];
222 	size_t name_len = strnlen(name, SETTINGS_FULL_NAME_LEN);
223 
224 	name_hash = sys_hash32(name, name_len) & ZMS_HASH_MASK;
225 	for (int i = 0; i <= cf->hash_collision_num; i++) {
226 		name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i);
227 		/* Get the name entry from ZMS */
228 		rc = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), r_name,
229 			      sizeof(r_name) - 1);
230 		if (rc <= 0) {
231 			/* Name with current collision number doesn't exist, but there might be
232 			 * one with a higher collision number
233 			 */
234 			continue;
235 		}
236 		/* Found a name, this might not include a trailing \0 */
237 		r_name[rc] = '\0';
238 		if (strcmp(name, r_name)) {
239 			/* Names are not equal let's continue to the next collision hash
240 			 * if it exists.
241 			 */
242 			continue;
243 		}
244 		/* At this step names are equal, we found the corresponding hash */
245 		return name_hash;
246 	}
247 
248 	return 0;
249 }
250 
settings_zms_load_one(struct settings_store * cs,const char * name,char * buf,size_t buf_len)251 static ssize_t settings_zms_load_one(struct settings_store *cs, const char *name, char *buf,
252 				     size_t buf_len)
253 {
254 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
255 	uint32_t name_hash = 0;
256 	ssize_t rc = 0;
257 	uint32_t value_id;
258 
259 	/* verify that name is not NULL */
260 	if (!name || !buf) {
261 		return -EINVAL;
262 	}
263 
264 	name_hash = settings_zms_find_hash_from_name(cf, name);
265 	if (name_hash) {
266 		/* we found a name_hash corresponding to name */
267 		value_id = ZMS_DATA_ID_FROM_HASH(name_hash);
268 		rc = zms_read(&cf->cf_zms, value_id, buf, buf_len);
269 
270 		return (rc == buf_len) ? zms_get_data_length(&cf->cf_zms, value_id) : rc;
271 	}
272 
273 	return 0;
274 }
275 
276 /* Gets the next linked list node either from cache (if enabled) or from persistent
277  * storage if cache is full or cache is not enabled.
278  * It updates as well the next cache index and the next linked list node ID.
279  */
settings_zms_get_next_ll(struct settings_zms * cf,uint32_t * ll_hash_id,uint32_t * ll_cache_index __maybe_unused)280 static int settings_zms_get_next_ll(struct settings_zms *cf, uint32_t *ll_hash_id,
281 				    uint32_t *ll_cache_index __maybe_unused)
282 {
283 	struct settings_hash_linked_list settings_element;
284 	int ret = 0;
285 
286 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
287 	if (*ll_cache_index < cf->ll_cache_next) {
288 		settings_element = cf->ll_cache[*ll_cache_index];
289 		*ll_cache_index = *ll_cache_index + 1;
290 	} else if (*ll_hash_id == cf->second_to_last_hash_id) {
291 		/* The last ll node is not stored in the cache as it is already
292 		 * in the cf->last_hash_id.
293 		 */
294 		settings_element.next_hash = cf->last_hash_id;
295 	} else {
296 #endif
297 		ret = zms_read(&cf->cf_zms, *ll_hash_id, &settings_element,
298 			       sizeof(struct settings_hash_linked_list));
299 		if (ret < 0) {
300 			return ret;
301 		}
302 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
303 	}
304 #endif
305 	/* update next ll_hash_id */
306 	*ll_hash_id = settings_element.next_hash;
307 
308 	return 0;
309 }
310 
settings_zms_load(struct settings_store * cs,const struct settings_load_arg * arg)311 static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg)
312 {
313 	int ret = 0;
314 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
315 	struct settings_zms_read_fn_arg read_fn_arg;
316 	char name[SETTINGS_FULL_NAME_LEN];
317 	ssize_t rc1;
318 	ssize_t rc2;
319 	uint32_t ll_hash_id;
320 	uint32_t prev_ll_hash_id;
321 	uint32_t ll_cache_index = 0;
322 
323 #ifdef CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH
324 	/* If arg->subtree is not null we must first load settings in that subtree */
325 	if (arg->subtree != NULL) {
326 		ret = settings_zms_load_subtree(cs, arg);
327 		if (ret) {
328 			return ret;
329 		}
330 	}
331 #endif /* CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH */
332 
333 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
334 	if (cf->ll_has_changed) {
335 		/* reload the linked list in cache */
336 		ret = settings_zms_get_last_hash_ids(cf);
337 		if (ret < 0) {
338 			return ret;
339 		}
340 	}
341 #endif
342 
343 	/* Load all found Settings */
344 	ll_hash_id = ZMS_LL_HEAD_HASH_ID;
345 	ret = settings_zms_get_next_ll(cf, &ll_hash_id, &ll_cache_index);
346 	if (ret < 0) {
347 		return ret;
348 	}
349 
350 	while (ll_hash_id) {
351 		/* In the ZMS backend, each setting item is stored in two ZMS
352 		 * entries one for the setting's name and one with the
353 		 * setting's value.
354 		 */
355 		rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id), &name,
356 			       sizeof(name) - 1);
357 		/* get the length of data and verify that it exists */
358 		rc2 = zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_LL_NODE(ll_hash_id));
359 
360 		/* updated the next linked list node in case the called handler will
361 		 * delete this settings entry.
362 		 */
363 		prev_ll_hash_id = ll_hash_id;
364 		ret = settings_zms_get_next_ll(cf, &ll_hash_id, &ll_cache_index);
365 		if (ret < 0) {
366 			return ret;
367 		}
368 
369 		if ((rc1 <= 0) || (rc2 <= 0)) {
370 			/* In case we are not updating the linked list, this is an empty mode
371 			 * Just continue
372 			 */
373 #ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
374 			/* Otherwise, Settings item is not stored correctly in the ZMS.
375 			 * ZMS entry's name or value is either missing or deleted.
376 			 * Clean dirty entries to make space for future settings items.
377 			 */
378 			ret = settings_zms_delete(cf, ZMS_NAME_ID_FROM_LL_NODE(prev_ll_hash_id));
379 			if (ret < 0) {
380 				return ret;
381 			}
382 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
383 			continue;
384 		}
385 
386 		/* Found a name, this might not include a trailing \0 */
387 		name[rc1] = '\0';
388 		read_fn_arg.fs = &cf->cf_zms;
389 		read_fn_arg.id = ZMS_DATA_ID_FROM_LL_NODE(prev_ll_hash_id);
390 
391 		ret = settings_call_set_handler(name, rc2, settings_zms_read_fn, &read_fn_arg, arg);
392 		if (ret) {
393 			return ret;
394 		}
395 	}
396 
397 	return ret;
398 }
399 
settings_zms_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)400 static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
401 			     size_t val_len)
402 {
403 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
404 	struct settings_hash_linked_list settings_element;
405 	char rdname[SETTINGS_FULL_NAME_LEN];
406 	uint32_t name_hash;
407 	uint32_t collision_num = 0;
408 	bool delete;
409 	bool write_name;
410 	bool hash_collision;
411 	int rc = 0;
412 	int first_available_hash_index = -1;
413 	size_t name_len;
414 
415 	if (!name) {
416 		return -EINVAL;
417 	}
418 
419 	/* get the name length */
420 	name_len = strnlen(name, SETTINGS_FULL_NAME_LEN);
421 
422 	/* Find out if we are doing a delete */
423 	delete = ((value == NULL) || (val_len == 0));
424 
425 	name_hash = sys_hash32(name, name_len) & ZMS_HASH_MASK;
426 	/* MSB is always 1 */
427 	name_hash |= BIT(31);
428 
429 	/* Let's find out if there are hash collisions in the storage */
430 	write_name = true;
431 	hash_collision = true;
432 
433 	for (int i = 0; i <= cf->hash_collision_num; i++) {
434 		rc = zms_read(&cf->cf_zms, name_hash + i * LSB_GET(ZMS_COLLISIONS_MASK), &rdname,
435 			      sizeof(rdname) - 1);
436 		if (rc == -ENOENT) {
437 			if (first_available_hash_index < 0) {
438 				first_available_hash_index = i;
439 			}
440 			continue;
441 		} else if (rc < 0) {
442 			/* error while reading */
443 			return rc;
444 		}
445 		/* Settings entry exist, let's verify if this is the same
446 		 * name
447 		 */
448 		__ASSERT_NO_MSG(rc < sizeof(rdname));
449 
450 		rdname[rc] = '\0';
451 		if ((rc == name_len) && !memcmp(name, rdname, rc)) {
452 			/* Hash exist and the names are equal, we should
453 			 * not write the names again.
454 			 */
455 			write_name = false;
456 			name_hash += i * LSB_GET(ZMS_COLLISIONS_MASK);
457 			goto no_hash_collision;
458 		}
459 		/* At this step a Hash collision exists and names are different.
460 		 * If we are in the middle of the loop, we should continue checking
461 		 * all other possible hash collisions.
462 		 * If we reach the end of the loop, either we should select the first
463 		 * free hash value otherwise we increment it to the next free value and
464 		 * update hash_collision_num
465 		 */
466 		collision_num++;
467 	}
468 
469 	if (collision_num <= cf->hash_collision_num) {
470 		/* At this step there is a free hash found */
471 		name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, first_available_hash_index);
472 		goto no_hash_collision;
473 	} else if (collision_num > cf->hash_collision_num) {
474 		/* We must create a new hash based on incremented collision_num */
475 		if (collision_num > ZMS_MAX_COLLISIONS) {
476 			/* At this step there is no more space to store hash values */
477 			LOG_ERR("Maximum hash collisions reached");
478 			return -ENOSPC;
479 		}
480 		cf->hash_collision_num = collision_num;
481 		name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, collision_num);
482 	}
483 
484 no_hash_collision:
485 	if (delete) {
486 		if (write_name) {
487 			/* hash doesn't exist, do not write anything here */
488 			return 0;
489 		}
490 
491 		rc = settings_zms_delete(cf, name_hash);
492 		return rc;
493 	}
494 
495 	/* write the value */
496 	rc = zms_write(&cf->cf_zms, ZMS_DATA_ID_FROM_NAME(name_hash), value, val_len);
497 	if (rc < 0) {
498 		return rc;
499 	}
500 
501 	/* write the name if required */
502 	if (write_name) {
503 		/* First let's update the linked list */
504 #ifdef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
505 		/* verify that the ll_node doesn't exist otherwise do not update it */
506 		rc = zms_read(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
507 			      sizeof(struct settings_hash_linked_list));
508 		if (rc >= 0) {
509 			goto no_ll_update;
510 		} else if (rc != -ENOENT) {
511 			return rc;
512 		}
513 		/* else the LL node doesn't exist let's update it */
514 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
515 		/* write linked list structure element */
516 		settings_element.next_hash = 0;
517 		/* Verify first that the linked list last element is not broken.
518 		 * Settings subsystem uses ID that starts from ZMS_LL_HEAD_HASH_ID.
519 		 */
520 		if (cf->last_hash_id < ZMS_LL_HEAD_HASH_ID) {
521 			LOG_WRN("Linked list for hashes is broken, Trying to recover");
522 			rc = settings_zms_get_last_hash_ids(cf);
523 			if (rc < 0) {
524 				return rc;
525 			}
526 		}
527 		settings_element.previous_hash = cf->last_hash_id;
528 		rc = zms_write(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
529 			       sizeof(struct settings_hash_linked_list));
530 		if (rc < 0) {
531 			return rc;
532 		}
533 
534 		/* Now update the previous linked list element */
535 		settings_element.next_hash = ZMS_LL_NODE_FROM_NAME_ID(name_hash);
536 		settings_element.previous_hash = cf->second_to_last_hash_id;
537 		rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
538 			       sizeof(struct settings_hash_linked_list));
539 		if (rc < 0) {
540 			return rc;
541 		}
542 		cf->second_to_last_hash_id = cf->last_hash_id;
543 		cf->last_hash_id = ZMS_LL_NODE_FROM_NAME_ID(name_hash);
544 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
545 		if (cf->ll_cache_next < CONFIG_SETTINGS_ZMS_LL_CACHE_SIZE) {
546 			cf->ll_cache[cf->ll_cache_next] = settings_element;
547 			cf->ll_cache_next = cf->ll_cache_next + 1;
548 		}
549 #endif
550 #ifdef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
551 no_ll_update:
552 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
553 		/* Now let's write the name */
554 		rc = zms_write(&cf->cf_zms, name_hash, name, name_len);
555 		if (rc < 0) {
556 			return rc;
557 		}
558 	}
559 	return 0;
560 }
561 
settings_zms_get_val_len(struct settings_store * cs,const char * name)562 static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name)
563 {
564 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
565 	uint32_t name_hash = 0;
566 
567 	/* verify that name is not NULL */
568 	if (!name) {
569 		return -EINVAL;
570 	}
571 
572 	name_hash = settings_zms_find_hash_from_name(cf, name);
573 	if (name_hash) {
574 		return zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_HASH(name_hash));
575 	}
576 
577 	return 0;
578 }
579 
580 /* This function inits the linked list head if it doesn't exist or recover it
581  * if the ll_last_hash_id is different than the head hash ID
582  */
settings_zms_init_or_recover_ll(struct settings_zms * cf,uint32_t ll_last_hash_id)583 static int settings_zms_init_or_recover_ll(struct settings_zms *cf, uint32_t ll_last_hash_id)
584 {
585 	struct settings_hash_linked_list settings_element;
586 	int rc = 0;
587 
588 	if (ll_last_hash_id == ZMS_LL_HEAD_HASH_ID) {
589 		/* header doesn't exist */
590 		settings_element.previous_hash = 0;
591 		settings_element.next_hash = 0;
592 		rc = zms_write(&cf->cf_zms, ZMS_LL_HEAD_HASH_ID, &settings_element,
593 			       sizeof(struct settings_hash_linked_list));
594 		if (rc < 0) {
595 			return rc;
596 		}
597 		cf->last_hash_id = ZMS_LL_HEAD_HASH_ID;
598 		cf->second_to_last_hash_id = 0;
599 	} else {
600 		/* let's recover it by keeping all nodes until the last one */
601 		settings_element.previous_hash = cf->second_to_last_hash_id;
602 		settings_element.next_hash = 0;
603 		rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
604 			       sizeof(struct settings_hash_linked_list));
605 		if (rc < 0) {
606 			return rc;
607 		}
608 	}
609 
610 	return 0;
611 }
612 
settings_zms_get_last_hash_ids(struct settings_zms * cf)613 static int settings_zms_get_last_hash_ids(struct settings_zms *cf)
614 {
615 	struct settings_hash_linked_list settings_element;
616 	uint32_t ll_last_hash_id = ZMS_LL_HEAD_HASH_ID;
617 	uint32_t previous_ll_hash_id = 0;
618 	int rc = 0;
619 
620 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
621 	cf->ll_cache_next = 0;
622 #endif
623 	cf->hash_collision_num = 0;
624 	do {
625 		rc = zms_read(&cf->cf_zms, ll_last_hash_id, &settings_element,
626 			      sizeof(settings_element));
627 		if (rc == -ENOENT) {
628 			/* header doesn't exist or linked list broken, reinitialize the header
629 			 * if it doesn't exist and recover it if it is broken
630 			 */
631 			return settings_zms_init_or_recover_ll(cf, ll_last_hash_id);
632 		} else if (rc < 0) {
633 			return rc;
634 		}
635 
636 		if (settings_element.previous_hash != previous_ll_hash_id) {
637 			/* This is a special case that can happen when a power down occurred
638 			 * when deleting a linked list node.
639 			 * If the power down occurred after updating the previous linked list node,
640 			 * then we would end up with a state where the previous_hash of the linked
641 			 * list is broken. Let's recover from this
642 			 */
643 			rc = zms_delete(&cf->cf_zms, settings_element.previous_hash);
644 			if (rc < 0) {
645 				return rc;
646 			}
647 			/* Now recover the linked list */
648 			settings_element.previous_hash = previous_ll_hash_id;
649 			rc = zms_write(&cf->cf_zms, ll_last_hash_id, &settings_element,
650 				  sizeof(struct settings_hash_linked_list));
651 			if (rc < 0) {
652 				return rc;
653 			}
654 		}
655 		previous_ll_hash_id = ll_last_hash_id;
656 
657 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
658 		if ((cf->ll_cache_next < CONFIG_SETTINGS_ZMS_LL_CACHE_SIZE) &&
659 		    (settings_element.next_hash)) {
660 			cf->ll_cache[cf->ll_cache_next] = settings_element;
661 			cf->ll_cache_next = cf->ll_cache_next + 1;
662 		}
663 #endif
664 		/* increment hash collision number if necessary */
665 		if (ZMS_COLLISION_NUM(ll_last_hash_id) > cf->hash_collision_num) {
666 			cf->hash_collision_num = ZMS_COLLISION_NUM(ll_last_hash_id);
667 		}
668 		cf->last_hash_id = ll_last_hash_id;
669 		cf->second_to_last_hash_id = settings_element.previous_hash;
670 		ll_last_hash_id = settings_element.next_hash;
671 	} while (settings_element.next_hash);
672 
673 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
674 	cf->ll_has_changed = false;
675 #endif
676 	return 0;
677 }
678 
679 /* Initialize the zms backend. */
settings_zms_backend_init(struct settings_zms * cf)680 static int settings_zms_backend_init(struct settings_zms *cf)
681 {
682 	int rc;
683 
684 	cf->cf_zms.flash_device = cf->flash_dev;
685 	if (cf->cf_zms.flash_device == NULL) {
686 		return -ENODEV;
687 	}
688 
689 	rc = zms_mount(&cf->cf_zms);
690 	if (rc) {
691 		return rc;
692 	}
693 
694 	cf->hash_collision_num = 0;
695 
696 	rc = settings_zms_get_last_hash_ids(cf);
697 
698 	LOG_DBG("ZMS backend initialized");
699 	return rc;
700 }
701 
settings_backend_init(void)702 int settings_backend_init(void)
703 {
704 	static struct settings_zms default_settings_zms;
705 	int rc;
706 	uint32_t cnt = 0;
707 	size_t zms_sector_size;
708 	const struct flash_area *fa;
709 	struct flash_sector hw_flash_sector;
710 	uint32_t sector_cnt = 1;
711 
712 	rc = flash_area_open(SETTINGS_PARTITION, &fa);
713 	if (rc) {
714 		return rc;
715 	}
716 
717 	rc = flash_area_get_sectors(SETTINGS_PARTITION, &sector_cnt, &hw_flash_sector);
718 	if (rc != 0 && rc != -ENOMEM) {
719 		return rc;
720 	}
721 
722 	zms_sector_size = CONFIG_SETTINGS_ZMS_SECTOR_SIZE_MULT * hw_flash_sector.fs_size;
723 
724 	if (zms_sector_size > UINT32_MAX) {
725 		return -EDOM;
726 	}
727 
728 #if defined(CONFIG_SETTINGS_ZMS_CUSTOM_SECTOR_COUNT)
729 	size_t zms_size = 0;
730 
731 	while (cnt < CONFIG_SETTINGS_ZMS_SECTOR_COUNT) {
732 		zms_size += zms_sector_size;
733 		if (zms_size > fa->fa_size) {
734 			break;
735 		}
736 		cnt++;
737 	}
738 #else
739 	cnt = fa->fa_size / zms_sector_size;
740 #endif
741 	/* initialize the zms file system structure using the page_info */
742 	default_settings_zms.cf_zms.sector_size = zms_sector_size;
743 	default_settings_zms.cf_zms.sector_count = cnt;
744 	default_settings_zms.cf_zms.offset = fa->fa_off;
745 	default_settings_zms.flash_dev = fa->fa_dev;
746 
747 	rc = settings_zms_backend_init(&default_settings_zms);
748 	if (rc) {
749 		return rc;
750 	}
751 
752 	rc = settings_zms_src(&default_settings_zms);
753 
754 	if (rc) {
755 		return rc;
756 	}
757 
758 	rc = settings_zms_dst(&default_settings_zms);
759 
760 	return rc;
761 }
762 
settings_zms_storage_get(struct settings_store * cs)763 static void *settings_zms_storage_get(struct settings_store *cs)
764 {
765 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
766 
767 	return &cf->cf_zms;
768 }
769