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/logging.h>
48 #include <openthread/platform/misc.h>
49 #include <openthread/platform/radio.h>
50 #include <openthread/platform/settings.h>
51 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
52 #include <openthread/platform/secure_settings.h>
53 #endif
54 
55 #include "common/code_utils.hpp"
56 #include "common/encoding.hpp"
57 #include "posix/platform/settings.hpp"
58 
59 #include "system.hpp"
60 
61 static const size_t kMaxFileNameSize = sizeof(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH) + 32;
62 
63 static int sSettingsFd = -1;
64 
65 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
66 static const uint16_t *sSensitiveKeys       = nullptr;
67 static uint16_t        sSensitiveKeysLength = 0;
68 
isSensitiveKey(uint16_t aKey)69 static bool isSensitiveKey(uint16_t aKey)
70 {
71     bool ret = false;
72 
73     VerifyOrExit(sSensitiveKeys != nullptr);
74 
75     for (uint16_t i = 0; i < sSensitiveKeysLength; i++)
76     {
77         VerifyOrExit(aKey != sSensitiveKeys[i], ret = true);
78     }
79 
80 exit:
81     return ret;
82 }
83 #endif
84 
getSettingsFileName(otInstance * aInstance,char aFileName[kMaxFileNameSize],bool aSwap)85 static void getSettingsFileName(otInstance *aInstance, char aFileName[kMaxFileNameSize], bool aSwap)
86 {
87     const char *offset = getenv("PORT_OFFSET");
88     uint64_t    nodeId;
89 
90     otPlatRadioGetIeeeEui64(aInstance, reinterpret_cast<uint8_t *>(&nodeId));
91     nodeId = ot::Encoding::BigEndian::HostSwap64(nodeId);
92     snprintf(aFileName, kMaxFileNameSize, OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/%s_%" PRIx64 ".%s",
93              offset == nullptr ? "0" : offset, nodeId, (aSwap ? "swap" : "data"));
94 }
95 
swapOpen(otInstance * aInstance)96 static int swapOpen(otInstance *aInstance)
97 {
98     char fileName[kMaxFileNameSize];
99     int  fd;
100 
101     getSettingsFileName(aInstance, fileName, true);
102 
103     fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
104     VerifyOrDie(fd != -1, OT_EXIT_ERROR_ERRNO);
105 
106     return fd;
107 }
108 
109 /**
110  * This function reads @p aLength bytes from the data file and appends to the swap file.
111  *
112  * @param[in]   aFd     The file descriptor of the current swap file.
113  * @param[in]   aLength Number of bytes to copy.
114  *
115  */
swapWrite(otInstance * aInstance,int aFd,uint16_t aLength)116 static void swapWrite(otInstance *aInstance, int aFd, uint16_t aLength)
117 {
118     OT_UNUSED_VARIABLE(aInstance);
119 
120     const size_t kBlockSize = 512;
121     uint8_t      buffer[kBlockSize];
122 
123     while (aLength > 0)
124     {
125         uint16_t count = aLength >= sizeof(buffer) ? sizeof(buffer) : aLength;
126         ssize_t  rval  = read(sSettingsFd, buffer, count);
127 
128         VerifyOrDie(rval > 0, OT_EXIT_FAILURE);
129         count = static_cast<uint16_t>(rval);
130         rval  = write(aFd, buffer, count);
131         assert(rval == count);
132         VerifyOrDie(rval == count, OT_EXIT_FAILURE);
133         aLength -= count;
134     }
135 }
136 
swapPersist(otInstance * aInstance,int aFd)137 static void swapPersist(otInstance *aInstance, int aFd)
138 {
139     char swapFile[kMaxFileNameSize];
140     char dataFile[kMaxFileNameSize];
141 
142     getSettingsFileName(aInstance, swapFile, true);
143     getSettingsFileName(aInstance, dataFile, false);
144 
145     VerifyOrDie(0 == close(sSettingsFd), OT_EXIT_ERROR_ERRNO);
146     VerifyOrDie(0 == fsync(aFd), OT_EXIT_ERROR_ERRNO);
147     VerifyOrDie(0 == rename(swapFile, dataFile), OT_EXIT_ERROR_ERRNO);
148 
149     sSettingsFd = aFd;
150 }
151 
swapDiscard(otInstance * aInstance,int aFd)152 static void swapDiscard(otInstance *aInstance, int aFd)
153 {
154     char swapFileName[kMaxFileNameSize];
155 
156     VerifyOrDie(0 == close(aFd), OT_EXIT_ERROR_ERRNO);
157     getSettingsFileName(aInstance, swapFileName, true);
158     VerifyOrDie(0 == unlink(swapFileName), OT_EXIT_ERROR_ERRNO);
159 }
160 
otPlatSettingsInit(otInstance * aInstance,const uint16_t * aSensitiveKeys,uint16_t aSensitiveKeysLength)161 void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength)
162 {
163 #if !OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
164     OT_UNUSED_VARIABLE(aSensitiveKeys);
165     OT_UNUSED_VARIABLE(aSensitiveKeysLength);
166 #endif
167 
168     otError error = OT_ERROR_NONE;
169 
170 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
171     sSensitiveKeys       = aSensitiveKeys;
172     sSensitiveKeysLength = aSensitiveKeysLength;
173 #endif
174 
175     // Don't touch the settings file the system runs in dry-run mode.
176     VerifyOrExit(!IsSystemDryRun());
177 
178     {
179         struct stat st;
180 
181         if (stat(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, &st) == -1)
182         {
183             VerifyOrDie(mkdir(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, 0755) == 0, OT_EXIT_ERROR_ERRNO);
184         }
185     }
186 
187     {
188         char fileName[kMaxFileNameSize];
189 
190         getSettingsFileName(aInstance, fileName, false);
191         sSettingsFd = open(fileName, O_RDWR | O_CREAT | O_CLOEXEC, 0600);
192     }
193 
194     VerifyOrDie(sSettingsFd != -1, OT_EXIT_ERROR_ERRNO);
195 
196     for (off_t size = lseek(sSettingsFd, 0, SEEK_END), offset = lseek(sSettingsFd, 0, SEEK_SET); offset < size;)
197     {
198         uint16_t key;
199         uint16_t length;
200         ssize_t  rval;
201 
202         rval = read(sSettingsFd, &key, sizeof(key));
203         VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
204 
205         rval = read(sSettingsFd, &length, sizeof(length));
206         VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
207 
208         offset += sizeof(key) + sizeof(length) + length;
209         VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
210     }
211 
212 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
213     otPosixSecureSettingsInit(aInstance);
214 #endif
215 
216 exit:
217     if (error == OT_ERROR_PARSE)
218     {
219         VerifyOrDie(ftruncate(sSettingsFd, 0) == 0, OT_EXIT_ERROR_ERRNO);
220     }
221 }
222 
otPlatSettingsDeinit(otInstance * aInstance)223 void otPlatSettingsDeinit(otInstance *aInstance)
224 {
225     OT_UNUSED_VARIABLE(aInstance);
226 
227     VerifyOrExit(!IsSystemDryRun());
228 
229 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
230     otPosixSecureSettingsDeinit(aInstance);
231 #endif
232 
233     VerifyOrExit(sSettingsFd != -1);
234     VerifyOrDie(close(sSettingsFd) == 0, OT_EXIT_ERROR_ERRNO);
235 
236 exit:
237     return;
238 }
239 
otPlatSettingsGet(otInstance * aInstance,uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength)240 otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
241 {
242     OT_UNUSED_VARIABLE(aInstance);
243 
244     otError error = OT_ERROR_NOT_FOUND;
245 
246     VerifyOrExit(!IsSystemDryRun());
247 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
248     if (isSensitiveKey(aKey))
249     {
250         error = otPosixSecureSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength);
251     }
252     else
253 #endif
254     {
255         error = ot::Posix::PlatformSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength);
256     }
257 
258 exit:
259     VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
260     return error;
261 }
262 
otPlatSettingsSet(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)263 otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
264 {
265     otError error = OT_ERROR_NONE;
266 
267 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
268     if (isSensitiveKey(aKey))
269     {
270         error = otPosixSecureSettingsSet(aInstance, aKey, aValue, aValueLength);
271     }
272     else
273 #endif
274     {
275         ot::Posix::PlatformSettingsSet(aInstance, aKey, aValue, aValueLength);
276     }
277 
278     return error;
279 }
280 
otPlatSettingsAdd(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)281 otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
282 {
283     OT_UNUSED_VARIABLE(aInstance);
284 
285     otError error = OT_ERROR_NONE;
286 
287 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
288     if (isSensitiveKey(aKey))
289     {
290         error = otPosixSecureSettingsAdd(aInstance, aKey, aValue, aValueLength);
291     }
292     else
293 #endif
294     {
295         ot::Posix::PlatformSettingsAdd(aInstance, aKey, aValue, aValueLength);
296     }
297 
298     return error;
299 }
300 
otPlatSettingsDelete(otInstance * aInstance,uint16_t aKey,int aIndex)301 otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
302 {
303     otError error;
304 
305 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
306     if (isSensitiveKey(aKey))
307     {
308         error = otPosixSecureSettingsDelete(aInstance, aKey, aIndex);
309     }
310     else
311 #endif
312     {
313         error = ot::Posix::PlatformSettingsDelete(aInstance, aKey, aIndex, nullptr);
314     }
315 
316     return error;
317 }
318 
otPlatSettingsWipe(otInstance * aInstance)319 void otPlatSettingsWipe(otInstance *aInstance)
320 {
321     OT_UNUSED_VARIABLE(aInstance);
322 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
323     otPosixSecureSettingsWipe(aInstance);
324 #endif
325 
326     VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO);
327 }
328 
329 namespace ot {
330 namespace Posix {
331 
PlatformSettingsGet(otInstance * aInstance,uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength)332 otError PlatformSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
333 {
334     OT_UNUSED_VARIABLE(aInstance);
335 
336     otError     error  = OT_ERROR_NOT_FOUND;
337     const off_t size   = lseek(sSettingsFd, 0, SEEK_END);
338     off_t       offset = lseek(sSettingsFd, 0, SEEK_SET);
339 
340     VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
341 
342     while (offset < size)
343     {
344         uint16_t key;
345         uint16_t length;
346         ssize_t  rval;
347 
348         rval = read(sSettingsFd, &key, sizeof(key));
349         VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
350 
351         rval = read(sSettingsFd, &length, sizeof(length));
352         VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
353 
354         if (key == aKey)
355         {
356             if (aIndex == 0)
357             {
358                 error = OT_ERROR_NONE;
359 
360                 if (aValueLength)
361                 {
362                     if (aValue)
363                     {
364                         uint16_t readLength = (length <= *aValueLength ? length : *aValueLength);
365 
366                         VerifyOrExit(read(sSettingsFd, aValue, readLength) == readLength, error = OT_ERROR_PARSE);
367                     }
368 
369                     *aValueLength = length;
370                 }
371 
372                 break;
373             }
374             else
375             {
376                 --aIndex;
377             }
378         }
379 
380         offset += sizeof(key) + sizeof(length) + length;
381         VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
382     }
383 
384 exit:
385     return error;
386 }
387 
PlatformSettingsSet(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)388 void PlatformSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
389 {
390     int swapFd = -1;
391 
392     switch (PlatformSettingsDelete(aInstance, aKey, -1, &swapFd))
393     {
394     case OT_ERROR_NONE:
395     case OT_ERROR_NOT_FOUND:
396         break;
397 
398     default:
399         assert(false);
400         break;
401     }
402 
403     VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
404                     write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
405                     write(swapFd, aValue, aValueLength) == aValueLength,
406                 OT_EXIT_FAILURE);
407 
408     swapPersist(aInstance, swapFd);
409 }
410 
PlatformSettingsAdd(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)411 void PlatformSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
412 {
413     off_t size   = lseek(sSettingsFd, 0, SEEK_END);
414     int   swapFd = swapOpen(aInstance);
415 
416     if (size > 0)
417     {
418         VerifyOrDie(0 == lseek(sSettingsFd, 0, SEEK_SET), OT_EXIT_ERROR_ERRNO);
419         swapWrite(aInstance, swapFd, static_cast<uint16_t>(size));
420     }
421 
422     VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
423                     write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
424                     write(swapFd, aValue, aValueLength) == aValueLength,
425                 OT_EXIT_FAILURE);
426 
427     swapPersist(aInstance, swapFd);
428 }
429 
PlatformSettingsDelete(otInstance * aInstance,uint16_t aKey,int aIndex,int * aSwapFd)430 otError PlatformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd)
431 {
432     otError error  = OT_ERROR_NOT_FOUND;
433     off_t   size   = lseek(sSettingsFd, 0, SEEK_END);
434     off_t   offset = lseek(sSettingsFd, 0, SEEK_SET);
435     int     swapFd = swapOpen(aInstance);
436 
437     assert(swapFd != -1);
438     assert(offset == 0);
439     VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
440 
441     while (offset < size)
442     {
443         uint16_t key;
444         uint16_t length;
445         ssize_t  rval;
446 
447         rval = read(sSettingsFd, &key, sizeof(key));
448         VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
449 
450         rval = read(sSettingsFd, &length, sizeof(length));
451         VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
452 
453         offset += sizeof(key) + sizeof(length) + length;
454 
455         if (aKey == key)
456         {
457             if (aIndex == 0)
458             {
459                 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
460                 swapWrite(aInstance, swapFd, static_cast<uint16_t>(size - offset));
461                 error = OT_ERROR_NONE;
462                 break;
463             }
464             else if (aIndex == -1)
465             {
466                 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
467                 error = OT_ERROR_NONE;
468                 continue;
469             }
470             else
471             {
472                 --aIndex;
473             }
474         }
475 
476         rval = write(swapFd, &key, sizeof(key));
477         assert(rval == sizeof(key));
478         VerifyOrDie(rval == sizeof(key), OT_EXIT_FAILURE);
479 
480         rval = write(swapFd, &length, sizeof(length));
481         assert(rval == sizeof(length));
482         VerifyOrDie(rval == sizeof(length), OT_EXIT_FAILURE);
483 
484         swapWrite(aInstance, swapFd, length);
485     }
486 
487 exit:
488     VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
489 
490     if (aSwapFd != nullptr)
491     {
492         *aSwapFd = swapFd;
493     }
494     else if (error == OT_ERROR_NONE)
495     {
496         swapPersist(aInstance, swapFd);
497     }
498     else if (error == OT_ERROR_NOT_FOUND)
499     {
500         swapDiscard(aInstance, swapFd);
501     }
502 
503     return error;
504 }
505 
506 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
PlatformSettingsGetSensitiveKeys(otInstance * aInstance,const uint16_t ** aKeys,uint16_t * aKeysLength)507 void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aKeys, uint16_t *aKeysLength)
508 {
509     OT_UNUSED_VARIABLE(aInstance);
510 
511     assert(aKeys != nullptr);
512     assert(aKeysLength != nullptr);
513 
514     *aKeys       = sSensitiveKeys;
515     *aKeysLength = sSensitiveKeysLength;
516 }
517 #endif
518 
519 } // namespace Posix
520 } // namespace ot
521 
522 #ifndef SELF_TEST
523 #define SELF_TEST 0
524 #endif
525 
526 #if SELF_TEST
527 
otLogCritPlat(const char * aFormat,...)528 void otLogCritPlat(const char *aFormat, ...) { OT_UNUSED_VARIABLE(aFormat); }
529 
otExitCodeToString(uint8_t aExitCode)530 const char *otExitCodeToString(uint8_t aExitCode)
531 {
532     OT_UNUSED_VARIABLE(aExitCode);
533     return "";
534 }
535 
otPlatRadioGetIeeeEui64(otInstance * aInstance,uint8_t * aIeeeEui64)536 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
537 {
538     OT_UNUSED_VARIABLE(aInstance);
539 
540     memset(aIeeeEui64, 0, sizeof(uint64_t));
541 }
542 
543 // Stub implementation for testing
IsSystemDryRun(void)544 bool IsSystemDryRun(void) { return false; }
545 
main()546 int main()
547 {
548     otInstance *instance = nullptr;
549     uint8_t     data[60];
550 
551     for (uint8_t i = 0; i < sizeof(data); ++i)
552     {
553         data[i] = i;
554     }
555 
556     otPlatSettingsInit(instance, nullptr, 0);
557 
558     // verify empty situation
559     otPlatSettingsWipe(instance);
560     {
561         uint8_t  value[sizeof(data)];
562         uint16_t length = sizeof(value);
563 
564         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NOT_FOUND);
565         assert(otPlatSettingsDelete(instance, 0, 0) == OT_ERROR_NOT_FOUND);
566         assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NOT_FOUND);
567     }
568 
569     // verify write one record
570     assert(otPlatSettingsSet(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
571     {
572         uint8_t  value[sizeof(data)];
573         uint16_t length = sizeof(value);
574 
575         assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NONE);
576         assert(otPlatSettingsGet(instance, 0, 0, nullptr, &length) == OT_ERROR_NONE);
577         assert(length == sizeof(data) / 2);
578 
579         length = sizeof(value);
580         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
581         assert(length == sizeof(data) / 2);
582         assert(0 == memcmp(value, data, length));
583 
584         // insufficient buffer
585         length -= 1;
586         value[length] = 0;
587         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
588         // verify length becomes the actual length of the record
589         assert(length == sizeof(data) / 2);
590         // verify this byte is not changed
591         assert(value[length] == 0);
592 
593         // wrong index
594         assert(otPlatSettingsGet(instance, 0, 1, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
595         // wrong key
596         assert(otPlatSettingsGet(instance, 1, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
597     }
598     otPlatSettingsWipe(instance);
599 
600     // verify write two records
601     assert(otPlatSettingsSet(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
602     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
603     {
604         uint8_t  value[sizeof(data)];
605         uint16_t length = sizeof(value);
606 
607         assert(otPlatSettingsGet(instance, 0, 1, value, &length) == OT_ERROR_NONE);
608         assert(length == sizeof(data) / 2);
609         assert(0 == memcmp(value, data, length));
610 
611         length = sizeof(value);
612         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
613         assert(length == sizeof(data));
614         assert(0 == memcmp(value, data, length));
615     }
616     otPlatSettingsWipe(instance);
617 
618     // verify write two records of different keys
619     assert(otPlatSettingsSet(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
620     assert(otPlatSettingsAdd(instance, 1, data, sizeof(data) / 2) == OT_ERROR_NONE);
621     {
622         uint8_t  value[sizeof(data)];
623         uint16_t length = sizeof(value);
624 
625         assert(otPlatSettingsGet(instance, 1, 0, value, &length) == OT_ERROR_NONE);
626         assert(length == sizeof(data) / 2);
627         assert(0 == memcmp(value, data, length));
628 
629         length = sizeof(value);
630         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
631         assert(length == sizeof(data));
632         assert(0 == memcmp(value, data, length));
633     }
634     otPlatSettingsWipe(instance);
635 
636     // verify delete record
637     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
638     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
639     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 3) == OT_ERROR_NONE);
640     {
641         uint8_t  value[sizeof(data)];
642         uint16_t length = sizeof(value);
643 
644         // wrong key
645         assert(otPlatSettingsDelete(instance, 1, 0) == OT_ERROR_NOT_FOUND);
646         assert(otPlatSettingsDelete(instance, 1, -1) == OT_ERROR_NOT_FOUND);
647 
648         // wrong index
649         assert(otPlatSettingsDelete(instance, 0, 3) == OT_ERROR_NOT_FOUND);
650 
651         // delete one record
652         assert(otPlatSettingsDelete(instance, 0, 1) == OT_ERROR_NONE);
653         assert(otPlatSettingsGet(instance, 0, 1, value, &length) == OT_ERROR_NONE);
654         assert(length == sizeof(data) / 3);
655         assert(0 == memcmp(value, data, length));
656 
657         // delete all records
658         assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NONE);
659         assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
660     }
661     otPlatSettingsWipe(instance);
662 
663     // verify delete all records of a type
664     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
665     assert(otPlatSettingsAdd(instance, 1, data, sizeof(data) / 2) == OT_ERROR_NONE);
666     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 3) == OT_ERROR_NONE);
667     {
668         uint8_t  value[sizeof(data)];
669         uint16_t length = sizeof(value);
670 
671         assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NONE);
672         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NOT_FOUND);
673         assert(otPlatSettingsGet(instance, 1, 0, value, &length) == OT_ERROR_NONE);
674         assert(length == sizeof(data) / 2);
675         assert(0 == memcmp(value, data, length));
676 
677         assert(otPlatSettingsDelete(instance, 0, 0) == OT_ERROR_NOT_FOUND);
678         assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
679     }
680     otPlatSettingsWipe(instance);
681     otPlatSettingsDeinit(instance);
682 
683     return 0;
684 }
685 #endif
686