From 81542cada99b6a6a6a7336b3d7d9637779b2692a Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Tue, 17 Dec 2024 00:49:11 +0800 Subject: [PATCH] [posix] move settings file related methods to a separate file (#11023) This commit refactors the settings module to move the settings file related methods to a seperate file, so that other modules could resue the same code to save thier settings to another settings file. --- src/posix/platform/CMakeLists.txt | 2 + src/posix/platform/settings.cpp | 316 ++--------------------- src/posix/platform/settings.hpp | 51 ---- src/posix/platform/settings_file.cpp | 370 +++++++++++++++++++++++++++ src/posix/platform/settings_file.hpp | 131 ++++++++++ 5 files changed, 525 insertions(+), 345 deletions(-) create mode 100644 src/posix/platform/settings_file.cpp create mode 100644 src/posix/platform/settings_file.hpp diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 269fda2e529..4b94da29374 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -146,6 +146,7 @@ add_library(openthread-posix rcp_caps_diag.cpp resolver.cpp settings.cpp + settings_file.cpp spinel_manager.cpp spi_interface.cpp system.cpp @@ -207,6 +208,7 @@ target_include_directories(openthread-posix PRIVATE add_executable(ot-posix-test-settings settings.cpp + settings_file.cpp ) target_compile_definitions(ot-posix-test-settings PRIVATE -DSELF_TEST=1 -DOPENTHREAD_CONFIG_LOG_PLATFORM=0 diff --git a/src/posix/platform/settings.cpp b/src/posix/platform/settings.cpp index 844e9bb4e69..8faef601ded 100644 --- a/src/posix/platform/settings.cpp +++ b/src/posix/platform/settings.cpp @@ -54,12 +54,11 @@ #include "common/code_utils.hpp" #include "common/encoding.hpp" #include "posix/platform/settings.hpp" +#include "posix/platform/settings_file.hpp" #include "system.hpp" -static const size_t kMaxFileNameSize = sizeof(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH) + 32; - -static int sSettingsFd = -1; +static ot::Posix::SettingsFile sSettingsFile; #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE static const uint16_t *sSensitiveKeys = nullptr; @@ -81,79 +80,20 @@ static bool isSensitiveKey(uint16_t aKey) } #endif -static void getSettingsFileName(otInstance *aInstance, char aFileName[kMaxFileNameSize], bool aSwap) +static otError settingsFileInit(otInstance *aInstance) { - const char *offset = getenv("PORT_OFFSET"); - uint64_t nodeId; + static constexpr size_t kMaxFileBaseNameSize = 32; + char fileBaseName[kMaxFileBaseNameSize]; + const char *offset = getenv("PORT_OFFSET"); + uint64_t nodeId; otPlatRadioGetIeeeEui64(aInstance, reinterpret_cast(&nodeId)); nodeId = ot::BigEndian::HostSwap64(nodeId); - snprintf(aFileName, kMaxFileNameSize, OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/%s_%" PRIx64 ".%s", - offset == nullptr ? "0" : offset, nodeId, (aSwap ? "swap" : "data")); -} - -static int swapOpen(otInstance *aInstance) -{ - char fileName[kMaxFileNameSize]; - int fd; - - getSettingsFileName(aInstance, fileName, true); - - fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); - VerifyOrDie(fd != -1, OT_EXIT_ERROR_ERRNO); - - return fd; -} - -/** - * Reads @p aLength bytes from the data file and appends to the swap file. - * - * @param[in] aFd The file descriptor of the current swap file. - * @param[in] aLength Number of bytes to copy. - */ -static void swapWrite(otInstance *aInstance, int aFd, uint16_t aLength) -{ - OT_UNUSED_VARIABLE(aInstance); - - const size_t kBlockSize = 512; - uint8_t buffer[kBlockSize]; - - while (aLength > 0) - { - uint16_t count = aLength >= sizeof(buffer) ? sizeof(buffer) : aLength; - ssize_t rval = read(sSettingsFd, buffer, count); - - VerifyOrDie(rval > 0, OT_EXIT_FAILURE); - count = static_cast(rval); - rval = write(aFd, buffer, count); - assert(rval == count); - VerifyOrDie(rval == count, OT_EXIT_FAILURE); - aLength -= count; - } -} - -static void swapPersist(otInstance *aInstance, int aFd) -{ - char swapFile[kMaxFileNameSize]; - char dataFile[kMaxFileNameSize]; - - getSettingsFileName(aInstance, swapFile, true); - getSettingsFileName(aInstance, dataFile, false); - - VerifyOrDie(0 == close(sSettingsFd), OT_EXIT_ERROR_ERRNO); - VerifyOrDie(0 == fsync(aFd), OT_EXIT_ERROR_ERRNO); - VerifyOrDie(0 == rename(swapFile, dataFile), OT_EXIT_ERROR_ERRNO); - - sSettingsFd = aFd; -} -static void swapDiscard(otInstance *aInstance, int aFd) -{ - char swapFileName[kMaxFileNameSize]; + snprintf(fileBaseName, sizeof(fileBaseName), "%s_%" PRIx64, offset == nullptr ? "0" : offset, nodeId); + VerifyOrDie(strlen(fileBaseName) < kMaxFileBaseNameSize, OT_EXIT_FAILURE); - VerifyOrDie(0 == close(aFd), OT_EXIT_ERROR_ERRNO); - getSettingsFileName(aInstance, swapFileName, true); - VerifyOrDie(0 == unlink(swapFileName), OT_EXIT_ERROR_ERRNO); + return sSettingsFile.Init(fileBaseName); } void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength) @@ -163,8 +103,6 @@ void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, u OT_UNUSED_VARIABLE(aSensitiveKeysLength); #endif - otError error = OT_ERROR_NONE; - #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE sSensitiveKeys = aSensitiveKeys; sSensitiveKeysLength = aSensitiveKeysLength; @@ -172,50 +110,14 @@ void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, u // Don't touch the settings file the system runs in dry-run mode. VerifyOrExit(!IsSystemDryRun()); - - { - struct stat st; - - if (stat(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, &st) == -1) - { - VerifyOrDie(mkdir(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, 0755) == 0, OT_EXIT_ERROR_ERRNO); - } - } - - { - char fileName[kMaxFileNameSize]; - - getSettingsFileName(aInstance, fileName, false); - sSettingsFd = open(fileName, O_RDWR | O_CREAT | O_CLOEXEC, 0600); - } - - VerifyOrDie(sSettingsFd != -1, OT_EXIT_ERROR_ERRNO); - - for (off_t size = lseek(sSettingsFd, 0, SEEK_END), offset = lseek(sSettingsFd, 0, SEEK_SET); offset < size;) - { - uint16_t key; - uint16_t length; - ssize_t rval; - - rval = read(sSettingsFd, &key, sizeof(key)); - VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE); - - rval = read(sSettingsFd, &length, sizeof(length)); - VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE); - - offset += sizeof(key) + sizeof(length) + length; - VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE); - } + SuccessOrExit(settingsFileInit(aInstance)); #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE otPosixSecureSettingsInit(aInstance); #endif exit: - if (error == OT_ERROR_PARSE) - { - VerifyOrDie(ftruncate(sSettingsFd, 0) == 0, OT_EXIT_ERROR_ERRNO); - } + return; } void otPlatSettingsDeinit(otInstance *aInstance) @@ -228,9 +130,7 @@ void otPlatSettingsDeinit(otInstance *aInstance) otPosixSecureSettingsDeinit(aInstance); #endif - VerifyOrExit(sSettingsFd != -1); - VerifyOrDie(close(sSettingsFd) == 0, OT_EXIT_ERROR_ERRNO); - sSettingsFd = -1; + sSettingsFile.Deinit(); exit: return; @@ -251,7 +151,7 @@ otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint else #endif { - error = ot::Posix::PlatformSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength); + error = sSettingsFile.Get(aKey, aIndex, aValue, aValueLength); } exit: @@ -261,6 +161,8 @@ otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { + OT_UNUSED_VARIABLE(aInstance); + otError error = OT_ERROR_NONE; #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE @@ -271,7 +173,7 @@ otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *a else #endif { - ot::Posix::PlatformSettingsSet(aInstance, aKey, aValue, aValueLength); + sSettingsFile.Set(aKey, aValue, aValueLength); } return error; @@ -291,7 +193,7 @@ otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *a else #endif { - ot::Posix::PlatformSettingsAdd(aInstance, aKey, aValue, aValueLength); + sSettingsFile.Add(aKey, aValue, aValueLength); } return error; @@ -299,6 +201,8 @@ otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *a otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) { + OT_UNUSED_VARIABLE(aInstance); + otError error; #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE @@ -309,7 +213,7 @@ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) else #endif { - error = ot::Posix::PlatformSettingsDelete(aInstance, aKey, aIndex, nullptr); + error = sSettingsFile.Delete(aKey, aIndex); } return error; @@ -322,187 +226,11 @@ void otPlatSettingsWipe(otInstance *aInstance) otPosixSecureSettingsWipe(aInstance); #endif - VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO); + sSettingsFile.Wipe(); } namespace ot { namespace Posix { - -otError PlatformSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NOT_FOUND; - const off_t size = lseek(sSettingsFd, 0, SEEK_END); - off_t offset = lseek(sSettingsFd, 0, SEEK_SET); - - VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE); - - while (offset < size) - { - uint16_t key; - uint16_t length; - ssize_t rval; - - rval = read(sSettingsFd, &key, sizeof(key)); - VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE); - - rval = read(sSettingsFd, &length, sizeof(length)); - VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE); - - if (key == aKey) - { - if (aIndex == 0) - { - error = OT_ERROR_NONE; - - if (aValueLength) - { - if (aValue) - { - uint16_t readLength = (length <= *aValueLength ? length : *aValueLength); - - VerifyOrExit(read(sSettingsFd, aValue, readLength) == readLength, error = OT_ERROR_PARSE); - } - - *aValueLength = length; - } - - break; - } - else - { - --aIndex; - } - } - - offset += sizeof(key) + sizeof(length) + length; - VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE); - } - -exit: - return error; -} - -void PlatformSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) -{ - int swapFd = -1; - - switch (PlatformSettingsDelete(aInstance, aKey, -1, &swapFd)) - { - case OT_ERROR_NONE: - case OT_ERROR_NOT_FOUND: - break; - - default: - assert(false); - break; - } - - VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) && - write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) && - write(swapFd, aValue, aValueLength) == aValueLength, - OT_EXIT_FAILURE); - - swapPersist(aInstance, swapFd); -} - -void PlatformSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) -{ - off_t size = lseek(sSettingsFd, 0, SEEK_END); - int swapFd = swapOpen(aInstance); - - if (size > 0) - { - VerifyOrDie(0 == lseek(sSettingsFd, 0, SEEK_SET), OT_EXIT_ERROR_ERRNO); - swapWrite(aInstance, swapFd, static_cast(size)); - } - - VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) && - write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) && - write(swapFd, aValue, aValueLength) == aValueLength, - OT_EXIT_FAILURE); - - swapPersist(aInstance, swapFd); -} - -otError PlatformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd) -{ - otError error = OT_ERROR_NOT_FOUND; - off_t size = lseek(sSettingsFd, 0, SEEK_END); - off_t offset = lseek(sSettingsFd, 0, SEEK_SET); - int swapFd = swapOpen(aInstance); - - assert(swapFd != -1); - assert(offset == 0); - VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_FAILED); - - while (offset < size) - { - uint16_t key; - uint16_t length; - ssize_t rval; - - rval = read(sSettingsFd, &key, sizeof(key)); - VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED); - - rval = read(sSettingsFd, &length, sizeof(length)); - VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED); - - offset += sizeof(key) + sizeof(length) + length; - - if (aKey == key) - { - if (aIndex == 0) - { - VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED); - swapWrite(aInstance, swapFd, static_cast(size - offset)); - error = OT_ERROR_NONE; - break; - } - else if (aIndex == -1) - { - VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED); - error = OT_ERROR_NONE; - continue; - } - else - { - --aIndex; - } - } - - rval = write(swapFd, &key, sizeof(key)); - VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED); - - rval = write(swapFd, &length, sizeof(length)); - VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED); - - swapWrite(aInstance, swapFd, length); - } - -exit: - if (aSwapFd != nullptr) - { - *aSwapFd = swapFd; - } - else if (error == OT_ERROR_NONE) - { - swapPersist(aInstance, swapFd); - } - else if (error == OT_ERROR_NOT_FOUND) - { - swapDiscard(aInstance, swapFd); - } - else if (error == OT_ERROR_FAILED) - { - swapDiscard(aInstance, swapFd); - DieNow(error); - } - - return error; -} - #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aKeys, uint16_t *aKeysLength) { diff --git a/src/posix/platform/settings.hpp b/src/posix/platform/settings.hpp index bb158c90ba7..ee3730979ab 100644 --- a/src/posix/platform/settings.hpp +++ b/src/posix/platform/settings.hpp @@ -32,57 +32,6 @@ namespace ot { namespace Posix { -/** - * Gets a setting from the persisted file. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aIndex The index of the specific item to get. - * @param[out] aValue A pointer to where the value of the setting should be written. - * @param[in,out] aValueLength A pointer to the length of the value. - * - * @retval OT_ERROR_NONE The given setting was found and fetched successfully. - * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. - */ -otError PlatformSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength); - -/** - * Sets a setting in the persisted file. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aValue A pointer to where the new value of the setting should be read from. - * @param[in] aValueLength The length of the data pointed to by aValue. - */ -void PlatformSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); - -/** - * Adds a setting to the persisted file. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aValue A pointer to where the new value of the setting should be read from. - * @param[in] aValueLength The length of the data pointed to by aValue. - */ -void PlatformSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); - -/** - * Removes a setting either from swap file or persisted file. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this aKey will be removed. - * @param[out] aSwapFd A optional pointer to receive file descriptor of the generated swap file descriptor. - * - * @note - * If @p aSwapFd is null, operate deleting on the setting file. - * If @p aSwapFd is not null, operate on the swap file, and aSwapFd will point to the swap file descriptor. - * - * @retval OT_ERROR_NONE The given key and index was found and removed successfully. - * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. - */ -otError PlatformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd); - /** * Gets the sensitive keys that should be stored in the secure area. * diff --git a/src/posix/platform/settings_file.cpp b/src/posix/platform/settings_file.cpp new file mode 100644 index 00000000000..b885b6586d7 --- /dev/null +++ b/src/posix/platform/settings_file.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the settings file module for getting, setting and deleting the key-value pairs. + */ + +#include "openthread-posix-config.h" +#include "platform-posix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "posix/platform/settings_file.hpp" + +namespace ot { +namespace Posix { + +otError SettingsFile::Init(const char *aSettingsFileBaseName) +{ + otError error = OT_ERROR_NONE; + const char *directory = OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH; + + OT_ASSERT((aSettingsFileBaseName != nullptr) && (strlen(aSettingsFileBaseName) < kMaxFileBaseNameSize)); + strncpy(mSettingFileBaseName, aSettingsFileBaseName, sizeof(mSettingFileBaseName) - 1); + + { + struct stat st; + + if (stat(directory, &st) == -1) + { + VerifyOrDie(mkdir(directory, 0755) == 0, OT_EXIT_ERROR_ERRNO); + } + } + + { + char fileName[kMaxFilePathSize]; + + GetSettingsFilePath(fileName, false); + mSettingsFd = open(fileName, O_RDWR | O_CREAT | O_CLOEXEC, 0600); + } + + VerifyOrDie(mSettingsFd != -1, OT_EXIT_ERROR_ERRNO); + + for (off_t size = lseek(mSettingsFd, 0, SEEK_END), offset = lseek(mSettingsFd, 0, SEEK_SET); offset < size;) + { + uint16_t key; + uint16_t length; + ssize_t rval; + + rval = read(mSettingsFd, &key, sizeof(key)); + VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE); + + rval = read(mSettingsFd, &length, sizeof(length)); + VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE); + + offset += sizeof(key) + sizeof(length) + length; + VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE); + } + +exit: + if (error == OT_ERROR_PARSE) + { + VerifyOrDie(ftruncate(mSettingsFd, 0) == 0, OT_EXIT_ERROR_ERRNO); + } + + return error; +} + +void SettingsFile::Deinit(void) +{ + VerifyOrExit(mSettingsFd != -1); + VerifyOrDie(close(mSettingsFd) == 0, OT_EXIT_ERROR_ERRNO); + mSettingsFd = -1; + +exit: + return; +} + +otError SettingsFile::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + otError error = OT_ERROR_NOT_FOUND; + off_t size; + off_t offset; + + OT_ASSERT(mSettingsFd >= 0); + + size = lseek(mSettingsFd, 0, SEEK_END); + offset = lseek(mSettingsFd, 0, SEEK_SET); + VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE); + + while (offset < size) + { + uint16_t key; + uint16_t length; + ssize_t rval; + + rval = read(mSettingsFd, &key, sizeof(key)); + VerifyOrExit(rval == sizeof(key), error = OT_ERROR_PARSE); + + rval = read(mSettingsFd, &length, sizeof(length)); + VerifyOrExit(rval == sizeof(length), error = OT_ERROR_PARSE); + + if (key == aKey) + { + if (aIndex == 0) + { + error = OT_ERROR_NONE; + + if (aValueLength) + { + if (aValue) + { + uint16_t readLength = (length <= *aValueLength ? length : *aValueLength); + + VerifyOrExit(read(mSettingsFd, aValue, readLength) == readLength, error = OT_ERROR_PARSE); + } + + *aValueLength = length; + } + + break; + } + else + { + --aIndex; + } + } + + offset += sizeof(key) + sizeof(length) + length; + VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE); + } + +exit: + return error; +} + +void SettingsFile::Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + int swapFd = -1; + + OT_ASSERT(mSettingsFd >= 0); + + switch (Delete(aKey, -1, &swapFd)) + { + case OT_ERROR_NONE: + case OT_ERROR_NOT_FOUND: + break; + + default: + OT_ASSERT(false); + break; + } + + VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) && + write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) && + write(swapFd, aValue, aValueLength) == aValueLength, + OT_EXIT_FAILURE); + + SwapPersist(swapFd); +} + +void SettingsFile::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + off_t size; + int swapFd; + + OT_ASSERT(mSettingsFd >= 0); + + size = lseek(mSettingsFd, 0, SEEK_END); + swapFd = SwapOpen(); + + if (size > 0) + { + VerifyOrDie(0 == lseek(mSettingsFd, 0, SEEK_SET), OT_EXIT_ERROR_ERRNO); + SwapWrite(swapFd, static_cast(size)); + } + + VerifyOrDie(write(swapFd, &aKey, sizeof(aKey)) == sizeof(aKey) && + write(swapFd, &aValueLength, sizeof(aValueLength)) == sizeof(aValueLength) && + write(swapFd, aValue, aValueLength) == aValueLength, + OT_EXIT_FAILURE); + + SwapPersist(swapFd); +} + +otError SettingsFile::Delete(uint16_t aKey, int aIndex) { return Delete(aKey, aIndex, nullptr); } + +otError SettingsFile::Delete(uint16_t aKey, int aIndex, int *aSwapFd) +{ + otError error = OT_ERROR_NOT_FOUND; + off_t size; + off_t offset; + int swapFd; + + OT_ASSERT(mSettingsFd >= 0); + + size = lseek(mSettingsFd, 0, SEEK_END); + offset = lseek(mSettingsFd, 0, SEEK_SET); + swapFd = SwapOpen(); + + OT_ASSERT(swapFd != -1); + OT_ASSERT(offset == 0); + VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_FAILED); + + while (offset < size) + { + uint16_t key; + uint16_t length; + ssize_t rval; + + rval = read(mSettingsFd, &key, sizeof(key)); + VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED); + + rval = read(mSettingsFd, &length, sizeof(length)); + VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED); + + offset += sizeof(key) + sizeof(length) + length; + + if (aKey == key) + { + if (aIndex == 0) + { + VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED); + SwapWrite(swapFd, static_cast(size - offset)); + error = OT_ERROR_NONE; + break; + } + else if (aIndex == -1) + { + VerifyOrExit(offset == lseek(mSettingsFd, length, SEEK_CUR), error = OT_ERROR_FAILED); + error = OT_ERROR_NONE; + continue; + } + else + { + --aIndex; + } + } + + rval = write(swapFd, &key, sizeof(key)); + VerifyOrExit(rval == sizeof(key), error = OT_ERROR_FAILED); + + rval = write(swapFd, &length, sizeof(length)); + VerifyOrExit(rval == sizeof(length), error = OT_ERROR_FAILED); + + SwapWrite(swapFd, length); + } + +exit: + if (aSwapFd != nullptr) + { + *aSwapFd = swapFd; + } + else if (error == OT_ERROR_NONE) + { + SwapPersist(swapFd); + } + else if (error == OT_ERROR_NOT_FOUND) + { + SwapDiscard(swapFd); + } + else if (error == OT_ERROR_FAILED) + { + SwapDiscard(swapFd); + DieNow(error); + } + + return error; +} + +void SettingsFile::Wipe(void) { VerifyOrDie(0 == ftruncate(mSettingsFd, 0), OT_EXIT_ERROR_ERRNO); } + +void SettingsFile::GetSettingsFilePath(char aFileName[kMaxFilePathSize], bool aSwap) +{ + snprintf(aFileName, kMaxFilePathSize, OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/%s.%s", mSettingFileBaseName, + (aSwap ? "Swap" : "data")); +} + +int SettingsFile::SwapOpen(void) +{ + char fileName[kMaxFilePathSize]; + int fd; + + GetSettingsFilePath(fileName, true); + + fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); + VerifyOrDie(fd != -1, OT_EXIT_ERROR_ERRNO); + + return fd; +} + +void SettingsFile::SwapWrite(int aFd, uint16_t aLength) +{ + const size_t kBlockSize = 512; + uint8_t buffer[kBlockSize]; + + while (aLength > 0) + { + uint16_t count = aLength >= sizeof(buffer) ? sizeof(buffer) : aLength; + ssize_t rval = read(mSettingsFd, buffer, count); + + VerifyOrDie(rval > 0, OT_EXIT_FAILURE); + count = static_cast(rval); + rval = write(aFd, buffer, count); + OT_ASSERT(rval == count); + VerifyOrDie(rval == count, OT_EXIT_FAILURE); + aLength -= count; + } +} + +void SettingsFile::SwapPersist(int aFd) +{ + char swapFile[kMaxFilePathSize]; + char dataFile[kMaxFilePathSize]; + + GetSettingsFilePath(swapFile, true); + GetSettingsFilePath(dataFile, false); + + VerifyOrDie(0 == close(mSettingsFd), OT_EXIT_ERROR_ERRNO); + VerifyOrDie(0 == fsync(aFd), OT_EXIT_ERROR_ERRNO); + VerifyOrDie(0 == rename(swapFile, dataFile), OT_EXIT_ERROR_ERRNO); + + mSettingsFd = aFd; +} + +void SettingsFile::SwapDiscard(int aFd) +{ + char swapFileName[kMaxFilePathSize]; + + VerifyOrDie(0 == close(aFd), OT_EXIT_ERROR_ERRNO); + GetSettingsFilePath(swapFileName, true); + VerifyOrDie(0 == unlink(swapFileName), OT_EXIT_ERROR_ERRNO); +} + +} // namespace Posix +} // namespace ot diff --git a/src/posix/platform/settings_file.hpp b/src/posix/platform/settings_file.hpp new file mode 100644 index 00000000000..2cb50351a56 --- /dev/null +++ b/src/posix/platform/settings_file.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_POSIX_PLATFORM_SETTINGS_FILE_HPP_ +#define OT_POSIX_PLATFORM_SETTINGS_FILE_HPP_ + +#include "openthread-posix-config.h" +#include "platform-posix.h" + +namespace ot { +namespace Posix { + +class SettingsFile +{ +public: + SettingsFile(void) + : mSettingsFd(-1) + { + } + + /** + * Performs the initialization for the settings file. + * + * @param[in] aSettingsFileBaseName A pointer to the base name of the settings file. + * + * @retval OT_ERROR_NONE The given settings file was initialized successfully. + * @retval OT_ERROR_PARSE The key-value format could not be parsed (invalid format). + */ + otError Init(const char *aSettingsFileBaseName); + + /** + * Performs the de-initialization for the settings file. + */ + void Deinit(void); + + /** + * Gets a setting from the settings file. + * + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the specific item to get. + * @param[out] aValue A pointer to where the value of the setting should be written. + * @param[in,out] aValueLength A pointer to the length of the value. + * + * @retval OT_ERROR_NONE The given setting was found and fetched successfully. + * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. + */ + otError Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength); + + /** + * Sets a setting in the settings file. + * + * @param[in] aKey The key associated with the requested setting. + * @param[in] aValue A pointer to where the new value of the setting should be read from. + * @param[in] aValueLength The length of the data pointed to by aValue. + */ + void Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); + + /** + * Adds a setting to the settings file. + * + * @param[in] aKey The key associated with the requested setting. + * @param[in] aValue A pointer to where the new value of the setting should be read from. + * @param[in] aValueLength The length of the data pointed to by aValue. + */ + void Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); + + /** + * Removes a setting from the settings file. + * + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this aKey will be + * removed. + * + * @retval OT_ERROR_NONE The given key and index was found and removed successfully. + * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. + */ + otError Delete(uint16_t aKey, int aIndex); + + /** + * Deletes all settings from the setting file. + */ + void Wipe(void); + +private: + static const size_t kMaxFileDirectorySize = sizeof(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH); + static const size_t kSlashLength = 1; + static const size_t kMaxFileBaseNameSize = 64; + static const size_t kMaxFileExtensionLength = 5; ///< The length of `.Swap` or `.data`. + static const size_t kMaxFilePathSize = + kMaxFileDirectorySize + kSlashLength + kMaxFileBaseNameSize + kMaxFileExtensionLength; + + otError Delete(uint16_t aKey, int aIndex, int *aSwapFd); + void GetSettingsFilePath(char aFileName[kMaxFilePathSize], bool aSwap); + int SwapOpen(void); + void SwapWrite(int aFd, uint16_t aLength); + void SwapPersist(int aFd); + void SwapDiscard(int aFd); + + char mSettingFileBaseName[kMaxFileBaseNameSize]; + int mSettingsFd; +}; + +} // namespace Posix +} // namespace ot + +#endif // OT_POSIX_PLATFORM_SETTINGS_FILE_HPP_