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