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 <ctype.h>
9 #include <string.h>
10 
11 #include <zephyr/settings/settings.h>
12 #include "settings_priv.h"
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
16 
17 struct settings_io_cb_s {
18 	int (*read_cb)(void *ctx, off_t off, char *buf, size_t *len);
19 	int (*write_cb)(void *ctx, off_t off, char const *buf, size_t len);
20 	size_t (*get_len_cb)(void *ctx);
21 	uint8_t rwbs;
22 } static settings_io_cb;
23 
settings_line_write(const char * name,const char * value,size_t val_len,off_t w_loc,void * cb_arg)24 int settings_line_write(const char *name, const char *value, size_t val_len,
25 			off_t w_loc, void *cb_arg)
26 {
27 	size_t w_size, rem, add;
28 
29 	bool done;
30 	char w_buf[32]; /* write buff, must be aligned either to minimal */
31 			/* base64 encoding size and write-block-size */
32 	int rc;
33 	uint8_t wbs = settings_io_cb.rwbs;
34 #ifdef CONFIG_SETTINGS_ENCODE_LEN
35 	uint16_t len_field;
36 #endif
37 
38 	rem = strlen(name);
39 
40 #ifdef CONFIG_SETTINGS_ENCODE_LEN
41 	len_field = settings_line_len_calc(name, val_len);
42 	memcpy(w_buf, &len_field, sizeof(len_field));
43 	w_size = 0;
44 
45 
46 	add = sizeof(len_field) % wbs;
47 	if (add) {
48 		w_size = wbs - add;
49 		if (rem < w_size) {
50 			w_size = rem;
51 		}
52 
53 		memcpy(w_buf + sizeof(len_field), name, w_size);
54 		name += w_size;
55 		rem -= w_size;
56 	}
57 
58 	w_size += sizeof(len_field);
59 	if (w_size % wbs == 0) {
60 		rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
61 		if (rc) {
62 			return -EIO;
63 		}
64 	}
65 	/* The Alternative to condition above mean that `rem == 0` as `name` */
66 	/* must have been consumed					     */
67 #endif
68 	w_size = rem - rem % wbs;
69 	rem %= wbs;
70 
71 	rc = settings_io_cb.write_cb(cb_arg, w_loc, name, w_size);
72 	w_loc += w_size;
73 	name += w_size;
74 	w_size = rem;
75 
76 	if (rem) {
77 		memcpy(w_buf, name, rem);
78 	}
79 
80 	w_buf[rem] = '=';
81 	w_size++;
82 
83 	rem = val_len;
84 	done = false;
85 
86 	while (1) {
87 		while (w_size < sizeof(w_buf)) {
88 			if (rem) {
89 				add = MIN(rem, sizeof(w_buf) - w_size);
90 				memcpy(&w_buf[w_size], value, add);
91 				value += add;
92 				rem -= add;
93 				w_size += add;
94 			} else {
95 				add = (w_size) % wbs;
96 				if (add) {
97 					add = wbs - add;
98 					memset(&w_buf[w_size], '\0',
99 					       add);
100 					w_size += add;
101 				}
102 				done = true;
103 				break;
104 			}
105 		}
106 
107 		rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
108 		if (rc) {
109 			return -EIO;
110 		}
111 
112 		if (done) {
113 			break;
114 		}
115 		w_loc += w_size;
116 		w_size = 0;
117 	}
118 
119 	return 0;
120 }
121 
122 #ifdef CONFIG_SETTINGS_ENCODE_LEN
settings_next_line_ctx(struct line_entry_ctx * entry_ctx)123 int settings_next_line_ctx(struct line_entry_ctx *entry_ctx)
124 {
125 	size_t len_read;
126 	uint16_t readout;
127 	int rc;
128 
129 	entry_ctx->seek += entry_ctx->len; /* to begin of next line */
130 
131 	entry_ctx->len = 0; /* ask read handler to ignore len */
132 
133 	rc = settings_line_raw_read(0, (char *)&readout, sizeof(readout),
134 			   &len_read, entry_ctx);
135 	if (rc == 0) {
136 		if (len_read != sizeof(readout)) {
137 			if (len_read != 0) {
138 				rc = -ESPIPE;
139 			}
140 		} else {
141 			entry_ctx->seek += sizeof(readout);
142 			entry_ctx->len = readout;
143 		}
144 	}
145 
146 	return rc;
147 }
148 #endif
149 
settings_line_len_calc(const char * name,size_t val_len)150 int settings_line_len_calc(const char *name, size_t val_len)
151 {
152 	/* <name>=<value> */
153 	return strlen(name) + 1 + val_len;
154 }
155 
156 
157 /**
158  * Read RAW settings line entry data until a char from the storage.
159  *
160  * @param seek offset from the line beginning.
161  * @param[out] out buffer for name
162  * @param[in] len_req size of <p>out</p> buffer
163  * @param[out] len_read length of read name
164  * @param[in] until_char pointer on char value until which all line data will
165  * be read. If Null entire data will be read.
166  * @param[in] cb_arg settings line storage context expected by the
167  * <p>read_cb</p> implementation
168  *
169  * @retval 0 on success,
170  * -ERCODE on storage errors
171  */
settings_line_raw_read_until(off_t seek,char * out,size_t len_req,size_t * len_read,char const * until_char,void * cb_arg)172 static int settings_line_raw_read_until(off_t seek, char *out, size_t len_req,
173 				 size_t *len_read, char const *until_char,
174 				 void *cb_arg)
175 {
176 	size_t rem_size, len;
177 	char temp_buf[32]; /* buffer for fit read-block-size requirements */
178 	size_t exp_size, read_size;
179 	uint8_t rbs = settings_io_cb.rwbs;
180 	off_t off;
181 	int rc = -EINVAL;
182 
183 	if (len_req == 0) {
184 		return -EINVAL;
185 	}
186 
187 	rem_size = len_req;
188 
189 	while (rem_size) {
190 		off = seek / rbs * rbs;
191 
192 		read_size = sizeof(temp_buf);
193 		exp_size = read_size;
194 
195 		rc = settings_io_cb.read_cb(cb_arg, off, temp_buf, &read_size);
196 		if (rc) {
197 			return -EIO;
198 		}
199 
200 		off = seek - off;
201 		len = read_size - off;
202 		len = MIN(rem_size, len);
203 
204 		if (until_char != NULL) {
205 			char *pend;
206 			pend = memchr(&temp_buf[off], *until_char, len);
207 			if (pend != NULL) {
208 				len = pend - &temp_buf[off];
209 				rc = 1; /* will cause loop expiration */
210 			}
211 		}
212 
213 		memcpy(out, &temp_buf[off], len);
214 
215 		rem_size -= len;
216 
217 		if (exp_size > read_size || rc) {
218 			break;
219 		}
220 
221 		out += len;
222 		seek += len;
223 	}
224 
225 	*len_read = len_req - rem_size;
226 
227 	if (until_char != NULL) {
228 		return (rc) ? 0 : 1;
229 	}
230 
231 	return 0;
232 }
233 
settings_line_raw_read(off_t seek,char * out,size_t len_req,size_t * len_read,void * cb_arg)234 int settings_line_raw_read(off_t seek, char *out, size_t len_req,
235 			   size_t *len_read, void *cb_arg)
236 {
237 	return settings_line_raw_read_until(seek, out, len_req, len_read,
238 					    NULL, cb_arg);
239 }
240 
241 /* off from value begin */
settings_line_val_read(off_t val_off,off_t off,char * out,size_t len_req,size_t * len_read,void * cb_arg)242 int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
243 			   size_t *len_read, void *cb_arg)
244 {
245 	return settings_line_raw_read(val_off + off, out, len_req, len_read,
246 				      cb_arg);
247 }
248 
settings_line_val_get_len(off_t val_off,void * read_cb_ctx)249 size_t settings_line_val_get_len(off_t val_off, void *read_cb_ctx)
250 {
251 	size_t len;
252 
253 	len = settings_io_cb.get_len_cb(read_cb_ctx);
254 
255 	return len - val_off;
256 }
257 
258 /**
259  * @param line_loc offset of the settings line, expect that it is aligned to rbs physically.
260  * @param seek offset form the line beginning.
261  * @retval 0 : read proper name
262  * 1 : when read unproper name
263  * -ERCODE for storage errors
264  */
settings_line_name_read(char * out,size_t len_req,size_t * len_read,void * cb_arg)265 int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
266 			    void *cb_arg)
267 {
268 	char const until_char = '=';
269 
270 	return settings_line_raw_read_until(0, out, len_req, len_read,
271 					    &until_char, cb_arg);
272 }
273 
274 
settings_line_entry_copy(void * dst_ctx,off_t dst_off,void * src_ctx,off_t src_off,size_t len)275 int settings_line_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
276 			     off_t src_off, size_t len)
277 {
278 	int rc = -EINVAL;
279 	char buf[16];
280 	size_t chunk_size;
281 
282 	while (len) {
283 		chunk_size = MIN(len, sizeof(buf));
284 
285 		rc = settings_io_cb.read_cb(src_ctx, src_off, buf, &chunk_size);
286 		if (rc) {
287 			break;
288 		}
289 
290 		size_t write_size = chunk_size;
291 
292 		if (chunk_size % settings_io_cb.rwbs) {
293 			write_size += settings_io_cb.rwbs -
294 				      chunk_size % settings_io_cb.rwbs;
295 		}
296 
297 		rc = settings_io_cb.write_cb(dst_ctx, dst_off, buf, write_size);
298 
299 		if (rc) {
300 			break;
301 		}
302 
303 		src_off += chunk_size;
304 		dst_off += chunk_size;
305 		len -= chunk_size;
306 	}
307 
308 	return rc;
309 }
310 
settings_line_io_init(int (* read_cb)(void * ctx,off_t off,char * buf,size_t * len),int (* write_cb)(void * ctx,off_t off,char const * buf,size_t len),size_t (* get_len_cb)(void * ctx),uint8_t io_rwbs)311 void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
312 					  size_t *len),
313 			  int (*write_cb)(void *ctx, off_t off, char const *buf,
314 					  size_t len),
315 			  size_t (*get_len_cb)(void *ctx),
316 			  uint8_t io_rwbs)
317 {
318 	settings_io_cb.read_cb = read_cb;
319 	settings_io_cb.write_cb = write_cb;
320 	settings_io_cb.get_len_cb = get_len_cb;
321 	settings_io_cb.rwbs = io_rwbs;
322 }
323 
324 
325 /* val_off - offset of value-string within line entries */
settings_line_cmp(char const * val,size_t val_len,void * val_read_cb_ctx,off_t val_off)326 static int settings_line_cmp(char const *val, size_t val_len,
327 			     void *val_read_cb_ctx, off_t val_off)
328 {
329 	size_t len_read, exp_len;
330 	size_t rem;
331 	char buf[16];
332 	int rc = -EINVAL;
333 	off_t off = 0;
334 
335 	if (val_len == 0) {
336 		return -EINVAL;
337 	}
338 
339 	for (rem = val_len; rem > 0; rem -= len_read) {
340 		len_read = exp_len = MIN(sizeof(buf), rem);
341 		rc = settings_line_val_read(val_off, off, buf, len_read,
342 					    &len_read, val_read_cb_ctx);
343 		if (rc) {
344 			break;
345 		}
346 
347 		if (len_read != exp_len) {
348 			rc = 1;
349 			break;
350 		}
351 
352 		rc = memcmp(val, buf, len_read);
353 		if (rc) {
354 			break;
355 		}
356 		val += len_read;
357 		off += len_read;
358 	}
359 
360 	return rc;
361 }
362 
settings_line_dup_check_cb(const char * name,void * val_read_cb_ctx,off_t off,void * cb_arg)363 int settings_line_dup_check_cb(const char *name, void *val_read_cb_ctx,
364 				off_t off, void *cb_arg)
365 {
366 	struct settings_line_dup_check_arg *cdca;
367 	size_t len_read;
368 
369 	cdca = (struct settings_line_dup_check_arg *)cb_arg;
370 	if (strcmp(name, cdca->name)) {
371 		return 0;
372 	}
373 
374 	len_read = settings_line_val_get_len(off, val_read_cb_ctx);
375 	if (len_read != cdca->val_len) {
376 		cdca->is_dup = 0;
377 	} else if (len_read == 0) {
378 		cdca->is_dup = 1;
379 	} else {
380 		if (!settings_line_cmp(cdca->val, cdca->val_len,
381 				       val_read_cb_ctx, off)) {
382 			cdca->is_dup = 1;
383 		} else {
384 			cdca->is_dup = 0;
385 		}
386 	}
387 	return 0;
388 }
389 
settings_line_read_cb(void * cb_arg,void * data,size_t len)390 static ssize_t settings_line_read_cb(void *cb_arg, void *data, size_t len)
391 {
392 	struct settings_line_read_value_cb_ctx *value_context = cb_arg;
393 	size_t len_read;
394 	int rc;
395 
396 	rc = settings_line_val_read(value_context->off, 0, data, len,
397 				    &len_read,
398 				    value_context->read_cb_ctx);
399 
400 	if (rc == 0) {
401 		return len_read;
402 	}
403 
404 	return -1;
405 }
406 
settings_line_load_cb(const char * name,void * val_read_cb_ctx,off_t off,void * cb_arg)407 int settings_line_load_cb(const char *name, void *val_read_cb_ctx, off_t off,
408 			  void *cb_arg)
409 {
410 	size_t len;
411 	struct settings_line_read_value_cb_ctx value_ctx;
412 	struct settings_load_arg *arg = cb_arg;
413 	value_ctx.read_cb_ctx = val_read_cb_ctx;
414 	value_ctx.off = off;
415 	len = settings_line_val_get_len(off, val_read_cb_ctx);
416 
417 	return settings_call_set_handler(name, len, settings_line_read_cb,
418 					 &value_ctx, arg);
419 }
420