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 "settings/settings.h"
12 #include "settings_priv.h"
13 #include "bt_errno.h"
14 #ifdef CONFIG_SETTINGS_USE_BASE64
15 #include "base64.h"
16 #endif
17 
18 //#include <logging/log.h>
19 //LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
20 
21 struct settings_io_cb_s {
22 	int (*read_cb)(void *ctx, off_t off, char *buf, size_t *len);
23 	int (*write_cb)(void *ctx, off_t off, char const *buf, size_t len);
24 	size_t (*get_len_cb)(void *ctx);
25 	u8_t rwbs;
26 } static settings_io_cb;
27 
28 #define MAX_ENC_BLOCK_SIZE 4
29 
settings_line_write(const char * name,const char * value,size_t val_len,off_t w_loc,void * cb_arg)30 int settings_line_write(const char *name, const char *value, size_t val_len,
31 			off_t w_loc, void *cb_arg)
32 {
33 	size_t w_size, rem, add;
34 
35 #ifdef CONFIG_SETTINGS_USE_BASE64
36 	/* minimal buffer for encoding base64 + EOL*/
37 	char enc_buf[MAX_ENC_BLOCK_SIZE + 1];
38 
39 	char *p_enc = enc_buf;
40 	size_t enc_len = 0;
41 #endif
42 
43 	bool done;
44 	char w_buf[16]; /* write buff, must be aligned either to minimal */
45 			/* base64 encoding size and write-block-size */
46 	int rc;
47 	u8_t wbs = settings_io_cb.rwbs;
48 #ifdef CONFIG_SETTINGS_ENCODE_LEN
49 	u16_t len_field;
50 #endif
51 
52 	rem = strlen(name);
53 
54 #ifdef CONFIG_SETTINGS_ENCODE_LEN
55 	len_field = settings_line_len_calc(name, val_len);
56 	memcpy(w_buf, &len_field, sizeof(len_field));
57 	w_size = 0;
58 
59 
60 	add = sizeof(len_field) % wbs;
61 	if (add) {
62 		w_size = wbs - add;
63 		if (rem < w_size) {
64 			w_size = rem;
65 		}
66 
67 		memcpy(w_buf + sizeof(len_field), name, w_size);
68 		name += w_size;
69 		rem -= w_size;
70 	}
71 
72 	w_size += sizeof(len_field);
73 	if (w_size % wbs == 0) {
74 		rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
75 		if (rc) {
76 			return -EIO;
77 		}
78 	}
79 	/* The Alternative to condition above mean that `rem == 0` as `name` */
80 	/* must have been consumed					     */
81 #endif
82 	w_size = rem - rem % wbs;
83 	rem %= wbs;
84 
85 	rc = settings_io_cb.write_cb(cb_arg, w_loc, name, w_size);
86 	w_loc += w_size;
87 	name += w_size;
88 	w_size = rem;
89 
90 	if (rem) {
91 		memcpy(w_buf, name, rem);
92 	}
93 
94 	w_buf[rem] = '=';
95 	w_size++;
96 
97 	rem = val_len;
98 	done = false;
99 
100 	while (1) {
101 		while (w_size < sizeof(w_buf)) {
102 #ifdef CONFIG_SETTINGS_USE_BASE64
103 			if (enc_len) {
104 				add = MIN(enc_len, sizeof(w_buf) - w_size);
105 				memcpy(&w_buf[w_size], p_enc, add);
106 				enc_len -= add;
107 				w_size += add;
108 				p_enc += add;
109 			} else {
110 #endif
111 				if (rem) {
112 #ifdef CONFIG_SETTINGS_USE_BASE64
113 					add = MIN(rem, MAX_ENC_BLOCK_SIZE/4*3);
114 					rc = base64_encode(enc_buf, sizeof(enc_buf), &enc_len, value, add);
115 					if (rc) {
116 						return -EINVAL;
117 					}
118 					value += add;
119 					rem -= add;
120 					p_enc = enc_buf;
121 #else
122 					add = MIN(rem, sizeof(w_buf) - w_size);
123 					memcpy(&w_buf[w_size], value, add);
124 					value += add;
125 					rem -= add;
126 					w_size += add;
127 #endif
128 				} else {
129 					add = (w_size) % wbs;
130 					if (add) {
131 						add = wbs - add;
132 						memset(&w_buf[w_size], '\0',
133 						       add);
134 						w_size += add;
135 					}
136 					done = true;
137 					break;
138 				}
139 #ifdef CONFIG_SETTINGS_USE_BASE64
140 			}
141 #endif
142 		}
143 
144 		rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
145 		if (rc) {
146 			return -EIO;
147 		}
148 
149 		if (done) {
150 			break;
151 		}
152 		w_loc += w_size;
153 		w_size = 0;
154 	}
155 
156 	return 0;
157 }
158 
159 #ifdef CONFIG_SETTINGS_ENCODE_LEN
settings_next_line_ctx(struct line_entry_ctx * entry_ctx)160 int settings_next_line_ctx(struct line_entry_ctx *entry_ctx)
161 {
162 	size_t len_read;
163 	u16_t readout;
164 	int rc;
165 
166 	entry_ctx->seek += entry_ctx->len; /* to begin of nex line */
167 
168 	entry_ctx->len = 0; /* ask read handler to ignore len */
169 
170 	rc = settings_line_raw_read(0, (char *)&readout, sizeof(readout),
171 			   &len_read, entry_ctx);
172 	if (rc == 0) {
173 		if (len_read != sizeof(readout)) {
174 			if (len_read != 0) {
175 				rc = -ESPIPE;
176 			}
177 		} else {
178 			entry_ctx->seek += sizeof(readout);
179 			entry_ctx->len = readout;
180 		}
181 	}
182 
183 	return rc;
184 }
185 #endif
186 
settings_line_len_calc(const char * name,size_t val_len)187 int settings_line_len_calc(const char *name, size_t val_len)
188 {
189 	int len;
190 
191 #ifdef CONFIG_SETTINGS_USE_BASE64
192 	/* <enc(value)> */
193 	len = val_len/3*4 + ((val_len%3) ? 4 : 0);
194 #else
195 	/* <evalue> */
196 	len = val_len;
197 #endif
198 	/* <name>=<enc(value)> */
199 	len += strlen(name) + 1;
200 
201 	return len;
202 }
203 
204 
205 /**
206  * Read RAW settings line entry data until a char from the storage.
207  *
208  * @param seek offset form the line beginning.
209  * @param[out] out buffer for name
210  * @param[in] len_req size of <p>out</p> buffer
211  * @param[out] len_read length of read name
212  * @param[in] until_char pointer on char value until which all line data will
213  * be read. If Null entire data will be read.
214  * @param[in] cb_arg settings line storage context expected by the
215  * <p>read_cb</p> implementation
216  *
217  * @retval 0 on success,
218  * -ERCODE on storage errors
219  */
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)220 static int settings_line_raw_read_until(off_t seek, char *out, size_t len_req,
221 				 size_t *len_read, char const *until_char,
222 				 void *cb_arg)
223 {
224 	size_t rem_size, len;
225 	char temp_buf[16]; /* buffer for fit read-block-size requirements */
226 	size_t exp_size, read_size;
227 	u8_t rbs = settings_io_cb.rwbs;
228 	off_t off;
229 	int rc;
230 
231 	if (len_req == 0) {
232 		return -EINVAL;
233 	}
234 
235 	rem_size = len_req;
236 
237 	while (rem_size) {
238 		off = seek / rbs * rbs;
239 
240 		read_size = sizeof(temp_buf);
241 		exp_size = read_size;
242 
243 		rc = settings_io_cb.read_cb(cb_arg, off, temp_buf, &read_size);
244 		if (rc) {
245 			return -EIO;
246 		}
247 
248 		off = seek - off;
249 		len = read_size - off;
250 		len = MIN(rem_size, len);
251 
252 		if (until_char != NULL) {
253 			char *pend;
254 			pend = memchr(&temp_buf[off], *until_char, len);
255 			if (pend != NULL) {
256 				len = pend - &temp_buf[off];
257 				rc = 1; /* will cause loop expiration */
258 			}
259 		}
260 
261 		memcpy(out, &temp_buf[off], len);
262 
263 		rem_size -= len;
264 
265 		if (exp_size > read_size || rc) {
266 			break;
267 		}
268 
269 		out += len;
270 		seek += len;
271 	}
272 
273 	*len_read = len_req - rem_size;
274 
275 	if (until_char != NULL) {
276 		return (rc) ? 0 : 1;
277 	}
278 
279 	return 0;
280 }
281 
settings_line_raw_read(off_t seek,char * out,size_t len_req,size_t * len_read,void * cb_arg)282 int settings_line_raw_read(off_t seek, char *out, size_t len_req,
283 			   size_t *len_read, void *cb_arg)
284 {
285 	return settings_line_raw_read_until(seek, out, len_req, len_read,
286 					    NULL, cb_arg);
287 }
288 
289 #ifdef CONFIG_SETTINGS_USE_BASE64
290 /* 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)291 int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
292 			   size_t *len_read, void *cb_arg)
293 {
294 	char enc_buf[16 + 1];
295 	char dec_buf[sizeof(enc_buf)/4 * 3 + 1];
296 	size_t rem_size, read_size, exp_size, clen, olen;
297 	off_t seek_begin, off_begin;
298 	int rc;
299 
300 
301 	rem_size = len_req;
302 
303 	while (rem_size) {
304 		seek_begin = off / 3 * 4;
305 		off_begin = seek_begin / 4 * 3;
306 
307 		read_size = rem_size / 3 * 4;
308 		read_size += (rem_size % 3 != 0 || off_begin != off) ? 4 : 0;
309 
310 		read_size = MIN(read_size, sizeof(enc_buf) - 1);
311 		exp_size = read_size;
312 
313 		rc = settings_line_raw_read(val_off + seek_begin, enc_buf,
314 					    read_size, &read_size, cb_arg);
315 		if (rc) {
316 			return rc;
317 		}
318 
319 		enc_buf[read_size] = 0; /* breaking guaranteed */
320 		read_size = strlen(enc_buf);
321 
322 		if (read_size == 0) {
323 			/* a NULL value (deleted entry) */
324 			*len_read = 0;
325 			return 0;
326 		}
327 
328 		if (read_size % 4) {
329 			/* unexpected use case - an encoding problem */
330 			return -EINVAL;
331 		}
332 
333 		rc = base64_decode(dec_buf, sizeof(dec_buf), &olen, enc_buf,
334 				   read_size);
335 
336 		if (rc) {
337 			return rc;
338 		}
339 
340 		dec_buf[olen] = 0;
341 
342 		clen = MIN(olen + off_begin - off, rem_size);
343 
344 		memcpy(out, &dec_buf[off - off_begin], clen);
345 		rem_size -= clen;
346 
347 		if (exp_size > read_size || olen < read_size/4*3) {
348 			break;
349 		}
350 
351 		out += clen;
352 		off += clen;
353 	}
354 
355 	*len_read = len_req - rem_size;
356 
357 	return 0;
358 }
359 #else
360 
361 /* 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)362 int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
363 			   size_t *len_read, void *cb_arg)
364 {
365 	return settings_line_raw_read(val_off + off, out, len_req, len_read,
366 				      cb_arg);
367 }
368 #endif
369 
settings_line_val_get_len(off_t val_off,void * read_cb_ctx)370 size_t settings_line_val_get_len(off_t val_off, void *read_cb_ctx)
371 {
372 	size_t len;
373 
374 	len = settings_io_cb.get_len_cb(read_cb_ctx);
375 #ifdef CONFIG_SETTINGS_USE_BASE64
376 	u8_t raw[2];
377 	int rc;
378 	size_t len_base64 = len - val_off;
379 
380 	/* don't care about lack of alignmet to 4 B */
381 	/* entire value redout call will return error anyway */
382 	if (len_base64 >= 4) {
383 		/* read last 2 B of base64 */
384 		rc = settings_line_raw_read(len - 2, raw, 2, &len, read_cb_ctx);
385 		if (rc || len != 2) {
386 			/* very unexpected error */
387 			if (rc != 0) {
388 				LOG_ERR("Failed to read the storage (%d)", rc);
389 			}
390 			return 0;
391 		}
392 
393 		len = (len_base64 / 4) * 3;
394 
395 		/* '=' is the padding of Base64 */
396 		if (raw[0] == '=') {
397 			len -= 2;
398 		} else if (raw[1] == '=') {
399 			len--;
400 		}
401 
402 		return len;
403 	} else {
404 		return 0;
405 	}
406 #else
407 	return len - val_off;
408 #endif
409 }
410 
411 /**
412  * @param line_loc offset of the settings line, expect that it is aligned to rbs physically.
413  * @param seek offset form the line beginning.
414  * @retval 0 : read proper name
415  * 1 : when read unproper name
416  * -ERCODE for storage errors
417  */
settings_line_name_read(char * out,size_t len_req,size_t * len_read,void * cb_arg)418 int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
419 			    void *cb_arg)
420 {
421 	char const until_char = '=';
422 
423 	return settings_line_raw_read_until(0, out, len_req, len_read,
424 					    &until_char, cb_arg);
425 }
426 
427 
settings_line_entry_copy(void * dst_ctx,off_t dst_off,void * src_ctx,off_t src_off,size_t len)428 int settings_line_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
429 			     off_t src_off, size_t len)
430 {
431 	int rc;
432 	char buf[16];
433 	size_t chunk_size;
434 
435 	while (len) {
436 		chunk_size = MIN(len, sizeof(buf));
437 
438 		rc = settings_io_cb.read_cb(src_ctx, src_off, buf, &chunk_size);
439 		if (rc) {
440 			break;
441 		}
442 
443 		rc = settings_io_cb.write_cb(dst_ctx, dst_off, buf, chunk_size);
444 		if (rc) {
445 			break;
446 		}
447 
448 		src_off += chunk_size;
449 		dst_off += chunk_size;
450 		len -= chunk_size;
451 	}
452 
453 	return rc;
454 }
455 
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),u8_t io_rwbs)456 void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
457 					  size_t *len),
458 			  int (*write_cb)(void *ctx, off_t off, char const *buf,
459 					  size_t len),
460 			  size_t (*get_len_cb)(void *ctx),
461 			  u8_t io_rwbs)
462 {
463 	settings_io_cb.read_cb = read_cb;
464 	settings_io_cb.write_cb = write_cb;
465 	settings_io_cb.get_len_cb = get_len_cb;
466 	settings_io_cb.rwbs = io_rwbs;
467 }
468 
469 
470 /* 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)471 static int settings_line_cmp(char const *val, size_t val_len,
472 			     void *val_read_cb_ctx, off_t val_off)
473 {
474 	size_t len_read, exp_len;
475 	size_t rem;
476 	char buf[16];
477 	int rc;
478 	off_t off = 0;
479 
480 	if (val_len == 0) {
481 		return -EINVAL;
482 	}
483 
484 	for (rem = val_len; rem > 0; rem -= len_read) {
485 		len_read = exp_len = MIN(sizeof(buf), rem);
486 		rc = settings_line_val_read(val_off, off, buf, len_read,
487 					    &len_read, val_read_cb_ctx);
488 		if (rc) {
489 			break;
490 		}
491 
492 		if (len_read != exp_len) {
493 			rc = 1;
494 			break;
495 		}
496 
497 		rc = memcmp(val, buf, len_read);
498 		if (rc) {
499 			break;
500 		}
501 		val += len_read;
502 		off += len_read;
503 	}
504 
505 	return rc;
506 }
507 
settings_line_dup_check_cb(const char * name,void * val_read_cb_ctx,off_t off,void * cb_arg)508 int settings_line_dup_check_cb(const char *name, void *val_read_cb_ctx,
509 				off_t off, void *cb_arg)
510 {
511 	struct settings_line_dup_check_arg *cdca;
512 	size_t len_read;
513 
514 	cdca = (struct settings_line_dup_check_arg *)cb_arg;
515 	if (strcmp(name, cdca->name)) {
516 		return 0;
517 	}
518 
519 	len_read = settings_line_val_get_len(off, val_read_cb_ctx);
520 	if (len_read != cdca->val_len) {
521 		cdca->is_dup = 0;
522 	} else if (len_read == 0) {
523 		cdca->is_dup = 1;
524 	} else {
525 		if (!settings_line_cmp(cdca->val, cdca->val_len,
526 				       val_read_cb_ctx, off)) {
527 			cdca->is_dup = 1;
528 		} else {
529 			cdca->is_dup = 0;
530 		}
531 	}
532 	return 0;
533 }
534 
settings_line_read_cb(void * cb_arg,void * data,size_t len)535 static ssize_t settings_line_read_cb(void *cb_arg, void *data, size_t len)
536 {
537 	struct settings_line_read_value_cb_ctx *value_context = cb_arg;
538 	size_t len_read;
539 	int rc;
540 
541 	rc = settings_line_val_read(value_context->off, 0, data, len,
542 				    &len_read,
543 				    value_context->read_cb_ctx);
544 
545 	if (rc == 0) {
546 		return len_read;
547 	}
548 
549 	return -1;
550 }
551 
settings_line_load_cb(const char * name,void * val_read_cb_ctx,off_t off,void * cb_arg)552 int settings_line_load_cb(const char *name, void *val_read_cb_ctx, off_t off,
553 			  void *cb_arg)
554 {
555 	size_t len;
556 	struct settings_line_read_value_cb_ctx value_ctx;
557 	struct settings_load_arg *arg = cb_arg;
558 	value_ctx.read_cb_ctx = val_read_cb_ctx;
559 	value_ctx.off = off;
560 	len = settings_line_val_get_len(off, val_read_cb_ctx);
561 
562 	return settings_call_set_handler(name, len, settings_line_read_cb,
563 					 &value_ctx, arg);
564 }
565