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