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