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::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  * 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     sSettingsFd = -1;
236 
237 exit:
238     return;
239 }
240 
otPlatSettingsGet(otInstance * aInstance,uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength)241 otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
242 {
243     OT_UNUSED_VARIABLE(aInstance);
244 
245     otError error = OT_ERROR_NOT_FOUND;
246 
247     VerifyOrExit(!IsSystemDryRun());
248 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
249     if (isSensitiveKey(aKey))
250     {
251         error = otPosixSecureSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength);
252     }
253     else
254 #endif
255     {
256         error = ot::Posix::PlatformSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength);
257     }
258 
259 exit:
260     VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
261     return error;
262 }
263 
otPlatSettingsSet(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)264 otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
265 {
266     otError error = OT_ERROR_NONE;
267 
268 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
269     if (isSensitiveKey(aKey))
270     {
271         error = otPosixSecureSettingsSet(aInstance, aKey, aValue, aValueLength);
272     }
273     else
274 #endif
275     {
276         ot::Posix::PlatformSettingsSet(aInstance, aKey, aValue, aValueLength);
277     }
278 
279     return error;
280 }
281 
otPlatSettingsAdd(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)282 otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
283 {
284     OT_UNUSED_VARIABLE(aInstance);
285 
286     otError error = OT_ERROR_NONE;
287 
288 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
289     if (isSensitiveKey(aKey))
290     {
291         error = otPosixSecureSettingsAdd(aInstance, aKey, aValue, aValueLength);
292     }
293     else
294 #endif
295     {
296         ot::Posix::PlatformSettingsAdd(aInstance, aKey, aValue, aValueLength);
297     }
298 
299     return error;
300 }
301 
otPlatSettingsDelete(otInstance * aInstance,uint16_t aKey,int aIndex)302 otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
303 {
304     otError error;
305 
306 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
307     if (isSensitiveKey(aKey))
308     {
309         error = otPosixSecureSettingsDelete(aInstance, aKey, aIndex);
310     }
311     else
312 #endif
313     {
314         error = ot::Posix::PlatformSettingsDelete(aInstance, aKey, aIndex, nullptr);
315     }
316 
317     return error;
318 }
319 
otPlatSettingsWipe(otInstance * aInstance)320 void otPlatSettingsWipe(otInstance *aInstance)
321 {
322     OT_UNUSED_VARIABLE(aInstance);
323 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
324     otPosixSecureSettingsWipe(aInstance);
325 #endif
326 
327     VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO);
328 }
329 
330 namespace ot {
331 namespace Posix {
332 
PlatformSettingsGet(otInstance * aInstance,uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength)333 otError PlatformSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
334 {
335     OT_UNUSED_VARIABLE(aInstance);
336 
337     otError     error  = OT_ERROR_NOT_FOUND;
338     const off_t size   = lseek(sSettingsFd, 0, SEEK_END);
339     off_t       offset = lseek(sSettingsFd, 0, SEEK_SET);
340 
341     VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
342 
343     while (offset < size)
344     {
345         uint16_t key;
346         uint16_t length;
347         ssize_t  rval;
348 
349         rval = read(sSettingsFd, &key, sizeof(key));
350         VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
351 
352         rval = read(sSettingsFd, &length, sizeof(length));
353         VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
354 
355         if (key == aKey)
356         {
357             if (aIndex == 0)
358             {
359                 error = OT_ERROR_NONE;
360 
361                 if (aValueLength)
362                 {
363                     if (aValue)
364                     {
365                         uint16_t readLength = (length <= *aValueLength ? length : *aValueLength);
366 
367                         VerifyOrExit(read(sSettingsFd, aValue, readLength) == readLength, error = OT_ERROR_PARSE);
368                     }
369 
370                     *aValueLength = length;
371                 }
372 
373                 break;
374             }
375             else
376             {
377                 --aIndex;
378             }
379         }
380 
381         offset += sizeof(key) + sizeof(length) + length;
382         VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
383     }
384 
385 exit:
386     return error;
387 }
388 
PlatformSettingsSet(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)389 void PlatformSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
390 {
391     int swapFd = -1;
392 
393     switch (PlatformSettingsDelete(aInstance, aKey, -1, &swapFd))
394     {
395     case OT_ERROR_NONE:
396     case OT_ERROR_NOT_FOUND:
397         break;
398 
399     default:
400         assert(false);
401         break;
402     }
403 
404     VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
405                     write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
406                     write(swapFd, aValue, aValueLength) == aValueLength,
407                 OT_EXIT_FAILURE);
408 
409     swapPersist(aInstance, swapFd);
410 }
411 
PlatformSettingsAdd(otInstance * aInstance,uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)412 void PlatformSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
413 {
414     off_t size   = lseek(sSettingsFd, 0, SEEK_END);
415     int   swapFd = swapOpen(aInstance);
416 
417     if (size > 0)
418     {
419         VerifyOrDie(0 == lseek(sSettingsFd, 0, SEEK_SET), OT_EXIT_ERROR_ERRNO);
420         swapWrite(aInstance, swapFd, static_cast<uint16_t>(size));
421     }
422 
423     VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
424                     write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
425                     write(swapFd, aValue, aValueLength) == aValueLength,
426                 OT_EXIT_FAILURE);
427 
428     swapPersist(aInstance, swapFd);
429 }
430 
PlatformSettingsDelete(otInstance * aInstance,uint16_t aKey,int aIndex,int * aSwapFd)431 otError PlatformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd)
432 {
433     otError error  = OT_ERROR_NOT_FOUND;
434     off_t   size   = lseek(sSettingsFd, 0, SEEK_END);
435     off_t   offset = lseek(sSettingsFd, 0, SEEK_SET);
436     int     swapFd = swapOpen(aInstance);
437 
438     assert(swapFd != -1);
439     assert(offset == 0);
440     VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_FAILED);
441 
442     while (offset < size)
443     {
444         uint16_t key;
445         uint16_t length;
446         ssize_t  rval;
447 
448         rval = read(sSettingsFd, &key, sizeof(key));
449         VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED);
450 
451         rval = read(sSettingsFd, &length, sizeof(length));
452         VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED);
453 
454         offset += sizeof(key) + sizeof(length) + length;
455 
456         if (aKey == key)
457         {
458             if (aIndex == 0)
459             {
460                 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED);
461                 swapWrite(aInstance, swapFd, static_cast<uint16_t>(size - offset));
462                 error = OT_ERROR_NONE;
463                 break;
464             }
465             else if (aIndex == -1)
466             {
467                 VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED);
468                 error = OT_ERROR_NONE;
469                 continue;
470             }
471             else
472             {
473                 --aIndex;
474             }
475         }
476 
477         rval = write(swapFd, &key, sizeof(key));
478         VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED);
479 
480         rval = write(swapFd, &length, sizeof(length));
481         VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED);
482 
483         swapWrite(aInstance, swapFd, length);
484     }
485 
486 exit:
487     if (aSwapFd != nullptr)
488     {
489         *aSwapFd = swapFd;
490     }
491     else if (error == OT_ERROR_NONE)
492     {
493         swapPersist(aInstance, swapFd);
494     }
495     else if (error == OT_ERROR_NOT_FOUND)
496     {
497         swapDiscard(aInstance, swapFd);
498     }
499     else if (error == OT_ERROR_FAILED)
500     {
501         swapDiscard(aInstance, swapFd);
502         DieNow(error);
503     }
504 
505     return error;
506 }
507 
508 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
PlatformSettingsGetSensitiveKeys(otInstance * aInstance,const uint16_t ** aKeys,uint16_t * aKeysLength)509 void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aKeys, uint16_t *aKeysLength)
510 {
511     OT_UNUSED_VARIABLE(aInstance);
512 
513     assert(aKeys != nullptr);
514     assert(aKeysLength != nullptr);
515 
516     *aKeys       = sSensitiveKeys;
517     *aKeysLength = sSensitiveKeysLength;
518 }
519 #endif
520 
521 } // namespace Posix
522 } // namespace ot
523 
524 #ifndef SELF_TEST
525 #define SELF_TEST 0
526 #endif
527 
528 #if SELF_TEST
529 
otLogCritPlat(const char * aFormat,...)530 void otLogCritPlat(const char *aFormat, ...) { OT_UNUSED_VARIABLE(aFormat); }
531 
otExitCodeToString(uint8_t aExitCode)532 const char *otExitCodeToString(uint8_t aExitCode)
533 {
534     OT_UNUSED_VARIABLE(aExitCode);
535     return "";
536 }
537 
otPlatRadioGetIeeeEui64(otInstance * aInstance,uint8_t * aIeeeEui64)538 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
539 {
540     OT_UNUSED_VARIABLE(aInstance);
541 
542     memset(aIeeeEui64, 0, sizeof(uint64_t));
543 }
544 
545 // Stub implementation for testing
IsSystemDryRun(void)546 bool IsSystemDryRun(void) { return false; }
547 
main()548 int main()
549 {
550     otInstance *instance = nullptr;
551     uint8_t     data[60];
552 
553     for (uint8_t i = 0; i < sizeof(data); ++i)
554     {
555         data[i] = i;
556     }
557 
558     otPlatSettingsInit(instance, nullptr, 0);
559 
560     // verify empty situation
561     otPlatSettingsWipe(instance);
562     {
563         uint8_t  value[sizeof(data)];
564         uint16_t length = sizeof(value);
565 
566         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NOT_FOUND);
567         assert(otPlatSettingsDelete(instance, 0, 0) == OT_ERROR_NOT_FOUND);
568         assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NOT_FOUND);
569     }
570 
571     // verify write one record
572     assert(otPlatSettingsSet(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
573     {
574         uint8_t  value[sizeof(data)];
575         uint16_t length = sizeof(value);
576 
577         assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NONE);
578         assert(otPlatSettingsGet(instance, 0, 0, nullptr, &length) == OT_ERROR_NONE);
579         assert(length == sizeof(data) / 2);
580 
581         length = sizeof(value);
582         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
583         assert(length == sizeof(data) / 2);
584         assert(0 == memcmp(value, data, length));
585 
586         // insufficient buffer
587         length -= 1;
588         value[length] = 0;
589         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
590         // verify length becomes the actual length of the record
591         assert(length == sizeof(data) / 2);
592         // verify this byte is not changed
593         assert(value[length] == 0);
594 
595         // wrong index
596         assert(otPlatSettingsGet(instance, 0, 1, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
597         // wrong key
598         assert(otPlatSettingsGet(instance, 1, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
599     }
600     otPlatSettingsWipe(instance);
601 
602     // verify write two records
603     assert(otPlatSettingsSet(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
604     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
605     {
606         uint8_t  value[sizeof(data)];
607         uint16_t length = sizeof(value);
608 
609         assert(otPlatSettingsGet(instance, 0, 1, value, &length) == OT_ERROR_NONE);
610         assert(length == sizeof(data) / 2);
611         assert(0 == memcmp(value, data, length));
612 
613         length = sizeof(value);
614         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
615         assert(length == sizeof(data));
616         assert(0 == memcmp(value, data, length));
617     }
618     otPlatSettingsWipe(instance);
619 
620     // verify write two records of different keys
621     assert(otPlatSettingsSet(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
622     assert(otPlatSettingsAdd(instance, 1, data, sizeof(data) / 2) == OT_ERROR_NONE);
623     {
624         uint8_t  value[sizeof(data)];
625         uint16_t length = sizeof(value);
626 
627         assert(otPlatSettingsGet(instance, 1, 0, value, &length) == OT_ERROR_NONE);
628         assert(length == sizeof(data) / 2);
629         assert(0 == memcmp(value, data, length));
630 
631         length = sizeof(value);
632         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NONE);
633         assert(length == sizeof(data));
634         assert(0 == memcmp(value, data, length));
635     }
636     otPlatSettingsWipe(instance);
637 
638     // verify delete record
639     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
640     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 2) == OT_ERROR_NONE);
641     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 3) == OT_ERROR_NONE);
642     {
643         uint8_t  value[sizeof(data)];
644         uint16_t length = sizeof(value);
645 
646         // wrong key
647         assert(otPlatSettingsDelete(instance, 1, 0) == OT_ERROR_NOT_FOUND);
648         assert(otPlatSettingsDelete(instance, 1, -1) == OT_ERROR_NOT_FOUND);
649 
650         // wrong index
651         assert(otPlatSettingsDelete(instance, 0, 3) == OT_ERROR_NOT_FOUND);
652 
653         // delete one record
654         assert(otPlatSettingsDelete(instance, 0, 1) == OT_ERROR_NONE);
655         assert(otPlatSettingsGet(instance, 0, 1, value, &length) == OT_ERROR_NONE);
656         assert(length == sizeof(data) / 3);
657         assert(0 == memcmp(value, data, length));
658 
659         // delete all records
660         assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NONE);
661         assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
662     }
663     otPlatSettingsWipe(instance);
664 
665     // verify delete all records of a type
666     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data)) == OT_ERROR_NONE);
667     assert(otPlatSettingsAdd(instance, 1, data, sizeof(data) / 2) == OT_ERROR_NONE);
668     assert(otPlatSettingsAdd(instance, 0, data, sizeof(data) / 3) == OT_ERROR_NONE);
669     {
670         uint8_t  value[sizeof(data)];
671         uint16_t length = sizeof(value);
672 
673         assert(otPlatSettingsDelete(instance, 0, -1) == OT_ERROR_NONE);
674         assert(otPlatSettingsGet(instance, 0, 0, value, &length) == OT_ERROR_NOT_FOUND);
675         assert(otPlatSettingsGet(instance, 1, 0, value, &length) == OT_ERROR_NONE);
676         assert(length == sizeof(data) / 2);
677         assert(0 == memcmp(value, data, length));
678 
679         assert(otPlatSettingsDelete(instance, 0, 0) == OT_ERROR_NOT_FOUND);
680         assert(otPlatSettingsGet(instance, 0, 0, nullptr, nullptr) == OT_ERROR_NOT_FOUND);
681     }
682     otPlatSettingsWipe(instance);
683     otPlatSettingsDeinit(instance);
684 
685     return 0;
686 }
687 #endif
688