1 /*
2 * Copyright (c) 2024, 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 settings file module for getting, setting and deleting the key-value pairs.
32 */
33
34 #include "openthread-posix-config.h"
35 #include "platform-posix.h"
36
37 #include <fcntl.h>
38 #include <inttypes.h>
39 #include <stddef.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44
45 #include "common/code_utils.hpp"
46 #include "common/debug.hpp"
47 #include "posix/platform/settings_file.hpp"
48
49 namespace ot {
50 namespace Posix {
51
Init(const char * aSettingsFileBaseName)52 otError SettingsFile::Init(const char *aSettingsFileBaseName)
53 {
54 otError error = OT_ERROR_NONE;
55 const char *directory = OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH;
56
57 OT_ASSERT((aSettingsFileBaseName != nullptr) && (strlen(aSettingsFileBaseName) < kMaxFileBaseNameSize));
58 strncpy(mSettingFileBaseName, aSettingsFileBaseName, sizeof(mSettingFileBaseName) - 1);
59
60 {
61 struct stat st;
62
63 if (stat(directory, &st) == -1)
64 {
65 VerifyOrDie(mkdir(directory, 0755) == 0, OT_EXIT_ERROR_ERRNO);
66 }
67 }
68
69 {
70 char fileName[kMaxFilePathSize];
71
72 GetSettingsFilePath(fileName, false);
73 mSettingsFd = open(fileName, O_RDWR | O_CREAT | O_CLOEXEC, 0600);
74 }
75
76 VerifyOrDie(mSettingsFd != -1, OT_EXIT_ERROR_ERRNO);
77
78 for (off_t size = lseek(mSettingsFd, 0, SEEK_END), offset = lseek(mSettingsFd, 0, SEEK_SET); offset < size;)
79 {
80 uint16_t key;
81 uint16_t length;
82 ssize_t rval;
83
84 rval = read(mSettingsFd, &key, sizeof(key));
85 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
86
87 rval = read(mSettingsFd, &length, sizeof(length));
88 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
89
90 offset += sizeof(key) + sizeof(length) + length;
91 VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
92 }
93
94 exit:
95 if (error == OT_ERROR_PARSE)
96 {
97 VerifyOrDie(ftruncate(mSettingsFd, 0) == 0, OT_EXIT_ERROR_ERRNO);
98 }
99
100 return error;
101 }
102
Deinit(void)103 void SettingsFile::Deinit(void)
104 {
105 VerifyOrExit(mSettingsFd != -1);
106 VerifyOrDie(close(mSettingsFd) == 0, OT_EXIT_ERROR_ERRNO);
107 mSettingsFd = -1;
108
109 exit:
110 return;
111 }
112
Get(uint16_t aKey,int aIndex,uint8_t * aValue,uint16_t * aValueLength)113 otError SettingsFile::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
114 {
115 otError error = OT_ERROR_NOT_FOUND;
116 off_t size;
117 off_t offset;
118
119 OT_ASSERT(mSettingsFd >= 0);
120
121 size = lseek(mSettingsFd, 0, SEEK_END);
122 offset = lseek(mSettingsFd, 0, SEEK_SET);
123 VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
124
125 while (offset < size)
126 {
127 uint16_t key;
128 uint16_t length;
129 ssize_t rval;
130
131 rval = read(mSettingsFd, &key, sizeof(key));
132 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE);
133
134 rval = read(mSettingsFd, &length, sizeof(length));
135 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE);
136
137 if (key == aKey)
138 {
139 if (aIndex == 0)
140 {
141 error = OT_ERROR_NONE;
142
143 if (aValueLength)
144 {
145 if (aValue)
146 {
147 uint16_t readLength = (length <= *aValueLength ? length : *aValueLength);
148
149 VerifyOrExit(read(mSettingsFd, aValue, readLength) == readLength, error = OT_ERROR_PARSE);
150 }
151
152 *aValueLength = length;
153 }
154
155 break;
156 }
157 else
158 {
159 --aIndex;
160 }
161 }
162
163 offset += sizeof(key) + sizeof(length) + length;
164 VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
165 }
166
167 exit:
168 return error;
169 }
170
Set(uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)171 void SettingsFile::Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
172 {
173 int swapFd = -1;
174
175 OT_ASSERT(mSettingsFd >= 0);
176
177 switch (Delete(aKey, -1, &swapFd))
178 {
179 case OT_ERROR_NONE:
180 case OT_ERROR_NOT_FOUND:
181 break;
182
183 default:
184 OT_ASSERT(false);
185 break;
186 }
187
188 VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
189 write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
190 write(swapFd, aValue, aValueLength) == aValueLength,
191 OT_EXIT_FAILURE);
192
193 SwapPersist(swapFd);
194 }
195
Add(uint16_t aKey,const uint8_t * aValue,uint16_t aValueLength)196 void SettingsFile::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
197 {
198 off_t size;
199 int swapFd;
200
201 OT_ASSERT(mSettingsFd >= 0);
202
203 size = lseek(mSettingsFd, 0, SEEK_END);
204 swapFd = SwapOpen();
205
206 if (size > 0)
207 {
208 VerifyOrDie(0 == lseek(mSettingsFd, 0, SEEK_SET), OT_EXIT_ERROR_ERRNO);
209 SwapWrite(swapFd, static_cast<uint16_t>(size));
210 }
211
212 VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) &&
213 write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) &&
214 write(swapFd, aValue, aValueLength) == aValueLength,
215 OT_EXIT_FAILURE);
216
217 SwapPersist(swapFd);
218 }
219
Delete(uint16_t aKey,int aIndex)220 otError SettingsFile::Delete(uint16_t aKey, int aIndex) { return Delete(aKey, aIndex, nullptr); }
221
Delete(uint16_t aKey,int aIndex,int * aSwapFd)222 otError SettingsFile::Delete(uint16_t aKey, int aIndex, int *aSwapFd)
223 {
224 otError error = OT_ERROR_NOT_FOUND;
225 off_t size;
226 off_t offset;
227 int swapFd;
228
229 OT_ASSERT(mSettingsFd >= 0);
230
231 size = lseek(mSettingsFd, 0, SEEK_END);
232 offset = lseek(mSettingsFd, 0, SEEK_SET);
233 swapFd = SwapOpen();
234
235 OT_ASSERT(swapFd != -1);
236 OT_ASSERT(offset == 0);
237 VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_FAILED);
238
239 while (offset < size)
240 {
241 uint16_t key;
242 uint16_t length;
243 ssize_t rval;
244
245 rval = read(mSettingsFd, &key, sizeof(key));
246 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED);
247
248 rval = read(mSettingsFd, &length, sizeof(length));
249 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED);
250
251 offset += sizeof(key) + sizeof(length) + length;
252
253 if (aKey == key)
254 {
255 if (aIndex == 0)
256 {
257 VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED);
258 SwapWrite(swapFd, static_cast<uint16_t>(size - offset));
259 error = OT_ERROR_NONE;
260 break;
261 }
262 else if (aIndex == -1)
263 {
264 VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED);
265 error = OT_ERROR_NONE;
266 continue;
267 }
268 else
269 {
270 --aIndex;
271 }
272 }
273
274 rval = write(swapFd, &key, sizeof(key));
275 VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED);
276
277 rval = write(swapFd, &length, sizeof(length));
278 VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED);
279
280 SwapWrite(swapFd, length);
281 }
282
283 exit:
284 if (aSwapFd != nullptr)
285 {
286 *aSwapFd = swapFd;
287 }
288 else if (error == OT_ERROR_NONE)
289 {
290 SwapPersist(swapFd);
291 }
292 else if (error == OT_ERROR_NOT_FOUND)
293 {
294 SwapDiscard(swapFd);
295 }
296 else if (error == OT_ERROR_FAILED)
297 {
298 SwapDiscard(swapFd);
299 DieNow(error);
300 }
301
302 return error;
303 }
304
Wipe(void)305 void SettingsFile::Wipe(void) { VerifyOrDie(0 == ftruncate(mSettingsFd, 0), OT_EXIT_ERROR_ERRNO); }
306
GetSettingsFilePath(char aFileName[kMaxFilePathSize],bool aSwap)307 void SettingsFile::GetSettingsFilePath(char aFileName[kMaxFilePathSize], bool aSwap)
308 {
309 snprintf(aFileName, kMaxFilePathSize, OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/%s.%s", mSettingFileBaseName,
310 (aSwap ? "Swap" : "data"));
311 }
312
SwapOpen(void)313 int SettingsFile::SwapOpen(void)
314 {
315 char fileName[kMaxFilePathSize];
316 int fd;
317
318 GetSettingsFilePath(fileName, true);
319
320 fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
321 VerifyOrDie(fd != -1, OT_EXIT_ERROR_ERRNO);
322
323 return fd;
324 }
325
SwapWrite(int aFd,uint16_t aLength)326 void SettingsFile::SwapWrite(int aFd, uint16_t aLength)
327 {
328 const size_t kBlockSize = 512;
329 uint8_t buffer[kBlockSize];
330
331 while (aLength > 0)
332 {
333 uint16_t count = aLength >= sizeof(buffer) ? sizeof(buffer) : aLength;
334 ssize_t rval = read(mSettingsFd, buffer, count);
335
336 VerifyOrDie(rval > 0, OT_EXIT_FAILURE);
337 count = static_cast<uint16_t>(rval);
338 rval = write(aFd, buffer, count);
339 OT_ASSERT(rval == count);
340 VerifyOrDie(rval == count, OT_EXIT_FAILURE);
341 aLength -= count;
342 }
343 }
344
SwapPersist(int aFd)345 void SettingsFile::SwapPersist(int aFd)
346 {
347 char swapFile[kMaxFilePathSize];
348 char dataFile[kMaxFilePathSize];
349
350 GetSettingsFilePath(swapFile, true);
351 GetSettingsFilePath(dataFile, false);
352
353 VerifyOrDie(0 == close(mSettingsFd), OT_EXIT_ERROR_ERRNO);
354 VerifyOrDie(0 == fsync(aFd), OT_EXIT_ERROR_ERRNO);
355 VerifyOrDie(0 == rename(swapFile, dataFile), OT_EXIT_ERROR_ERRNO);
356
357 mSettingsFd = aFd;
358 }
359
SwapDiscard(int aFd)360 void SettingsFile::SwapDiscard(int aFd)
361 {
362 char swapFileName[kMaxFilePathSize];
363
364 VerifyOrDie(0 == close(aFd), OT_EXIT_ERROR_ERRNO);
365 GetSettingsFilePath(swapFileName, true);
366 VerifyOrDie(0 == unlink(swapFileName), OT_EXIT_ERROR_ERRNO);
367 }
368
369 } // namespace Posix
370 } // namespace ot
371