1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the OpenThread platform abstraction for non-volatile storage of settings.
32 *
33 */
34
35 #include "openthread-posix-config.h"
36 #include "platform-posix.h"
37
38 #include <assert.h>
39 #include <fcntl.h>
40 #include <inttypes.h>
41 #include <stddef.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46
47 #include <openthread/platform/misc.h>
48 #include <openthread/platform/radio.h>
49 #include <openthread/platform/settings.h>
50 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
51 #include <openthread/platform/secure_settings.h>
52 #endif
53
54 #include "common/code_utils.hpp"
55 #include "common/encoding.hpp"
56
57 static const size_t kMaxFileNameSize = sizeof(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH) + 32;
58
59 static int sSettingsFd = -1;
60
61 static otError platformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd);
62
63 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
64 static const uint16_t *sKeys = nullptr;
65 static uint16_t sKeysLength = 0;
66
otPlatSettingsSetCriticalKeys(otInstance * aInstance,const uint16_t * aKeys,uint16_t aKeysLength)67 void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength)
68 {
69 OT_UNUSED_VARIABLE(aInstance);
70
71 sKeys = aKeys;
72 sKeysLength = aKeysLength;
73 }
74
isCriticalKey(uint16_t aKey)75 static bool isCriticalKey(uint16_t aKey)
76 {
77 bool ret = false;
78
79 VerifyOrExit(sKeys != nullptr);
80
81 for (uint16_t i = 0; i < sKeysLength; i++)
82 {
83 VerifyOrExit(aKey != sKeys[i], ret = true);
84 }
85
86 exit:
87 return ret;
88 }
89 #endif
90
getSettingsFileName(otInstance * aInstance,char aFileName[kMaxFileNameSize],bool aSwap)91 static void getSettingsFileName(otInstance *aInstance, char aFileName[kMaxFileNameSize], bool aSwap)
92 {
93 const char *offset = getenv("PORT_OFFSET");
94 uint64_t nodeId;
95
96 otPlatRadioGetIeeeEui64(aInstance, reinterpret_cast<uint8_t *>(&nodeId));
97 nodeId = ot::Encoding::BigEndian::HostSwap64(nodeId);
98 snprintf(aFileName, kMaxFileNameSize, OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/%s_%" PRIx64 ".%s",
99 offset == nullptr ? "0" : offset, nodeId, (aSwap ? "swap" : "data"));
100 }
101
swapOpen(otInstance * aInstance)102 static int swapOpen(otInstance *aInstance)
103 {
104 char fileName[kMaxFileNameSize];
105 int fd;
106
107 getSettingsFileName(aInstance, fileName, true);
108
109 fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
110 VerifyOrDie(fd != -1, OT_EXIT_ERROR_ERRNO);
111
112 return fd;
113 }
114
115 /**
116 * This function reads @p aLength bytes from the data file and appends to the swap file.
117 *
118 * @param[in] aFd The file descriptor of the current swap file.
119 * @param[in] aLength Number of bytes to copy.
120 *
121 */
swapWrite(otInstance * aInstance,int aFd,uint16_t aLength)122 static void swapWrite(otInstance *aInstance, int aFd, uint16_t aLength)
123 {
124 OT_UNUSED_VARIABLE(aInstance);
125
126 const size_t kBlockSize = 512;
127 uint8_t buffer[kBlockSize];
128
129 while (aLength > 0)
130 {
131 uint16_t count = aLength >= sizeof(buffer) ? sizeof(buffer) : aLength;
132 ssize_t rval = read(sSettingsFd, buffer, count);
133
134 VerifyOrDie(rval > 0, OT_EXIT_FAILURE);
135 count = static_cast<uint16_t>(rval);
136 rval = write(aFd, buffer, count);
137 assert(rval == count);
138 VerifyOrDie(rval == count, OT_EXIT_FAILURE);
139 aLength -= count;
140 }
141 }
142
swapPersist(otInstance * aInstance,int aFd)143 static void swapPersist(otInstance *aInstance, int aFd)
144 {
145 char swapFile[kMaxFileNameSize];
146 char dataFile[kMaxFileNameSize];
147
148 getSettingsFileName(aInstance, swapFile, true);
149 getSettingsFileName(aInstance, dataFile, false);
150
151 VerifyOrDie(0 == close(sSettingsFd), OT_EXIT_ERROR_ERRNO);
152 VerifyOrDie(0 == fsync(aFd), OT_EXIT_ERROR_ERRNO);
153 VerifyOrDie(0 == rename(swapFile, dataFile), OT_EXIT_ERROR_ERRNO);
154
155 sSettingsFd = aFd;
156 }
157
swapDiscard(otInstance * aInstance,int aFd)158 static void swapDiscard(otInstance *aInstance, int aFd)
159 {
160 char swapFileName[kMaxFileNameSize];
161
162 VerifyOrDie(0 == close(aFd), OT_EXIT_ERROR_ERRNO);
163 getSettingsFileName(aInstance, swapFileName, true);
164 VerifyOrDie(0 == unlink(swapFileName), OT_EXIT_ERROR_ERRNO);
165 }
166
otPlatSettingsInit(otInstance * aInstance)167 void otPlatSettingsInit(otInstance *aInstance)
168 {
169 otError error = OT_ERROR_NONE;
170
171 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
172 otPosixSecureSettingsInit(aInstance);
173 #endif
174
175 {
176 struct stat st;
177
178 if (stat(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, &st) == -1)
179 {
180 mkdir(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, 0755);
181 }
182 }
183
184 {
185 char fileName[kMaxFileNameSize];
186
187 getSettingsFileName(aInstance, fileName, false);
188 sSettingsFd = open(fileName, O_RDWR | O_CREAT | O_CLOEXEC, 0600);
189 }
190
191 VerifyOrDie(sSettingsFd != -1, OT_EXIT_ERROR_ERRNO);
192
193 for (off_t size = lseek(sSettingsFd, 0, SEEK_END), offset = lseek(sSettingsFd, 0, SEEK_SET); offset < size;)
194 {
195 uint16_t key;
196 uint16_t length;
197 ssize_t rval;
198
199 rval = read(sSettingsFd, &key, sizeof(key));
200 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
201
202 rval = read(sSettingsFd, &length, sizeof(length));
203 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
204
205 offset += sizeof(key) + sizeof(length) + length;
206 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
207 }
208
209 exit:
210 if (error == OT_ERROR_PARSE)
211 {
212 VerifyOrDie(ftruncate(sSettingsFd, 0) == 0, OT_EXIT_ERROR_ERRNO);
213 }
214 }
215
otPlatSettingsDeinit(otInstance * aInstance)216 void otPlatSettingsDeinit(otInstance *aInstance)
217 {
218 OT_UNUSED_VARIABLE(aInstance);
219
220 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
221 otPosixSecureSettingsDeinit(aInstance);
222 #endif
223
224 assert(sSettingsFd != -1);
225 VerifyOrDie(close(sSettingsFd) == 0, OT_EXIT_ERROR_ERRNO);
226 }
227
otPlatSettingsGet(otInstance * aInstance,uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength)228 otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
229 {
230 OT_UNUSED_VARIABLE(aInstance);
231
232 otError error = OT_ERROR_NOT_FOUND;
233 const off_t size = lseek(sSettingsFd, 0, SEEK_END);
234 off_t offset = lseek(sSettingsFd, 0, SEEK_SET);
235
236 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
237 if (isCriticalKey(aKey))
238 {
239 ExitNow(error = otPosixSecureSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength));
240 }
241 #endif
242
243 VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
244
245 while (offset < size)
246 {
247 uint16_t key;
248 uint16_t length;
249 ssize_t rval;
250
251 rval = read(sSettingsFd, &key, sizeof(key));
252 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
253
254 rval = read(sSettingsFd, &length, sizeof(length));
255 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
256
257 if (key == aKey)
258 {
259 if (aIndex == 0)
260 {
261 error = OT_ERROR_NONE;
262
263 if (aValueLength)
264 {
265 if (aValue)
266 {
267 uint16_t readLength = (length <= *aValueLength ? length : *aValueLength);
268
269 VerifyOrExit(read(sSettingsFd, aValue, readLength) == readLength, error = OT_ERROR_PARSE);
270 }
271
272 *aValueLength = length;
273 }
274
275 break;
276 }
277 else
278 {
279 --aIndex;
280 }
281 }
282
283 offset += sizeof(key) + sizeof(length) + length;
284 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
285 }
286
287 exit:
288 VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
289 return error;
290 }
291
otPlatSettingsSet(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)292 otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
293 {
294 int swapFd = -1;
295 otError error = OT_ERROR_NONE;
296
297 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
298 if (isCriticalKey(aKey))
299 {
300 ExitNow(error = otPosixSecureSettingsSet(aInstance, aKey, aValue, aValueLength));
301 }
302 #endif
303
304 switch (platformSettingsDelete(aInstance, aKey, -1, &swapFd))
305 {
306 case OT_ERROR_NONE:
307 case OT_ERROR_NOT_FOUND:
308 break;
309
310 default:
311 assert(false);
312 break;
313 }
314
315 VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
316 write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
317 write(swapFd, aValue, aValueLength) == aValueLength,
318 OT_EXIT_FAILURE);
319
320 swapPersist(aInstance, swapFd);
321
322 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
323 exit:
324 #endif
325 return error;
326 }
327
otPlatSettingsAdd(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)328 otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
329 {
330 OT_UNUSED_VARIABLE(aInstance);
331
332 otError error = OT_ERROR_NONE;
333 off_t size = lseek(sSettingsFd, 0, SEEK_END);
334 int swapFd = swapOpen(aInstance);
335
336 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
337 if (isCriticalKey(aKey))
338 {
339 ExitNow(error = otPosixSecureSettingsAdd(aInstance, aKey, aValue, aValueLength));
340 }
341 #endif
342
343 if (size > 0)
344 {
345 VerifyOrDie(0 == lseek(sSettingsFd, 0, SEEK_SET), OT_EXIT_ERROR_ERRNO);
346 swapWrite(aInstance, swapFd, static_cast<uint16_t>(size));
347 }
348
349 VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
350 write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
351 write(swapFd, aValue, aValueLength) == aValueLength,
352 OT_EXIT_FAILURE);
353
354 swapPersist(aInstance, swapFd);
355
356 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
357 exit:
358 #endif
359 return error;
360 }
361
otPlatSettingsDelete(otInstance * aInstance,uint16_t aKey,int aIndex)362 otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
363 {
364 otError error;
365
366 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
367 if (isCriticalKey(aKey))
368 {
369 error = otPosixSecureSettingsDelete(aInstance, aKey, aIndex);
370 }
371 else
372 #endif
373 {
374 error = platformSettingsDelete(aInstance, aKey, aIndex, nullptr);
375 }
376
377 return error;
378 }
379
380 /**
381 * This function removes a setting either from swap file or persisted file.
382 *
383 * @param[in] aInstance The OpenThread instance structure.
384 * @param[in] aKey The key associated with the requested setting.
385 * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this aKey will be removed.
386 * @param[out] aSwapFd A optional pointer to receive file descriptor of the generated swap file descriptor.
387 *
388 * @note
389 * If @p aSwapFd is null, operate deleting on the setting file.
390 * If @p aSwapFd is not null, operate on the swap file, and aSwapFd will point to the swap file descriptor.
391 *
392 * @retval OT_ERROR_NONE The given key and index was found and removed successfully.
393 * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store.
394 *
395 */
platformSettingsDelete(otInstance * aInstance,uint16_t aKey,int aIndex,int * aSwapFd)396 static otError platformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd)
397 {
398 OT_UNUSED_VARIABLE(aInstance);
399
400 otError error = OT_ERROR_NOT_FOUND;
401 off_t size = lseek(sSettingsFd, 0, SEEK_END);
402 off_t offset = lseek(sSettingsFd, 0, SEEK_SET);
403 int swapFd = swapOpen(aInstance);
404
405 assert(swapFd != -1);
406 assert(offset == 0);
407 VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
408
409 while (offset < size)
410 {
411 uint16_t key;
412 uint16_t length;
413 ssize_t rval;
414
415 rval = read(sSettingsFd, &key, sizeof(key));
416 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
417
418 rval = read(sSettingsFd, &length, sizeof(length));
419 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
420
421 offset += sizeof(key) + sizeof(length) + length;
422
423 if (aKey == key)
424 {
425 if (aIndex == 0)
426 {
427 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
428 swapWrite(aInstance, swapFd, static_cast<uint16_t>(size - offset));
429 error = OT_ERROR_NONE;
430 break;
431 }
432 else if (aIndex == -1)
433 {
434 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
435 error = OT_ERROR_NONE;
436 continue;
437 }
438 else
439 {
440 --aIndex;
441 }
442 }
443
444 rval = write(swapFd, &key, sizeof(key));
445 assert(rval == sizeof(key));
446 VerifyOrDie(rval == sizeof(key), OT_EXIT_FAILURE);
447
448 rval = write(swapFd, &length, sizeof(length));
449 assert(rval == sizeof(length));
450 VerifyOrDie(rval == sizeof(length), OT_EXIT_FAILURE);
451
452 swapWrite(aInstance, swapFd, length);
453 }
454
455 exit:
456 VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
457
458 if (aSwapFd != nullptr)
459 {
460 *aSwapFd = swapFd;
461 }
462 else if (error == OT_ERROR_NONE)
463 {
464 swapPersist(aInstance, swapFd);
465 }
466 else if (error == OT_ERROR_NOT_FOUND)
467 {
468 swapDiscard(aInstance, swapFd);
469 }
470
471 return error;
472 }
473
otPlatSettingsWipe(otInstance * aInstance)474 void otPlatSettingsWipe(otInstance *aInstance)
475 {
476 OT_UNUSED_VARIABLE(aInstance);
477 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
478 otPosixSecureSettingsWipe(aInstance);
479 #endif
480
481 VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO);
482 }
483
484 #ifndef SELF_TEST
485 #define SELF_TEST 0
486 #endif
487
488 #if SELF_TEST
489
otPlatRadioGetIeeeEui64(otInstance * aInstance,uint8_t * aIeeeEui64)490 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
491 {
492 OT_UNUSED_VARIABLE(aInstance);
493
494 memset(aIeeeEui64, 0, sizeof(uint64_t));
495 }
496
main()497 int main()
498 {
499 otInstance *instance = nullptr;
500 uint8_t data[60];
501
502 for (uint8_t i = 0; i < sizeof(data); ++i)
503 {
504 data[i] = i;
505 }
506
507 otPlatSettingsInit(instance);
508
509 // verify empty situation
510 otPlatSettingsWipe(instance);
511 {
512 uint8_t value[sizeof(data)];
513 uint16_t length = sizeof(value);
514
515 assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NOT_FOUND);
516 assert(otPlatSettingsDelete(instance, 0, 0) == OT_ERROR_NOT_FOUND);
517 assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NOT_FOUND);
518 }
519
520 // verify write one record
521 assert(otPlatSettingsSet(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
522 {
523 uint8_t value[sizeof(data)];
524 uint16_t length = sizeof(value);
525
526 assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NONE);
527 assert(otPlatSettingsGet(instance, 0, 0, nullptr, &length) == OT_ERROR_NONE);
528 assert(length == sizeof(data) / 2);
529
530 length = sizeof(value);
531 assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
532 assert(length == sizeof(data) / 2);
533 assert(0 == memcmp(value, data, length));
534
535 // insufficient buffer
536 length -= 1;
537 value[length] = 0;
538 assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
539 // verify length becomes the actual length of the record
540 assert(length == sizeof(data) / 2);
541 // verify this byte is not changed
542 assert(value[length] == 0);
543
544 // wrong index
545 assert(otPlatSettingsGet(instance, 0, 1, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
546 // wrong key
547 assert(otPlatSettingsGet(instance, 1, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
548 }
549 otPlatSettingsWipe(instance);
550
551 // verify write two records
552 assert(otPlatSettingsSet(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
553 assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
554 {
555 uint8_t value[sizeof(data)];
556 uint16_t length = sizeof(value);
557
558 assert(otPlatSettingsGet(instance, 0, 1, value, &length) == OT_ERROR_NONE);
559 assert(length == sizeof(data) / 2);
560 assert(0 == memcmp(value, data, length));
561
562 length = sizeof(value);
563 assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
564 assert(length == sizeof(data));
565 assert(0 == memcmp(value, data, length));
566 }
567 otPlatSettingsWipe(instance);
568
569 // verify write two records of different keys
570 assert(otPlatSettingsSet(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
571 assert(otPlatSettingsAdd(instance, 1, data, sizeof(data) / 2) == OT_ERROR_NONE);
572 {
573 uint8_t value[sizeof(data)];
574 uint16_t length = sizeof(value);
575
576 assert(otPlatSettingsGet(instance, 1, 0, value, &length) == OT_ERROR_NONE);
577 assert(length == sizeof(data) / 2);
578 assert(0 == memcmp(value, data, length));
579
580 length = sizeof(value);
581 assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
582 assert(length == sizeof(data));
583 assert(0 == memcmp(value, data, length));
584 }
585 otPlatSettingsWipe(instance);
586
587 // verify delete record
588 assert(otPlatSettingsAdd(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
589 assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
590 assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 3) == OT_ERROR_NONE);
591 {
592 uint8_t value[sizeof(data)];
593 uint16_t length = sizeof(value);
594
595 // wrong key
596 assert(otPlatSettingsDelete(instance, 1, 0) == OT_ERROR_NOT_FOUND);
597 assert(otPlatSettingsDelete(instance, 1, -1) == OT_ERROR_NOT_FOUND);
598
599 // wrong index
600 assert(otPlatSettingsDelete(instance, 0, 3) == OT_ERROR_NOT_FOUND);
601
602 // delete one record
603 assert(otPlatSettingsDelete(instance, 0, 1) == OT_ERROR_NONE);
604 assert(otPlatSettingsGet(instance, 0, 1, value, &length) == OT_ERROR_NONE);
605 assert(length == sizeof(data) / 3);
606 assert(0 == memcmp(value, data, length));
607
608 // delete all records
609 assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NONE);
610 assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
611 }
612 otPlatSettingsWipe(instance);
613
614 // verify delete all records of a type
615 assert(otPlatSettingsAdd(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
616 assert(otPlatSettingsAdd(instance, 1, data, sizeof(data) / 2) == OT_ERROR_NONE);
617 assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 3) == OT_ERROR_NONE);
618 {
619 uint8_t value[sizeof(data)];
620 uint16_t length = sizeof(value);
621
622 assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NONE);
623 assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NOT_FOUND);
624 assert(otPlatSettingsGet(instance, 1, 0, value, &length) == OT_ERROR_NONE);
625 assert(length == sizeof(data) / 2);
626 assert(0 == memcmp(value, data, length));
627
628 assert(otPlatSettingsDelete(instance, 0, 0) == OT_ERROR_NOT_FOUND);
629 assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
630 }
631 otPlatSettingsWipe(instance);
632 otPlatSettingsDeinit(instance);
633
634 return 0;
635 }
636 #endif
637