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