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